mmdetection爆速入門

# この記事は？
mmdetectionという物体検出モデルを簡単に利用＆構築できる、最近便利に使わせていただいているツールの紹介です。

- [公式リポジトリ](https://github.com/open-mmlab/mmdetection)
- [公式ドキュメント](https://mmdetection.readthedocs.io/en/latest/)

色々な人が使い方を紹介してくださっている上に、公式にも[わかりやすいチュートリアル](https://github.com/open-mmlab/mmdetection/blob/master/demo/MMDet_Tutorial.ipynb)があるので自分で記事を書くのもどうかと思ったのですが、一応さらに簡潔でわかりやすいものを目指して書いています。

このツール、**データセットさえ用意すればConfigの編集だけである程度なんでもできる**ので、ぜひ試してみてください。

# 1. mmdetのインストール
[公式のチュートリアル](https://github.com/open-mmlab/mmdetection/blob/master/demo/MMDet_Tutorial.ipynb)ではgithubからcloneしてインストールしていますが、直接pipでインストールできます。

### 1-1. CUDAのバージョンを確認
あとで必要になるので確認します。
```bash
nvcc -V
 # nvcc: NVIDIA (R) Cuda compiler driver
 # Copyright (c) 2005-2020 NVIDIA Corporation
 # Built on Mon_Oct_12_20:09:46_PDT_2020
 # Cuda compilation tools, release 11.1, V11.1.105
 # Build cuda_11.1.TC455_06.29190527_0
```
### 1-2. pytorchのバージョンを確認
こちらも後で必要になるので確認します。
```python
import torch
print(torch.__version__)
 # 1.10.0+cu111
print(torch.cuda.is_available())
 # True
```
### 1-3. mmcvをpipでインストールする
mmdetはmmcvの機能を前提としています。以下の方法でインストールしましょう。
```bash
pip install mmcv-full -f https://download.openmmlab.com/mmcv/dist/cu111/torch1.10.0/index.html
```
ここで、`cu111`, `torch1.10.0`の部分を先ほど確かめた自分の環境に合わせた数字に置き換えてください。
* mmcv-fullのバージョンが新しすぎると入らない場合があります。
[少し古いバージョンを指定してインストールすると上手く動作することがあるようです](https://github.com/open-mmlab/mmcv/issues/1752)
`pip install mmcv-full==1.4.5 -f https://...`など
### 1-4. mmdetectionをpipでインストールする
これはとても簡単です。
```bash
pip install mmdet
```

# 2. 推論を行い動作確認する
### 2-1. 必要なデータのダウンロード
モデルのデータをダウンロードします。
configファイルを持ってくるためにgithubから公式リポジトリをcloneしていますが、必要なのは`configs`, `tools`, `demo`の３つのフォルダのみです。

```bash
mkdir checkpoints
wget -c https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_caffe_fpn_mstrain_3x_coco/faster_rcnn_r50_caffe_fpn_mstrain_3x_coco_20210526_095054-1f77628b.pth \
      -O checkpoints/faster_rcnn_r50_caffe_fpn_mstrain_3x_coco_20210526_095054-1f77628b.pth
git clone https://github.com/open-mmlab/mmdetection.git
```

### 2-2. 推論の実行
公式チュートリアルとconfigへのパスが異なるだけで、同じです。

```python
import mmcv
from mmcv.runner import load_checkpoint

from mmdet.apis import inference_detector, show_result_pyplot
from mmdet.models import build_detector

config = './mmdetection/configs/faster_rcnn/faster_rcnn_r50_caffe_fpn_mstrain_3x_coco.py'
checkpoint = './checkpoints/faster_rcnn_r50_caffe_fpn_mstrain_3x_coco_20210526_095054-1f77628b.pth'

device='cuda:0'
config = mmcv.Config.fromfile(config)
config.model.pretrained = None. # checkpointを読み込むのでいらない
model = build_detector(config.model)
checkpoint = load_checkpoint(model, checkpoint, map_location=device)

# 推論前にクラス数とconfigファイルを読み込ませる必要がある
model.CLASSES = checkpoint['meta']['CLASSES']
model.cfg = config
model.to(device)
model.eval()

# 推論の実行と結果の表示
img = './mmdetection/demo/demo.jpg'
result = inference_detector(model, img)
show_result_pyplot(model, img, result)
```
こんな画像が表示されたら成功です。
![ダウンロード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/516969/e5ce5045-94eb-1597-fa1d-65690084726e.png)
推論だけでしたら、configとcheckpointの部分さえ変えてしまえば動きます。

# 3. 独自データで訓練を行う
ここからが本番です。
公式では独自のデータに対応させる方法が詳しく書かれていますが、**基本的にCOCOなどの形式に沿って独自データを変換してあげたほうが楽です。**

ここでは例として、[Instance Segmentationのチュートリアル](https://github.com/open-mmlab/mmdetection/blob/master/demo/MMDet_InstanceSeg_Tutorial.ipynb)で取り上げられているbaloonデータセットに対して訓練を行ってみます。コードは前述のチュートリアルから引用させていただきました。


### 3-1. データセットの準備
まず、データをダウンロードします。
```
!wget https://github.com/matterport/Mask_RCNN/releases/download/v2.1/balloon_dataset.zip
!unzip balloon_dataset.zip > /dev/null
```

次に、データをCOCO形式へ変換します。
（データによってここは異なるため、非表示にしています。）

<details><summary>convert_balloon_to_coco</summary><div>

```python
import os.path as osp

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

    annotations = []
    images = []
    obj_count = 0
    for idx, v in enumerate(mmcv.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))

        bboxes = []
        labels = []
        masks = []
        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'}])
    mmcv.dump(coco_format_json, out_file)
```
</div></details>

```python
convert_balloon_to_coco(
    'balloon/train/via_region_data.json',
    'balloon/train/annotation_coco.json',
    'balloon/train/')
convert_balloon_to_coco(
    'balloon/val/via_region_data.json',
    'balloon/val/annotation_coco.json',
    'balloon/val/')
```
これでCOCO形式のデータが用意できました。

### 3-2. Configの編集
ここが1番重要です。
データに合わせてプリセットのConfigを書き換えていきます。



```python
from mmdet.apis import set_random_seed

# COCO形式のデータが読み込めるように変更
cfg.dataset_type = 'COCODataset'

# アノテーションファイルの場所、画像ファイルの場所、クラス名を記載
# train, validは訓練中に、testはテスト中に使用されるデータの定義
cfg.data.train.ann_file = 'balloon/train/annotation_coco.json'
cfg.data.train.img_prefix = 'balloon/train/'
cfg.data.train.classes = ('balloon',)

cfg.data.val.ann_file = 'balloon/val/annotation_coco.json'
cfg.data.val.img_prefix = 'balloon/val/'
cfg.data.val.classes = ('balloon',)

# ここではvalidと同じにしておく
cfg.data.test.ann_file = 'balloon/val/annotation_coco.json'
cfg.data.test.img_prefix = 'balloon/val/'
cfg.data.test.classes = ('balloon',)

# モデルの出力クラス数を変更
cfg.model.roi_head.bbox_head.num_classes = 1

# 事前学習モデルがある場合には
cfg.load_from = './checkpoints/faster_rcnn_r50_caffe_fpn_mstrain_3x_coco_20210526_095054-1f77628b.pth'

# logなどを吐き出す先を決定
cfg.work_dir = './tutorial_exps'

# 8GPU用に設定されている学習率を元に戻す
cfg.optimizer.lr = 0.02 / 8
cfg.lr_config.warmup = None
cfg.log_config.interval = 10

# 評価指標や評価の頻度を設定
cfg.evaluation.metric = 'bbox'
cfg.evaluation.interval = 2
cfg.checkpoint_config.interval = 2

# ランダムシードの設定
cfg.seed = 0
set_random_seed(0, deterministic=False)
cfg.gpu_ids = range(1)

# tensorboardも利用できる
cfg.log_config.hooks = [
    dict(type='TextLoggerHook'),
    dict(type='TensorboardLoggerHook')]

# いい感じに出力できる
# print(f'Config:\n{cfg.pretty_text}')

# ファイルとして保存もできる
cfg.dump('./my_config.py')
```

保存された`./my_config.py`を眺めると、大体どんなことができるか掴めると思います。

### 3-3. モデルの訓練
ここまでくれば簡単です。
`tools`以下に`train.py`というファイルがあるので、こちらを利用します。
```bash
python ./mmdetection/tools/train.py ./my_config.py
```

学習済みのモデルはConfigで指定した`tutorial_exps`に入っています。
学習が完了したら、先ほどと同様の手順で推論を行なってみましょう。

```python
config = './tutorial_exps/my_config.py'
checkpoint = './tutorial_exps/latest.pth'

device='cuda:0'
config = mmcv.Config.fromfile(config)
config.model.pretrained = None
model = build_detector(config.model)
checkpoint = load_checkpoint(model, checkpoint, map_location=device)

model.CLASSES = checkpoint['meta']['CLASSES']
model.cfg = config
model.to(device)
model.eval()

img = 'balloon/val/16335852991_f55de7958d_k.jpg'
result = inference_detector(model, img)
show_result_pyplot(model, img, result)
```
こんな画像が表示されたら成功です。
きちんと風船が認識されているのではないでしょうか？
![ダウンロード (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/516969/7b9f9ea5-1af4-c468-c425-f6e718c50ead.png)



### 3-4. モデルの評価など
先ほどと同様に、configファイルさえ作ってしまえばあとはどうとでもなります。
[公式ドキュメントのuseful_tools](https://mmdetection.readthedocs.io/en/latest/useful_tools.html)にさまざまなツールの使い方が書いてあるので参考にしてください。

# さいごに
さて、気づいたでしょうか。
実はモデルの学習を行うまでに自分で編集したファイルは、（データセット整備部分を除けば）Configファイルの生成部分だけです。

データセットの整理とConfigファイルの編集の部分さえなんとかしてしまえば、あとは[mmsegmentation](https://github.com/open-mmlab/mmsegmentation)などの他のmmlabが提供するツールも使えるようになるのではないでしょうか？

需要あるかわからないですが、次はConfigファイルを編集してBackboneモデルを載せ替えて訓練する記事なんかも書いてみようと思ってます。