【YOLOXで自前のアプリを作る。その3】- GoogleColaboratoryでYOLOXの学習を行う。

今回はYOLOの最新バージョンであるYOLOXをGoogleColaboratoryで学習させる方法について記事にしていきます。

最終的には自分で用意した画像から学習データを作成して、そのデータでYOLOXを学習させ自作のPythonアプリに組み込むところまでやろうと思います。
・[【YOLOXで自前のアプリを作る。その1】- YOLOXで学習させるための、COCO形式の自前データセットを作成する。](https://qiita.com/shigeharu_shibahata/items/f4ac1823c1172034e0fc)
・[【YOLOXで自前のアプリを作る。その2】- GoogleColaboratoryでYOLOXをとりあえず試す。](https://qiita.com/shigeharu_shibahata/items/b601a1672b41755e8a1e)
・【YOLOXで自前のアプリを作る。その3】- GoogleColaboratoryでYOLOXの学習を行う。 ← イマココ
・【YOLOXで自前のアプリを作る。その4】- YOLOXを自作のアプリに組み込む。

【参考URL】
・[GitHub - Megvii-BaseDetection/YOLOX](https://github.com/Megvii-BaseDetection/YOLOX)
・[【物体検出】YOLOXまとめ｜第2回：マスク着用判定〜カスタムデータの学習〜](https://tt-tsukumochi.com/archives/333)

# 環境
・GoogleColaboratory

# 流れ
・学習データをアップロード
・YOLOXの学習
・学習したモデルを使って物体検出を試す

# 学習データをアップロード
GoogleColaboratory上で学習を行うので、学習データをGoogleDriveにアップロードする必要があります。

YOLOXで使う学習データは実際の画像ファイルと、画像のどこに物体が写っているかといった情報が記載されたjsonファイルの2種類になります。
それぞれ`訓練用`と`検証用`に分かれています。

今回使う学習データには第一回で作成した自作のデータセットを使用します．このデータはすでに`訓練データ`と`検証データ`に分けてあります。分ける割合におそらく正解はないと思いますが`8:2`くらいの割合が一般的なのではないかと思います。

第一回で作成したデータはそれぞれ
画像データが`coco-annotator/datasets/my_dog_train`と`coco-annotator/datasets/my_dog_valid`の中に
jsonデータが`my_dog_train.json`と`my_dog_valid.json`といった名前で保存してあるはずです．
これらのデータをGoogleDrive上に正しく配置していきます．

まずはこのjsonファイルの名前を下記のように変更します．
```
     変更前               変更後
my_dog_train.json  =>  train.json
my_dog_valid.json  =>  val.json
```


次にGoogleDrive上の`YOLOX/datasets/`下に`mydog/`ディレクトリを作って下記のようにこれらのファイルを配置していきます。
画像ファイルの名前はこの例の通りに変更する必要はありません．

```
YOLOX
└── datasets
    └── mydog
        ├── annotations
        │   ├── train.json      <-- 訓練用jsonファイル
        │   └── val.json        <-- 検証用jsonファイル
        │
        ├── train2017
        │   ├── image0.jpg      <-- 訓練用画像ファイル
        │   ├── image1.jpg      <-- 訓練用画像ファイル
        │   ├── image2.jpg      <-- 訓練用画像ファイル
        │        ・
        │        ・
        │        ・
        │
        ├── val2017
        │   ├── image0.jpg      <-- 検証用画像ファイル
        │   ├── image1.jpg      <-- 検証用画像ファイル
        │   ├── image2.jpg      <-- 検証用画像ファイル
        │        ・
        │        ・
        │        ・
```

# YOLOXの学習
学習に使う設定ファイルを作成します。
今回はデフォルトで用意されているものをコピー編集して使います。
`YOLOX/exps/default/yolox_s.py`を`YOLOX/exps/example/custom`にコピーして`mydog.py`にリネームします。
そうしたら`mydog.py`を編集していきます。

編集前
```python
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# Copyright (c) Megvii, Inc. and its affiliates.

import os

from yolox.exp import Exp as MyExp


class Exp(MyExp):
    def __init__(self):
        super(Exp, self).__init__()
        self.depth = 0.33
        self.width = 0.50
        self.exp_name = os.path.split(os.path.realpath(__file__))[1].split(".")[0]

```

編集後
```python
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# Copyright (c) Megvii, Inc. and its affiliates.

import os

from yolox.exp import Exp as MyExp


class Exp(MyExp):
    def __init__(self):
        super(Exp, self).__init__()
        self.depth = 0.33
        self.width = 0.50
        self.exp_name = os.path.split(os.path.realpath(__file__))[1].split(".")[0]


        # Define yourself dataset path
        self.data_dir = "datasets/mydog"
        self.train_ann = "train.json"
        self.val_ann = "val.json"

        self.num_classes = 1

        self.max_epoch = 300
        self.data_num_workers = 4
        self.eval_interval = 1
```

編集するのは以下の部分になります。
```python
        # Define yourself dataset path
        self.data_dir = "datasets/mydog"
        self.train_ann = "train.json"
        self.val_ann = "val.json"

        self.num_classes = 1

        self.max_epoch = 300
        self.data_num_workers = 4
        self.eval_interval = 1
```

学習用のデータが入っているディレクトリの場所とアノテーションファイルを指定しています。
また、今回は学習させるクラスの数は一つだけなので`self.num_classes = 1`としています。
その他学習パラメータの設定をしています．

次に、`YOLOX/yolox/data/datasets/coco_classes.py`を編集します。このファイルには検出時に表示するクラスの名前が記載されています。デフォルトだといろんな種類のクラスが列挙されていますが、今回は1つのクラスのみ検出するので下記のように編集します。(検出時の表示に使うだけなので間違ったクラスを書いてしまっても実は問題ないです。そもそも編集しなくてもちゃんと動きます．)

```python
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# Copyright (c) Megvii, Inc. and its affiliates.

COCO_CLASSES = (
    "mydog",
)
```

ここまできたらあとは学習させるだけです．
前回同様GoogleColaboratoryでDriveをマウントして環境構築します．ランタイムの種類をGPUにするのを忘れずに．
```python
from google.colab import drive
drive.mount('/content/drive')

%cd /content/drive/MyDrive
!git clone https://github.com/Megvii-BaseDetection/YOLOX

%cd /content/drive/MyDrive/YOLOX
!pip install -U pip && pip install -r requirements.txt
!pip install -v -e .
```

環境が構築できたら学習させます．
学習はコマンド一発です．

```python
!python tools/train.py -f exps/example/custom/mydog.py -d 1 -b 8 --fp16 -o -c yolox_s.pth
```

学習には時間がかかります．
学習が終わると`YOLOX/YOLOX_outputs/mydog`の中に各エポックごとの学習済みデータと，中でも一番精度の良い学習済みデータとして`best_ckpt.pth`が保存されています．

# 学習したモデルを使って物体検出を試す
学習済みデータ`best_ckpt.pth`を使って物体検出を試してみます．
試す方法は前回とほとんど一緒です．

まずは検出試したい画像を`YOLOX/`の中に保存します．ファイル名はmydog.jpgとします．
そして下記のコマンドで検出を行います．

```python
!python tools/demo.py image -f exps/example/custom/mydog.py -c ./YOLOX_outputs/mydog/best_ckpt.pth --path ./mydog.jpg --conf 0.3 --nms 0.45 --tsize 640 --save_result --device gpu
```

結果は`YOLOX/YOLOX_outputs/mydog/vis_res`の中に日付ごとに保存されています．
98.4%の確率で私の超可愛い愛犬が検出できていますね．
みなさんはうまく検出できましたでしょうか．
![mydog.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2396681/5d21f660-587a-3bd7-94dd-a1a196707807.jpeg)