Ultralytics YOLO27: 加入候补名单。
Get Started
课程中级

标注并训练实例分割模型

编写多边形标签、用代码检查标签、在 COCO8-seg 上训练 yolo26n-seg.pt,并根据框指标与掩码指标之间的差距诊断模型。

实例分割会为每个对象生成独立轮廓,适用于面积、重叠或边界会影响决策的场景。多边形是大多数团队采购成本最高的标签,而且格式错误的行不会导致训练崩溃——它只会悄悄地让模型用错误的形状进行训练。本课会讲解正确的行格式、用代码检查标签集,并训练第一个模型,让你了解额外标注带来的收益:新增一项评估轮廓而非仅评估位置的指标。

学习成果

检查多边形标签集是否存在格式错误的行,在 coco8-seg.yaml 上训练 yolo26n-seg.pt,并根据框 mAP50-95 与掩码 mAP50-95 之间的差距决定需要修正什么。

快速通道
如果你已经熟悉相关内容,以下是简要版。
  1. 每个对象写一行:类别索引,后接归一化的 x y 顶点对,至少三个顶点。

  2. 运行标签检查,查找数值数量为奇数、坐标超出范围以及多边形填充率异常偏低的情况。

  3. yolo segment train data=coco8-seg.yaml model=yolo26n-seg.pt epochs=1 imgsz=320.

  4. 以掩码 mAP50-95(metrics.seg.map)作为门槛,并将它与框 mAP50-95(metrics.box.map)之间的差距用作诊断依据。

实践操作

多边形标签行#

Ultralytics YOLO 对城市人行横道进行实例分割,每辆汽车、公交车和每位行人都有单独的掩码

分割标签文件与检测标签类似,每个对象占一行,但不再使用框,而是以归一化顶点对列出对象的轮廓。以下是 COCO8-seg 中的一条真实标签行,经过了删减:

45 0.782016 0.986521 0.937078 0.874167 0.957297 0.782021 0.950562 0.739333 0.825844 0.561792 ...
位置检测行分割行
1类别索引(45 表示 bowl)类别索引
2–5x_center y_center width height前两个顶点,x1 y1 x2 y2
6+—更多顶点,数量取决于轮廓需要
单位归一化到 0–1归一化到 0–1(像素 x ÷ 图像宽度,像素 y ÷ 图像高度)

最重要的是三条规则:每个对象至少有三个顶点;坐标要除以图像尺寸;即使两个同类别对象相接,也要为每个实例单独写一行。分割数据集指南提供了完整规范。

多边形标签也能训练边界框和类别图

多边形是边界框标签的超集。同一个标签文件夹既能训练 yolo26n.pt(Ultralytics 根据多边形推导出每个边界框),也能训练 yolo26n-sem.pt(将多边形栅格化为类别图)——我们在 coco8-seg.yaml 上训练过这两种模型。标注完成后即使计划有变,投入也不会白费。

训练前检查标签#

数值数量为奇数、坐标以像素为单位,或轮廓描错对象,都不会让训练停止。这个检查器通过 Ultralytics 自带的加载器读取数据集,拒绝格式错误的行,并列出每个多边形的面积以及 Ultralytics 将据此生成的框:

from pathlib import Path

import numpy as np

from ultralytics.data.utils import check_det_dataset

data = check_det_dataset("coco8-seg.yaml")  # resolves paths, downloads if needed
for label in sorted(Path(data["path"], "labels", "train").glob("*.txt"))[:2]:
    for n, row in enumerate(label.read_text().splitlines(), 1):
        cls, *coords = row.split()
        if len(coords) % 2 or len(coords) < 6:
            print(f"{label.name}:{n} BAD: needs an even number of values and at least 3 vertices")
            continue
        xy = np.array(coords, dtype=float).reshape(-1, 2)
        if xy.min() < 0 or xy.max() > 1:
            print(f"{label.name}:{n} BAD: coordinates must be normalized to 0-1")
            continue
        x, y = xy[:, 0], xy[:, 1]
        area = 0.5 * abs(np.dot(x, np.roll(y, 1)) - np.dot(y, np.roll(x, 1)))  # shoelace formula
        w, h = xy.max(0) - xy.min(0)  # the box Ultralytics derives from the polygon
        name = data["names"][int(cls)]
        print(f"{label.stem} {name:9s} {len(xy):3d} vertices  polygon {area:.3f}  box {w * h:.3f}  fill {area / (w * h):.2f}")
000000000009 bowl       12 vertices  polygon 0.391  box 0.569  fill 0.69
000000000009 bowl       14 vertices  polygon 0.145  box 0.238  fill 0.61
000000000009 broccoli   50 vertices  polygon 0.161  box 0.252  fill 0.64
000000000009 bowl       55 vertices  polygon 0.079  box 0.531  fill 0.15
000000000009 orange     13 vertices  polygon 0.007  box 0.011  fill 0.64
000000000009 orange     13 vertices  polygon 0.005  box 0.009  fill 0.61
000000000009 orange     18 vertices  polygon 0.012  box 0.019  fill 0.61
000000000009 orange     12 vertices  polygon 0.010  box 0.022  fill 0.44
000000000025 giraffe    82 vertices  polygon 0.072  box 0.234  fill 0.31
000000000025 giraffe    19 vertices  polygon 0.010  box 0.027  fill 0.38

面积是图像面积的比例,因此第一个碗占照片的 39%。填充率是多边形面积除以框面积,是你需要重点查看的一列:

  • 大多数形状紧凑的对象(碗、橙子、西兰花)的填充率约为 0.6–0.7。
  • 长颈鹿的 0.31 是合理的:它细长的腿和脖子让框内大部分区域留空——这正是该对象需要掩码的原因。
  • 第三个碗的填充率为 0.15,审核人员应该打开对应标签行仔细查看。碗的大部分被食物挡住,只露出一圈窄窄的边缘,因此轮廓是在大框内的一条窄带。这里的情况是正确的;但在你的数据中,如此低的填充率往往也意味着轮廓可能描错了对象。
判断规则

训练前修复每一行 BAD 数据。然后按填充率排序,在标注工具中打开每个类别中填充率最低的几行:填充率低可能是因为物体细长或被遮挡(保留),也可能是描绘错误(修正)。

无需逐边描摹也能生成多边形#

手动描摹轮廓是数据标注中最耗时的部分。开始之前,先看看哪种快捷方式适用:

你已经有工具
带分割标注的 COCO JSON来自 ultralytics.data.converter 的 convert_coco(labels_dir, use_segments=True)
PNG 掩码(像素值 = 类别索引 + 1,0 = 背景)convert_segment_masks_to_yolo_seg(masks_dir, output_dir, classes)——同一类别中相接的对象会合并成一个多边形,因此需要在审核时拆分
只有框标签yolo_bbox2segment(im_dir)——使用每个框提示 SAM 生成多边形
还没有任何数据智能标注和批量标注——点击一次,再审核多边形

在 Ultralytics Platform 上,智能模式会运行 SAM:在对象上单击一次作为正向提示,再在要排除的区域上单击一次作为负向提示,生成掩码供你审核。

Ultralytics Platform 的智能模式已选择 SAM 2.1 Tiny:在仓库机器人上进行一次正向点击和一次负向点击,生成机器人的掩码

检查每一个机器生成的多边形

如果掩码蔓延到背景,模型也会学着这么做。根据边界框与掩码中的质量标准检查机器生成的多边形,然后重新运行上面的标签检查。

在 COCO8-seg 上训练#

COCO8-seg 训练拼图:斑马、长颈鹿、装有西兰花和橙子的饭盒以及一瓶花,每个对象都有自己的彩色掩码和类别索引

训练流程与检测相同,只需使用 -seg 模型和分割数据集。在笔记本电脑 CPU 上以 imgsz=320 训练一个周期只需几秒钟,足以验证整个流程:

from ultralytics import YOLO

model = YOLO("yolo26n-seg.pt")
model.train(data="coco8-seg.yaml", epochs=1, imgsz=320, device="cpu")
metrics = model.val()
print(f"box  mAP50-95: {metrics.box.map:.3f}")
print(f"mask mAP50-95: {metrics.seg.map:.3f}")
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95)     Mask(P          R      mAP50  mAP50-95)
                   all          4         17      0.909      0.449      0.661      0.482      0.685      0.409      0.557      0.377
                 horse          1          2          1      0.993      0.995      0.564      0.619      0.857      0.663      0.199
box  mAP50-95: 0.482
mask mAP50-95: 0.377

只有四张验证图像,因此这些数字没有统计意义;关键在于训练顺利完成,并在 runs/segment/ 下生成 weights/best.pt。对于真实数据,请遵循 Segment 任务指南,并在 GPU 上完成完整训练计划。

并排查看框指标和掩码指标#

验证表包含两个部分。Box 根据每个预测框与标签对应框的匹配程度进行评分;Mask 根据预测像素与标签多边形的匹配程度进行评分。两者使用相同的 mAP 计算方法,区别仅在于使用的 IoU 不同。

掩码 mAP50-95 几乎总是低于框 mAP50-95,因为像素 IoU 更严格:框偏差几个像素仍能保持较高重叠度,而掩码漏掉手臂或延伸到路面后,在 0.75–0.95 阈值下 IoU 会迅速下降。horse 这一行最能体现这种差异——框 mAP50-95 为 0.564,掩码为 0.199。模型找到了两匹马,框的位置也很好,但没有描出马腿和马尾。根据差距判断要修正什么:

  • 两者都低:问题在于没有找到对象,而不是轮廓描得不好。先为遗漏的情况补充数据。
  • 框指标好,掩码指标低很多:模型能找到对象,但轮廓效果不好。运行标签检查、尝试更大的模型,或使用更高的 imgsz 进行训练和验证。
  • 掩码指标在提升,框指标停滞:这没问题——对于分割产品,掩码 mAP50-95 才是发布门槛。
两个不会改变掩码分辨率的参数

训练时,overlap_mask=True 会将实例掩码合并到一张图像中,而 mask_ratio=4 会对训练掩码进行下采样。这两者都不会改变预测掩码的分辨率——那由下一课中的 retina_masks 决定。除非有测量结果支持调整,否则请保留默认值。

试试看

在整个 COCO8-seg(训练集和验证集)上运行 check_seg_labels.py,并列出填充率最低的三行。然后在 coco8-seg.yaml 上以 imgsz=320 训练 yolo26n-seg.pt 一个周期,记录框 mAP50-95、掩码 mAP50-95 和差距最大的类别,并在验证文件夹中打开 val_batch0_pred.jpg,找出一个掩码明显不如框准确的对象。

完成条件
满足以下所有条件,即表示你已完成本课程。
  • 标签检查报告中 BAD 行为零,并且你列出了填充率最低的三行,分别给出保留或修正的结论。

  • 训练已完成,runs/segment/ 下存在 best.pt,并且你记录了两个 mAP50-95 数值。

  • 你指出了框与掩码差距最大的类别,以及决策图建议的下一步操作。

显示解答

要检查所有数据划分,请将循环中的文件夹替换为 Path(data["path"], "labels").rglob("*.txt")。然后训练:

from ultralytics import YOLO

model = YOLO("yolo26n-seg.pt")
model.train(data="coco8-seg.yaml", epochs=1, imgsz=320, device="cpu")
metrics = model.val()
print(f"box  mAP50-95: {metrics.box.map:.3f}")   # 0.482 in our run
print(f"mask mAP50-95: {metrics.seg.map:.3f}")   # 0.377 in our run

0.1 的差距是预期之中的:掩码 IoU 会惩罚每个边界像素,而框 IoU 只关注四条边。在我们的运行中,差距最大的类别是 horse(框 0.564,掩码 0.199)。框 mAP 较好,而掩码 mAP 低很多,因此决策图指向轮廓质量:先检查这些多边形,再尝试更大的模型或更高的 imgsz。

接下来做什么

接下来,我们把预测掩码转化为数值:面积、轮廓、重叠区域和透明抠图。

登录以保存学习进度