Cで書かれたニューラルネット環境darknetですが、訓練済のネットを使う例はあちこちで見かけますが、訓練をしている例が見当たりません。darknet本家にもこの手のドキュメントはないので、ソースを読みながら見よう見まねでやってみることにしました。ソースはすっきりしていて読みやすいですね。
githubにもろもろ素材を上げておきました(こちら)ので参考にしてください。
準備
元データのダウンロード
data/mnist/以下に THE MNIST DATABASEから下記をダウンロードします。
- t10k-images-idx3-ubyte.gz
- t10k-labels-idx1-ubyte.gz
- train-images-idx3-ubyte.gz
- train-labels-idx1-ubyte.gz
gunzipで展開しておきます。
データフォーマット調整
データ・フォーマットはこちらの下のほうに書かれています。ヘッダがあってあとはバイトの羅列のようなのでnumpyで簡単に抜き出せます。
ラベルファイルという、クラス番号とクラスIDを紐付けるものが必要なようです。今回は下記のようにします。行がクラス番号0-9に相当します。mnist.label.listというファイルに書いておきます。
c0
c1
..
c9
画像についてはファイル名のリストをtxtで羅列しますが、classifierの場合はパスの文字列からクラスIDの文字列を検索することでクラスと対応付けているようなので、trainとvalidをそれぞれ下記のように、"[trainかvalidか]_[画像ID]_[クラスID].png"のように命名します。
- t_00000_c0.png
- v_00000_c0.png
ファイル名一覧をmnist.train.list, mnist.valid.listに書き出します。
ダウンロードから変換までを行うスクリプトがこちらです。
datasetファイル(データのありかなど)
cfg/mnist.dataset ファイルに記述します。
classes=10
train = data/mnist/mnist.train.list
valid = data/mnist/mnist.valid.list
backup = /tmp/backup/
labels = data/mnist.labels.list
names = data/mnist.names.list
top = 5
train, valid, labels はさきほど作りましたね。namesはどのclass_idに名前をつける場合に使うテキストです。MNISTなので0から9と書いておけばよいですね。
0
1
...
9
cfgファイル(ネットワーク構成)
いよいよニューラルネットワークの構成を書きます。cfg/mnist_lenet.cfg ファイルに記述します。
冒頭には[net]という項目に、学習条件などが含まれています。注意ですが、darknetのclassifierはカラー画像を前提としていますので、channel=3と指定する必要があります。元画像が1チャネルでもopencvが3チャネル化しながら読み込みます。
angleからaspectはaugmentation用の条件ですね。これはclassifierでは使われていないように見えますが、ひとまず変形はしないということで全部1にしておきます。学習のループ回数は max_batches回です。
[net]
batch=100
subdivisions=1
height=28
width=28
channels=3
momentum=0.9
decay=0.00005
max_crop=28
learning_rate=0.01
policy=poly
power=4
max_batches=500
angle=1
hue=1
saturation=1
exposure=1
aspect=1
これ以降にはネットワーク構造を書きます。羅列しておくとSequencialな構造になるようですね。逆にSequencialな構造しか書けないのかな。
今回は TensorFlowのチュートリアルにある4層のLe-Netを記述してみます。
[convolutional]
filters=32
size=5
stride=1
pad=1
activation=relu
[maxpool]
size=2
stride=2
[convolutional]
filters=64
size=5
stride=1
pad=1
activation=relu
[maxpool]
size=2
stride=2
[connected]
output= 1024
activation=relu
[dropout]
probability=.5
[connected]
output= 10
activation=linear
[softmax]
groups=1
[cost]
type=sse
訓練
./darknet classifier train cfg/mnist.dataset cfg/mnist_lenet.cfg
costとか学習率とかが表示されていきます。最終的に、/tmp/backupに重みが出力されます。
推論
/tmp/backout/mnist_lenet.weightsをコピーしてきます。
predict.c/predict_classifier()のresize_network()でエラーになるので
network.c/resize_network()の
if(w == net->w && h == net->h) return 0;
のコメントを外してdarknetをビルドしなおします。
(2018/10/20追記:twitterで指摘いただいてチェックしましたが、現時点での最新のdarknetではエラーにはなりませんでした。)
./darknet classifier predict cfg/mnist.dataset cfg/mnist_lenet.cfg ./mnist_lenet.weights data/mnist/images/v_00000_c7.png
とすると、下記が出力されます。
layer filters size input output
0 conv 32 5 x 5 / 1 28 x 28 x 3 -> 28 x 28 x 32
1 max 2 x 2 / 2 28 x 28 x 32 -> 14 x 14 x 32
2 conv 64 5 x 5 / 1 14 x 14 x 32 -> 14 x 14 x 64
3 max 2 x 2 / 2 14 x 14 x 64 -> 7 x 7 x 64
4 connected 3136 -> 1024
5 dropout p = 0.50 1024 -> 1024
6 connected 1024 -> 10
7 softmax 10
8 cost 10
Loading weights from ./mnist_lenet.weights...Done!
data/mnist/images/v_00000_c7.png: Predicted in 0.004880 seconds.
7: 0.995198
5: 0.003397
9: 0.000618
0: 0.000574
3: 0.000106
7の画像が「99.5%で7」と認識されました。いいですね。