
简介YOLOv8完整项目资源覆盖目标检测、语义分割与姿态估计三大核心视觉任务适合计算机视觉初学者、算法工程师及科研人员快速理解YOLOv8架构并落地实践。共670个文件压缩包1.94MB包含358个md文档原理讲解与使用说明、159个py脚本训练、推理、评估代码、81个yaml配置文件模型结构与数据配置、6个ipynb笔记本交互式演示以及Dockerfile系列与C源码支持多种部署环境目录清晰。已有4880人学习浏览通过md文档可掌握网络设计与参数含义py脚本可直接修改运行yaml配置降低调参门槛ipynb便于分步实验C与Dockerfile材料则为工程化部署提供参考。整体非常适合以项目驱动方式学习YOLOv8从理论到代码再到部署一条龙掌握。1. 一个模型三种任务YOLOv8 为什么值得同时学检测、分割和姿态估计做视觉项目的人第一次接触 YOLOv8 时基本都会冒出同一个问题一个权重文件怎么既能框出目标又能输出像素掩码还能把人的骨架点顶出来YOLOv8 是 Ultralytics 维护的统一检测框架共享一套特征提取主干通过 detect、segment、pose 三种输出头分别做目标检测、实例分割和多人姿态估计。对想快速出 demo 的工程师这意味着你可以用同一条数据管线维护三类任务对做算法改进的人它把 head 差异暴露在同一个工程里方便对比实验。这篇笔记按“结构差异 → 最小训练命令 → 自制数据集 → 踩坑 → 验证导出”的顺序写适合手里有检测需求、又想顺带解决分割和姿态估计的人照着复现。2. YOLOv8 网络结构图和三种任务头先分清你要跑的是哪条分支2.1 共享的 Backbone 和 Neck分叉的 HeadYOLOv8 不是三个独立模型而是一套骨架换头。Backbone 是 CSPDarknet 加 C2f 模块负责从输入图像里提取多层特征Neck 是 PAN-FPN负责把深层语义特征和浅层空间细节融合再往后才是按任务区分的地方。目标检测头是 anchor-free 的解耦头一条支路输出类别另一条支路输出边框回归参数。它没有传统 YOLO 里的 objectness 分支因为回归和分类已经拆开不需要额外判断“这里有没有目标”。这也是很多人改 YOLOv8 时优先动 head 的原因检测头结构简单改起来不容易破坏主干。分割头在检测头的基础上多了一条 mask 分支整体思路类似 YOLACT网络先输出一组 mask prototype再对每个检测到的目标输出 mask 系数最后把两者加权组合出实例掩码。理解这一点很重要因为你改了分割 loss真正影响的其实是 mask 系数和 prototype 的质量而不是直接输出一个语义分割图。姿态估计头的输出更直接每个检测目标额外带 K 个关键点的坐标和可见性标志。YOLOv8 默认的 COCO 姿态模型是 17 个关键点一次推理就能把画面里多个人物的骨架全部输出所以它常被直接当多人姿态估计用。把三种任务的 YOLOv8 网络结构图放在一起看你会发现 90% 的层都一样真正决定任务的是 head 后面那几层卷积和 loss 计算方式。2.2 COCO 与 YOLO 的标签格式差异检测、分割、姿态各要什么训练前必须把数据集格式弄清楚这一步挡掉的人最多。YOLOv8 的 label 文件是每张图一个 txt每一行代表一个目标。三类任务的行格式完全不一样。任务一行内容示例目标检测class_id x_center y_center width height0 0.5 0.5 0.2 0.3实例分割class_id x1 y1 x2 y2 ...归一化多边形0 0.41 0.32 0.46 0.41 ...姿态估计class_id bbox 四个值 每个关键点的 x y visible0 0.5 0.5 0.2 0.3 0.51 0.42 2 ...所有坐标都要除以图像宽高归一化到 0~1 之间。检测和片段标注的坐标原点都是左上角姿态估计里的 visible 一般用 0 表示关键点没有标注1 表示被遮挡2 表示可见。如果你从 COCO 数据集转出来还要注意类别编号和关键点顺序。COCO 检测是 80 类COCO 姿态的 17 个关键点有固定顺序比如 0 是鼻子、1 是左眼、2 是右眼。转换脚本一旦把顺序写错后续训练出来的骨架就会左右乱跳。这个坑在自制关键点数据集时尤其常见。2.3 说好的语义分割YOLOv8 给的是实例分割标题里写“语义分割”但 YOLOv8 官方 segment 任务实际做的是实例分割。它输出的每个 mask 都带实例编号同类别的两个物体会有两个独立 mask而不是把它们合并成一个语义区域。如果你的需求是像素级语义分割比如把遥感图像里的建筑区域整块涂出来不区分这一栋那一栋你有两条路。第一条是训练时只用一个类别并把同类别的实例 mask 在 loss 之外的后处理里按类别叠加第二条是调整 head把 mask 输出按类别聚合。常见做法是前者工程改动小结果也能满足“语义分割图”的展示需求。但要注意YOLOv8-seg 的评估指标是按实例计算的 mask/mAP不是按类别的 IoU。如果你拿它去跟 DeepLab 那种语义分割模型对比指标口径不同不能直接说谁更强。这个边界在项目汇报里一定要提前说清楚不然很容易在验收时被当成性能问题。3. 用 ultralytics 跑通三种训练从 Ubuntu 20.04 环境到最小命令3.1 安装CPU 和 GPU 两条路径在 Ubuntu 20.04 上搭 YOLOv8 环境最省事的办法是直接用 ultralytics 包。GPU 机器直接装pip install ultralytics这个命令会把 torch、torchvision 和相关依赖一起带上来。首次运行训练命令时它还会自动下载对应权重比如 yolov8n.pt。很多人问“yolov8 哪里下载”其实不需要手动去找把 model 参数写成模型名字第一次训练会自动从官方 release 拉取。如果你的网络环境访问 GitHub 不稳定才需要提前把权重文件放到当前目录。CPU 环境要稍微小心一点默认 pip 安装 torch 可能会拉到带 CUDA 的版本白白多下载几个 GB。Ubuntu 20.04 CPU 版本我一般这样装pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu pip install ultralytics先装 CPU 版 torch再装 ultralytics后者检测到 torch 已存在就不会重复拉大包。跑 CPU 训练时ultralytics 会识别不到 GPU自动用 CPU 执行。3.2 三份最小训练命令环境就绪后先用 ultralytics 自带的迷你数据集跑通三条任务。目标检测用 coco128分割用 coco128-seg姿态估计用 coco8-pose都是很小的数据集几分钟内能跑一个 epoch。yolo detect train datacoco128.yaml modelyolov8n.pt epochs30 imgsz640 batch16 device0yolo segment train datacoco128-seg.yaml modelyolov8n-seg.pt epochs30 imgsz640 batch16 device0yolo pose train datacoco8-pose.yaml modelyolov8n-pose.pt epochs30 imgsz640 batch16 device0这三条命令的区别只在任务关键字和权重后缀。detect 对应目标检测segment 对应实例分割pose 对应姿态估计。同一个 ultralytics 环境里可以交叉跑三个任务不需要分别建虚拟环境。设备参数 device 很常用device0 表示第一块 GPUdevicecpu 强制用 CPUdevice0,1 表示双卡。显存小的机器可以把 batch 降到 4 或 8或者把 imgsz 降到 416。首次训练我建议跑 30 epoch 以内先确认数据和 loss 曲线正常不要一上来就 300 epoch。3.3 必调参数含义用 YOLOv8 训练自己的数据集时训练参数的含义一定要看懂否则只是被动抄命令。下面是几个影响最大的参数。参数含义常见值imgsz输入图片短边缩放到多少640 起步小目标用 1280batch每个 step 的图片数量GPU 6G 用 4~816G 用 16epochs训练轮数30 看趋势100 左右做收敛patience验证指标不再提升多少轮就早停10~20device使用 CPU 还是 GPU0 / cpu / 0,1workers数据加载线程数4~8CPU 机器别开太大cache是否把数据提前缓存到内存True 或 False显存小建议 Falselr0初始学习率默认 0.01小数据集可降到 0.001flip_lr水平翻转增强检测默认 0.5关键点要配 flip_idxworkers这个参数比较容易被忽略。数据预处理跟不上 GPU 时训练进度条会一直停在某个 epoch看 top 也没明显异常十有八九是 workers 开太低或磁盘 IO 卡住。本地机械硬盘上我一般用 workers4SSD 上开 8 没问题。4. 处理数据集用于 YOLOv8 训练labelme JSON 转 txt 的完整脚本4.1 检测和分割的转换同一段 JSON两种输出自制数据集时labelme 是最常用的标注工具它每张图生成一个 JSONshapes 里记录矩形、多边形或点。LabelImg、CVAT 也能标但导出格式各不相同。这里介绍最常见的流程labelme 标注后转成 YOLO 需要的 txt。import json CLASS_MAP {person: 0, car: 1, dog: 2} def convert_detect_segment(json_path, out_path, taskdetect): with open(json_path, encodingutf-8) as f: data json.load(f) image_w data[imageWidth] image_h data[imageHeight] lines [] for shape in data[shapes]: points shape[points] # labelme 里矩形只有两个点转成四个点方便统一处理 if shape[shape_type] rectangle: x1, y1 points[0] x2, y2 points[1] points [[x1, y1], [x2, y1], [x2, y2], [x1, y2]] class_id CLASS_MAP.get(shape[label], 0) if task segment: coords .join( f{p[0] / image_w:.6f} {p[1] / image_h:.6f} for p in points ) lines.append(f{class_id} {coords}) else: xs [p[0] for p in points] ys [p[1] for p in points] x_min, x_max min(xs), max(xs) y_min, y_max min(ys), max(ys) x_center (x_min x_max) / 2.0 / image_w y_center (y_min y_max) / 2.0 / image_h width (x_max - x_min) / image_w height (y_max - y_min) / image_h lines.append( f{class_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f} ) with open(out_path, w, encodingutf-8) as f: f.write(\n.join(lines))这段逻辑的核心是把 labelme 的 JSON 点坐标除以图像宽高。检测任务取最小外接矩形分割任务保留完整多边形。注意 labelme 的点是绝对像素坐标YOLO 全部要归一化这两个数一旦没除训练时 loss 会非常大而且基本不收敛。4.2 姿态估计关键点怎么标和怎么转姿态估计的数据集制作比检测麻烦因为每个人物有 17 个关键点还要区分这是第几个人。labelme 里的常见做法是每个关键点单独画一个 point 形状label 写关键点名称group_id 写同一个人的编号。COCO_KPT_ORDER { nose: 0, left_eye: 1, right_eye: 2, left_ear: 3, right_ear: 4, left_shoulder: 5, right_shoulder: 6, left_elbow: 7, right_elbow: 8, left_wrist: 9, right_wrist: 10, left_hip: 11, right_hip: 12, left_knee: 13, right_knee: 14, left_ankle: 15, right_ankle: 16 } def convert_pose(json_path, out_path): with open(json_path, encodingutf-8) as f: data json.load(f) image_w data[imageWidth] image_h data[imageHeight] persons {} for shape in data[shapes]: if shape[shape_type] ! point: continue label shape[label] if label person: continue pid shape.get(group_id, 0) kpt_index COCO_KPT_ORDER[label] x, y shape[points][0] # 标了就算可见先默认 2被遮挡的关键点可以手动改成 1 persons.setdefault(pid, {})[kpt_index] (x / image_w, y / image_h, 2) lines [] for pid, kpts in persons.items(): xs [v[0] for v in kpts.values()] ys [v[1] for v in kpts.values()] x_center sum(xs) / len(xs) y_center sum(ys) / len(ys) w max(xs) - min(xs) h max(ys) - min(ys) kpt_str [] for i in range(17): if i in kpts: x, y, vis kpts[i] else: x, y, vis 0.0, 0.0, 0 kpt_str.append(f{x:.6f} {y:.6f} {vis}) lines.append( f0 {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f} .join(kpt_str) ) with open(out_path, w, encodingutf-8) as f: f.write(\n.join(lines))这段代码把 group_id 相同的 point 聚成一个人再按 COCO 17 顺序补全关键点。缺失的关键点坐标写 0visible 写 0。这里有个血泪经验一定要保证 group_id 在每张图里没写错不然两个人物的关键点会混在一个框里训练出来的骨架会非常诡异。4.3 目录划分和 YAML 配置转换完 txt 后目录结构要按 ultralytics 约定的方式组织。我的习惯是 images 和 labels 分开放train 和 val 统一在 yaml 里写绝对路径。dataset/ images/ train/ val/ labels/ train/ val/对应的数据集 yaml 是这样train: /home/user/dataset/images/train val: /home/user/dataset/images/val nc: 3 names: 0: person 1: car 2: dog训练时把 data 参数指向这个 yaml 就行。分割和检测共用同一个 yaml姿态估计则要多加两行kpt_shape: [17, 3] flip_idx: [0, 2, 1, 4, 3, 6, 5, 8, 7, 10, 9, 12, 11, 14, 13, 16, 15]kpt_shape 里 17 是关键点数量3 是每个关键点的 x、y、visible 三个值。flip_idx 是水平翻转后左右关键点对应的互换关系这个后面会细说。注意labelme 的 JSON 是每张图一个文件转换脚本要把 json_path 和 out_path 按文件名一一配对。我习惯用 glob 遍历 images 目录再按同名文件到 labels 目录下生成 txt避免漏转换。5. YOLOv8 训练避坑显存不足、loss 不收敛、mask 边缘锯齿的 5 个常见问题5.1 GTX 1660 Ti 训练爆显存不是先换卡是先压 imgsz 和 batch现象是训练刚开始几秒就报 CUDA out of memory。GTX 1660 Ti 只有 6G 显存直接抄别人的 batch16 imgsz640 很容易爆。原因是显存占用跟 batch 和 imgsz 几乎线性相关还叠加了 mosaic 增强和混合精度计算的临时缓冲。解决方法是先降 batch 再降 imgsz。我在这类卡上常用的组合是yolo detect train datamy_data.yaml modelyolov8n.pt imgsz640 batch4 workers4 device0如果还想保留 640 分辨率batch 就压到 4。如果 batch 4 还是不够再把 imgsz 降到 416。还有一个容易被忽略的选项是rectTrue矩形训练会按宽高比做 padding减少无效计算6G 卡上通常能多撑一点 batch。5.2 loss 变 NaN 或 metrics 一直为 0现象是训练能跑但 loss 曲线突然掉到 NaN或者 val 的 mAP 在整个训练过程中都是 0。这类问题大部分出在标签上不是模型结构。最常见的原因是 txt 里的 class_id 大于 yaml 里的 nc或者某一行坐标不是 0~1 之间的数。另一种情况是分割数据集的 polygon 点数太少有的 shape 只有两个点转出来的多边形退化成了线。解决方法是训练前先做一轮标签检查。写个小脚本遍历 labels 目录对每一行判断 class_id 是否在合法范围、浮点数是否都在 0~1 之间、点数是否足够。我排查时还会打印一下这类异常行对应的图片肉眼确认是不是 labelme 里画错了。NaN 的另一个来源是学习率过大小数据集上把 lr0 从默认 0.01 降到 0.001多半能缓解。5.3 分割掩码边缘锯齿、小目标漏检现象是训练完分割模型在大图或遥感图像上效果很差小目标要么没检出来要么掩码边缘像锯齿。原因是 YOLOv8 默认 imgsz640高分图像缩到 640 之后小目标往往只剩几个像素。掩码边缘锯齿则是 mask prototype 的低分辨率输出被上采样导致的原图越大越明显。解决方法是针对高分辨率场景把 imgsz 提上去。遥感图像或者工业质检图我一般用 imgsz1280同时把 batch 降一半。如果显存不允许就先把原图切成小块训练推理时再做 tiles 拼接。分割模型的 mask 后处理也可以在导出后加一步高斯滤波或形态学操作但这个只能修饰边缘没办法补漏检。5.4 姿态估计关键点左右反了现象是训练完的姿态模型人物的左眼和右眼频繁互换左右手骨架经常交叉。很多人第一反应是数据有问题其实大概率是数据增强没有配好。YOLOv8 默认开启水平翻转增强 flip_lr0.5。翻转后图片左右镜像关键点顺序必须跟着互换。比如原图里的左眼在翻转后应该变成右眼如果训练时没有指定 flip_idx网络就会被“左右矛盾”的样本带偏。解决方法是把 COCO 17 关键点的 flip_idx 写进数据集 yaml。上面第 4.3 节给的那行 flip_idx 就是官方 COCO 顺序0 是鼻子这种对称点其他位置把左右配对互换。如果你用的是自定义关键点也要手动列出对应的交换关系。5.5 数据集路径写错或训练反复下载文件现象是训练命令没问题但每次都提示 dataset not found或者在启动阶段反复下载已经存在的文件。常见原因是 yaml 里写相对路径换个终端工作目录就找不到数据了。另一个常见问题是 labels 目录名不叫 labels。ultralytics 默认会在 images 同级目录下找 labels如果你把标注文件放在其它名字的目录里它会认为整张图都没有标注跑完 mAP 全是 0。解决方法是把 yaml 里的 train 和 val 都写成绝对路径启动训练前手工检查一下 labels 目录里是否已经有和图片同名的 txt。CPU 机器上如果 workers 开太大DataLoader 也可能卡在启动阶段把 workers 降到 0 试试能跑就说明是 IO 或线程问题。这几个问题都不是模型本身的问题但几乎每个新手都会踩一遍。6. 导出前先做这两件事val 指标和 loss 曲线怎么帮你选最终模型训练目录里默认会生成 last.pt 和 best.pt很多人直接拿 last.pt 去部署这是最容易踩的坑。last.pt 只是最后一轮保存的权重best.pt 才是训练过程中验证集表现最好的节点。导出前一定要先用 val 命令确认 best.pt 的效果。yolo detect val modelruns/detect/train/weights/best.pt datamy_data.yamldetect 任务会输出 mAP50、mAP50-95、precision 和 recallsegment 任务会多出 mask/mAP 相关指标pose 任务则有关键点的 AP。不要只看 mAP50多看一眼 mAP50-95它会反映模型在严格 IoU 下的定位能力对板端部署更有参考价值。训练过程中产生的指标都记在 results.csv 里这个文件比训练日志更完整。画 loss 曲线时可以直接读它import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/detect/train/results.csv) epoch df[epoch] plt.plot(epoch, df[train/box_loss], labeltrain_box_loss) plt.plot(epoch, df[val/box_loss], labelval_box_loss) plt.xlabel(epoch) plt.ylabel(loss) plt.legend() plt.show()不同任务的 loss 列名不同分割是 train/seg_loss、val/seg_loss姿态是 train/pose_loss、val/pose_loss。画完曲线后看一个关键点val loss 如果先降后升说明从某个 epoch 开始过拟合了这时候不要加训练轮数应该回到前面某轮的权重重新评估。确认好 best.pt 之后再考虑导出。板端部署到 RK3588、海思这类 NPU 平台常见链路是先导出 ONNX再用平台工具链转成自己的模型格式。导出命令很简单yolo export modelruns/detect/train/weights/best.pt formatonnx imgsz640 opset12导出时提前固定 imgsz并且不要开动态 batch。RKNN 这类工具链对动态 shape 支持很差固定成 batch1、640x640 最省事。导出后我习惯用 onnxruntime 在电脑上先推理一张 val 图确认输出 tensor 的形状和推理结果跟 PyTorch 一致再送去板端。这一步看似多花十分钟实际能帮你避开很多黑匣子式的问题。在我自己的项目流程里训练、val、导出三件事已经固定成一个脚本训练完先看 results.csv再换几张验证图看效果。模型不是训练完就结束在导出前把指标和可视化都确认一遍能省下大量板端排错的时间。希望帮到你。本文还有配套的精品资源点击获取