タイトル通り、ネットワーク定義時にInitializerを変更する方法と、バッチサイズを1より大きく取った時の、Accuracyのカウントの仕方について書きます。
えらく、ピンポイントのテーマになりますが、一応、理由はあります。
ニューラルネットワークコンソールで設計・学習したモデルをエクスポートしただけだと、プロパティはデフォルトになるんです。
いくら、ニューラルネットワークコンソール側でInitializerとか変更しててもです。
なので、ニューラルネットワークコンソールで学習・評価した時のAccuracyの数値と、NNablaで推論を実行した結果数値が異なる場合がでてきます。
それを回避するためには、エクスポートしたnetwork()に対して、NNabla上でInitializerを変更してやる必要があるわけです。
ところが、意外にうまくいかない。
ネットで探してみても、NNablaでInitializerを変更しているサンプルって、本当にない。
それで、「こんなことでてこずるとは思わなかったね。」みたいな話を友人としていたら、その友人が英語のグループサイトで質問してくれて、やっとこさでわかった・・というわけなのです。
なので、知ったものの義務としてブログに書いておかないといけないかな。
そう思ったということなんですね。
もうひとつの、バッチサイズを1より大きく取った時の、Accuracyのカウントの仕方というのも、だいたい同じようなことです。
さて、それじゃあ、やってみますね。
Initializerの変更
まず、変更前のソースです。
ニューラルネットワークコンソールからエクスポートした状態です。(今回、不要な部分はカットしています)
def network(x, y, test=False):
# Input -> 1,28,28
# Convolution -> 16,22,22
with parameter_scope('Convolution'):
h = PF.convolution(x, 16, (7,7), (0,0))
# ReLU
h = F.relu(h, True)
これ、活性化関数がReLuなので、Initializerをデフォルトではなくて、「calc_normal_std_he_forward」にしたいんですけど、残念ながら、エクスポートしただけではデフォルトのままで処理されてしまいます。
ちなみに、なぜReLuだとInitializerを変えたいのかは、こちらの記事に書いているので、不明な方は参照ください。
それでリファレンスを見ると、Convolution の引数で、w_init にInitializerを指定してやれば良いみたいなことが書いてます。
ところが、w_initに「calc_normal_std_he_forward」を指定してもエラーがでて、さっぱり学習してくれず、サンプルも見当たらずで困ったのは前に書いたとおりです。
で、結局、得られた答えがこうでした。
import nnabla.initializer as I
def network(x, y, test=False):
# Input -> 1,28,28
# Convolution -> 16,22,22
inchannels =x.shape[1]
outchannels =16
s = I.calc_normal_std_he_forward(inchannels,outchannels)
w_init = I.NormalInitializer(s)
with nn.parameter_scope('Convolution'):
h =PF.convolution(x,outchannels,(7, 7),(0,0),w_init=w_init)
# ReLU
h = F.relu(h, True)
わかってしまえば、「calc_normal_std_he_forward」を、「NormalInitializer」でラップしてやれば良いという簡単なことなんですけどね。
やれやれ、これでinitializerの変更ができるようになりました。
Accuracyのカウントの仕方
これは、例えば以下のようにバッチサイズ「64」とかで、評価用のデータを読み込んだときの方法です。
test_data = data_iterator_csv_dataset("C:\\neural_network_console_100\\samples\\sample_dataset\\MNIST\\mnist_test.csv",64,shuffle=True)
こうしたときに、「test_data.next」ってすると、当然ですけど、バッチサイズ分64この塊が取り出されるわけです。
推論自体は、「y.forward()」とかするだけなので、全然困らないです。
でも、そのあとで正確さ(Accuracy)を出力しようと思ったときに、各1件ごとに結果が正しいかどうかを検査して、正解をカウントして、最後に何%のAccuracyかを出力する時にちょっと迷います。
こうすれば良いのですけどね。
mch=0
tch=0
for i in range(test_data.size // 64):
x.d, t.d = test_data.next()
y.forward()
for j in range(t.size):
tch += 1
if(t.d[j] == y.d.argmax(axis=1)[j]):
mch += 1print("Accuracy:{}".format(mch / tch))
二重ループにして、内部ループでバッチサイズ分、比較を繰り返せばいいだけではあります。
そこは別に難しくもなんともないです。
ちょっと試行錯誤が必要だったのはは1点だけ。
if(t.d[j] == y.d.argmax(axis=1)[j]):
mch += 1
データをPrintして、イメージしてみないとわかりづらいとは思います。
このaxis=1が重要です。
行方向(axis=0)か列方向(axis=1)のことなんですけど。
この答えは、1行に10列(0~9)の確率がはいってますから、横方向=列方向に最大値をとって、そのインデックス(答えの数字になっている)と正解ラベルの数字を比較しているわけです。
ほぼ、定型的な形になりがちで、つい、何気なくつかってますが、わりとサンプルがない部分でもあるので、これも備忘録的に書いておこうかなあ・・と。
ということで。
今回は、こんな感じです。
関連情報
NNabla関連の記事一覧はこちらです。
ニューラルネットワークコンソール関連の記事一覧はこちらです。