Ultralytics YOLO27: ウェイトリストに登録する。
Get Started
レッスン中級

インスタンスセグメンテーションのラベル付けとトレーニング

ポリゴンラベルを作成し、コードで確認して、COCO8-segでyolo26n-seg.ptをトレーニングし、ボックス指標とマスク指標の差からモデルを診断します。

インスタンスセグメンテーションでは、各オブジェクトに個別の輪郭を割り当てます。面積、重なり、境界が判断基準となる場合に必要な機能です。ポリゴンは多くのチームが購入するラベルの中でも特にコストが高く、形式が不正な行があってもトレーニングは停止せず、誤った形状で気付かないまま学習が進みます。このレッスンでは、行の形式を正しく整え、コードでラベルセットを確認し、最初のモデルをトレーニングします。これにより、追加のラベル付けで得られるもの、つまり位置だけでなく輪郭も評価する2つ目の指標を確認できます。

成果

ポリゴンラベルセットを確認して形式が不正な行を検出し、yolo26n-seg.ptをcoco8-seg.yamlでトレーニングして、ボックスとマスクのmAP50-95の差から修正すべき点を判断します。

短期集中コース
すでに使い方をご存じの方は、こちらをご覧ください。
  1. オブジェクトごとに1行を作成します。クラスインデックスに続けて、正規化されたx yの頂点ペアを3つ以上記述します。

  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でインスタンスセグメンテーションした例。各車、バス、歩行者に個別のマスクが付いています

セグメンテーションのラベルファイルは、検出ラベルと同様にオブジェクトごとに1行で構成されます。ただし、ボックスの代わりに、オブジェクトの輪郭を正規化された頂点ペアで記述します。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最初の2つの頂点、x1 y1 x2 y2
6+—輪郭に必要な数の頂点を追加
単位0~1に正規化0~1に正規化(ピクセルx ÷ 画像幅、ピクセルy ÷ 画像高さ)

重要なルールは3つです。オブジェクトごとに頂点を3つ以上指定すること、画像サイズで割った座標を使うこと、同じクラスのオブジェクトが接していてもインスタンスごとに1行を作成することです。完全な仕様については、セグメンテーションデータセットガイドをご覧ください。

ポリゴンラベルはボックスとクラスマップのトレーニングにも使える

ポリゴンはボックスラベルを包含する形式です。同じラベルフォルダーを使って 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は妥当な値です。長い脚と首のため、ボックスの大部分が空いています。これこそ、このオブジェクトにマスクが必要な理由です。
  • 3つ目のボウルの0.15は、レビュアーが確認すべき行です。食べ物の周囲にボウルの縁が細く見えるだけなので、輪郭は大きなボックス内の細い帯状になります。この例では正しい結果ですが、独自のデータでは、塗りつぶし率がこれほど低い場合、誤ったオブジェクトの輪郭をなぞっていることもよくあります。
判断基準

トレーニング前に、BADと判定された行をすべて修正してください。次にfillで並べ替え、各クラスの中でfillが最も低い数パーセントの行をアノテーションツールで開いてください。fillが低い場合は、細いオブジェクトや遮蔽されたオブジェクト(維持)か、誤ったトレース(修正)のどちらかです。

すべての輪郭を手作業でなぞらずにポリゴンを取得する#

輪郭を手作業でなぞる作業は、データラベリングの中でも特にコストがかかります。作業を始める前に、どの方法が適用できるか確認しましょう。

すでにあるものツール
セグメンテーションを含むCOCO JSONconvert_coco(labels_dir, use_segments=True)(ultralytics.data.converterから)
PNGマスク(ピクセル値 = クラスインデックス + 1、0 = 背景)convert_segment_masks_to_yolo_seg(masks_dir, output_dir, classes) — 同一クラスの接触したオブジェクトは1つのポリゴンになるため、レビューで分割してください
ボックスラベルのみyolo_bbox2segment(im_dir) — 各ボックスをプロンプトとしてSAMに渡し、ポリゴンを生成します
まだ何もないスマートアノテーションと一括ラベリング — 1回クリックして、ポリゴンを確認します

Ultralytics Platformでは、スマートモードでSAMを実行します。対象をクリックする正のクリックと、除外する対象をクリックする負のクリックでマスクを生成し、その後レビューします。

SAM 2.1 Tinyを選択したUltralytics Platformのスマートモード。倉庫ロボットに正のクリックと負のクリックを行い、マスクを生成しています

機械生成されたポリゴンをすべて確認する

背景にはみ出したマスクを使うと、モデルも同様のマスクを生成するようトレーニングされます。機械生成ポリゴンをボックスとマスクの品質基準に照らして確認し、上記のラベルチェックを再実行してください。

COCO8-segでトレーニングする#

COCO8-segのトレーニングモザイク。シマウマ、キリン、ブロッコリーとオレンジの弁当箱、花瓶の花が描かれ、各オブジェクトに個別の色付きマスクとクラスインデックスが付いています

トレーニングは、-segモデルとセグメンテーションデータセットを使う検出ワークフローです。ノートPCのCPUでimgsz=320の1エポックを数秒で完了でき、パイプライン全体の動作を確認できます。

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

検証画像が4枚だけでは、これらの数値はノイズにすぎません。重要なのは、実行が完了し、runs/segment/の下にweights/best.ptが書き出されることです。実データでは、セグメントタスクガイドに従い、GPUでスケジュール全体をトレーニングしてください。

ボックスとマスクの指標を並べて確認する#

検証テーブルには2つのブロックがあります。Boxは予測ボックスをラベルから推定されるボックスと比較して評価し、Maskは予測ピクセルをラベル付けされたポリゴンと比較して評価します。どちらも同じmAPの計算を使い、異なるのはIoUだけです。

マスクmAP50-95は、ほぼ常にボックスmAP50-95を下回ります。ピクセル単位のIoUのほうが厳しいためです。ボックスが数ピクセルずれても十分に重なりますが、マスクが腕を捉え損ねたり道路にはみ出したりすると、しきい値0.75~0.95ではIoUが急激に低下します。horseの行は、その極端な例です。ボックスmAP50-95は0.564、マスクは0.199です。モデルは両方の馬を検出し、ボックスで正しく囲みましたが、脚と尾の輪郭は捉えられませんでした。この差を使って修正内容を決めましょう。

  • 両方とも低い場合:問題は輪郭ではなくオブジェクトの検出です。まず、検出できなかった条件のデータを追加します。
  • ボックスは良好で、マスクが大きく下回る場合:モデルはオブジェクトを検出していますが、輪郭の精度が低い状態です。ラベルを確認し、より大きなモデルを試すか、より高いimgszでトレーニングと検証を行います。
  • ボックスの改善が停滞しても、マスクが改善している場合:問題ありません。セグメンテーション製品では、リリース判定にマスクmAP50-95を使います。
マスク解像度を変えない2つのパラメーター

overlap_mask=True はトレーニング時にインスタンスマスクを1枚の画像に統合し、mask_ratio=4 はトレーニングマスクをダウンサンプリングします。どちらも予測マスクの解像度は変更しません。解像度を決めるのは次のレッスンで扱う retina_masks です。変更する理由を測定で確認できていない場合は、どちらもデフォルトのままにしてください。

試してみる

COCO8-seg全体(trainとval)でcheck_seg_labels.pyを実行し、塗りつぶし率が最も低い3行を一覧にします。次に、coco8-seg.yamlでyolo26n-seg.ptをimgsz=320の1エポック分トレーニングし、ボックスmAP50-95、マスクmAP50-95、差が最大のクラスを記録します。検証フォルダーでval_batch0_pred.jpgを開き、ボックスに比べてマスクが明らかに劣るオブジェクトを見つけます。

完了条件
以下のすべてを満たすと、レッスンは完了です。
  • ラベルチェックで不正な行が0件と報告され、塗りつぶし率が最も低い3行について、それぞれ保持するか修正するかの判断を示しました。

  • トレーニングが完了し、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では4辺だけが対象です。今回の実行で差が最大だったのはhorse(ボックス0.564、マスク0.199)です。ボックスmAPは良好ですが、マスクは大きく下回っているため、チャートは輪郭の品質を示しています。まずポリゴンを確認し、その後、より大きなモデルか、より高いimgszを試してください。

次のステップ

次に、予測マスクを面積、輪郭、重なり、透明な切り抜きなどの数値に変換します。