简介:这份压缩包围绕 Ultralytics YOLOv8 展开,提供实例分割与目标检测从模型训练到部署的完整工程,面向计算机视觉研究者、深度学习开发者,以及需要把检测分割能力落地到安防监控、无人机视觉、自动驾驶等场景的技术人员,帮助他们省去环境搭建与流程串联的重复劳动。包内共788个文件,整体约26.63MB,其中347个 Markdown 文档承担官方指南与模块说明,173个 Python 脚本覆盖训练、验证、推理与导出,75个 YAML 配置对应数据集与模型结构;另含2个 pt 预训练权重、1个 onnx 导出模型、Docker 系列构建文件和少量 C++、Rust 源码,兼顾快速上手与二次开发,目前已有528人学习下载。读者拿到的是预训练模型、数据集示例、训练脚本与部署配置的组合:既能直接跑通检测与分割推理,也能基于自有数据修改 YAML、调整超参完成自定义训练,并借助 Dockerfile 在 CPU、ARM64、Jetson 等环境完成部署;配套文档还可作为排查训练异常、理解目录组织的参考。
1. 拆开 ultralytics.rar:YOLOv8 实例分割与目标检测先看任务边界
拿到一个名为 ultralytics.rar 的资源包,很多人第一反应是找训练命令,结果在人工智能项目里最先翻车的往往不是模型,而是任务边界:目标检测输出的是框,实例分割输出的是每个目标的掩码,两者共用 backbone,却对标签、后处理和部署接口要求不同。这个包围绕 YOLOv8 把检测、分割、训练和部署放在同一套 ultralytics 工程里,适合想从检测过渡到实例分割的开发者,也适合要把模型接进业务侧、需要看清导出和阈值边界的人。它不是点开就能上线的黑匣子,而是一份需要按数据、训练、推理、验证顺序拆的工程素材。先分清你要框还是要掩码,再决定读哪一段代码。
2. 任务头与标签:检测框和分割掩码在 YOLOv8 里怎么对齐
YOLOv8 的 detect 和 segment 走的是同一套 ultralytics 调用习惯,模型加载、训练入口、推理接口看起来很像,真正拉开差距的是标签格式和后处理。目标检测的标签是一行五个值:类别、归一化中心点 x、归一化中心点 y、归一化宽、归一化高;实例分割的标签是一行多边形点串:类别后面跟若干组归一化 x、y。拿检测标签去训分割,常见表现不是立刻报一个很清楚的错,而是 seg_loss 不降、mask 分支学不到东西,最后 mAP 看着有数值但掩码完全不能用。
2.1 检测头与分割头的差别不在“多一个掩码”
检测任务关心的是目标在哪里、是什么,输出框和类别就够了。实例分割还要回答“这个目标的像素边界在哪”,所以分割头会额外输出掩码系数和原型掩码,推理阶段再组合成每个实例的掩码。这个差别带来的直接后果是:分割标注成本更高,标签检查更麻烦,导出到 ONNX 或 TensorRT 后还要确认掩码输出有没有被正确解码。业务只要计数、定位、报警,检测通常更稳;要做面积测量、轮廓提取、遮挡关系分析、像素级抠图,才值得上实例分割。
选型时不要只看验证集 mAP。检测框准不代表掩码边缘准,掩码边缘准也不代表小目标不丢。我的习惯是先用检测权重跑一遍可视化,确认类别和框的位置没有系统性偏移,再把同一批图转成多边形标签做分割。转换脚本要重点检查多边形点顺序、顶点数量、坐标是否越界、有没有自相交。YOLO 的类别索引从 0 开始,data.yaml 里的 names 顺序必须和标签里的类别号严格一致,差一位就会把人和车全叫反。
提示:同一份数据不要一边放检测 txt 一边放分割 txt,目录里混着两种格式时,训练脚本可能读到一半才暴露问题。
2.2 data.yaml 与目录结构的最小闭环
ultralytics 工程对目录不强制要求某种花哨结构,但 images 和 labels 要能按 split 对上,图片名和标签名除扩展名外要一致。常见做法是 dataset 下分 images/train、images/val、labels/train、labels/val,data.yaml 放在 dataset 根目录。路径能写相对路径就写相对路径,换机器时少改一处。下面这份 data.yaml 是最小闭环,names 的顺序就是类别号,不要随手调换。
# dataset/data.yaml path: ./dataset train: images/train val: images/val names: 0: person 1: car 2: cone这段配置里,path 是数据集根目录,train 和 val 是相对 path 的子路径,names 的键从 0 开始。训练命令里用model=yolov8n-seg.pt时,任务类型由权重名里的 seg 决定;如果你用 Python 加载模型,也同样按分割权重走。不要在这个文件里写图片绝对路径,除非你确定部署机器路径完全一致。验证集不要从训练集里随机抽相邻帧,视频数据尤其要注意,同一段连续帧跨集会让验证分数虚高,业务侧一上就漏。
2.3 用脚本把标签问题提前暴露
标签检查不要等训练报错才做。下面这个脚本只做静态检查:图片和标签是否配对、类别号是否越界、一行是检测框还是多边形、多边形点数是否为偶数、坐标是否落在 0 到 1 之间。它不修复数据,只把问题打印出来,方便你回头改标注或转换脚本。
from pathlib import Path import yaml root = Path("./dataset") data = yaml.safe_load((root / "data.yaml").read_text(encoding="utf-8")) names = data["names"] num_classes = len(names) for split in ["train", "val"]: img_dir = root / "images" / split lbl_dir = root / "labels" / split if not img_dir.exists() or not lbl_dir.exists(): print(f"[跳过] {split} 目录不存在") continue for lbl in lbl_dir.glob("*.txt"): # 图片扩展名按你的数据改,这里以 jpg 为例 img = img_dir / (lbl.stem + ".jpg") if not img.exists(): print(f"[缺图] {lbl}") continue for line_no, line in enumerate(lbl.read_text(encoding="utf-8").splitlines(), 1): parts = line.split() if not parts: continue cls = int(parts[0]) vals = [float(x) for x in parts[1:]] if cls < 0 or cls >= num_classes: print(f"[类别越界] {lbl}:{line_no} cls={cls}") continue if len(vals) == 4: # 检测框格式:cx cy w h,四个值都应在 0~1 if any(v < 0 or v > 1 for v in vals): print(f"[框越界] {lbl}:{line_no} {vals}") elif len(vals) >= 6 and len(vals) % 2 == 0: # 分割多边形格式:x1 y1 x2 y2 ...,成对出现 if any(v < 0 or v > 1 for v in vals): print(f"[点越界] {lbl}:{line_no}") else: print(f"[格式可疑] {lbl}:{line_no} 值数量={len(vals)}")脚本里的 root 改成你的数据集路径,图片扩展名按实际情况调整。检测框四个值的检查只能发现明显越界,宽高为 0 或负数还要另外加判断。多边形检查只看点数和范围,自相交、点顺序反了、面积过小这些几何问题,需要可视化抽查。这个脚本跑完没有输出,不代表标签一定对,但至少能把“训练半天发现类别号从 1 开始”这种低级问题挡在训练之前。
3. 训练链路:从 data.yaml 到分割权重的参数怎么设
训练实例分割模型时,最容易被忽略的是“先证明这条链路能学会”。一上来就全量数据、大 epochs、大 imgsz,显存先炸,日志也看不出是标签问题还是参数问题。更稳的顺序是:小样本过拟合、确认 loss 和 mask 指标能动、再放大数据规模和分辨率。YOLOv8 的训练入口在 ultralytics 里已经封装得比较完整,命令行和 Python 都能走,选一条你团队能维护的就行,不要两套混着改。
3.1 训练入口与关键参数
下面这段 Python 训练代码是一个可抄的基线。模型用分割权重,data 指向上一节的 data.yaml,project 和 name 用来固定输出目录,exist_ok 避免重复跑时新建一堆带时间戳的目录。参数不是越大越好,batch 和 imgsz 先按显存给一个能跑完的值,再逐步加。
from ultralytics import YOLO # 分割权重入口,任务类型由权重名决定 model = YOLO("yolov8n-seg.pt") model.train( data="dataset/data.yaml", epochs=100, imgsz=640, batch=8, workers=4, device=0, patience=30, project="runs/seg", name="baseline", exist_ok=True, pretrained=True, optimizer="auto", lr0=0.01, lrf=0.01, warmup_epochs=3.0, cache=False, amp=True, )这段代码里,epochs 是最大轮数,patience 是验证指标连续多少轮不涨就早停;imgsz 是训练分辨率,小目标多时不要直接拉到 1280,先看显存和速度;batch 先给 8,OOM 就降到 4 或 2;workers 在 Windows 或容器里经常是卡死来源,可以先设 0 或 2;device 用 0 表示第一块 GPU,CPU 训练就写 cpu;lr0 是初始学习率,lrf 是最终学习率相对初始值的比例;warmup_epochs 让前期学习率慢慢起来;cache 开 True 会吃内存,数据不大时可以加速;amp 混合精度在多数显卡上能省显存,但出现 NaN 时先关掉排查。
| 参数 | 作用 | 常见起法 | 注意点 |
|---|---|---|---|
| imgsz | 训练分辨率 | 640 | 小目标多再试 960/1280,先看显存 |
| batch | 批大小 | 8 或 16 | OOM 先降它,不要先怪模型 |
| workers | 数据加载进程 | 2 到 8 | 容器里卡死就设 0 |
| patience | 早停轮数 | 30 到 50 | 太小会误杀后期才涨的模型 |
| lr0 | 初始学习率 | 0.01 左右 | 微调时降到 0.001 量级 |
| freeze | 冻结层数 | 0 或 10 | 冻太多分割头学不动 |
注意:参数表里的数值是起步参考,不是固定答案。换数据集、换显卡、换预训练权重,第一轮都应该先小跑验证。
3.2 从日志判断过拟合与欠拟合
训练日志里重点看 box_loss、seg_loss、cls_loss 和验证侧的 mask mAP。box_loss 降、seg_loss 不降,优先怀疑掩码标签格式;训练 loss 一直降、验证 mAP 不涨,优先怀疑过拟合或数据划分太相似;训练和验证都不动,优先怀疑学习率、标签路径、类别数。不要只看一个总 loss,分割任务的掩码分支和分类分支是分开动的,混在一起看容易误判。
小样本过拟合是训练前的必做检查。挑二三十张覆盖主要类别的图,复制成一个小数据集,训练几十轮,正常情况应该能很快把训练集拟合到很高。如果小样本都学不会,不要继续加数据,先回去查标签、查图片和标签是否同名、查 data.yaml 的 names 顺序。这个检查很土,但能省掉大量“全量训练跑了一晚上,结果标签全错”的血泪经验。
3.3 增量训练与冻结:别把预训练权重当后悔药
增量训练不是万能后悔药。加载已有权重继续训练时,数据分布、类别表、输入分辨率都要尽量保持一致,类别数变了要确认模型头是否重建。冻结前几层可以让小数据集训练更稳,但 freeze 太深会把分割分支也冻住,表现是 loss 降得很慢、mask 边缘几乎不变。下面这段代码从已有 best.pt 继续训练,学习率调低,冻结前 10 层。
from ultralytics import YOLO # 从已有分割权重继续训练 model = YOLO("runs/seg/baseline/weights/best.pt") model.train( data="dataset/data.yaml", epochs=50, imgsz=640, batch=8, freeze=10, # 冻结前 10 层,小数据集可试,太大分割头学不动 lr0=0.001, # 微调学习率低于从头训练 project="runs/seg", name="finetune", exist_ok=True, )freeze 的值按模型结构调,不确定就从 0 开始,先确认不冻结能正常收敛,再试 5 或 10。lr0 降到 0.001 是常见微调起法,数据量很小还可以再低。增量训练后不要只看训练 loss,要用固定验证集和固定阈值跑一遍可视化,确认掩码边缘没有整体偏移。预训练权重能帮你省时间,但不能替你检查数据。
4. 推理与导出:把掩码结果接进工程时先处理哪三件事
训练出 best.pt 只是中间状态,真正接进工程要过推理结果解析、导出格式选择、预处理对齐这三关。实例分割的坑在于 masks 和 boxes 是两个张量,过滤时如果只过滤 boxes 不过滤 masks,后面保存的掩码就会和框错位。导出后还要确认 letterbox 的填充、归一化顺序、NMS 位置和输出张量顺序,任何一处不一致,表现都是“PyTorch 里好好的,部署后框偏了或掩码糊了”。
4.1 推理结果解析:boxes、masks、conf 三者的顺序
下面这段推理代码把 masks、boxes、conf、cls 一起取出来,按置信度过滤时同步过滤四个数组,再把掩码 resize 回原图尺寸。注意 masks.data 的尺寸是模型输入尺寸,不是原图尺寸,保存前必须还原。
from ultralytics import YOLO import cv2 import numpy as np model = YOLO("runs/seg/baseline/weights/best.pt") results = model.predict( "demo.jpg", imgsz=640, conf=0.35, iou=0.5, device=0, max_det=100, ) r = results[0] img = cv2.imread("demo.jpg") if r.masks is not None: masks = r.masks.data.cpu().numpy() # N,H,W,模型输入尺寸 boxes = r.boxes.xyxy.cpu().numpy() # N,4 confs = r.boxes.conf.cpu().numpy() # N clss = r.boxes.cls.cpu().numpy().astype(int) # 关键:四个数组按同一掩码过滤,避免框和掩码错位 keep = confs >= 0.35 masks, boxes, confs, clss = masks[keep], boxes[keep], confs[keep], clss[keep] for i, m in enumerate(masks): # 掩码还原到原图尺寸,最近邻插值避免产生中间灰度 m = cv2.resize(m, (img.shape[1], img.shape[0]), interpolation=cv2.INTER_NEAREST) cv2.imwrite(f"mask_{i}_{clss[i]}.png", (m > 0.5).astype(np.uint8) * 255)conf 是置信度阈值,业务漏检多就降,误报多就升;iou 是 NMS 的 IoU 阈值,重叠目标多时不要压得太狠;max_det 限制每张图最多保留多少个实例,密集场景要调大。masks 的顺序和 boxes 一致,这个前提在过滤后必须保持。保存掩码用 0.5 二值化只是常见做法,边缘要求高时可以把概率图一起存下来,后处理再定阈值。
4.2 导出格式选择:ONNX、TensorRT、OpenVINO 的边界
导出格式没有绝对优劣,要看部署端有什么。ONNX 通用性好,适合先做跨平台验证;TensorRT 在 NVIDIA 显卡上延迟低,但导出和运行环境要匹配;OpenVINO 适合 Intel CPU 或集成显卡场景。导出命令本身不复杂,难的是导出后的预处理和后处理对齐。
yolo export model=runs/seg/baseline/weights/best.pt format=onnx imgsz=640 simplify=True dynamic=Falseformat 指定导出格式,imgsz 要和训练分辨率一致,simplify 会尝试简化计算图,dynamic=False 表示固定输入尺寸,部署侧少处理动态 shape。实例分割导出后要确认输出里包含掩码相关张量,不要只检查框输出。下面这张表是选型时的边界参考,不写具体性能数字,只写适用场景和注意点。
| 格式 | 适用场景 | 注意点 |
|---|---|---|
| ONNX | 跨平台验证、Python/C++ 推理 | 确认输入 layout、归一化、NMS 是否内置 |
| TensorRT | NVIDIA GPU 线上服务 | 版本匹配、固定 shape、掩码解码要对齐 |
| OpenVINO | Intel CPU/集显边缘设备 | 预处理 letterbox 和输出顺序要复核 |
| TorchScript | 继续在 PyTorch 生态内跑 | 自定义后处理不一定能一起导出 |
提示:导出前先用同一张图在 PyTorch 里存一份框和掩码结果,导出后逐项对比,不要等部署联调才发现偏移。
4.3 部署侧预处理与后处理对齐
部署侧最常见的翻车是预处理不一致。训练时 ultralytics 内部会做 letterbox,把图等比缩放后填充到正方形,推理时如果你自己 resize 成 640x640 不填充,框就会整体偏移。下面这个 letterbox 函数只做等比缩放和填充,返回值里带缩放比例和左上角偏移,方便把框和掩码映射回原图。
import cv2 def letterbox(img, new_shape=640, color=(114, 114, 114)): h, w = img.shape[:2] r = min(new_shape / h, new_shape / w) nh, nw = int(round(h * r)), int(round(w * r)) resized = cv2.resize(img, (nw, nh), interpolation=cv2.INTER_LINEAR) top = (new_shape - nh) // 2 bottom = new_shape - nh - top left = (new_shape - nw) // 2 right = new_shape - nw - left padded = cv2.copyMakeBorder( resized, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color ) return padded, r, (left, top)new_shape 要和导出时的 imgsz 一致,color 用训练时的填充色,常见是 114。返回的 r 和 (left, top) 用来把模型输出的框坐标还原:先减偏移,再除以 r。掩码还原同理,先裁掉填充区域,再 resize 回原图。BGR 和 RGB 顺序也要确认,OpenCV 读图默认 BGR,有些部署端按 RGB 传,颜色通道反了不一定会报错,但类别分数会莫名其妙地低。
5. 避坑与排查:实例分割项目里最容易翻车的五个现场
实例分割的排查顺序和检测不一样。检测可以先看框,分割必须同时看框、类别、掩码边缘和实例数量。下面五个现场是按实际项目里出现频率排的,每条按现象、原因、解决写,能对上就先处理,不要同时改训练参数和标签格式,否则出了问题不知道是谁的锅。
5.1 数据与标签的两个现场
现象一:训练能跑,seg_loss 几乎不降,mask 可视化是一团糊。原因通常是标签格式不对:拿检测 txt 当分割标签用,一行只有五个值,模型读不到多边形;或者多边形点没有归一化,坐标全是像素值。解决方法是先用第 2 章的检查脚本看每行值的数量,检测框是 4 个值,多边形是偶数个且至少 6 个值,所有值应在 0 到 1 之间。确认格式后再看类别号,YOLO 从 0 开始,names 顺序和标签类别号必须一一对应。
现象二:验证集 mAP 看着不低,实际推理类别全错或置信度很低。原因常见于 data.yaml 的 names 顺序和标注工具导出顺序不一致,或者转换脚本把类别号加了 1。解决方法是抽十张图,把标签画回原图,逐类核对;再用固定 conf 跑推理,打印 cls 和 names 的映射。类别错位不会让训练立刻崩,但会让模型学到一个错误的分类边界,越训越偏。
5.2 训练与显存的两个现场
现象三:训练刚开始就 CUDA out of memory,或者跑几轮后突然 OOM。原因不一定是模型大,batch、imgsz、workers、cache 都会吃资源。解决顺序是先把 batch 减半,再把 imgsz 从 640 降到 512 试一轮,workers 在容器里先设 0,cache 先关。amp 能省显存,但出现 NaN 时先关掉。不要一上来就换更大显存的卡,先把参数压到能稳定跑完,再逐步加。
现象四:验证集指标很好,业务侧漏检严重。原因常见于数据划分同源,训练集和验证集来自同一段视频或同一批相似背景,模型记住了背景;也可能是推理 conf 设得太高,把低置信度的真实目标过滤掉了。解决方法是按场景、日期或设备划分数据集,不要把相邻帧拆到两边;再拿业务侧真实图片做难例集,单独调 conf 和 iou。验证集分数是参考,不是上线保证。
5.3 推理与导出的一个现场
现象五:导出 ONNX 或 TensorRT 后,框整体偏移,掩码和框对不上。原因通常是预处理不一致:训练用 letterbox,部署直接 resize;或者归一化顺序反了;或者导出时关了 NMS,部署侧又没补 NMS。解决方法是固定 imgsz,按第 4 章的 letterbox 逻辑对齐预处理,导出前后用同一张图对比框坐标和掩码 IoU。动态 shape 先关掉,等一致性通过再开。导出不是终点,一致性验证才是。
6. 验证与进阶:用一致性检查和难例回灌把迭代稳住
模型训练完,我一般先做一轮一致性检查,不急着看业务指标。同一批图分别用 PyTorch 权重和导出模型跑,比较框数量、类别、置信度分布和掩码 IoU。框数量差太多,先查 NMS 和 conf;类别不一致,查预处理和类别映射;掩码边缘差,查 letterbox 和掩码还原。下面这张检查表可以直接照着填。
| 检查项 | 通过标准 | 失败先看 |
|---|---|---|
| 框数量 | 两端口径接近 | conf、iou、max_det |
| 类别序列 | 逐图一致 | names 映射、通道顺序 |
| 掩码 IoU | 同实例高于约定阈值 | letterbox、mask resize |
| 小目标召回 | 难例集不掉点 | imgsz、训练样本分布 |
一致性通过后,再做难例回灌:把业务侧漏检、误检、掩码断裂的图挑出来,补标后加进训练集,下一轮用低学习率微调。不要一次把难例全灌进去,先按场景分组,小步验证。我吃过亏,曾经把验证集调得很漂亮就上线,结果换一个光照条件漏了一半;从那以后我每次导出前都强制走一遍一致性检查,再留一小批从不参与训练的业务图做回归。希望帮到你。
本文还有配套的精品资源,点击获取