麻雀の点数計算アプリを作る#6

#データセットを準備する

###データベース
・画像を用意
精度は考えずに，簡易なモデルで学習を行います．今回は全34種の牌それぞれを0°, 90°, 180°, 270°ずつ回転させた136枚の画像を用意しました．ファイル名が荒れていてごめんなさい．

<img width="905" alt="スクリーンショット 2019-01-12 16.39.57.png" src="https://qiita-image-store.s3.amazonaws.com/0/335220/33254cf0-adc0-c9aa-b7b8-38e171c729f9.png">

・アノテーション
アノテーションは学習データにラベルを付ける作業です．Darknetには__labelImg__というアノテーションツールが付属しており，作業を効率化することができます．はじめにDarknet/labelImg/data/predifined_classes.txtにクラスを記述しておきます．

```predefined_classes.txt
1p
2p
3p
4p
5p
6p
7p
8p
9p
1m
2m
3m
4m
5m
6m
7m
8m
9m
1s
2s
3s
4s
5s
6s
7s
8s
9s
South
North
East
West
Haku
Hatsu
Chun
```

以下のコマンドでlabalImgを起動．

```
cd darknet/labelimg
python labeling.py
```

<img width="780" alt="スクリーンショット 2019-01-12 17.27.06.png" src="https://qiita-image-store.s3.amazonaws.com/0/335220/6985b644-dfe3-936c-6a93-c24c9273e6ce.png">

Open Dirからデータベースを開き，File Listからファイルを選択します．Create RectBoxを使って対象を選択．次に対象が`predifined_classes.txt`に入力したクラスのうち，どれかを選び確定，保存します．このときアノテーション情報を格納するファイルの形式を__YOLO__(.txt)にしておきます．__PascalVOC__(.xml)はYOLOでは読み取れないので，注意が必要です．

<img width="901" alt="スクリーンショット 2019-01-12 18.25.24.png" src="https://qiita-image-store.s3.amazonaws.com/0/335220/d9d3647e-a6ed-c77a-b46e-fc94341cc379.png">

このように，画像1枚にテキストファイル1つが対応することになります．最後に以下をprocess.pyとして保存．これはデータベースを学習データと教師データに分割するプログラムです．出来上がったデータベースをDarknet/data/mahjongにコピー．学習に使うデータの場所は，パス指定ファイルで指定するので任意です．

```python:process.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import glob, os

# Current directory
current_dir = os.path.dirname(os.path.abspath(__file__))

# Directory where the data will reside, relative to 'darknet.exe'
path_data = 'data/mahjong/'

# Percentage of images to be used for the test set
percentage_test = 10;

# Create and/or truncate train.txt and test.txt
file_train = open('train.txt', 'w')  
file_test = open('test.txt', 'w')

# Populate train.txt and test.txt
counter = 1  
index_test = round(100 / percentage_test)  
for pathAndFilename in glob.iglob(os.path.join(current_dir, "*.jpg")):  
    title, ext = os.path.splitext(os.path.basename(pathAndFilename))

    if counter == index_test:
        counter = 1
        file_test.write(path_data + title + '.jpg' + "\n")
    else:
        file_train.write(path_data + title + '.jpg' + "\n")
        counter = counter + 1

```

###コンフィグファイル
ハイパーパラメータを設定します．Darknet/cfgに様々なコンフィグファイルがありますが今回はyolov2-voc.cfgを書き換えて使うことにします．

6|batch=64
7|subdivision=8
237|filters=195(計算式(classes+5)*5=195から求めています)
244|classes=34

`batch`は一回の学習で使う画像の枚数で， `subdivisions`でバッチで除算します(8*8グリッドで学習を行う？)．GPUの性能により適当な値があるようですが，理解が出来ていない部分ですので先人たちに倣うことにします．

###クラスファイル
Darknet/cfg/obj.namesに，predefined_classes.txtと同様にクラスを記述します．

###パス指定ファイル
Darknet/cfg/obj.dataを以下のように作成します．

```obj.data
classes=34
train = data/mahjong/train.txt 
valid = data/mahjong/test.txt 
labels = cfg/obj.names 
backup = backup/
```

trainとvalidは前述の通り，process.pyで生成される学習データと教師データを指定しています．

###重みの初期値データ
学習を収束しやすくするためのデータです．
YOLOv2とYOLOv3では使用するものが違うので注意します．
・v2の場合
darknet19_448.conv.23を以下からダウンロード．
https://pjreddie.com/media/files/darknet19_448.conv.23
・v3の場合
darknet53.conv.74を以下からダウンロード．
https://pjreddie.com/media/files/darknet53.conv.74

ダウンロードしたものをDarknet/cfgにコピーします．

###学習結果が出力される間隔を調整する
学習結果は，学習回数が1000回までは100回ごとに出力し，それ以降は10000回ごとに出力される設定となっています．GPUの性能次第では1000以降に学習結果が出力されるのが数日後，ということも十分ありえますのDarknet/examples/detector.cの138行目を，`if(i%100==0 || (i < 1000 && i%100 == 0)){`とします．これにより常に100回おきに学習結果が出力されます．

#学習の準備が整いました
以下のコマンドで学習を実行します．

```
cd Darknet
./darknet detector train cfg/obj.data cfg/yolov2-voc.cfg cfg/darknet19_448.conv.23
```

<img width="885" alt="スクリーンショット 2019-01-12 19.47.47.png" src="https://qiita-image-store.s3.amazonaws.com/0/335220/d039e410-b7f1-34e1-3526-b05da985070a.png">

成功です．書き起こしてみると淡々と進んでいますが，ここまで来るのにかなりの時間を要しました．このまま走らせて様子を見ることにします．