機械学習
記事内に商品プロモーションを含む場合があります

【物体検出】YOLO11で独自データを使った学習と推論を実践

Aru

いつのまにかYOLO11が登場していましたので、以前YOLOv8/v9やった独自のカスタムデータ(オリジナルデータ)を使った物体検出(Object Detection)の学習と推論をやってみました。本記事も、前回と同じデータセットを使ってYOLOv11での学習を行いました。

あわせて読みたい
YOLOv8(YOLOv9)で独自データを使った学習と推論を実践(物体検出)
YOLOv8(YOLOv9)で独自データを使った学習と推論を実践(物体検出)

YOLO11の概要

概要

今回紹介するのは、Ultralytics社のgithubリポジトリにあるYOLO11です。YOLOv8/v9についてはウォッチしていたのですが、最近は大規模言語モデル(LLM)の方を追っかけていたら、いつの間にか11にアップしていました。

この記事では、YOLO11のライブラリの使い方を中心に解説します。YOLO11の技術的な改善点については触れません。興味のある方は以下の論文を参照してみてください。

arXiv: https://arxiv.org/abs/2410.17725

引用元:https://github.com/ultralytics/ultralytics

YOLO11も以前のモデルと同様に、物体検出(Detect)、セグメンテーション、姿勢推定、物体追跡などの複数のタスクに対応しています。

今回は、このYOLO11を使って、自分で用意したデータを学習し、学習した結果を用いて推論するという一連の流れをPythonを使って行います。

なお、学習・推論のコードはKaggle Notebookに公開していますので参考にしてください。

YOLO11の性能

以下は、公式にある性能グラフです。YOLOv8→v9→v10→11と性能がアップしていることがわかります。また、同じ性能であればYOLO11が高速です。

引用元:https://github.com/ultralytics/ultralytics

この速度で進化されると、自分でモデルを作ったり、改良したりするより公開モデルをそのまま利用した方が良い気さえしてきます。

モデルの一覧は以下の通りです。

YOLOv8nとYOLOv9nを比較すると、mAPが37.3→39.5にアップ、CPUでの速度は80.4ms→56.1msとスピードアップ、パラメータ数も3.2M→2.6Mと小さくなっています。

もちろんOBB(Oriented Bounding Box)にも対応していますので応用範囲も広いです。

OBBとは

OBBは回転したバウンディングボックスのことです(縦横水平垂直ではなく、斜め角度の枠などを表現可能です)。厳密に枠をつけたい場合に利用します

オリジナル(独自)データで学習させる

利用したデータ

今回も、kaggleのCar Object Detectionデータセットを利用して学習を行います。このデータは車両にアノテーションが行われたデータです。

自分で画像を収集した場合は、自身でアノテーションを行わなければなりません。YOLOフォーマットでアノテーションを行う場合、LabelStudioやLabelImgが便利です。LabelImgはサポートが終了していますが、ちょっとしたアノテーションには便利が良いので個人でちょっとアノテーションする場合はこちらがおすすめです。

ultralyticsのライブラリをインストール

YOLO11を利用するにはultralyticsのYOLOパッケージをインストールする必要があります。

そのほかにもPython>=3.8, Pytorch>=1.8が必要になりますが、Kaggle NotebookやCoogle Colabの場合はこれらはインストール済みなので、ultralyticsだけインストールすればOKです。

Notebookの先頭で以下のコマンドを実行してインストールします。

# install yolov8
!pip install ultralytics

パッケージのインポート

ultralyticsからYOLOを、また必要なパッケージをインポートします。必要なパッケージはコード実行に必要となるものです(必要のないものも含まれているかもしれせんが問題ありません)

from ultralytics import YOLO
import os
import random
import shutil
import numpy as np
import pandas as pd
import cv2
import yaml
import matplotlib.pyplot as plt
import glob
from sklearn.model_selection import train_test_split

データセットを準備する

トレーニング用のデータセットを準備します。今回は、kaggleのCar Object Detectionデータセットを利用します。

このデータセットは、テスト用の画像フォルダと訓練用の画像フォルダが用意されており、それぞれjpegフォーマットで画像が格納されています。

また、アノテーションデータがcsvファイル形式で準備されています。

このため自分でアノテーションを行う必要はありません

ここでは、このデータをYOLOフォーマットに変換する手順を説明します。もし、データセットがYOLOフォーマットで準備できている場合は、「データセットを準備する」の部分は読み飛ばして、次の「学習(トレーニング)」に進んでください

データセットをYOLOフォーマットに変換

まずは、データのあるディレクトリ等を変数に設定しておきます。

IMAGESLABELSはYOLOフォーマットに変換したデータを格納する場所です。また、TRAINTESTは変換前のデータが格納された場所です。

DIR = "/kaggle/working/datasets/cars/"
IMAGES = DIR +"images/"
LABELS = DIR +"labels/"

TRAIN = "/kaggle/input/car-object-detection/data/training_images"
TEST = "/kaggle/input/car-object-detection/data/testing_images"

次に、アノテーションデータが格納されたcsvファイルを読み込みます。

df = pd.read_csv("/kaggle/input/car-object-detection/data/train_solution_bounding_boxes (1).csv")
df

ファイルの内容は以下のようになります。1列目は画像ファイル名、2列目〜5列目は枠の場所で、1行が1つの枠に対応しています。

1つの画像に複数の枠が存在する場合は、同じ画像名が複数行存在することになります。YOLOフォーマットでは、1つのファイルに1つの画像のアノテーションデータを全て格納するので、この部分に気をつけて変換する必要があります。

提供されるファイルはtrainとtestだけですが、ここではtrainデータを訓練用(train)と評価用(validation)に分割します。これには、train_test_split関数を利用します。

ここでは、test_size=0.2とし訓練用と評価用を8:2の割合に分割しました。

files = list(df.image.unique())
files_train, files_valid = train_test_split(files, test_size = 0.2)

次に、YOLOフォーマットで格納するフォルダにtrainvalidフォルダを作成します。フォルダは画像(IMAGES)とラベル(LABELS)それぞれに用意します。

なお、exist_okは、すでにフォルダが存在している場合にエラーにならないように指定してます。

# make directories
os.makedirs(IMAGES+"train", exist_ok=True)
os.makedirs(LABELS+"train", exist_ok=True)
os.makedirs(IMAGES+"valid", exist_ok=True)
os.makedirs(LABELS+"valid", exist_ok=True)

フォルダが準備できたら、画像ファイルをコピーしていきます。

train_filenameにファイル名が存在する場合には訓練用、そうでない場合は評価用のフォルダにコピーしています。

train_filename = set(files_train)
valid_filename = set(files_valid)
for file in glob.glob(TRAIN+"/*"):
    fname =os.path.basename(file)
    if fname in train_filename:
        shutil.copy(file, IMAGES+"train")
    elif fname in valid_filename:
        shutil.copy(file, IMAGES+"valid")

画像をコピーしたら、YOLOフォーマットのアノテーションデータを作成します。

YOLOフォーマットでは、画像ファイルと同名のテキスト(.txt)データを作成してそこに枠情報を配置していきます。

以下は、与えられたCSVデータからアノテーションデータを作成するコードです。

YOLOフォーマットでは、(クラスID, X中心、Y中心、W、H)の順でテキストファイルに書き込みを行います。なお、元のデータは676×380のサイズでの(xmin, ymin)-(xmax, ymax)という座標になっていますが、YOLOフォーマットでは、中心座標と幅と高さ情報のデータが必要となりますので変換が必要です。また、スケールも画像の幅、高さを1.0としたスケールで指定する必要があります。

ここではCSVのデータを(クラスID, X中心、Y中心、W、H)に変換すると同時に、スケールも0.0〜1.0に変換しています。

for _, row in df.iterrows():    
    image_file = row['image']
    class_id = "0"
    x = row['xmin']
    y = row['ymin']
    width = row['xmax'] - row['xmin']
    height = row['ymax'] - row['ymin']

    x_center = x + (width / 2)
    y_center = y + (height / 2)
    x_center /= 676
    y_center /= 380
    width /= 676
    height /= 380

    if image_file in train_filename:   
        annotation_file = os.path.join(LABELS) + "train/" + image_file.replace('.jpg', '.txt')
    else:
        annotation_file = os.path.join(LABELS) + "valid/" + image_file.replace('.jpg', '.txt')
        
    with open(annotation_file, 'a') as ann_file:
        ann_file.write(f"{class_id} {x_center} {y_center} {width} {height}\n")

YOLOのフォーマットでは、アノテーションデータは以下のフォーマットになります(xy座標と幅は、画像の幅、高さを1とした値(0~1)になります

YOLOフォーマット

クラスID Xの中心 Yの中心 W H
クラスID Xの中心 Yの中心 W H
クラスID Xの中心 Yの中心 W H
    : (オブジェクトの数だけ繰り返し)

下図は、フォーマット変換のイメージ図です。dfの内容を解析し、YOLOフォーマットに変換し、対応するtextファイルに書き出します。open(annotation_file, 'a')追加モードでファイルをopenしているので同じ画像に対する枠情報は1つのファイルに追記されることになります。

変換の処理を図示すると、以下のようなイメージなります。

変換後のフォルダ構成は以下のようになります。

以上でデータセットの準備は完了です。

実は、学習よりも、枠情報がない場合のアノテーションや、枠情報が存在してもYOLOフォーマットでないデータ場合のYOLOフォーマットに変換する作業が一番面倒だったりします。有名なフォーマットについては、コンバーターなどが存在していますのでそれを積極的に利用することをお勧めします。

独自のデータを学習させたい方

枠情報がないデータを利用して学習する場合、アノテーション作業が必要になります。結構面倒ですが、ツールを使うことで大分楽になります。ちょっとしたあのーてションの場合には、LabelImg、本格的な場合はLabelStudioをお勧めします。これらのツールについては以下を参照してください。

物体検出用アノテーションツール「labelImg」の使い方(YOLO対応)
物体検出用アノテーションツール「labelImg」の使い方(YOLO対応)
【超多機能!】LabelStudioを使った物体検知のアノテーション手順
【超多機能!】LabelStudioを使った物体検知のアノテーション手順

学習(トレーニング)

先に説明した手順でデータセットを作成した場合は、以下のようなフォルダ構造ができているはずです。carsフォルダがデータセットのフォルダで、imageslabelsの下に画像と枠情報が格納されています。

これらの設定をトレーニングに反映させるために、yamlファイル(設定ファイル)を作成します。Notebookでyamlファイルを作成するには、マジックコマンドを利用します。

あわせて読みたい
Jupyter NotebookのTIPS3選|枠を広げる、シェルコマンド、マジックコマンド
Jupyter NotebookのTIPS3選|枠を広げる、シェルコマンド、マジックコマンド

1行目の%%writefileはファイルに書き出すためのマジックコマンドで、セルの内容が指定したファイル名で書き出されます。

下記は、trainとvalidのフォルダと、クラス数とクラス名を指定するyamlファイルを書き出すものです。

%%writefile dataset.yaml
# Path
path: ./cars
train: images/train
val: images/valid

# Classes
nc: 1
names: ['car']

注意ポイント

学習中にwandbへアクセスしようとする場合は以下のコードでwandbを停止させてください。アカウントを持っている場合は、接続して、実験記録を保存させることで学習の記録してもOKです

# disable wandb
import wandb
wandb.init(mode="disabled")

yamlファイルを作成したら、いよいよ学習です。

学習はモデルのtrain()を呼び出すだけです。あとは、訓練が終了するまで待ちます。

model = YOLO('yolo11n.pt')

model.train(data="dataset.yaml", epochs=100, batch=8)

今回は、YOLO11のモデルの中で一番小さいモデルを選びましたが、yolo11n.pt以外に、以下のものを選ぶことができます。

  • YOLO11n
  • YOLO11s
  • YOLO11m
  • YOLO11l
  • YOLO11x

モデルサイズはn→s→m→l→xの順番で大きくなり、サイズが大きくなるほど処理時間が大きくなるしGPUのメモリも必要になりますので注意してください。

学習(train)の引数について

以下は、trainに指定できる引数の一覧です。引数を指定するだけで、スケジューラを切り替えたり、ラベルスムージングのON/OFFができたりと、ハイパーパラメータの細かな調整が可能です。

引数説明
modelNoneモデル名またはモデルファイルへのパス, i.e. yolov8n.pt, yolov8n.yaml
dataNoneデータファイルへのパス(xxxx.yamlファイル)
epochs100EPOCH数
patience50性能改善しないときに、早期打ち切りするまでのEPOCH数( Early Stopping)
batch16バッチサイズ(-1で自動)
imgsz640入力の画像サイズ(整数 または w, h)
saveTrue訓練中の予測結果とチェックポイントを保存
save_period-1チェックポイントの間隔(EPOCH数、-1の場合は無効)
cacheFalseキャッシュあり・なし
deviceNoneデバイス 
GPUの場合、device=cudaの場合は0,1,2,3。マルチCPUの場合は[0,1]など
CPUの場合、device=’cpu’。
M1/M2Macの場合、device=’mps’が利用可能
workers8ワーカースレッドの数
projectNoneプロジェクト名
nameNone実験名
exist_okFalse同じ実験がある場合、記録を上書きするかどうか
pretrainedFalseトレーニング済みモデルを利用するかどうか
optimizer'auto'オプティマイザ
SGD, Adam, Adamax, AdamW, NAdam, RAdam, RMSProp, auto
verboseFalse詳細出力をするかどうか
seed0乱数のシード
deterministicTruedeterministicモードを有効にするかどうか。再現性に影響
single_clsFalseマルチクラスデータをシングルクラスとして訓練するかどうか
rectFalserectangular training with each batch collated for minimum padding
cos_lrFalseコサインスケジューラを使用するかどうか
close_mosaic0(int) 最終EPOCHではモザイクオーグメンテーションを無効にする
resumeFalse訓練を最後のチェックポイントから再開する
ampTrueAutomatic Mixed Precision (AMP) を使うかどうか
fraction1.0訓練に使うデータセットの割合 (デフォルトは全部(1.0))
profileFalseprofile ONNX and TensorRT speeds during training for loggers
lr00.01 初期の学習率
lrf0.01最後の学習率
momentum0.937SGD momentum/Adam beta1
weight_decay0.0005optimizer weight decay 5e-4
warmup_epochs3.0ウォームアップEPOCH数
warmup_momentum0.8ウォームアップ時の初期momentum
warmup_bias_lr0.1ウォームアップ時の初期lr
box7.5box lossのgain
cls0.5cls(クラス) lossのgain (scale with pixels)
dfl1.5dfl lossの gain
pose12.0pose loss のgain (pose-only)
kobj2.0keypoint obj lossの gain (pose-only)
label_smoothing0.0ラベルスムージング
nbs64nominal batch size
overlap_maskTruemasks should overlap during training (segment train only)
mask_ratio4mask downsample ratio (segment train only)
dropout0.0ドロップアウト率 (classify train only)
valTrue訓練中に検証を行うかどうかのフラグ
出典:ultralytics「train」

学習結果の確認

YOLO11の学習の結果はグラフ形式で格納されています。

格納場所とファイル名は.run/detect/train/result.pngです。

以下のコードはこのファイルを読み込み、表示するものです。

from IPython.display import Image
Image("/kaggle/working/runs/detect/train/results.png")

結果を見ると、train lossは順調に下がっていることがわかります。また、val lossは若干乱れていますが、徐々に収束しています。今回100epoch学習させましたが、学習回数を増やせばまだまだ学習する雰囲気があります。ただ、mAP50は10epoch付近で1.0に近くなっているので、10epoch程度である程度学習ができていることもわかります。

物体検出ではmAPで評価することが多いです

学習結果を使った推論を実行する

推論コード

学習したモデルを実際に使って推論してみます。

YOLO11の場合、推論コードもシンプルです。まず、学習したパラメータを読み込んでモデルを生成し、引数に画像ファイルやフォルダを指定して呼び出すだけです。

今回は、データセットのtesting_imagesフォルダにある画像を利用しました。この画像はトレーニングには利用していない画像です。

また、引数でconf=0.2, iou=0.5として検出の閾値を設定しています。confはクラスの確らしさ、iouは枠の確らしさの閾値で、これを超えたものだけが検出されることになります。

model = YOLO('./runs/detect/train/weights/last.pt')
ret = model("/kaggle/input/car-object-detection/data/testing_images",save=True, conf=0.2, iou=0.5)

処理結果は、./runs/detect/predictに格納されます。以下のコードは、格納されている画像のいくつかを表示するものです。

# display result
files = glob.glob("./runs/detect/predict/*")
for i in range(0, 30, 3):
    img = Image(files[i])
    display(img)

結果を見ると、きちんと車に枠が付いているようです。枠の上の数値は枠の確らしさです。

車が写っていない画像では、誤検出も起きていないようです。

推論時のパラメータ

以下は、推論時のパラメータの一覧です。

推論側もかなりのパラメータを指定可能です。

引数説明
source'ultralytics/assets'ソース画像/映像データのディレクトリ
conf0.25検出するオブジェクトの閾値
iou0.7NMSの交差判定のIoUの閾値
halfFalseFP16を利用するかどうかのフラグ
deviceNone実行するデバイス。GPU(cuda) = 0/1/2/3または”cpu”
showFalse結果を表示(可能な場合)
saveFalse結果画像を保存するかどうか
save_txtFalse結果をテキストファイルで保存するかどうか
save_confFalse結果に信頼度スコアを含めて保存するかどうか
save_cropFalse結果に切り取った画像を含めて保存するかどうか
hide_labelsFalseラベルを隠す(非表示にする)
hide_confFalse信頼度スコアを隠す(非表示にする)
max_det300最大検出数
vid_strideFalsevideo frame-rate stride
line_widthNoneバウンディングボックスのライン幅。Noneの場合は画像サイズに合わせて自動調整
visualizeFalseモデルの特徴を可視化するかどうか
augmentFalseデータ拡張を予測で利用するかどうか
agnostic_nmsFalseclass-agnostic NMS
retina_masksFalse高解像度のセグメンテーションマスクを利用するかどうか
classesNonefilter results by class, i.e. class=0, or class=[0,2,3]
boxesTrueセグメンテーションに枠を表示するかどうか

ソース(source)として設定できるのは以下になります。これをみるとYoutubeやrtspから直接入力することができるようです。

ソース名データ型コメント
image'image.jpg'str or Path単一の画像ファイル
URL'https://ultralytics.com/images/bus.jpg'str画像のURL
screenshot'screen'strスクリーンショットのキャプチャ
PILImage.open('im.jpg')PIL.ImageRGB画像(Height、Width, Channel)フォーマット
OpenCVcv2.imread('im.jpg')np.ndarray of uint8 (0-255)BGR画像(Height、Width, Channel)フォーマット
numpynp.zeros((640,1280,3))np.ndarray of uint8 (0-255)BGR画像(Height、Width, Channel)フォーマット
torchtorch.zeros(16,3,320,640)torch.Tensor of float32 (0.0-1.0)RGB画像(Batch, Channel, Height, Width)フォーマット
CSV'sources.csv'str or Path画像、ビデオ、またはディレクトリへのパスを含むCSVファイル
video 'video.mp4'str or PathMP4、AVIなどのビデオファイル
directory 'path/'str or Path画像または動画を含むディレクトリへのパス
glob'path/*.jpg'strワイルドカードなどを含んだ画像ファイル名
(*.jpgなど)
YouTube'https://youtu.be/Zgi9g1ksQHc'strYoutubeのURL
stream 'rtsp://example.com/media.mp4'strRTSP, RTMPのURLやIPアドレス(WebカメラなどのRTSPアドレス)
出典:ultralytics「predict」

まとめ

今回はYOLO11を使って、独自データの学習と推論を行ってみました。基本的にはYOLOv8とほとんど同じコードで学習・推論することができました。ultralyticsのYOLOはモデル名だけ入れ替えればYOLOv8と同じように使うことができるようです。

なので、以前のYOLOで作ったアプリも簡単にアップデートすることができそうです。

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

ABOUT ME
ある/Aru
ある/Aru
IT&機械学習エンジニア/ファイナンシャルプランナー(CFP®)
専門分野は並列処理・画像処理・機械学習・ディープラーニング。プログラミング言語はC, C++, Go, Pythonを中心として色々利用。現在は、Kaggle, 競プロなどをしながら悠々自適に活動中
記事URLをコピーしました