ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

YOLOv8多任务实战:目标检测、语义分割与姿态估计全流程解析

YOLOv8多任务实战:目标检测、语义分割与姿态估计全流程解析 简介本资源面向计算机视觉方向的学习者与开发者聚焦YOLOv8在目标检测、语义分割与姿态估计三大任务上的完整实现适合具备一定深度学习基础、希望快速上手或复现多任务模型的中高级用户。压缩包共670个文件整体约1.94MB以358个Markdown文档、159个Python脚本、81个YAML配置及少量YML、TXT、Rust、Jupyter Notebook、Shell、C等文件构成涵盖模型配置、训练推理代码、环境依赖与说明文档结构清晰便于按任务模块检索。目前已有4880人学习下载热度较高。读者可从中获取目标检测、分割与姿态估计的统一代码框架、参数配置模板、推理示例及多平台Dockerfile部署方案便于对照实验、迁移到自有数据集或作为项目基线快速搭建可运行的YOLOv8多任务实验环境。1. 从一次产线误检说起这套 YOLOv8 资源到底能干什么上周帮朋友看一条小包装产线相机拍到的药板里混进了一片颜色接近的铝箔碎屑传统阈值分割直接把它当成了正常区域漏检。换成 YOLOv8 的实例分割模型后碎屑的轮廓被单独抠出来误检率从 8% 掉到 0.6%。这件事让我重新翻出手里这套 yolov8-目标检测、语义分割、姿态估计的资源包——它不是三个独立脚本的拼盘而是一套共享 backbone、共享数据加载逻辑、共享训练入口的多任务工程。目标检测负责框出「有没有、在哪」语义分割负责回答「每个像素属于哪一类」姿态估计则把人体或物体的关键点坐标拉出来。适合谁做工业质检、安防行为分析、运动姿态评估的工程师以及正在找毕业设计落点的学生。你不需要从零搭环境但需要知道每个任务的数据格式和输出张量长什么样否则调参就是玄学。2. 环境搭建与模型选型CPU 版 Ubuntu 20.04 怎么跑通第一帧2.1 为什么先锁 CPU 版而不是直接上 GPU很多教程一上来就让你装 CUDA结果显卡驱动版本和 PyTorch 对不上卡在torch.cuda.is_available()返回 False 那一步。我一般建议先在 CPU 上把推理链路跑通确认模型文件、预处理、后处理都没问题再切 GPU。Ubuntu 20.04 的 Python 3.8 是官方支持最稳的组合YOLOv8 依赖的ultralytics包对 3.8 兼容性最好。CPU 版推理一张 640×640 的图大约 200400ms足够验证逻辑训练才需要 GPU。# 创建独立环境避免和系统 Python 打架 conda create -n yolov8_cpu python3.8 -y conda activate yolov8_cpu # 安装 PyTorch CPU 版注意 index-url 指向 cpu 源 pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu # 安装 ultralytics它会自动拉取 opencv、numpy 等依赖 pip install ultralytics # 验证安装打印版本和可用设备 python -c import ultralytics; print(ultralytics.__version__); import torch; print(torch.cuda.is_available())逻辑说明conda create隔离环境是血泪经验系统 Python 里装过 ROS 或 OpenCV 的机器直接 pip 装 ultralytics 大概率冲突。--index-url指定 CPU 源否则 pip 会默认去拉 CUDA 版 torch下载 2GB 且装完发现用不了。最后一行输出False是正常的说明当前是 CPU 模式。参数说明python3.8不要随意升到 3.11部分旧版 numpy 和 opencv 在 3.11 上编译轮子缺失。ultralytics不指定版本会装最新如果复现旧项目建议pip install ultralytics8.0.x锁版本。2.2 模型选型n/s/m/l/x 到底选哪个YOLOv8 官方提供五种尺度参数量和推理速度差异很大。我整理了一张实际测过的表测试环境是 i7-10700 CPU、640×640 输入模型参数量CPU 单帧耗时mAP50COCO适用场景yolov8n3.2M180ms37.3边缘设备、实时性优先yolov8s11.2M320ms44.9普通服务器 CPU 推理yolov8m25.9M780ms50.2GPU 入门卡、精度均衡yolov8l43.7M1.4s52.9离线批量处理yolov8x68.2M2.3s53.9精度极限、不计速度选型原则如果目标是 RK3588 或 Hi3516CV610 这类板端部署直接选 n 或 s别犹豫。如果是 GTX1660Ti 跑训练m 是性价比拐点。l 和 x 只在刷榜或离线分析时用。分割和姿态估计的模型命名多一个-seg和-pose后缀比如yolov8n-seg.pt、yolov8n-pose.pt下载时注意区分。from ultralytics import YOLO # 加载预训练权重首次运行会自动下载到当前目录 model YOLO(yolov8n.pt) # 推理一张本地图片saveTrue 会把结果画框后存到 runs/detect/ 下 results model(test.jpg, saveTrue, conf0.25, iou0.45) # 打印检测到的类别和坐标 for box in results[0].boxes: print(box.cls, box.xyxy, box.conf)逻辑说明YOLO(yolov8n.pt)如果本地没有权重会从官方地址拉取国内网络可能慢可以手动下载后把路径写进去。conf0.25是置信度阈值低于这个值的框被丢弃iou0.45是 NMS 的 IoU 阈值重叠度高于它的框会被合并。这两个参数是误检和漏检的主要调节旋钮。参数说明saveTrue会生成可视化图片调试时有用批量推理时关掉省 IO。results[0].boxes里的xyxy是左上角和右下角坐标格式是 tensor转 numpy 用.cpu().numpy()。3. 三大任务的数据格式与训练入口别把分割标签丢给检测3.1 目标检测YOLO 格式的 txt 和 data.yaml检测任务的数据集目录结构是固定的dataset/ ├── images/ │ ├── train/ (jpg/png) │ └── val/ ├── labels/ │ ├── train/ (txt) │ └── val/ └── data.yaml每个 txt 文件对应一张图每行格式class_id x_center y_center width height全部归一化到 01。常见翻车点是用 labelme 标完直接导出 COCO JSON忘了转 YOLO txt。转换脚本我一般这么写import json import os # 把 labelme 的 JSON 转成 YOLO txt def labelme_to_yolo(json_path, output_dir, class_map): with open(json_path, r, encodingutf-8) as f: data json.load(f) img_w data[imageWidth] img_h data[imageHeight] lines [] for shape in data[shapes]: label shape[label] if label not in class_map: continue cls_id class_map[label] points shape[points] xs [p[0] for p in points] ys [p[1] for p in points] # 计算外接矩形并归一化 x_center (min(xs) max(xs)) / 2.0 / img_w y_center (min(ys) max(ys)) / 2.0 / img_h w (max(xs) - min(xs)) / img_w h (max(ys) - min(ys)) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) base os.path.splitext(os.path.basename(json_path))[0] with open(os.path.join(output_dir, base .txt), w) as f: f.write(\n.join(lines))逻辑说明labelme 的矩形标注shape_type是rectangle点只有两个对角点但脚本里用 min/max 兼容多边形。class_map是类别名到 id 的映射必须和data.yaml里的names顺序一致否则训练时类别全乱。参数说明归一化用 6 位小数足够YOLO 读取时按 float 解析。如果图片有旋转或 EXIF 方向先用 PIL 的ImageOps.exif_transpose矫正否则坐标对不上。data.yaml内容path: /home/user/dataset train: images/train val: images/val nc: 3 names: [scratch, dent, stain]训练命令yolo detect train datadata.yaml modelyolov8n.pt epochs100 imgsz640 batch163.2 语义分割多边形标注和 mask 的取舍语义分割和实例分割在 YOLOv8 里共用-seg模型但数据格式不同。语义分割要求每个像素有类别通常用 labelme 的多边形标注导出或者用 mask PNG。YOLOv8 的-seg训练实际走的是实例分割路线每个目标独立成 mask但推理时可以通过后处理合并成语义图。数据格式labelme 多边形标注转 YOLO seg txt每行class_id x1 y1 x2 y2 ...坐标归一化。转换脚本和检测类似只是把外接矩形换成所有点# 多边形点直接展开不做外接矩形 points_norm [] for x, y in points: points_norm.append(f{x/img_w:.6f}) points_norm.append(f{y/img_h:.6f}) lines.append(f{cls_id} .join(points_norm))逻辑说明YOLOv8 seg 的 txt 每行点数不固定但同一张图里所有多边形的点数最好一致否则 dataloader 拼 batch 时会报错。常见做法是重采样到固定点数或者用collate_fn自定义。参数说明imgsz对分割影响比检测大640 是底线小目标多建议 1024。batch要相应调小否则显存爆。训练命令yolo segment train dataseg_data.yaml modelyolov8n-seg.pt epochs100 imgsz640 batch83.3 姿态估计COCO 关键点格式和多人场景姿态估计的数据格式是 COCO keypoints 的变体每个实例有 17 个关键点COCO 定义每个点x y vv0 表示未标注1 表示标注但不可见2 表示可见。YOLOv8 pose 的 txt 格式class_id x_center y_center w h px1 py1 v1 px2 py2 v2 ...。多人姿态估计的坑在于一张图里多个人每个人的框和关键点要对应。用 labelme 标关键点很痛苦常见做法是用 COCO 格式的 JSON 直接转或者用专门的 keypoint 标注工具。转换时注意num_keypoints字段少于 17 个点的实例在训练时会被忽略或补零。# COCO JSON 转 YOLOv8 pose txt 的核心逻辑 for ann in coco[annotations]: if ann[num_keypoints] 5: # 点太少直接跳过 continue bbox ann[bbox] # xywh kps ann[keypoints] # [x,y,v, x,y,v, ...] x_center (bbox[0] bbox[2]/2) / img_w y_center (bbox[1] bbox[3]/2) / img_h w bbox[2] / img_w h bbox[3] / img_h kp_str .join([f{kps[i]/img_w:.6f} {kps[i1]/img_h:.6f} {int(kps[i2])} for i in range(0, len(kps), 3)]) lines.append(f0 {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f} {kp_str})逻辑说明num_keypoints 5的实例通常是遮挡严重或标注不全强行训练会拉低精度。kps里 v0 的点坐标可能是 0归一化后变成 0训练时 loss 会忽略 v0 的点但坐标 0 会参与计算所以最好把 v0 的坐标也置 0。参数说明flip_idx在data.yaml里要配否则水平翻转增强时左右关键点会错位。COCO 的 17 点 flip_idx 是[0,2,1,4,3,6,5,8,7,10,9,12,11,14,13,16,15]。训练命令yolo pose train datapose_data.yaml modelyolov8n-pose.pt epochs100 imgsz640 batch164. 训练参数怎么调从损失曲线看模型是不是在学4.1 损失函数曲线图的读法YOLOv8 训练完会在runs/detect/train/下生成results.csv里面记录了每个 epoch 的 box_loss、cls_loss、dfl_loss。画图用 pandas matplotlibimport pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/detect/train/results.csv) df.columns df.columns.str.strip() # 列名可能有空格 fig, axes plt.subplots(1, 3, figsize(15, 4)) axes[0].plot(df[epoch], df[train/box_loss], labeltrain) axes[0].plot(df[epoch], df[val/box_loss], labelval) axes[0].set_title(box_loss) axes[0].legend() axes[1].plot(df[epoch], df[train/cls_loss], labeltrain) axes[1].plot(df[epoch], df[val/cls_loss], labelval) axes[1].set_title(cls_loss) axes[1].legend() axes[2].plot(df[epoch], df[metrics/mAP50(B)], labelmAP50) axes[2].plot(df[epoch], df[metrics/mAP50-95(B)], labelmAP50-95) axes[2].set_title(mAP) axes[2].legend() plt.savefig(loss_curve.png, dpi150)逻辑说明train loss 持续下降但 val loss 在某个 epoch 后抬头是过拟合的典型信号需要加 dropout 或减 epochs。box_loss 震荡大通常是学习率太高或 batch 太小。mAP50 曲线平台期超过 20 个 epoch 不涨说明模型容量到顶了换大模型或加数据。参数说明results.csv的列名带空格strip()是必须的。dpi150保证论文里能看清。4.2 学习率和 batch 的联动YOLOv8 默认用 SGD初始 lr 0.01warmup 3 个 epoch。如果 batch 从 16 降到 8lr 要相应降到 0.005 左右否则梯度更新太猛loss 直接飞。常见做法是线性缩放lr 0.01 * batch / 16。# 自定义学习率和 batch yolo detect train datadata.yaml modelyolov8n.pt epochs100 imgsz640 batch8 lr00.005 lrf0.01 warmup_epochs3参数说明lr0是初始学习率lrf是最终学习率因子最终 lr lr0 * lrfwarmup_epochs是预热轮数。如果 loss 在前 5 个 epoch 就 NaN先把 lr0 砍半。4.3 数据增强的开关和边界YOLOv8 默认开 mosaic、mixup、copy-paste。小数据集1000 张建议全开大数据集可以关掉 mixup 省时间。但 mosaic 对分割任务有时会破坏 mask 的连续性如果分割边缘一直学不好试试mosaic0.5降低概率。yolo segment train dataseg_data.yaml modelyolov8n-seg.pt epochs100 mosaic0.5 mixup0.1 copy_paste0.1参数说明mosaic0.5表示 50% 的概率做马赛克增强不是强度。copy_paste对实例分割有效对语义分割意义不大。5. 避坑与排查那些让我重训三次的坑5.1 现象训练 loss 正常但 mAP 一直是 0原因data.yaml里的names顺序和 txt 里的 class_id 对不上或者 val 路径写错导致验证集为空。YOLOv8 不会报错只会静默跳过验证。解决训练前用yolo detect train ... valFalse先跑一个 epoch确认 dataloader 能读到图。然后手动检查labels/val/下有没有 txt以及 txt 里的 class_id 是否在nc范围内。5.2 现象CPU 推理一张图要 5 秒以上原因模型加载时默认用了halfTrue或输入尺寸被自动放大到 1280。CPU 不支持 FP16会回退到 FP32 但多一次转换开销。解决推理时显式指定imgsz640和halfFalseresults model(test.jpg, imgsz640, halfFalse, devicecpu)5.3 现象分割 mask 边缘锯齿严重原因imgsz太小mask 上采样时插值方式默认是最近邻。或者训练时overlap_maskFalse实例 mask 被裁剪。解决推理时加retina_masksTrue训练时overlap_maskTrue。如果还不行把imgsz提到 1024。5.4 现象姿态估计关键点全挤在框中心原因关键点坐标没有归一化或者归一化时用了错误的 img_w/img_h比如用了 resize 后的尺寸而不是原图尺寸。解决检查转换脚本里img_w和img_h是否来自原图 JSON 的imageWidth/imageHeight。YOLOv8 内部会自己 resize你只需要提供原图归一化坐标。5.5 现象多卡训练报 NCCL 错误原因batch设置不是卡数的整数倍或者device参数写成了0,1但实际只有一张卡。解决batch设为卡数的倍数device0,1确认nvidia-smi能看到两张卡。单卡调试时直接device0。6. 进阶把三个任务的输出拼成一张业务图实际项目里很少只跑一个任务。我最近做的一个健身动作评估需要同时输出人体框、关键点、以及器械的像素级分割。做法是加载三个模型共享同一张输入图后处理时把结果画在同一张画布上。from ultralytics import YOLO import cv2 import numpy as np det_model YOLO(yolov8n.pt) seg_model YOLO(yolov8n-seg.pt) pose_model YOLO(yolov8n-pose.pt) img cv2.imread(gym.jpg) canvas img.copy() # 检测画框 det_res det_model(img, verboseFalse)[0] for box in det_res.boxes: x1, y1, x2, y2 map(int, box.xyxy[0].tolist()) cv2.rectangle(canvas, (x1, y1), (x2, y2), (0, 255, 0), 2) # 分割叠加半透明 mask seg_res seg_model(img, verboseFalse)[0] if seg_res.masks is not None: masks seg_res.masks.data.cpu().numpy() for m in masks: m cv2.resize(m, (img.shape[1], img.shape[0])) color np.random.randint(0, 255, 3).tolist() canvas[m 0.5] canvas[m 0.5] * 0.5 np.array(color) * 0.5 # 姿态画关键点和骨架 pose_res pose_model(img, verboseFalse)[0] if pose_res.keypoints is not None: kps pose_res.keypoints.data.cpu().numpy() for person in kps: for i, (x, y, conf) in enumerate(person): if conf 0.5: cv2.circle(canvas, (int(x), int(y)), 3, (0, 0, 255), -1) cv2.imwrite(fused_result.jpg, canvas)逻辑说明三个模型独立推理互不干扰。分割 mask 的 resize 必须用原图尺寸因为seg_res.masks.data是模型输入尺寸下的 mask。姿态关键点的conf是每个点的置信度低于 0.5 的点不画避免噪声。参数说明verboseFalse关掉每帧的日志输出批量处理时能省不少终端刷屏。np.random.randint给每个 mask 随机颜色实际项目里应该按类别固定颜色。验证方法拿一张有两个人、一个器械的图跑完看输出。如果分割 mask 盖住了人但没盖住器械说明seg_model的类别里没有器械需要在自己的数据上微调。如果关键点画在了框外检查pose_res.keypoints的坐标系是否和原图一致——YOLOv8 返回的是原图坐标不需要额外缩放。从那以后我每次做多任务融合都强制先单独跑一遍每个模型确认各自的输出坐标系和尺寸再写融合代码。希望帮到你。本文还有配套的精品资源点击获取
返回列表