Help us understand the problem. What is going on with this article?

darknetでMNISTを学習する

More than 1 year has passed since last update.

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」と認識されました。いいですね。

ashitani
趣味で機械学習をやっている者です。
http://ashitani.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away