MMDetection フレームワークのトレーニングとテストの全プロセス

序文

  • MMDetectionこれはオブジェクト検出ツールボックスであり、豊富なオブジェクト検出、インスタンス セグメンテーション、パノラマ セグメンテーション アルゴリズム、関連コンポーネントとモジュール、github プロジェクト アドレスが含まれています。
  • サポートされている物体検出 ( Object Detection) モデル (近年の一部の SOTA モデル): DAB-DETR、RTMDet、GLIP、Detic、DINO
  • サポートされているインスタンス セグメンテーション ( Instance Segmentation) モデル (近年の一部の SOTA モデル): Mask2former、BoxInst、SparseInst、RTMDet
  • サポートされているパノプティック セグメンテーション ( Panoptic Segmentation) モデル: パノプティック FPN、MaskFormer、Mask2Former
  • インスタンス セグメンテーションとパノラマ セグメンテーションの違い: パノラマ セグメンテーションはピクセル レベルのセマンティック カテゴリとインスタンス識別子の両方を提供しますが、インスタンス セグメンテーションはオブジェクト インスタンスの境界とセグメンテーションのみに焦点を当てます。パノラマ セグメンテーションは、より包括的な情報を提供し、自動運転など、各ピクセルの詳細な分析が必要なタスクに適しています。インスタンス セグメンテーションは、オブジェクト インスタンスの検出とセグメント化に重点を置いており、オブジェクトの検出や画像のセグメンテーションなどのタスクに適しています。
  • この記事では、主にMMDetectionトレーニングとテストのプロセスを紹介し、データセット上でモデルをDog and Cat Detection微調整し、モデルを分析し、最終的なモデルインデックスは0.952 に達しました。RTMDetRTMDetbbox_mAP

環境構成

  • 完全な環境構成コードは次のとおりです。段階的な分析を見たくない場合は、このセクションの残りの部分をスキップしてください。
import IPython.display as display

!pip install openmim
!mim install mmengine==0.7.2
# 构建wheel,需要30分钟,构建好以后将whl文件放入单独的文件夹
# !git clone https://github.com/open-mmlab/mmcv.git
# !cd mmcv && CUDA_HOME=/usr/local/cuda-11.8 MMCV_WITH_OPS=1 pip wheel --wheel-dir=/kaggle/working .
!pip install -q /kaggle/input/frozen-packages-mmdetection/mmcv-2.0.1-cp310-cp310-linux_x86_64.whl

!rm -rf mmdetection
!git clone https://github.com/open-mmlab/mmdetection.git
!git clone https://github.com/open-mmlab/mmyolo.git
%cd mmdetection

%pip install -e .

!pip install wandb
display.clear_output()
  • 最初にopen-mmlabパッケージ管理ライブラリをインストールしopenmim、次にmmengineライブラリをインストールします。コードは次のとおりです。
!pip install openmim
!mim install mmengine==0.7.2
  • 直接インストールするkaggleことはできないため(後続のトレーニングでエラーが報告されます)、ビルドすることによってのみインストールできます。mimmmcvwheel
!git clone https://github.com/open-mmlab/mmcv.git
!cd mmcv && CUDA_HOME=/usr/local/cuda-11.8 MMCV_WITH_OPS=1 pip wheel --wheel-dir=/kaggle/working .
  • 上記の手順は約 30 分間待つ必要があります。その後、ディレクトリ内にファイルが/kaggle/working見つかるので、インストールを使用するだけです。ただし、時間を節約し、実行のたびに長時間待機する必要がないように、毎回データ セットをロードするだけでインストールできるようにビルドをダウンロードしてアップロードします。データ アドレスはここで提供されます。したがって、インストールコードは次のようになります。mmcv-2.0.1-cp310-cp310-linux_x86_64.whlpip install -q /kaggle/working/mmcv-2.0.1-cp310-cp310-linux_x86_64.whlwheelkaggle Datasets
!pip install -q /kaggle/input/frozen-packages-mmdetection/mmcv-2.0.1-cp310-cp310-linux_x86_64.whl
  • データセットはgit cloneサフィックスであるため、後で形式を変換するツールを使用する必要があるため一緒にダウンロードしますが、インストールはしませんmmdetection.xmlmmyolommyolo
!rm -rf mmdetection
!git clone https://github.com/open-mmlab/mmdetection.git
!git clone https://github.com/open-mmlab/mmyolo.git

# 进入mmdetection项目文件夹
%cd mmdetection

# 安装mmdetection
%pip install -e .
!pip install wandb

import wandb
wandb.login()

モデル推論

  • checkpointsまず、モデルの事前トレーニングされた重みを保存するフォルダーを作成します。RTMDetモデルを選択したので、対応するウェイトをダウンロードします。
  • mmdetectionGithub プロジェクトのアドレスを開いてconfigs/rtmdetパスを入力すると、README.mdファイル内に詳細な事前トレーニングの重みが含まれています。
    ここに画像の説明を挿入
  • モデル パラメーター ( )Paramsが多いほど、box AP精度指数 ( ) が高くなることがわかります。適度な量のパラメーターを持つモデルを選択しRTMDet-l、対応するconfigsファイル名は ですrtmdet_l_8xb32-300e_coco.pybatch size8 つの GPU (それぞれ 32 個) 上で、cocoデータセットの300 のepochs重みでトレーニングされたRTMDet-l モデルを意味します。checkpointsダウンロードしてフォルダに保存
!mkdir ./checkpoints
!mim download mmdet --config rtmdet_l_8xb32-300e_coco --dest ./checkpoints
  • モデルを使用して推論し、推論の結果を視覚化する
from mmdet.apis import DetInferencer

model_name = 'rtmdet_l_8xb32-300e_coco'
checkpoint = './checkpoints/rtmdet_l_8xb32-300e_coco_20220719_112030-5a0be7c4.pth'

device = 'cuda:0'

inferencer = DetInferencer(model_name, checkpoint, device)

img = './demo/demo.jpg'

result = inferencer(img, out_dir='./output')
display.clear_output()

from PIL import Image
Image.open('./output/vis/demo.jpg')

画像の説明を追加してください

  • ここまで問題がなければ、環境構築は非常に成功しており、RTMDet モデルが推論を行います。

データ照合

  • データセットDog and Cat Detectionファイルの構成情報:
 - Dog-and-Cat-Detection
     - annotations
         - Cats_Test0.xml
         - Cats_Test1.xml
         - Cats_Test2.xml
         - ...
     - images
         - Cats_Test0.png
         - Cats_Test1.png
         - Cats_Test2.png
         - ...
  • パスの下に設定されたデータは読み取り専用であるため、変更することはできません。マークされたファイルは変換する必要がある形式です。ここでは、まず画像をディレクトリにコピーkaggleますinput.xml./data/images
import shutil

# 复制文件到工作目录
shutil.copytree('/kaggle/input/dog-and-cat-detection/images', './data/images')
  • その後のデータセットのセグメント化では.json形式としてラベル情報が必要となるため、dog-and-cat-detection/annotationsフォルダー内のファイルを.xml1 つのファイルに変換します.json
import xml.etree.ElementTree as ET
import os
import json

coco = dict()
coco['images'] = []
coco['type'] = 'instances'
coco['annotations'] = []
coco['categories'] = []

category_set = dict()
image_set = set()

category_item_id = -1
image_id = 0
annotation_id = 0


def addCatItem(name):
    global category_item_id
    category_item = dict()
    category_item['supercategory'] = 'none'
    category_item_id += 1
    category_item['id'] = category_item_id
    category_item['name'] = name
    coco['categories'].append(category_item)
    category_set[name] = category_item_id
    return category_item_id


def addImgItem(file_name, size):
    global image_id
    if file_name is None:
        raise Exception('Could not find filename tag in xml file.')
    if size['width'] is None:
        raise Exception('Could not find width tag in xml file.')
    if size['height'] is None:
        raise Exception('Could not find height tag in xml file.')
    image_id += 1
    image_item = dict()
    image_item['id'] = image_id
    image_item['file_name'] = file_name + ".png"
    image_item['width'] = size['width']
    image_item['height'] = size['height']
    coco['images'].append(image_item)
    image_set.add(file_name)
    return image_id


def addAnnoItem(object_name, image_id, category_id, bbox):
    global annotation_id
    annotation_item = dict()
    annotation_item['segmentation'] = []
    seg = []
    seg.append(bbox[0])
    seg.append(bbox[1])
    seg.append(bbox[0])
    seg.append(bbox[1] + bbox[3])
    seg.append(bbox[0] + bbox[2])
    seg.append(bbox[1] + bbox[3])
    seg.append(bbox[0] + bbox[2])
    seg.append(bbox[1])

    annotation_item['segmentation'].append(seg)

    annotation_item['area'] = bbox[2] * bbox[3]
    annotation_item['iscrowd'] = 0
    annotation_item['ignore'] = 0
    annotation_item['image_id'] = image_id
    annotation_item['bbox'] = bbox
    annotation_item['category_id'] = category_id
    annotation_id += 1
    annotation_item['id'] = annotation_id
    coco['annotations'].append(annotation_item)


def parseXmlFiles(xml_path):
    for f in os.listdir(xml_path):
        if not f.endswith('.xml'):
            continue
        xmlname = f.split('.xml')[0]

        bndbox = dict()
        size = dict()
        current_image_id = None
        current_category_id = None
        file_name = None
        size['width'] = None
        size['height'] = None
        size['depth'] = None

        xml_file = os.path.join(xml_path, f)

        tree = ET.parse(xml_file)
        root = tree.getroot()
        if root.tag != 'annotation':
            raise Exception('pascal voc xml root element should be annotation, rather than {}'.format(root.tag))

        for elem in root:
            current_parent = elem.tag
            current_sub = None
            object_name = None

            if elem.tag == 'folder':
                continue

            if elem.tag == 'filename':
                file_name = xmlname
                if file_name in category_set:
                    raise Exception('file_name duplicated')

            elif current_image_id is None and file_name is not None and size['width'] is not None:
                if file_name not in image_set:
                    current_image_id = addImgItem(file_name, size)
                else:

                    raise Exception('duplicated image: {}'.format(file_name))

            for subelem in elem:
                bndbox['xmin'] = None
                bndbox['xmax'] = None
                bndbox['ymin'] = None
                bndbox['ymax'] = None

                current_sub = subelem.tag
                if current_parent == 'object' and subelem.tag == 'name':
                    object_name = subelem.text
                    if object_name not in category_set:
                        current_category_id = addCatItem(object_name)
                    else:
                        current_category_id = category_set[object_name]

                elif current_parent == 'size':
                    if size[subelem.tag] is not None:
                        raise Exception('xml structure broken at size tag.')
                    size[subelem.tag] = int(subelem.text)

                for option in subelem:
                    if current_sub == 'bndbox':
                        if bndbox[option.tag] is not None:
                            raise Exception('xml structure corrupted at bndbox tag.')
                        bndbox[option.tag] = int(float(option.text))

                if bndbox['xmin'] is not None:
                    if object_name is None:
                        raise Exception('xml structure broken at bndbox tag')
                    if current_image_id is None:
                        raise Exception('xml structure broken at bndbox tag')
                    if current_category_id is None:
                        raise Exception('xml structure broken at bndbox tag')
                    bbox = []
                    bbox.append(bndbox['xmin'])
                    bbox.append(bndbox['ymin'])
                    bbox.append(bndbox['xmax'] - bndbox['xmin'])
                    bbox.append(bndbox['ymax'] - bndbox['ymin'])
                    addAnnoItem(object_name, current_image_id, current_category_id, bbox)

os.makedirs('./data/annotations')
xml_path = '/kaggle/input/dog-and-cat-detection/annotations'
json_file = './data/annotations/annotations_all.json'
parseXmlFiles(xml_path)
json.dump(coco, open(json_file, 'w'))
  • 現在の作業ディレクトリのデータ ストレージ ファイル構成情報:
 - mmdetection
    - data
         - annotations
             - annotations_all.json
         - images
             - Cats_Test0.png
             - Cats_Test1.png
             - Cats_Test2.png
             - ....
     - ...
  • mmyoloプロジェクト ファイル内のスクリプトを使用してデータをトレーニング セットとテスト セットに分割する必要があるため、最初にmmyoloプロジェクト フォルダーに入ります。
# 切换到mmyolo项目文件夹
%cd /kaggle/working/mmyolo
  • セグメンテーション スクリプト ファイルは にありtools/misc/coco_split.py、パラメータは上から下まで: --json (生成された.jsonファイルのパス)、--out-dir (生成されたセグメンテーション.jsonファイルの保存フォルダーのパス)、--ratios 0.8 0.2 (トレーニング セット、テスト セット) Proportion); –shuffle (順序をシャッフルするかどうか); –seed (乱数シード)
# 切分训练、测试集
!python tools/misc/coco_split.py --json /kaggle/working/mmdetection/data/annotations/annotations_all.json \
                                --out-dir /kaggle/working/mmdetection/data/annotations \
                                --ratios 0.8 0.2 \
                                --shuffle \
                                --seed 2023
  • 出力:
Split info: ====== 
Train ratio = 0.8, number = 2949
Val ratio = 0, number = 0
Test ratio = 0.2, number = 737
Set the global seed: 2023
shuffle dataset.
Saving json to /kaggle/working/mmdetection/data/annotations/trainval.json
Saving json to /kaggle/working/mmdetection/data/annotations/test.json
All done!
  • mmdetection次に、プロジェクト フォルダーに戻ります。
%cd /kaggle/working/mmdetection
  • このとき、作業ディレクトリのデータ保存ファイル構成情報は次のとおりです。
 - mmdetection
    - data
         - annotations
             - test.json
             - trainval.json
             - annotations_all.json
         - images
             - Cats_Test0.png
             - Cats_Test1.png
             - Cats_Test2.png
             - ....
     - ...

RTMDet モデル構成の編集

  • RTMDetモデル アーキテクチャ図は、対応するパラメーター フォルダーのドキュメントにありますREADME.md
    画像の説明を追加してください

  • github構成ファイルを開くことができますconfigs/rtmdet/rtmdet_l_8xb32-300e_coco.py(_base_ 値を確認し、継承関係がある場合は、メイン ファイルが見つかるまで検索できます)。モデルモデルはすでにメイン ファイルになっており、直接RTMDet-l表示できます。

  • 主に変更したいのは_base_、(上位ファイルの継承)、data_root(データ保存フォルダ)、 (トレーニングtrain_batch_size_per_gpuごと)、(コアジョブの数、一般的に)、(最大数)、(基本学習率)、(タイプ情報と各カテゴリに対応するパレット)、(ピクチャ パスとトレーニング セットのラベリング情報)、(ピクチャ パスと検証セットのラベリング情報)、(検証セットのラベリング情報)、(凍結されたバックボーン ネットワークの数、カテゴリの数)、(学習率の減衰)傾向)、(学習率の割り当て)、(モデルの重み保存戦略)、(データ パイプラインの切り替え)、(トレーニング前の重み読み込みパス)、(割り当てと検証測定)、(固定乱数シード)、(可視化プラットフォームの選択)GPUbatch sizetrain_num_workersn GPU x 4max_epochsepochbase_lrmetainfotrain_dataloaderval_dataloaderval_evaluatormodelstagesparam_scheduleroptim_wrapperdefault_hookscustom_hooksload_fromtrain_cfgmax_epochsrandomnessvisualizer

  • 設定ファイルで最も重要なのはmetainfoパラメーターとmodelパラメーターであり、カテゴリの数が正しいかどうか、パレットの数が一致しているかどうかを必ず確認してください。注: カテゴリが 1 つしかない場合でも、metainfo次のように記述する必要があります。'classes': ('cat', ),括弧内にはカンマが必要ですそうでない場合は、エラーが報告されます。modelinbbox_headは種の数とも一致する必要があります。

  • 学習率のスケーリングは通常、次の経験則に従いますbase_lr_default * (your_bs / default_bs)RTMDet上の構造図から、 4 つのモデルがあることがわかりstagesmodel構成はdict(backbone=dict(frozen_stages=4), bbox_head=dict(num_classes=2))4 つのモデルがフリーズしているstages、つまりバックボーン ネットワークが完全にフリーズしていることを示しています。

config_animals = """
# Inherit and overwrite part of the config based on this config
_base_ = './rtmdet_l_8xb32-300e_coco.py'

data_root = './data/' # dataset root

train_batch_size_per_gpu = 24
train_num_workers = 4

max_epochs = 50
stage2_num_epochs = 6
base_lr = 0.000375


metainfo = {
    'classes': ('cat', 'dog', ),
    'palette': [
        (252, 215, 99), (153, 197, 252), 
    ]
}

train_dataloader = dict(
    batch_size=train_batch_size_per_gpu,
    num_workers=train_num_workers,
    dataset=dict(
        data_root=data_root,
        metainfo=metainfo,
        data_prefix=dict(img='images/'),
        ann_file='annotations/trainval.json'))

val_dataloader = dict(
    batch_size=train_batch_size_per_gpu,
    num_workers=train_num_workers,
    dataset=dict(
        data_root=data_root,
        metainfo=metainfo,
        data_prefix=dict(img='images/'),
        ann_file='annotations/trainval.json'))

test_dataloader = val_dataloader

val_evaluator = dict(ann_file=data_root + 'annotations/trainval.json')

test_evaluator = val_evaluator

model = dict(backbone=dict(frozen_stages=4), bbox_head=dict(num_classes=2))

# learning rate
param_scheduler = [
    dict(
        type='LinearLR',
        start_factor=1.0e-5,
        by_epoch=False,
        begin=0,
        end=1000),
    dict(
        # use cosine lr from 10 to 20 epoch
        type='CosineAnnealingLR',
        eta_min=base_lr * 0.05,
        begin=max_epochs // 2,
        end=max_epochs,
        T_max=max_epochs // 2,
        by_epoch=True,
        convert_to_iter_based=True),
]

train_pipeline_stage2 = [
    dict(type='LoadImageFromFile', backend_args=None),
    dict(type='LoadAnnotations', with_bbox=True),
    dict(
        type='RandomResize',
        scale=(640, 640),
        ratio_range=(0.1, 2.0),
        keep_ratio=True),
    dict(type='RandomCrop', crop_size=(640, 640)),
    dict(type='YOLOXHSVRandomAug'),
    dict(type='RandomFlip', prob=0.5),
    dict(type='Pad', size=(640, 640), pad_val=dict(img=(114, 114, 114))),
    dict(type='PackDetInputs')
]

# optimizer
optim_wrapper = dict(
    _delete_=True,
    type='OptimWrapper',
    optimizer=dict(type='AdamW', lr=base_lr, weight_decay=0.05),
    paramwise_cfg=dict(
        norm_decay_mult=0, bias_decay_mult=0, bypass_duplicate=True))

default_hooks = dict(
    checkpoint=dict(
        interval=5,
        max_keep_ckpts=2,  # only keep latest 2 checkpoints
        save_best='auto'
    ),
    logger=dict(type='LoggerHook', interval=20))

custom_hooks = [
    dict(
        type='PipelineSwitchHook',
        switch_epoch=max_epochs - stage2_num_epochs,
        switch_pipeline=train_pipeline_stage2)
]

# load COCO pre-trained weight
load_from = './checkpoints/rtmdet_l_8xb32-300e_coco_20220719_112030-5a0be7c4.pth'

train_cfg = dict(type='EpochBasedTrainLoop', max_epochs=max_epochs, val_begin=20, val_interval=1)
randomness = dict(seed=2023, deterministic=True, diff_rank_seed=False)
visualizer = dict(vis_backends=[dict(type='LocalVisBackend'), dict(type='WandbVisBackend')])
"""

with open('./configs/rtmdet/rtmdet_l_1xb4-100e_animals.py', 'w') as f:
    f.write(config_animals)

モデルトレーニング

  • 上記の作業を行った後、モデルのトレーニングを開始できます
!python tools/train.py configs/rtmdet/rtmdet_l_1xb4-100e_animals.py
  • モデルepoch = 50- 時間精度
 Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.952
 Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets=100 ] = 1.000
 Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets=100 ] = 0.995
 Average Precision  (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.800
 Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.919
 Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.959
 Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=  1 ] = 0.964
 Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets= 10 ] = 0.965
 Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.965
 Average Recall     (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.800
 Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.939
 Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.970
07/10 07:35:26 - mmengine - INFO - bbox_mAP_copypaste: 0.952 1.000 0.995 0.800 0.919 0.959
07/10 07:35:27 - mmengine - INFO - Epoch(val) [50][123/123]    coco/bbox_mAP: 0.9520  coco/bbox_mAP_50: 1.0000  coco/bbox_mAP_75: 0.9950  coco/bbox_mAP_s: 0.8000  coco/bbox_mAP_m: 0.9190  coco/bbox_mAP_l: 0.9590  data_time: 0.0532  time: 0.8068
  • wandbプラットフォームを開き、トレーニングの精度を追跡し、さまざまな指標を視覚化できます
    ここに画像の説明を挿入
    ここに画像の説明を挿入

モデル推論

  • モデルを微調整した後、画像に基づいて推論することができます
from mmdet.apis import DetInferencer
import glob

config = 'configs/rtmdet/rtmdet_l_1xb4-100e_animals.py'
checkpoint = glob.glob('./work_dirs/rtmdet_l_1xb4-100e_animals/best_coco*.pth')[0]

device = 'cuda:0'

inferencer = DetInferencer(config, checkpoint, device)

img = './data/images/Cats_Test1011.png'
result = inferencer(img, out_dir='./output', pred_score_thr=0.6)

display.clear_output()
Image.open('./output/vis/Cats_Test1011.png')

画像の説明を追加してください

img = './data/images/Cats_Test1035.png'
result = inferencer(img, out_dir='./output', pred_score_thr=0.6)

display.clear_output()
Image.open('./output/vis/Cats_Test1035.png')

画像の説明を追加してください

おすすめ

転載: blog.csdn.net/qq_20144897/article/details/131717202