YOLOのオリジナルデータ学習手順 #3-1 YOLOv3 Keras版実装

**Chapters**

📘 **Chapter #0**　[YOLOとは](https://qiita.com/tfukumori/items/65056a4d51b531cdd45c)
📘 **Chapter #1**　[環境設定](https://qiita.com/tfukumori/items/6f33af8f9dc2ac974f43)
📘 **Chapter #2**　[アノテーション](https://qiita.com/tfukumori/items/0911c8fe1f5319530d4b)
📘 **Chapter #3**
　📗 **Chapter #3-1**　[YOLOv3 Keras版実装](https://qiita.com/tfukumori/items/d1f79078803d8a7fe103)
　📗 **Chapter #3-2**　[YOLOv3 Darknet版](https://qiita.com/tfukumori/items/15edd532e0a04c0a4672)
📘 **Chapter #A**
　📗 **Chapter #A-1**　[YOLOの各バージョンについてまとめ](https://qiita.com/tfukumori/items/519d84bf3feb8d246924)
　📗 **Chapter #A-2**　[YOLOv3 Keras版実装に関して関連記事のまとめ](https://qiita.com/tfukumori/items/9e62c4a7acd7ea6ddd3b)
　📗 **Chapter #A-3**　[ONNX変換・確認ライブラリ、アプリケーションまとめ](https://qiita.com/tfukumori/items/3f438294e722bc29fe16)

---

# TL;DR

- [YOLOv3のKeras版実装](https://github.com/qqwweee/keras-yolo3)でのオリジナルデータ（独自データ）学習手順について、記載します。
- tensorflowは[V2になって大きく変わっています](https://qiita.com/tfukumori/items/b4a3507e13c1c4a1161b#tensorflow-v2%E3%81%A7%E5%A4%A7%E3%81%8D%E3%81%8F%E5%A4%89%E3%82%8F%E3%81%A3%E3%81%9F%E3%81%93%E3%81%A8)。YOLOv3のKeras版実装はtensorflowのV1でテストされているため、使用ライブラリなどの注意点があります。
- 環境設定は終わっている前提。環境設定についての記事はこちら：[YOLOのオリジナルデータ学習手順 #1 環境設定](https://qiita.com/tfukumori/items/6f33af8f9dc2ac974f43)
- 学習データのアノテーション作業は終わっている前提。アノテーションについての記事はこちら：[YOLOのオリジナルデータ学習手順 #2 アノテーション](https://qiita.com/tfukumori/items/0911c8fe1f5319530d4b)

# 最終調査日

2020年10月4日時点

# 環境

## ソフト

- Windows 10 Pro 1909 x64
- GeForce GTX 1080 Ti
- Anaconda
- Git
- Visual Studio Code

## Pythonモジュール

### [公式](https://github.com/qqwweee/keras-yolo3)

- Python 3.5.2
- Keras 2.1.5
- tensorflow 1.6.0
- OpenCV

### この記事

- Python 3.6
- Keras 2.2.4
- tensorflow 1.14.0
- OpenCV
- pillow 
- numpy
- matplotlib

# 1. スクリプトの準備

```
git clone https://github.com/tfukumori/keras-yolo3
```

- [変更を反映したソースコード](https://github.com/tfukumori/keras-yolo3)
- [フォーク元との相違点](https://github.com/qqwweee/keras-yolo3/compare/master...tfukumori:master)
- [変更内容（日本語での説明）](https://github.com/tfukumori/keras-yolo3/blob/master/README_code_edited_ja.md)

# 2. 仮想環境の作成

### 2.1. Anacondaで仮想環境を作成

```posh
conda create -n keras-yolo3 python=3.6
```

- CPU版のtensorflowを使用する場合は"keras-yolo3"
- GPU版のtensorflowを使用する場合は"keras-yolo3-gpu"

※名称に意味はない。わかりやすさのため

## 2.2. 仮想環境に必要なモジュールをインストール

以下は、仮想環境の名称が`keras-yolo3`という前提で記載します。

```posh:仮想環境の切り替え
call activate.bat keras-yolo3
```

```posh:keras-yolo3の実行に必要なモジュールをインストール
conda install pillow -y
conda install numpy -y
conda install tensorflow==1.14.0 -y
conda install keras==2.2.4 -y
conda install matplotlib -y
conda install -c conda-forge opencv -y
```

※YOLOv3のKeras版実装は、最新版のtensorflowとの互換性がないため、tensorflowとkerasはバージョン指定してインストールします。

```posh:GPU版を使用する場合は、tensorflowの行を以下に変更する
conda install tensorflow-gpu==1.14.0 -y
```

⚠minicondaでconda-forgeを使用する場合は、`tensorflow-gpu`は存在しない
⚠未検証だが[tensorflowは1.15からgpu版が統合された](https://www.tensorflow.org/install/gpu?hl=ja)ので、pipでtensorflowを使用することについても検討が必要

# 3. 実施手順（エクスポート結果の配置）

[YOLOのオリジナルデータ学習手順 （アノテーション編）](https://qiita.com/tfukumori/items/0911c8fe1f5319530d4b)でエクスポートファイルを使用する。

※効率化の観点からこの処理も自動化（Pythonプログラム作成）することが望ましいです。

## 3.1. 最終的なフォルダ構成

先に最終的な構成を示します。

💡 対象画像がPNG形式の場合の例

```
keras-yolo3
└─model_data
    voc_classes.txt
└─VOCdevkit
    └─VOC2007
        └─Annotations
        │    <対象画像ファイル名>.xml
        │    ～
        ├─ImageSets
        │    └─Main
        │    train.txt
        │    val.txt
        │    test.txt
        └─JPEGImages
             <対象画像ファイル名>.png
             ～
```

## 3.2. エクスポートしたフォルダを既定の位置に配置

エクスポートしたフォルダを既定の位置にコピーします（再作業する場合には、既存のフォルダは削除しておきます）。

コピー元：`keras-yolo3\resize_image\<Vottのプロジェクト名>-PascalVOC-export`
コピー先：`keras-yolo3\VOCdevkit\<Vottのプロジェクト名>-PascalVOC-export`

リネームします（再作業する場合には、既存のフォルダは削除しておきます）。

リネーム前：`keras-yolo3\VOCdevkit\<Vottのプロジェクト名>-PascalVOC-export`
リネーム後：`keras-yolo3\VOCdevkit\VOC2007`

## 3.3. train.pyで読み込むアノテーションを学習させるtxtファイルを作成

1. 以下のファイルをリネームする

    リネーム前：`keras-yolo3\VOCdevkit\VOC2007\ImageSets\Main\<タグ名>.txt`
    リネーム後：`keras-yolo3\VOCdevkit\VOC2007\ImageSets\Main\train.txt`

    ※Vottで「テスト/トレーニング分割：100」と設定すると、「<タグ名>.txt」のみが作成されます（100以外なら<タグ名>_train.txtと<タグ名>_val.txt）。
    ※複数のタグを指定した場合はすべて結合して`train.txt`を作成する。

2. 空ファイルを作成する

    `keras-yolo3\VOCdevkit\VOC2007\ImageSets\Main\val.txt`
    `keras-yolo3\VOCdevkit\VOC2007\ImageSets\Main\test.txt`

3. 以下のファイルをエディタで開き、`.jpg 1`を除く。

    ```
    keras-yolo3\VOCdevkit\VOC2007\ImageSets\Main\train.txt
    keras-yolo3\VOCdevkit\VOC2007\ImageSets\Main\val.txt
    keras-yolo3\VOCdevkit\VOC2007\ImageSets\Main\test.txt
    ```

    ```:変更前
    <ファイル名>.jpg 1
    ～
    ```

    ```:変更後
    <ファイル名>
    ～
    ```

4. `voc_annotation.py`の一部を作成したタグに合わせて変更する

    ```python:`voc_annotation.py`の一部
    classes = [<作成したタグ文字列をカンマ区切りで記載>]
    # classes = ["aeroplane", "bicycle", "bird", "boat", "bottle", "bus", "car", "cat", "chair", "cow", "diningtable", "dog", "horse", "motorbike", "person", "pottedplant", "sheep", "sofa", "train", "tvmonitor"]
    ```
5. 以下を実行し、アノテーションを学習させるtxtファイルを作成

    ```posh:アノテーションを学習させるtxtファイルを作成
    python voc_annotation.py
    ```
    
    以下のファイルが作成されます。
    
    ```
    keras-yolo3\2007_test.txt
    keras-yolo3\2007_train.txt
    keras-yolo3\2007_val.txt
    ```
    
    > ℹ️ Annotationsフォルダ内のxmlファイル内のクラス名と座標の情報から作成されます

## 3.4. 認識するクラス名の指定

`model_data\voc_classes.txt`を別名でバックアップしておき、`model_data\voc_classes.txt`にVottで設定したクラス（タグ）を記載します。


> ℹ️ "voc_annotation.pyの一部を作成したタグに合わせて変更"とこの「3.4. 認識するクラス名の指定」で記載するクラス名の順番は同じにしておく必要があります。理由は、2007_test.txt、2007_train.txt、2007_val.txtではクラスを「<クラス名>ごとに振られた0から始まる番号」で区別しているため、順番が異なると違うクラスと判断されてしまうためです

# 4. 実施手順（ウエイトファイルの変換）

変換元ウエイトファイルが同じ場合は初回のみでOKです。

## 4.1. ウエイトファイルの配置

[Darknet](https://pjreddie.com/darknet/yolo/)から[ウエイトファイル](https://pjreddie.com/media/files/yolov3.weights)をダウンロードして、以下に配置します。

```
keras-yolo3\yolov3.weights
```

## 4.2. ウエイトファイルのコンバート

ダウンロードした「yolov3.weights」はそのままではkerasで使えないので、kerasモデルにコンバートします。

```posh:ウエイト作成
python convert.py -w yolov3.cfg yolov3.weights model_data/yolo_weights.h5
```

# 5. 実施手順（トレーニング）

「5.1. トレーニング方法1（プルリクエスト改善版）」の方が時間はかかりますが精度が良く、トレーニング途中でも学習状況を確認できるためお勧めです。

機械学習は精度を保証することはできないため、学習状況・結果を分析しながら改善を行っていくことがどの程度の精度になっているかを確認してくことが重要です。

## 5.1. トレーニング方法1（プルリクエスト改善版）　★おすすめ

1. TensorBoardの準備（初回のみ）

    仮想環境名は学習を行う環境と別で構いません。
    ここでは別に仮想環境を「tensorboard」として作成します。

    ```posh:TensorBoardのインストール
    conda activate tensorboard
    conda install tensorflow==1.14.0 -y
    conda install tensorboard -y
    ```

2. トレーニングのログファイルを使用したTensorBoardの起動。
    
    初回起動時は、yolo_logsフォルダは作成されていないので「4. トレーニングの実行」後に実行してください。
    `--host 0.0.0.0`を指定することで外部からアクセス可能となります。
    
    ```posh:run_tensorboard.bat
    CALL activate "C:\Users\<ログインユーザー名>\Anaconda3\envs\<仮想環境名>"
    tensorboard --logdir=<yolo_logs>のフルパス --host 0.0.0.0
    ```
    
3. TensorBoardを開く

    ブラウザで`http://localhost:6006/` or `http://<マシンのIPアドレス>:6006/`にアクセスすると、TensorBoardで解析結果を確認できます。
    トレーニング状況をリアルタイムで確認できますので、学習状況を確認できます。[例](https://github.com/qqwweee/keras-yolo3/pull/206)

4. トレーニングの実行

    `--yolo_val_file nano`を指定することで`2007_train.txt`の内容が自動的に分割されます。
    先にフォルダの整理が必要です。作業フォルダ（tmp_～）が中断等で残っていた場合は削除、結果フォルダ（yolo_logs）が残っていた場合には退避など。

    ```posh:トレーニング
    conda activate keras-yolo3
    python train_v2.py --yolo_train_file 2007_train.txt --anchors_file model_data/yolo_anchors.txt --yolo_val_file nano
    ```

## 5.2. トレーニング方法2（標準の方法）

[YOLOv3のKeras版実装](https://github.com/qqwweee/keras-yolo3)に記載されている方法です。

1. シンプルにトレーニングのみを行う方法です。
    
    ```posh:トレーニング
    python train.py
    ```
    
2. 処理が完了すると以下のファイルが作成されます。
    
    ```
    keras-yolo3\logs\000\trained_weights_final.h5
    ```
    
    ※`train.py`でGPU使用時にエラーが出る場合は、後述の[困ったときには](#困ったときには)を参照

# 6. 実施手順（テスト）

## 6.1. テスト

```posh:テスト
python yolo_video.py --image --model_path <作成したモデル拡張子が`h5`のファイルパス（`yolo_video.py`からの相対パス）> --classes_path <クラスを記載した`voc_classes.txt`のパス（`yolo_video.py`からの相対パス）>
```

- Python上のパスの区切り文字列は`\`ではなく`/`となるので留意ください。
- [3.2. yolo_video.pyの修正](#32-yolo_videopyの修正)に伴い、引数名が変更となっています。

**例1（トレーニング方法1の出力ファイル名）**

```posh
python yolo_video.py --image --model_path yolo_logs\models\Step2_yolo_weight_mAP_best.h5 --classes_path model_data/voc_classes.txt
```

**例2（トレーニング方法2の出力ファイル名）**

```posh
python yolo_video.py --image --model_path logs/000/trained_weights_final.h5 --classes_path model_data/voc_classes.txt
```
---

# YOLOv3-tinyを使用する場合

以下のように変更します。

**4.1. ウエイトファイルの配置**

[YOLOv3-tinyのWeightsファイル](https://pjreddie.com/media/files/yolov3-tiny.weights)をダウンロードして配置します。

```
keras-yolo3\yolov3-tiny.weights
```

**4.2. ウエイトファイルのコンバート**

```posh
python convert.py -w yolov3-tiny.cfg yolov3-tiny.weights model_data/tiny_yolov3_weights.h5
```

※5.2. の方法を使用する場合は、ファイル名を同一として、すでにYOLOv3-416のh5ファイルがある場合は退避しておくこと

**5.1. トレーニング方法1（プルリクエスト改善版）**

4.のアンカーファイルの指定を変更する

**変更前**

```posh
python train_v2.py --yolo_train_file 2007_train.txt --anchors_file model_data/yolo_anchors.txt --yolo_val_file nano
```

**変更後**

```posh
python train_v2.py --yolo_train_file 2007_train.txt --anchors_file model_data/tiny_yolo_anchors.txt --yolo_val_file nano
```

それ以外は手順通り実行する
（[yolov3-tinyを考慮した変更](https://github.com/tfukumori/keras-yolo3/commit/564cce259df453366b64921f34412e5dcd65cc64#diff-66577ce398d84426f234aced8909194e)が反映されている[最新のソース](https://github.com/tfukumori/keras-yolo3)であること）



**5.2. トレーニング方法2（標準の方法）**

以下のファイルのアンカーファイルを書き換える

**変更前**

```python:train.py
    anchors_path = 'model_data/yolo_anchors.txt'
```

**変更後**

```python:train.py
    anchors_path = 'model_data/tiny_yolo_anchors.txt'
```

```posh:トレーニング
python train.py
```

---

# 困ったときには

**`control_flow_ops`でエラーが表示される**

- Keras 2.2.5以降には対応していない。[Keras 2.2.4を使用する](https://github.com/qqwweee/keras-yolo3/issues/544)。

**`No such file or directory: 'yolov3.weights'`のエラーが表示される**

- 本手順の[4.1. ウエイトファイルの配置](#41-ウエイトファイルの配置)と[4.2. ウエイトファイルのコンバート](#42-ウエイトファイルのコンバート)が行われていない。

**Visual Studio CodeからPythonを実行した場合に`CommandNotFoundError: Your shell has not been properly configured to use 'conda activate'.`エラーが表示される**

- 本手順に記載の`conda init`を実行する。（[2.2.2. conda用にpowershellを設定](#222-conda用にpowershellを設定)）

**`train.py`実行時に、`tensorflow.python.framework.errors_impl.ResourceExhaustedError: 2 root error(s) found. `エラーが表示され、`trained_weights_stage_1.h5`作成まで行われない。**

- GPUのメモリが足りない。GPU（tensorflow-gpu）でなく、CPU（tensorflow）を使用するか、`train.py`の以下の部分を変更してエラーが出なくなるまで調整（小さく）する。

```python:train.pyの一部
batch_size = 32  # note that more GPU memory is required after unfreezing the body
```

参考：https://teratail.com/questions/246310

> GPU で演算を行なう場合、バッチサイズが大きいほうが、GPU とのデータ転送の回数がすくなくなるので、学習速度が早くなります。(GPU は一度に大量のデータを計算するのが得意)
> では、単純にバッチサイズを大きくすればいいのかというとそうではなく、以下の点を考慮して適当な値にします。
> - バッチサイズが大きいほど、GPU の消費メモリが増えるので、大きくしすぎると、メモリ不足でエラーになります。
> - バッチサイズが大きすぎると、局所解に陥りやすくなり、学習結果が悪くなるかもしれません。

**引数を指定しても無視される（渡したモデルパスが無視され、既定のモデルパスを読み出そうとしたエラーが表示される。`OSError: Unable to open file (unable to open file: name = 'model_data/yolo.h5', errno = 2, error message = 'No such file or directory', flags = 
0, o_flags = 0)`）**

- `yolo_video.py`の引数名と`yolo.py`の引数名が不一致であることが原因。`yolo_video.py`を修正する。

# 参考

- [YOLOv3 Keras版実装に関して関連記事のまとめ](https://qiita.com/tfukumori/items/9e62c4a7acd7ea6ddd3b)

## mAP関係のプルリクエスト関連記事

- [Training (add tensorboard debug, and mAP Calculation) by KUASWoodyLIN · Pull Request #206 · qqwweee/keras-yolo3](https://github.com/qqwweee/keras-yolo3/pull/206)
- [【物体検出】mAP ( mean Average Precision ) の算出方法 - Qiita](https://qiita.com/mdo4nt6n/items/08e11426e2fac8433fed)

## `py`ランチャー、`venv`利用時の環境作成

### tensorflow-gpu

```powershell
py -3.6 -m venv .venv
&.venv\Scripts\activate.ps1
pip install pillow numpy h5py==2.10.0 tensorflow-gpu==1.14.0 keras==2.2.4 matplotlib opencv-python
```

参考：https://qiita.com/Hiroki-Fujimoto/items/b078bfb680fb710c38c1

### tensorboard

```powershell
py -3.6 -m venv .venv
&.venv\Scripts\activate.ps1
pip install tensorboard tensorflow
```