🐘

Grounding DINOのファインチューニング(追加学習)の仕方

2024/10/21に公開

Grounding DINO とは

Grounding DINOとは、物体検出と視覚言語統合のモデルで、特にテキスト条件付き物体検出に強みを持つモデルです。

具体的には、Grounding DINOは自然言語のテキストを入力として受け取り、そのテキストに関連するオブジェクトを画像の中で検出できる機能を持っています。

これにより、従来の物体検出モデルがラベルやカテゴリに基づいて検出を行うのに対し、Grounding DINOはより柔軟で、事前定義されていないカテゴリでもテキストを使って検出が可能です。

この記事を読んでできるようになること

Grounding DINOは、訓練を行わなくても利用できるゼロショットモデルとして優れた性能を発揮しますが、航空画像のような特定の領域では物体検出が苦手な場合があります。

本記事では、像のデータセットを使用して、Grounding DINOを自分のデータセットに合わせてファインチューニング(追加学習)する手順を解説します。下の画像にある通り、ファインチューニングを行うことで、事前学習モデルでは認識できなかった小さな像や、空から撮影された像も正確に認識できるようになります。

ファインチューニングの方法

Grounding DINOは、2Dのオープンボキャブラリ物体検出とフレーズグラウンディングを統合する画期的なモデルであり、幅広い用途に適用可能です。しかし、そのトレーニング部分は公開されていないため、直接ファインチューニングを行うことは難しいです。

そこで、カスタムデータセットを用いてファインチューニングするには、代替版となるオープンソースを使います。今回は、OpenMMLabによって開発された、MM-Grounding-DINOを利用していきます。
https://github.com/open-mmlab/mmdetection/tree/main/configs/grounding_dino

MM-Grounding-DINOは、公式のGroundingDINOに引けを取らない性能を発揮しており、レーダーチャートの結果からも確認できるように、いくつかのデータセットでは公式モデルを上回るパフォーマンスを示しています。

では、実装していきましょう!

1. 事前準備

まず、Linux環境でpyenvを使った仮想環境を作成し、必要なパッケージをインストールする準備を行います。他のOS環境や別のパッケージ管理ツールを使用したい場合は、公式のPyTorchのスタートガイドを参照してください。

https://mmdetection.readthedocs.io/en/latest/get_started.html

仮想環境の構築
MMDetectionは、Linux、Windows、macOSの各環境で動作します。まず、pyenvを使用して仮想環境を作成します。ここでは、Python 3.10.0を使用し、仮想環境の名前を「elephant」とします。

pyenv virtualenv 3.10.0 elephant
pyenv activate elephant

Pytorchのインストール
次に、PyTorchをインストールして動作環境を整えましょう。PyTorchのインストール方法は、使用するハードウェア(GPUまたはCPU)、オペレーティングシステム(Linux、Windows、macOS)、パッケージ管理ツール(pipまたはconda)などに依存するため、これらの要素に応じた適切なインストールを行ってください。

以下はその一例です。

  1. GPU(NVIDIA)を使用する場合
    NVIDIAのGPUを使用する際は、CUDAに対応したPyTorchをインストールする必要があります。CUDAのバージョンに応じて、次のようにインストールします(例:CUDA 12.1の場合):
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
  1. CPUのみでPytorchの使用(CUDAは不要)
    GPUがない、またはGPUを使用しない場合、PyTorchはCPUで動作します。この場合、CUDAは不要です。
   pip install torch torchvision torchaudio

2. 必要パッケージのインストール

MMEngineとMMCVのインストール
次に、MMDetectionを動かすために必要なMMEngineとMMCVをインストールします。以下のコマンドを実行します。

pip install -U openmim
mim install mmengine
mim install "mmcv>=2.0.0"

https://github.com/open-mmlab/mmdetection/tree/main

新しいディレクトリの作成
MMDetectionを格納するディレクトリを作成します。今回は、ディレクトリ名をelephantとします。

mkdir elephant  #新しいディレクトリ(フォルダ)の作成
cd elephant   #作成された'elephant' ディレクトリに移動

MMdetectionのインストール
以下のコマンドでMMDetectionをインストールします。

git clone https://github.com/open-mmlab/mmdetection.git
cd mmdetection
pip install -v -e .

次に、拡張機能をインストールします。

mim install mmdet[multimodal]

3. デモ推論をしてみましょう

推論の準備が整ったら、MMDetectionに組み込まれているデモコードとデモ画像を使用して、推論環境が正しく動作しているか確認しましょう。

デモ推論では、以下の画像を使って「ベンチ」と「車」を検出します。

重みのダウンロード
まず、事前学習されたモデルの重みをダウンロードします。以下のコマンドを実行して、MMDetectionのディレクトリ内にGrounding DINOの重みファイルを保存します。この重みは、推論で使用するモデルのパラメータを含んでいます。

cd mmdetection

wget https://download.openmmlab.com/mmdetection/v3.0/grounding_dino/groundingdino_swint_ogc_mmdet-822d7e9d.pth

推論の実行
ダウンロードした重みファイルを使って、画像からオブジェクトを検出するためにPythonスクリプトを実行します。

推論を実行するためのコマンドフォーマットは以下のようになります。

python demo/image_demo.py \
    <画像パス> \
    <モデル設定ファイルパス> \
    --weights <重みファイルパス> \
    --texts '<検出したいオブジェクト>'

今回の場合は、以下のようにコマンドを実行します。

python demo/image_demo.py \
    demo/demo.jpg \
    configs/grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_cap4m.py \
    --weights groundingdino_swint_ogc_mmdet-822d7e9d.pth \
    --texts 'bench . car .'

以下のような画像が生成されれば、推論環境が正しく整っていることが確認できます。

エラー例
私の場合、上記のコードを一度実行した際に、以下のエラーが発生しました。

KeyError: 'Adafactor is already registered in optimizer at torch.optim'

このエラーは、複数のパッケージが同じオプティマイザー(Adafactor)を重複して登録しようとしたことが原因かもしれません。
同じエラーが発生した方は次の対処方法を試してください。

  1. 出力結果からbuild.pyをまずは開いてください。

  1. オプティマイザーを登録する関数を以下を参考に変更してください。
変更前

def register_transformers_optimizers():
    transformer_optimizers = []
    try:
        from transformers import Adafactor
    except ImportError:
        pass
    else:
        OPTIMIZERS.register_module(name='Adafactor', module=Adafactor)
        transformer_optimizers.append('Adafactor')
    return transformer_optimizers

変更後

def register_transformers_optimizers():
    transformer_optimizers = []
    try:
        from transformers import Adafactor
    except ImportError:
        pass
    else:
        # 既に登録されているか確認してから登録する
        if 'Adafactor' not in OPTIMIZERS.module_dict:
            OPTIMIZERS.register_module(name='Adafactor', module=Adafactor)
            transformer_optimizers.append('Adafactor')
    return transformer_optimizers

4. カスタムデータセットの準備

今回は、訓練用に352枚、評価用に35枚、テスト用に18枚の画像と、COCO形式のアノテーションファイルを含む像のデータセットを用意しました。

このデータセットは、以下のサイトから取得しました。

https://universe.roboflow.com/elephantdetection-90mt9/aerialelephants/dataset/1

なお、ダウンロードしたアノテーションファイルにはカテゴリ設定に誤りがあったため、上記のデータセットを利用する際は、アノテーションファイルの内容を確認するようにしてください。

カスタムデータセットが準備できたら、以下のディレクトリ構成を整えてください。

mmdetection/
├── configs/
│   └── grounding_dino/
│       └── grounding_dino_swin-t_finetune_elephant.py  # ファインチューニング用設定ファイル
├── data/
│   └── elephant/
│       ├── images/                                     # 画像データ
│       │   ├── train/                                  # トレーニング用画像
│       │   ├── val/                                    # バリデーション用画像
│       │   └── test/                                   # テスト用画像
│       ├── annotations/                                # COCO形式のアノテーションファイル
│       │   ├── train_annotations.coco.json             # トレーニング用アノテーション
│       │   ├── valid_annotations.coco.json             # バリデーション用アノテーション
│       │   └── test_annotations.coco.json              # テスト用アノテーション
                              

ファインチューニング用の設定ファイルについては「6.ファインチューニング」で解説します。

5. ファインチューニング前のモデル評価

ファインチューニングを行う前に、まず現在のモデルがどのように動作するか確認するために推論を実行してみましょう。
これにより、事前学習済みのモデルがどの程度正確にオブジェクトを検出できるかを確認できます。以下のコマンドを実行して、いくつかのテスト用画像に対する推論を行います。

python demo/image_demo.py \
    data/elephant/images/test/elephant_test_0.jpg \ #1枚目のテスト画像
    configs/grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_cap4m.py \
    --weights groundingdino_swint_ogc_mmdet-822d7e9d.pth \
    --texts 'elephant' \

この例では、テスト用の画像(elephant_test_0.jpg)に対して、事前学習済みの重みファイル(groundingdino_swint_ogc_mmdet-822d7e9d.pth)を使用し、象(elephant)を検出することを目的としています。

下の画像は、3枚のテスト画像に対して推論を行った結果です。象が比較的大きく、横から撮影された場合は正確に認識されていますが、空から撮影されたものや、小さく映っている象は認識されていない場合があります。

6. ファインチューニングの実施

次に、Grounding DINOモデルを自分のデータセットに合わせてファインチューニングしていきます。

ファインチューニングとは、事前学習されたモデルを基に、新しいデータセットに最適化するための追加学習を行うことです。この手順を行うことで、モデルが特定のタスクにより適した性能を発揮するようになります。

  1. 設定ファイルの準備

まず、ファインチューニング用の設定ファイル grounding_dino_swin-t_finetune_elephant.py を用意します。この設定ファイルには、データセットの場所や学習パラメータなどが記載されています。

_base_ = 'grounding_dino_swin-t_finetune_16xb2_1x_coco.py'


data_root = '/data/elephant/'
class_name = ('elephant', )
num_classes = len(class_name)
metainfo = dict(classes=class_name, palette=[(220, 128, 50)])  

# 事前学習モデルのパスを指定
load_from = 'groundingdino_swint_ogc_mmdet-822d7e9d.pth'

model = dict(bbox_head=dict(num_classes=num_classes))

# トレーニングデータローダー
train_dataloader = dict(
    dataset=dict(
        data_root=data_root,
        metainfo=metainfo,
        ann_file='annotations/train_annotations.coco.json',
        data_prefix=dict(img='images/train/')
    )
)

# バリデーションデータローダー
val_dataloader = dict(
    dataset=dict(
        metainfo=metainfo,
        data_root=data_root,
        ann_file='annotations/valid_annotations.coco.json',
        data_prefix=dict(img='images/valid/')
    )
)

# テストデータローダー
test_dataloader = dict(
    dataset=dict(
        metainfo=metainfo,
        data_root=data_root,
        ann_file='annotations/test_annotations.coco.json',
        data_prefix=dict(img='images/test/')
    )
)

val_evaluator = dict(ann_file=data_root + 'annotations/valid_annotations.coco.json')
test_evaluator = dict(ann_file=data_root + 'annotations/test_annotations.coco.json')

max_epoch = 20  # エポック数を20に設定

default_hooks = dict(
    checkpoint=dict(interval=1, max_keep_ckpts=1, save_best='auto'),
    logger=dict(type='LoggerHook', interval=5)
)
train_cfg = dict(max_epochs=max_epoch, val_interval=1)

param_scheduler = [
    dict(type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=30),
    dict(
        type='MultiStepLR',
        begin=0,
        end=max_epoch,
        by_epoch=True,
        milestones=[15],
        gamma=0.1)
]

optim_wrapper = dict(
    optimizer=dict(lr=0.00005),  # 学習率を低めに設定
    paramwise_cfg=dict(
        custom_keys={
            'absolute_pos_embed': dict(decay_mult=0.),
            'backbone': dict(lr_mult=0.1),
            'language_model': dict(lr_mult=0),
        })
)

auto_scale_lr = dict(base_batch_size=16)
  1. トレーニングの実行
    設定ファイルの準備ができたら、以下のコマンドでトレーニングを実行します。このコマンドでは、設定ファイルを指定し、elephant_work_dir という作業ディレクトリにトレーニング結果(モデル、ログ、チェックポイントなど)が保存されます。
python tools/train.py configs/grounding_dino/grounding_dino_swin-t_finetune_elephant.py --work-dir elephant_work_dir

7. ファインチューニング後のモデル評価

ファインチューニングが完了したら、最も良好なパフォーマンスを示したモデルで推論を行います。今回の場合、7エポック目のモデルが最も良い結果を出したため、このモデルを使用して推論を実行します。

以下のコマンドを使用して、7エポック目の学習済みモデルを使い、テスト画像に対する推論を行います。

python demo/image_demo.py \
    data/elephant/images/test/elephant_test_0.jpg \
    configs/grounding_dino/grounding_dino_swin-t_finetune_elephant.py \
    --weights elephant_work_dir/best_coco_bbox_mAP_epoch_7.pth \
    --texts 'elephant' \
    --out-dir posttrain_results/

ここでは、best_coco_bbox_mAP_epoch_7.pth という7エポック目で得られた最良の重みファイルを使用し、象の検出を行います。また、--out-dir オプションを使用して、推論結果の画像を保存するディレクトリを指定しています。

この結果から、ファインチューニング後はモデルの性能が大幅に向上し、象の検出がより正確になっていることが確認できます。推論結果の画像では、小さい象や異なる角度からの象もしっかりと認識されており、ファインチューニングの効果がはっきりと現れています。

Reference

https://github.com/open-mmlab/mmdetection?tab=readme-ov-file
https://github.com/open-mmlab/mmdetection/tree/main/configs/grounding_dino

Discussion