Разметка и обучение модели сегментации экземпляров
Создай полигональную разметку, проверь её в коде, обучи yolo26n-seg.pt на COCO8-seg и диагностируй модель по разнице между метриками ограничивающих рамок и масок.
Сегментация экземпляров выделяет каждый объект отдельным контуром — это нужно, когда решение зависит от площади, перекрытия или границы. Полигоны — самый дорогой тип разметки, за который платит большинство команд, а некорректная строка не прерывает обучение — она незаметно приводит к обучению на неправильной форме. В этом уроке ты научишься правильно оформлять строки, проверять набор разметки в коде и обучать первую модель, чтобы увидеть, что даёт дополнительная разметка: вторую метрику, которая оценивает контуры, а не только расположение объектов.
Практические занятия
Строка разметки полигона#

В файле разметки сегментации есть одна строка на объект, как и в разметке детекции, но вместо рамки в ней указаны нормализованные пары вершин контура объекта. Вот обрезанная реальная строка из 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–5 | x_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 с сегментацией | convert_coco(labels_dir, use_segments=True) из ultralytics.data.converter |
| PNG-маски (значение пикселя = индекс класса + 1, 0 = фон) | convert_segment_masks_to_yolo_seg(masks_dir, output_dir, classes) — соприкасающиеся объекты одного класса объединяются в один полигон, поэтому раздели их при проверке |
| Только рамки | yolo_bbox2segment(im_dir) — передаёт SAM каждую рамку для создания полигона |
| Пока ничего | Интеллектуальная разметка и пакетная маркировка — щёлкни один раз и проверь полигон |
На платформе Ultralytics в интеллектуальном режиме работает SAM: положительный щелчок по объекту и отрицательный щелчок по области, которую нужно исключить, создают маску для последующей проверки.

Если маска захватывает фон, модель обучится делать то же самое. Проверь автоматические полигоны в соответствии с критериями качества из раздела Рамки и маски, затем повторно запусти проверку меток выше.
Обучение на COCO8-seg#

Обучение повторяет процесс для детекции, но использует модель -seg и набор данных для сегментации. Одна эпоха с 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Четыре изображения для валидации дают слишком мало данных, и эти числа — шум. Важно, что запуск завершился и сохранил weights/best.pt в runs/segment/. Для реальных данных следуй руководству по задаче Segment и обучай модель полный цикл на GPU.
Сравни метрики рамок и масок#
Таблица валидации состоит из двух блоков. Box оценивает каждую предсказанную рамку относительно рамки, полученной из разметки; Mask сравнивает предсказанные пиксели с размеченным полигоном. Для обоих блоков используется один и тот же механизм mAP; меняется только IoU.
Mask mAP50-95 почти всегда ниже, чем Box mAP50-95, потому что IoU для пикселей строже: рамка, смещённая на несколько пикселей, всё ещё хорошо перекрывается с эталоном, а маска, не захватившая руку или захватившая часть дороги, быстро теряет IoU при порогах 0.75–0.95. Строка horse показывает крайний случай: box mAP50-95 — 0.564, mask — 0.199. Модель нашла обеих лошадей и хорошо обвела их рамками, но не смогла очертить ноги и хвосты. Используй разницу, чтобы решить, что исправить:
- Обе метрики низкие: проблема в обнаружении объектов, а не в их контурах. Сначала добавь данные для пропущенных условий.
- Хорошая метрика Box, значительно более низкая Mask: модель находит объекты, но плохо очерчивает их. Проверь разметку, попробуй модель большего размера или обучай и проверяй её при большем
imgsz. - Mask улучшается, а Box не меняется: это нормально — для продукта сегментации порогом выпуска служит mask mAP50-95.
overlap_mask=True объединяет маски объектов в одно изображение во время обучения, а mask_ratio=4 уменьшает разрешение обучающих масок. Ни один параметр не меняет разрешение предсказанных масок — за него отвечает retina_masks, который рассматривается в следующем уроке. Оставь оба параметра без изменений, если у тебя нет подтверждённой необходимости.
Показать решение
Чтобы проверить все выборки, замени папку в цикле на 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 (box 0.564, mask 0.199). Box mAP хорошая, а mask значительно ниже, поэтому схема указывает на качество контуров: сначала проверь эти полигоны, затем попробуй модель большего размера или большее значение imgsz.