IceVisionによるObjectDetection

#これは何
2021年で**最もイケている物体検出フレームワーク**と言っても過言ではないIceVisionを使って標準データセットのペットデータセットを使った**Object Detection**を簡単に行うためのhands-on

#IceVisionとは
[過去記事](https://qiita.com/Ikwus/items/2a5d4b5ac2d00baf7fec)参照

#環境
https://qiita.com/Ikwus/items/2a5d4b5ac2d00baf7fec

Jupyterは結局オンプレのUbuntuで動かしています。
一応google colabでの動作確認も行いました。

#手順
基本的には公式のチュートリアル「How to train the Fridge Objects Dataset」を踏襲します。
ただ、アップデートにdocument/exampleの更新が追い付いていないようなので、適宜[icedata](https://github.com/airctic/icedata)のリポジトリも参照しながら書き換えていきます。

まず、使うライブラリをimportします。

```python
from icevision.all import *
import icedata
```

##モデル作成

次に、使いたいmodelを定義します。とりあえず動かすだけなのでMMDetectionのRetinanetにしておきます。
**[MMDetection](https://github.com/airctic/icevision/tree/master/icevision/models/mmdet/models)、[Torchvision](https://github.com/airctic/icevision/tree/master/icevision/models/torchvision)、[EfficientDet](https://github.com/airctic/icevision/tree/master/icevision/models/ross)、Yolov5**をサポートしており```model.(library).(model_name)```のように指定することでモデル構造を定義できます。

```python
model_type = models.mmdet.retinanet
```

続けてbackboneを定義します。
これもかなりサポートされており、MMDetectionに関しては対応するモデルの[backboneフォルダ](https://github.com/airctic/icevision/blob/master/icevision/models/mmdet/models/retinanet/backbones/resnet_fpn.py)に使えるbackboneが一覧で記載されています。(YoLoもディレクトリ内[backbone.py](https://github.com/airctic/icevision/blob/master/icevision/models/ultralytics/yolov5/backbones.py)にあります。)
TorchvisionとEfficientDetに関してはちょっと[違うところ](https://github.com/airctic/icevision/blob/master/icevision/backbones/backbones_effecientdet.md)に説明が置いてあります。開発途中だからなのかな。。。
今回はとりあえず```resnet50_fpn_1x```を使います。

```python
backbone = model_type.backbones.resnet50_fpn_1x
```

##データセット作成

続けて、datasetを読み込みます。
今回はicevisionにインストールされている**[The Oxford-IIIT Pet Dataset](https://www.robots.ox.ac.uk/~vgg/data/pets/)** (*CVPR* 2012)を使います。Fine-Grained Object Detectionとでも言うタスクになるんでしょうか。
ちなみに、執筆時点ではreadme.mdに記載されているfridgeデータセットは使えません。
使えるデータセットはicedataの[document](https://airctic.github.io/icedata/)に記載があり、
Birds (Caltech-UCSD Birds 200 Dataset)
Biwi (BIWI Sample Keypoints (center of face))
Coco (COCO Dataset)
Fridge (Fridge Objects Dataset)
Ochuman (OCHuman Dataset)
Pennfudan (Penn-Fudan Database for Pedestrian Detection and Segmentation)
Voc(PASCAL VOC 2012 Dataset)
があるようです。
ここでdatasetとclass_mapを入れておきます。

```python
data_dir = icedata.pets.load_data()
class_map = icedata.pets.class_map()
```

データセットに付属のperserが ```icedata.(dataset).parser``` に標準装備されており、簡単にパーサーを設定することができます。

```python
parser = icedata.pets.parser(data_dir = data_dir, mask=False)
```

このパーサーは```parser.parse()```を用いて分割することができますが、これには```data_splitter```のオプションがあります。

```python
data_splitter = RandomSplitter([0.9, 0.1])
train_records, valid_records = parser.parse(data_splitter)
```

データセットの可視化も ```show_records```を用いることで簡単にできます。

```python
show_records(train_records[:6], ncols=3, class_map=class_map)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/726758/7b0d7a5f-6f2c-e13b-588c-bb449565a141.png)

petsデータセットは犬派(犬:猫 = 2:1)なんですよね。

次に、データセットの形を整形します。と言ってもかなり簡単です。
このライブラリの目的は、**できるだけ少ない労力でモデルを変えつつ実験を行うこと**なので、```size```はEfficientDetに合わせて384にしておきます。
また、augumentationは```tfms.A.aug_tfms```で指定できます。(ほかのaugumentationも使えます。)
できたものを```Dataset()```でPyTorchのDatasetクラスに変換します。

```python
presize = 512
size = 384
train_tfms = tfms.A.Adapter([*tfms.A.aug_tfms(size=size, presize=presize), tfms.A.Normalize()])
valid_tfms = tfms.A.Adapter([*tfms.A.resize_and_pad(size), tfms.A.Normalize()])
train_ds = Dataset(train_records, train_tfms)
valid_ds = Dataset(valid_records, valid_tfms)
```

次にデータローダーを準備します。

```python
train_dl = model_type.train_dl(train_ds, batch_size=16, num_workers=4, shuffle=True)
valid_dl = model_type.valid_dl(valid_ds, batch_size=16, num_workers=4, shuffle=False)
```

##学習

続けてモデルおよびmetricsを定義してしまいます。Retinanetの場合は以下のようになります。

```python
model = model_type.model(backbone = backbone(pretrained = True), num_classes=len(parser.class_map))
metrics = [COCOMetric(metric_type=COCOMetricType.bbox)]
```

ここから先は通常のfastai (or PyTorch-Lighting)と同じ使い方になります。

```python
learn = model_type.fastai.learner(dls=[train_dl, valid_dl], model=model, metrics=metrics)
learn.lr_find()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/726758/e02fafab-8b06-00fe-75a9-4b8e2295ae68.png)

```python
learn.fine_tune(20, 1e-3)
```

Nvidia RTX A6000で20epoch 30分程度でした。
これだけでpetの画像が学習できてしまいました。

#推論

推論も1行でできます。

```python
model_type.show_results(model, valid_ds, detection_threshold=.5)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/726758/377178b7-a791-a19e-7799-80a76d2e4198.png)

さすがRetinanet、そんなに長いこと計算を回してないのに、かなりいい感じに推論できています。

自前データセットでの学習だったり、指標の計算に続く...かもしれません