アラカン"BOKU"のITな日常

文系システムエンジニアの”BOKU”が勉強したこと、経験したこと、日々思うことを書いてます。

NNabla(Neural Network Libraries)でCovolution等のInitializerを変更する方法について

タイトル通り、ネットワーク定義時に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を変えたいのかは、こちらの記事に書いているので、不明な方は参照ください。

arakan-pgm-ai.hatenablog.com

 

それでリファレンスを見ると、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 += 1

print("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関連の記事一覧はこちらです。

arakan-pgm-ai.hatenablog.com

ニューラルネットワークコンソール関連の記事一覧はこちらです。

arakan-pgm-ai.hatenablog.com