Ultralytics YOLO27: ウェイトリストに登録する。
Get Started
高性能YOLOデータセットの構築·定義する·レッスン 2/10
レッスン初級

データセット仕様を定義する

収集前にシナリオ、割り当て数、ばらつき、エッジケース、背景画像を計画し、Ultralyticsのデータ量目標に照らして計画規模を決めます。

仕様のないチームは、明るい昼間に1台のカメラで撮影した写真を5,000枚集めた結果、運用開始初日の夕暮れにモデルが失敗する事態に陥りがちです。データセット仕様は画像の買い物リストです。モデルが対応すべきすべてのシナリオ、それぞれに必要な画像数、オーバーサンプリングする希少ケースをまとめます。作成にかかるのは1時間ですが、収集作業にどこまで労力をかけるかを左右します。

成果

ばらつきの要因とエッジケースを網羅する割り当て数付きのシナリオ表を作成し、ラベル付きデータセットが本番のデータ量目標にどれだけ届いていないかを測定します。

短期集中コース
すでに使い方をご存じの方は、こちらをご覧ください。
  1. カメラ、場所、時間、条件を行ごとにシナリオとして記述し、それぞれに割り当て数を設定します。

  2. 本番運用の目安として、クラスごとに画像1,500枚以上、ラベル付きインスタンス10,000件以上を基準に割り当て数を決めます。

  3. 既知のエッジケースはすべて個別の行に記載し、オーバーサンプリングします。

  4. 画像の0~10%を、オブジェクトのない背景画像として確保します。

実践的

仕様の位置づけ#

仕様は目標とカメラをつなぐ橋渡しです。データ収集の指針となり、最初のモデルが目標に届かなかったときに立ち返るページでもあります。

ループが戻る先は学習設定ではなく仕様です。目標未達の原因は通常、仕様で想定していなかったシナリオにあります。コンピュータービジョンプロジェクトの手順ガイドでも、同じライフサイクルを説明しています。

シナリオを行ごとに記述します#

シナリオとは、モデルが対応すべき撮影条件の組み合わせです。割り当て数とともに行にまとめると、誰かがカメラを手に取る前に不足が明らかになります。

シナリオカメラ場所時間帯条件割り当て数説明
昼間の荷積み場cam-1~cam-4A~Cバース08:00–17:00任意1,200最繁忙時間帯
夜間の荷積み場cam-1~cam-4A~Cバース22:00–04:00任意400ナトリウム灯、人が少ない
雨天時の荷積みcam-2、cam-3Bバース任意雨天のみ200まれだが重要
積み重ねた任意任意任意高さ2段超のパレット150背後にいる人が隠れる
背景任意任意任意空の荷積み場150オブジェクトなし、ラベルなし

書き出せないシナリオは、モデルが学習時に目にしないシナリオです。そして、それが本番で失敗するシナリオになります。

データ量目標に照らして仕様の規模を決めます#

Ultralyticsのトレーニング結果を向上させるヒントでは、本番向けの経験則を紹介しています。

目標推奨事項
クラスごとの画像数≥ 1,500
クラスごとのラベル付きインスタンス数≥ 10,000
背景画像(ラベルなし)データセットの0~10%。COCOでは約1%です
多様性異なる時間帯、季節、天候、照明、角度、撮影元、カメラ

本番モデルの基準はこの数値です。クラスごとにラベル付きオブジェクトが数百件あれば実験を始められます。また、最初のモデルを作ることで、どのシナリオでデータを追加する必要があるかを最も早く把握できます。データセットが基準にどれだけ届いていないかを確認するには、数を数えます。このスクリプトは、COCOから自動ダウンロードされる128枚の画像セット、COCO128で実行できます。

from collections import Counter
from pathlib import Path

from ultralytics.data.utils import IMG_FORMATS, check_det_dataset, img2label_paths

IMAGES_PER_CLASS, INSTANCES_PER_CLASS = 1500, 10_000  # Ultralytics production targets

data = check_det_dataset("coco128.yaml")  # downloads COCO128 (~7 MB) on first run; swap in your data YAML
images = [p for p in Path(data["train"]).rglob("*.*") if p.suffix[1:].lower() in IMG_FORMATS]
image_count, instance_count, background = Counter(), Counter(), 0
for label in map(Path, img2label_paths([str(p) for p in images])):
    classes = [int(r.split()[0]) for r in label.read_text().splitlines() if r.strip()] if label.exists() else []
    instance_count.update(classes)
    image_count.update(set(classes))
    background += not classes

print(f"{len(images)} images, {background} background ({background / len(images):.0%}, target 0-10%)")
print(f"{'class':>12} {'images':>7} {'instances':>10} {'of target':>10}")
ranked = instance_count.most_common()
for cls, n in ranked[:3] + ranked[-3:]:  # best and worst covered classes
    coverage = min(image_count[cls] / IMAGES_PER_CLASS, n / INSTANCES_PER_CLASS)
    print(f"{data['names'][cls]:>12} {image_count[cls]:>7} {n:>10} {coverage:>10.1%}")

never_labeled = [name for i, name in data["names"].items() if i not in instance_count]
print(f"{len(never_labeled)} classes in names with zero labels: {never_labeled[:4]} ...")
128 images, 2 background (2%, target 0-10%)
       class  images  instances  of target
      person      61        254       2.5%
         car      12         46       0.5%
         cup      10         36       0.4%
        bear       1          1       0.0%
      banana       1          1       0.0%
        skis       1          1       0.0%
9 classes in names with zero labels: ['fire hydrant', 'parking meter', 'sheep', 'cow'] ...

結果の読み方:

  • カバー率は、画像比率とインスタンス比率の小さい方です。クラスには両方が必要だからです。最もカバー率の高いクラスであるpersonでも、本番基準の2.5%にとどまっています。
  • インスタンスが1件だけのクラス(bear、banana、skis)は学習にも評価にも使えません。独自のデータセットでは、データを収集するか、v1から除外してください。
  • ラベルが0件のクラスはYAMLに記載されていますが、一度も描画されていません。それでもモデルの容量はそのクラスの予測に使われます。namesから削除するか、学習前にデータを収集してください。

ばらつきの要因を網羅します#

ウォーターフロントの遊歩道でのUltralytics YOLOの検出:カメラに近い人々や自転車、犬、距離の異なる車が、それぞれラベル付きのボックスと信頼度スコアで示されています

1つのフレームに、近くと遠くにいる人、自転車、犬、車がすでに混在しています。実際の仕様では、この範囲を意図的に計画する必要があります。各要因について、仕様が網羅しているか、該当しない理由を記録してください。空欄の行は、本番で初めて気づくシナリオです。

次元例
時間昼、夜、夜明け、夕暮れ
天候晴天、曇天、雨、霧、雪
照明明るい、薄暗い、まぶしさ、くっきりした影、複数の光源
カメラモデル、レンズ、設置高、視野
地理拠点、都市、国
人員とオペレーターシフト、チーム、制服、保護具
製品バリエーションSKU、パッケージの改訂版、色
物体のサイズ近距離で大きい、中距離、遠距離で小さい
遮蔽遮蔽なし、一部遮蔽、大きく遮蔽、ガラス越し
機器の状態汚れや曇りのあるレンズ、破損したパレット、摩耗した床の標示

量よりもばらつきが重要です。これらの行に分散した1,500枚の画像は、正午に1台のカメラで撮影した5,000枚より価値があります。

背景画像とエッジケースを計画する#

背景画像とは、空の荷積み場など、ラベル付けされた物体がないフレームです。物体が存在しないことをモデルに学習させ、誤検出を減らします。

背景画像の保存方法

Ultralytics YOLO形式では、背景画像はラベルファイルが空、またはラベルファイルが存在しない画像です。学習時のスキャン行(… 1200 images, 90 backgrounds, 0 corrupt)では、どちらもカウントされます。最初のエポックの前に、その数が目標の0~10%に収まっていることを確認します。

エッジケースには、大きな遮蔽、フレーム端で切れた物体、珍しい向き、積み重なった物体、フォークリフトのマストと鋼製の柱のような紛らわしい物体が含まれます。それぞれに独自の割当行を設けてください。

自然な出現頻度ではエッジケースが不足する

積み重ねたパレットの後ろに人がいるフレームが200枚に1枚なら、自然な頻度で収集したデータセットには1,500枚中およそ7枚しか含まれず、学習には少なすぎます。エッジケースは意図的にオーバーサンプリングします。割り当てる枚数は、実際の映像に占める割合より大幅に多くする必要があります。

仕様を最初から完全にする必要はない

紙の上ですべての状況を思いつくことはできません。仕様をおおむね整えたら、まずモデルをすばやく学習させ、その誤りから見落としていた状況を明らかにします。

試してみる

プロジェクト用のシナリオ表を作成します。5~10行を設け、それぞれに数値の割当数を設定し、さらに背景用の行と、既知のエッジケースごとに1行を追加します。導入先で働く人に内容を確認してもらい、その人が挙げたシナリオをすべて追加します。次に、すでにラベル付けしたデータ(またはCOCO128)に対して volume_gap.py を実行し、カバー率が最も高いクラスと最も低いクラスを記録します。成果物:表とカバー率の数値をまとめた spec.md。

完了条件
以下のすべてを満たすと、レッスンは完了です。
  • シナリオ表には、数値の割当数を設定した行が少なくとも5つあり、背景用とエッジケース用の行も含まれています。

  • ばらつきの10次元のうち少なくとも5つが網羅されているか、対象外であることが明記されています。

  • 割当数の合計は、クラスごとの画像数とインスタンス数の目標以上です。満たせない場合は、v1での例外として不足分が記録されています。

解答を表示

割当数は画像数の目標だけでなく、インスタンス数の目標を基準に決めます。ある駐車場の車両カウンターに2つのクラスがあり、試験撮影では画像1枚あたり平均3台の車と0.5台のトラックが写るとします。

クラス画像あたりのインスタンス数インスタンス10,000件に必要な画像数最低ラインの1,500枚を満たすために必要な画像数計画
car3.0≈ 3,3001,5003,300枚
truck0.5≈ 20,0001,500トラックの出現頻度が10倍高い荷受け入口で、対象を絞って撮影

通常の映像で車は十分にカバーできます。トラックをランダムに撮影して目標を満たすには20,000枚が必要となるため、あらゆる映像を6倍集めるのではなく、専用のシナリオ行(荷受け入口、平日06:00~10:00)を仕様書に追加します。背景用として約5%の行(夜間の空の駐車場)を追加し、雪に覆われた状態とフレーム外にはみ出したトラックのエッジケース用の行も追加します。

次のステップ

仕様書を作成したら、次のレッスンでは、その内容を実際に満たす画像を収集します。