Ultralytics YOLO27: Встать в лист ожидания.
Get Started
Уроксредний

Разметка и обучение модели сегментации экземпляров

Создай полигональную разметку, проверь её в коде, обучи yolo26n-seg.pt на COCO8-seg и диагностируй модель по разнице между метриками ограничивающих рамок и масок.

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

Результат

Проверь набор разметки полигонами на некорректные строки, обучи yolo26n-seg.pt на coco8-seg.yaml и реши, что исправить, по разнице между box и mask mAP50-95.

Ускоренное прохождение
Если ты уже освоился, вот краткая версия.
  1. Для каждого объекта создай одну строку: индекс класса, затем нормализованные пары вершин x y, не менее трёх вершин.

  2. Проверь разметку на нечётное число значений, координаты за пределами допустимого диапазона и подозрительно низкую заполненность полигона.

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

  4. Ориентируйся на mask mAP50-95 (metrics.seg.map), а разницу с box 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 с сегментацией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: положительный щелчок по объекту и отрицательный щелчок по области, которую нужно исключить, создают маску для последующей проверки.

Интеллектуальный режим Ultralytics Platform с выбранной SAM 2.1 Tiny: положительный и отрицательный щелчки по складскому роботу создают его маску

Проверяй каждый полигон, созданный автоматически

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

Обучение на COCO8-seg#

Мозаика 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, который рассматривается в следующем уроке. Оставь оба параметра без изменений, если у тебя нет подтверждённой необходимости.

Попробовать

Запусти check_seg_labels.py на всём COCO8-seg (train и val) и перечисли три строки с самым низким заполнением. Затем обучи yolo26n-seg.pt на coco8-seg.yaml в течение одной эпохи при imgsz=320, запиши box mAP50-95, mask mAP50-95 и класс с наибольшей разницей, а затем открой val_batch0_pred.jpg в папке валидации и найди объект, у которого маска заметно хуже рамки.

Критерии готовности
Урок считается завершённым, если выполнены все условия ниже.
  • Проверка разметки не обнаружила строк BAD, а для трёх строк с самым низким заполнением ты указал, оставить их или исправить.

  • Обучение завершилось, best.pt находится в runs/segment/, и ты записал оба значения 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 (box 0.564, mask 0.199). Box mAP хорошая, а mask значительно ниже, поэтому схема указывает на качество контуров: сначала проверь эти полигоны, затем попробуй модель большего размера или большее значение imgsz.

Что дальше

Далее мы превратим предсказанные маски в числовые показатели: площади, контуры, перекрытия и прозрачные вырезки.

Войди, чтобы сохранять прогресс