ショートカット

標準データセットでの事前定義済みモデルの訓練

MMDetection は、検出モデルを訓練するためのすぐに使えるツールも提供しています。このセクションでは、標準データセット(つまり COCO)で *事前定義済み* モデル(configs 下)を訓練する方法を示します。

データセットの準備

訓練にはデータセットの準備も必要です。詳細は上記のセクション データセットの準備 を参照してください。

注意: 現在、configs/cityscapes 下の構成ファイルは、初期化にCOCO事前学習済みウェイトを使用しています。ネットワーク接続が遅い場合や利用できない場合は、エラーを回避するために、訓練を開始する前に既存のモデルをダウンロードすることをお勧めします。

学習率の自動スケーリング

重要: 構成ファイルのデフォルトの学習率は、GPU 8 台と GPU あたり 2 サンプル(バッチサイズ = 8 * 2 = 16)です。そして、config/_base_/schedules/schedule_1x.pyauto_scale_lr.base_batch_size に設定されています。学習率は、バッチサイズ 16 の値に基づいて自動的にスケーリングされます。同時に、mmdet を使用する他のコードベースに影響を与えないように、auto_scale_lr.enable フラグのデフォルト設定は False です。

この機能を有効にするには、引数 --auto-scale-lr を追加する必要があります。そして、コマンドを実行する前に使用する構成名を確認する必要があります。なぜなら、構成名はデフォルトのバッチサイズを示しているからです。デフォルトでは、8 x 2 = 16 バッチサイズ です。例えば、faster_rcnn_r50_caffe_fpn_90k_coco.pypisa_faster_rcnn_x101_32x4d_fpn_1x_coco.py などです。その他の場合、構成ファイル名に _NxM_ が含まれていることがわかります。例えば、cornernet_hourglass104_mstest_32x3_210e_coco.py のバッチサイズは 32 x 3 = 96scnet_x101_64x4d_fpn_8x1_20e_coco.py のバッチサイズは 8 x 1 = 8 です。

使用する特定の構成ファイルの一番下を確認してください。16 以外のバッチサイズの場合、auto_scale_lr.base_batch_size が記載されています。これらの値が見つからない場合は、_base_=[xxx] の中の構成ファイルを確認してください。LR を自動的にスケーリングする場合は、これらの値を変更しないでください。

学習率の自動スケーリングの基本的な使い方は以下のとおりです。

python tools/train.py \
    ${CONFIG_FILE} \
    --auto-scale-lr \
    [optional arguments]

この機能を有効にすると、マシンのGPUの数とトレーニングのバッチサイズに応じて、学習率が自動的にスケーリングされます。詳細は 線形スケーリングルール を参照してください。例えば、GPUが4台で各GPUに2枚の画像がある場合、lr = 0.01 となります。そして、GPU が 16 台で各 GPU に 4 枚の画像がある場合は、lr = 0.08 に自動的にスケールされます。

使用しない場合は、線形スケーリングルール に従って学習率を手動で計算し、特定の構成ファイルで optimizer.lr を変更する必要があります。

単一GPUでの訓練

単一GPUで訓練ジョブを起動するために、tools/train.py を提供しています。基本的な使い方は以下のとおりです。

python tools/train.py \
    ${CONFIG_FILE} \
    [optional arguments]

訓練中、ログファイルとチェックポイントは、構成ファイルの work_dir または CLI 引数 --work-dir で指定された作業ディレクトリに保存されます。

デフォルトでは、モデルは毎エポック検証セットで評価されます。評価間隔は、以下のように構成ファイルで指定できます。

# evaluate the model every 12 epochs.
train_cfg = dict(val_interval=12)

このツールは、以下のいくつかのオプション引数を受け入れます。

  • --work-dir ${WORK_DIR}: 作業ディレクトリを上書きします。

  • --resume: work_dir 内の最新のチェックポイントから自動的に再開します。

  • --resume ${CHECKPOINT_FILE}: 特定のチェックポイントから再開します。

  • --cfg-options 'Key=value': 使用される構成の他の設定を上書きします。

注記

resumeload-from の違いがあります。

resume はモデルの重みとオプティマイザの状態の両方をロードし、指定されたチェックポイントから反復回数を継承するため、トレーニングは最初からやり直しません。load-from は、モデルの重みのみをロードし、トレーニングは最初から開始されます。ファインチューニングによく使用されます。load-from は構成ファイルに記述する必要がありますが、resume はコマンドライン引数として渡されます。

CPUでの訓練

CPUでの訓練プロセスは、単一GPUでの訓練と同様です。訓練プロセス前にGPUを無効にするだけです。

export CUDA_VISIBLE_DEVICES=-1

そして、上記のスクリプトを実行します。

注記:

CPU を使用したトレーニングは非常に遅いため、お勧めしません。この機能は、GPUのないマシンでユーザーが簡単にデバッグできるようにするためサポートしています。

複数GPUでのトレーニング

複数GPUでのトレーニング実行には、tools/dist_train.sh を提供しています。基本的な使い方は以下のとおりです。

bash ./tools/dist_train.sh \
    ${CONFIG_FILE} \
    ${GPU_NUM} \
    [optional arguments]

オプション引数は上記で説明したとおりです。

複数ジョブの同時実行

1台のマシン上で複数のジョブを実行する場合(例:8GPUのマシンで4GPUトレーニングのジョブを2つ実行する場合)、通信競合を避けるために、各ジョブに異なるポート(デフォルトは29500)を指定する必要があります。

dist_train.shを使用してトレーニングジョブを実行する場合は、コマンドでポートを設定できます。

CUDA_VISIBLE_DEVICES=0,1,2,3 PORT=29500 ./tools/dist_train.sh ${CONFIG_FILE} 4
CUDA_VISIBLE_DEVICES=4,5,6,7 PORT=29501 ./tools/dist_train.sh ${CONFIG_FILE} 4

複数マシンでのトレーニング

イーサネット接続の複数マシンで実行する場合は、以下のコマンドを実行するだけで済みます。

最初のマシン

NNODES=2 NODE_RANK=0 PORT=$MASTER_PORT MASTER_ADDR=$MASTER_ADDR sh tools/dist_train.sh $CONFIG $GPUS

2台目のマシン

NNODES=2 NODE_RANK=1 PORT=$MASTER_PORT MASTER_ADDR=$MASTER_ADDR sh tools/dist_train.sh $CONFIG $GPUS

InfiniBandなどの高速ネットワークがない場合は、通常遅くなります。

Slurmを使用したジョブ管理

Slurm は、コンピューティングクラスタのための優れたジョブスケジューリングシステムです。Slurmで管理されているクラスタでは、slurm_train.sh を使用してトレーニングジョブを生成できます。シングルノードとマルチノードのトレーニングの両方をサポートしています。

基本的な使い方は以下のとおりです。

[GPUS=${GPUS}] ./tools/slurm_train.sh ${PARTITION} ${JOB_NAME} ${CONFIG_FILE} ${WORK_DIR}

以下は、Slurmパーティション名「dev」で16個のGPUを使用してMask R-CNNをトレーニングし、作業ディレクトリを共有ファイルシステムに設定する例です。

GPUS=16 ./tools/slurm_train.sh dev mask_r50_1x configs/mask-rcnn_r50_fpn_1x_coco.py /nfs/xxxx/mask_rcnn_r50_fpn_1x

完全な引数と環境変数については、ソースコード を確認してください。

Slurmを使用する場合は、次のいずれかの方法でポートオプションを設定する必要があります。

  1. --options を通じてポートを設定します。元の構成を変更しないため、この方法が推奨されます。

    CUDA_VISIBLE_DEVICES=0,1,2,3 GPUS=4 ./tools/slurm_train.sh ${PARTITION} ${JOB_NAME} config1.py ${WORK_DIR} --cfg-options 'dist_params.port=29500'
    CUDA_VISIBLE_DEVICES=4,5,6,7 GPUS=4 ./tools/slurm_train.sh ${PARTITION} ${JOB_NAME} config2.py ${WORK_DIR} --cfg-options 'dist_params.port=29501'
    
  2. 構成ファイルを変更して、異なる通信ポートを設定します。

    config1.pyでは、以下を設定します。

    dist_params = dict(backend='nccl', port=29500)
    

    config2.pyでは、以下を設定します。

    dist_params = dict(backend='nccl', port=29501)
    

    その後、config1.pyconfig2.pyを使用して2つのジョブを実行できます。

    CUDA_VISIBLE_DEVICES=0,1,2,3 GPUS=4 ./tools/slurm_train.sh ${PARTITION} ${JOB_NAME} config1.py ${WORK_DIR}
    CUDA_VISIBLE_DEVICES=4,5,6,7 GPUS=4 ./tools/slurm_train.sh ${PARTITION} ${JOB_NAME} config2.py ${WORK_DIR}
    

カスタムデータセットでのトレーニング

このパートでは、カスタムデータセットで事前に定義されたモデルをトレーニングし、テストする方法について説明します。プロセス全体を説明するために、balloonデータセット を例として使用します。

基本的な手順は以下のとおりです。

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

  2. 設定ファイルの準備

  3. カスタムデータセットでのモデルのトレーニング、テスト、推論。

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

MMDetectionで新しいデータセットをサポートする方法は3つあります。

  1. データセットをCOCO形式に再構成する。

  2. データセットを中間形式に再構成する。

  3. 新しいデータセットを実装する。

通常、最初の2つの方法は3番目の方法よりも簡単であるため、推奨されます。

このノートでは、データをCOCO形式に変換する例を示します。

**注記:** MMDetection 3.0以降、CityScapesを除き、データセットとメトリクスは分離されています。そのため、検証中は、任意の形式のデータセットに任意の種類の評価メトリクスを使用できます。例:COCOデータセットをVOCメトリクスで評価する、またはOpenImagesデータセットをVOCとCOCOの両方のメトリクスで評価する。

COCOアノテーション形式

インスタンスセグメンテーションのためのCOCO形式に必要なキーは下記のとおりです。詳細については、こちらを参照してください。

{
    "images": [image],
    "annotations": [annotation],
    "categories": [category]
}

image = {
    "id": int,
    "width": int,
    "height": int,
    "file_name": str,
}

annotation = {
    "id": int,
    "image_id": int,
    "category_id": int,
    "segmentation": RLE or [polygon],
    "area": float,
    "bbox": [x,y,width,height], # (x, y) are the coordinates of the upper left corner of the bbox
    "iscrowd": 0 or 1,
}

categories = [{
    "id": int,
    "name": str,
    "supercategory": str,
}]

balloonデータセットを使用すると仮定します。データをダウンロードした後、アノテーション形式をCOCO形式に変換する関数を実装する必要があります。その後、実装されたCocoDatasetを使用してデータを読み込み、トレーニングと評価を実行できます。

データセットを見ると、データセット形式は以下のようになっていることがわかります。

{'base64_img_data': '',
 'file_attributes': {},
 'filename': '34020010494_e5cb88e1c4_k.jpg',
 'fileref': '',
 'regions': {'0': {'region_attributes': {},
   'shape_attributes': {'all_points_x': [1020,
     1000,
     994,
     1003,
     1023,
     1050,
     1089,
     1134,
     1190,
     1265,
     1321,
     1361,
     1403,
     1428,
     1442,
     1445,
     1441,
     1427,
     1400,
     1361,
     1316,
     1269,
     1228,
     1198,
     1207,
     1210,
     1190,
     1177,
     1172,
     1174,
     1170,
     1153,
     1127,
     1104,
     1061,
     1032,
     1020],
    'all_points_y': [963,
     899,
     841,
     787,
     738,
     700,
     663,
     638,
     621,
     619,
     643,
     672,
     720,
     765,
     800,
     860,
     896,
     942,
     990,
     1035,
     1079,
     1112,
     1129,
     1134,
     1144,
     1153,
     1166,
     1166,
     1150,
     1136,
     1129,
     1122,
     1112,
     1084,
     1037,
     989,
     963],
    'name': 'polygon'}}},
 'size': 1115004}

アノテーションはJSONファイルであり、各キーは画像のすべてのアノテーションを示しています。balloonデータセットをcoco形式に変換するコードは以下のとおりです。

import os.path as osp

import mmcv

from mmengine.fileio import dump, load
from mmengine.utils import track_iter_progress


def convert_balloon_to_coco(ann_file, out_file, image_prefix):
    data_infos = load(ann_file)

    annotations = []
    images = []
    obj_count = 0
    for idx, v in enumerate(track_iter_progress(data_infos.values())):
        filename = v['filename']
        img_path = osp.join(image_prefix, filename)
        height, width = mmcv.imread(img_path).shape[:2]

        images.append(
            dict(id=idx, file_name=filename, height=height, width=width))

        for _, obj in v['regions'].items():
            assert not obj['region_attributes']
            obj = obj['shape_attributes']
            px = obj['all_points_x']
            py = obj['all_points_y']
            poly = [(x + 0.5, y + 0.5) for x, y in zip(px, py)]
            poly = [p for x in poly for p in x]

            x_min, y_min, x_max, y_max = (min(px), min(py), max(px), max(py))

            data_anno = dict(
                image_id=idx,
                id=obj_count,
                category_id=0,
                bbox=[x_min, y_min, x_max - x_min, y_max - y_min],
                area=(x_max - x_min) * (y_max - y_min),
                segmentation=[poly],
                iscrowd=0)
            annotations.append(data_anno)
            obj_count += 1

    coco_format_json = dict(
        images=images,
        annotations=annotations,
        categories=[{
            'id': 0,
            'name': 'balloon'
        }])
    dump(coco_format_json, out_file)


if __name__ == '__main__':
    convert_balloon_to_coco(ann_file='data/balloon/train/via_region_data.json',
                            out_file='data/balloon/train/annotation_coco.json',
                            image_prefix='data/balloon/train')
    convert_balloon_to_coco(ann_file='data/balloon/val/via_region_data.json',
                            out_file='data/balloon/val/annotation_coco.json',
                            image_prefix='data/balloon/val')

上記の関数を使用すると、ユーザーはアノテーションファイルをJSON形式に正常に変換できます。その後、CocoDatasetを使用してCocoMetricでモデルのトレーニングと評価を行うことができます。

設定ファイルの準備

2番目の手順は、データセットが正常に読み込まれるように設定ファイルを用意することです。FPN付きのMask R-CNNを使用すると仮定すると、balloonデータセットで検出器をトレーニングするための設定ファイルは以下のとおりです。configs/balloon/ディレクトリにあり、mask-rcnn_r50-caffe_fpn_ms-poly-1x_balloon.pyという名前であると仮定します。設定ファイルは以下のとおりです。Learn about Configs - MMDetection 3.0.0 documentation を参照して、設定ファイルに関する詳細情報を入手してください。

# The new config inherits a base config to highlight the necessary modification
_base_ = '../mask_rcnn/mask-rcnn_r50-caffe_fpn_ms-poly-1x_coco.py'

# We also need to change the num_classes in head to match the dataset's annotation
model = dict(
    roi_head=dict(
        bbox_head=dict(num_classes=1), mask_head=dict(num_classes=1)))

# Modify dataset related settings
data_root = 'data/balloon/'
metainfo = {
    'classes': ('balloon', ),
    'palette': [
        (220, 20, 60),
    ]
}
train_dataloader = dict(
    batch_size=1,
    dataset=dict(
        data_root=data_root,
        metainfo=metainfo,
        ann_file='train/annotation_coco.json',
        data_prefix=dict(img='train/')))
val_dataloader = dict(
    dataset=dict(
        data_root=data_root,
        metainfo=metainfo,
        ann_file='val/annotation_coco.json',
        data_prefix=dict(img='val/')))
test_dataloader = val_dataloader

# Modify metric related settings
val_evaluator = dict(ann_file=data_root + 'val/annotation_coco.json')
test_evaluator = val_evaluator

# We can use the pre-trained Mask RCNN model to obtain higher performance
load_from = 'https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_caffe_fpn_mstrain-poly_3x_coco/mask_rcnn_r50_caffe_fpn_mstrain-poly_3x_coco_bbox_mAP-0.408__segm_mAP-0.37_20200504_163245-42aa3d00.pth'

新しいモデルのトレーニング

新しい設定ファイルでモデルをトレーニングするには、次のように実行するだけです。

python tools/train.py configs/balloon/mask-rcnn_r50-caffe_fpn_ms-poly-1x_balloon.py

詳細な使用方法については、トレーニングガイド を参照してください。

テストと推論

トレーニング済みのモデルをテストするには、次のように実行するだけです。

python tools/test.py configs/balloon/mask-rcnn_r50-caffe_fpn_ms-poly-1x_balloon.py work_dirs/mask-rcnn_r50-caffe_fpn_ms-poly-1x_balloon/epoch_12.pth

詳細な使用方法については、テストガイド を参照してください。