Caffeがコンパイルできているものとする。このチュートリアルでは、CAFFE_ROOTにCaffeがインストールされているものとする
データセットの準備
MNISTのWebサイトからデータをダウンロードし変換する必要がある。以下のコマンドを実行するcd $CAFFE_ROOT
./data/mnist/get_mnist.sh
./examples/mnist/create_mnist.sh
wgetやgunzipをインストールしておくこと コマンド終了後に、mnist_train_lmdb と mnist_test_lmdbのデータができている
LeNet: the MNIST Classification Model
学習プログラムを実行する前に、何が行われるか説明する。数字分類タスクで有名なLeNetを使用する。オリジナルと若干異なるバージョンを使用する。
sigmoid activationsの代わりに、ニューロン用のRectified Linear Unit (ReLU) activationsを代わりに使用する
LeNetのデザインは、ImageNetのようなおおきなモデルに依然として使用されているCNNの本質を含んでいる。
一般的に、プーリング層に続く畳み込み層、別のプーリング層に続く畳み込み層、従来の多層パーセプトロンに似ている2つの全結合層からなる。
$CAFFE_ROOT/examples/mnist/lenet_train_test.prototxtに、層の定義を行う
MNISTネットワークの定義
MNISTの手書き数字分類タスクのためのLeNetモデル向け、lenet_train_test.prototxt モデル定義の説明を行う。Google Protobufに関して知っているものとする。また、$CAFFE_ROOT/src/caffe/proto/caffe.proto にある、
Caffeが使用しているprotobufの定義を読んでいるものとする。
具体的には、caffe::NetParameter protobufを記述する
ネットワーク名から始める
name: "LeNet"
データ層の記述
デモで作成したlmdbからMNISTデータを読み込む。データ層に定義される
layers {
name: "mnist"
type: DATA
data_param {
source: "mnist_train_lmdb"
backend: LMDB
batch_size: 64
scale: 0.00390625
}
top: "data"
top: "label"
}
具体的にはこの層は、data タイプで、mnistの名前を有する。lmdbからデータを読み込む
バッチサイズ64を使用する。入力されるピクセルのレンジが[0,1)にスケーリングする
なぜ、0.00390625なのか、それは、1を256で割った値である。
この層は、データブロブとラベルブロブを出力する。
畳み込み層の記述
最初の畳み込み層を定義しよう
layers {
name: "conv1"
type: CONVOLUTION
blobs_lr: 1.
blobs_lr: 2.
convolution_param {
num_output: 20
kernelsize: 5
stride: 1
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
bottom: "data"
top: "conv1"
}
データ層により与えられるデータブロブを受け取り、conv1層を作成する。畳み込み関数のカーネルサイズ5で、ストライド1で、畳み込まれ、20チャンネルを出力する。
fillerは、ランダムに、重みとバイアスの初期化を行う。weight fillerには、入力と出力ニューロンの数を基準にし、初期化のスケールを決定する、xavierアルゴリズムを使用する。
bias fillerには、デフォルト値0の定数を単純に使用する。
blobs_lrは、層の学習可能なパラメータために、学習率の調整用である。
このケースでは、重み用の学習率を、実行時のソルバーにより与えられた学習率よ同じとし、バイアス学習率は、実行時のソルバーにより与えられた学習率の2倍とする。
通常より良く収束する、
プーリング層の記述
プーリング層の定義は簡単である
layers {
name: "pool1"
type: POOLING
pooling_param {
kernel_size: 2
stride: 2
pool: MAX
}
bottom: "conv1"
top: "pool1"
}
プールカーネルのサイズを2でストライド2(隣の領域との重なりはないことを意味する)として、max poolingを行う。
同様に、2回めの畳込みとプーリングを記述する。詳細は、
$CAFFE_ROOT/examples/mnist/lenet_train_test.prototxt
をみること
全結合層の記述
結合層記述は単純
layers {
name: "ip1"
type: INNER_PRODUCT
blobs_lr: 1.
blobs_lr: 2.
inner_product_param {
num_output: 500
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
bottom: "pool2"
top: "ip1"
}
出力500の、内部的事情で、innerproductとcaffeでは呼んでいる全結合層の定義である。ほかは馴染ものものである。
ReLU層の記述
ReLU層の記述は単純
layers {
name: "relu1"
type: RELU
bottom: "ip1"
top: "ip1"
}
ReLU は要素ごとの操作なので、メモリー節約のために、 in-place操作とする
これは単に、top ブロブと bottomブロブの名前を同じにする。
もちろん、他の層と同じブロブ名を使用しないこと。
ReLU層のあとに、別のinnerproduct層を記述できる
layers {
name: "ip2"
type: INNER_PRODUCT
blobs_lr: 1.
blobs_lr: 2.
inner_product_param {
num_output: 10
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
bottom: "ip1"
top: "ip2"
}
Loss層の記述
最後にLoss層
layers {
name: "loss"
type: SOFTMAX_LOSS
bottom: "ip2"
bottom: "label"
}
Loss層には、softmaxとmultinomial logistic loss (時間の節約と安定性の向上)を定義できる。2つのブロブを持つ。
1つは、予測であり、もうひとつは、データ層により与えられたラベルである。これは、何も出力しない。損失関数の値を計算することではない。
バックプロパゲーションが開始され、ip2に関して勾配を開始した時に報告する。
すべての魔法が始まるところである。
記述ルール
層の定義は、ネットワークの定義の中に、含めるべきかどうか、あるいはいつ含めるかのルールを、下記のように含めることができる。
layers {
// ...layer definition...
include: { phase: TRAIN }
}
現在のネットワークの状態に基づき、ネットワーク層のインクルードを制御するルールである。
$CAFFE_ROOT/src/caffe/proto/caffe.protoを参照すれば、モデルのスキーマと、層のルールの情報を得ることができる。
上記の例では、この層は学習フェーズでインクルードされる。もし、学習をテストに変えれば、テストフェーズのインクルードされるだろう。
デフォルトでは、層のルールがない場合、層は常にネットワークにインクルードされる。
lenet_train_test.prototxtでは、異なるバッチサイズの2つのデータ層を有し、1つは学習用、1つはテスト用である。
lenet_solver.prototxtに定義されているように、テストフェーズだけに含まれる、100回の繰り返しごとに、モデルの精度を報告する、精度層がある。
MNISTソルバーの定義
$CAFFE_ROOT/examples/mnist/lenet_solver.prototxtの各行の説明用のコメントを調べてみて下さい。(別の機会に)
学習とテストモデル
ネットワーク定義protobufとソルバーprotobufを記述すれば、モデルのテストは簡単である。train_lenet.shを実行するか、下記のコマンドを実行する。
cd $CAFFE_ROOT
./examples/mnist/train_lenet.sh
train_lenet.shは、簡単なスクリプトであり、簡単な説明を行う。学習用のメインのツールはcaffeであり、学習を行い、solver protobuf ファイルが引数である。
コードを実行すると下記のメッセージが表示され流れていく。このメッセージは、各層の詳細(各層の接続や出力形式)を示しており、デバッグに役立つだろう。
初期化のあと学習が始まる。
I1203 net.cpp:142] Network initialization done.
I1203 solver.cpp:36] Solver scaffolding done.
I1203 solver.cpp:44] Solving LeNet
ソルバーの設定により、100回の繰り返し事に学習ロスが表示される。
そして1000回の繰り返しごとに、ネットワークのテストを行い、下記のようなメッセージを見るだろう。
I1203 solver.cpp:204] Iteration 100, lr = 0.00992565
I1203 solver.cpp:66] Iteration 100, loss = 0.26044
...
I1203 solver.cpp:84] Testing net
I1203 solver.cpp:111] Test score #0: 0.9785
I1203 solver.cpp:111] Test score #1: 0.0606671
学習の繰り返しごとに、lrは、その繰り返しにおける、学習率である。lossは学習関数である。
学習フェーズの出力の場合、 score #0 は、精度で、score #1は、 損失関数である。
数分後に完了する。
I1203 solver.cpp:84] Testing net
I1203 solver.cpp:111] Test score #0: 0.9897
I1203 solver.cpp:111] Test score #1: 0.0324599
I1203 solver.cpp:126] Snapshotting to lenet_iter_10000
I1203 solver.cpp:133] Snapshotting solver state to lenet_iter_10000.solverstate
I1203 solver.cpp:78] Optimization Done.
バイナリーのprotobufファイルとして、lenet_iter_10000にモデルは保存される。
もし、実世界のデータを使用していたなら、あなたのアプリにモデルとして展開できる
CPUでの学習
全ての学習はGPUで行われた。もし、CPUで学習を行う場合、lenet_solver.prototxt ファイルの1行を# solver mode: CPU or GPU
solver_mode: CPU
に変更すれば良い
MNISTのデータセットは小さいので、GPUは、データの転送のオーバヘッドが大きいので、恩恵が少ない。ImageNetのような巨大なデータセットでは、計算速度は非常に重要である。
以上
0 件のコメント:
コメントを投稿