SE_BOKUのまとめノート的ブログ

SE_BOKUが知ってること・勉強したこと・考えたことetc

LeNet(CNN)の学習済パラメータを使って画像の分類だけPython (SONY Neural Network Libraries)で行う

前回の記事の続きです。

arakan-pgm-ai.hatenablog.com

LeNet(CNN)でMNISTを学習したわけですが、せっかく学習済のパラメータがあるのだから、それを外だしして、画像の分類とかだけできないかなと思ってます。

実用を考えると、まず、それが第一歩ですから。

今回は、その試行錯誤の一発目です。 

 

試行錯誤あれこれ

 

まずは、どのようなやり方ができるのか・・ですね。

  

Cli.pyはどうかな 

 

ニューラルネットワークコンソールの評価を実行すると、EVALUATIONウインドウにコンソールログが表示されます。

こんな感じです。

xxxx-xx-xx  xx:xx:xx,027 Evaluation process is started.
python "K:\neural_network_console_xxxxxx\libs\nnabla\python\src\nnabla\utils\cli\cli.py" forward

 

結局、内部で「cli.py」コマンドを実行してるだけなんだ・・というのが分かります。

ということは・・。

これをコピペしてデータ・セットパラメータとかを変えて実行すれば、外だしできるなというのは、普通に思いつきます。

本家のチュートリアルにも、そう書いてあります。

support.dl.sony.com

cli.py forward のオプションはこんな感じらしいので。

  • -c 学習結果フォルダのネットワーク定義ファイル(net.nntxt)
  • -p 学習結果フォルダのパラメータファイル(parameters.h5)
  • -d 評価データセットCSVファイル
  • -o 評価結果ファイルの出力フォルダ

これは楽かもしれないですが、自分的にはいまいち。

それだとニューラルネットワークコンソールを実行してるのと同じだから、いまいち楽しくない気がするのですね。 

 

2018/10/03追記

cli.pyを直接実行するのは、意外に難しいです。

>自分は、nnabla_cli.exeの方が簡単でした。

arakan-pgm-ai.hatenablog.com

 

NNabla(Neural Network Libraries)を使う

 

絶対、できるはずですからね。 

だって、ニューラルネットワークコンソール(Neural Network Console)は、NNablaの「統合開発環境」だと、SONYさんが言ってますから。

www.sonynetwork.co.jp

 

なお、これ以後の説明は、以下の記事の手順でインストールをしている前提で書いてますので、環境の異なる方は適宜読み替えてくださいね。

arakan-pgm-ai.hatenablog.com

 

NNabla環境でIDLEを立ち上げる

 

自分の環境は、nnablaを仮想環境にインストールしてますから、コマンドプロンプトを立ち上げて、「activate nnabla」します。 

nnabla環境で、IDLEを起動します。

f:id:arakan_no_boku:20170928225530j:plain

 

IDLEコンソールで、File>New File で新規にエディタを開いておいてください。

f:id:arakan_no_boku:20170928225739j:plain

 

ニューラルネットワークコンソールからエクスポート

 

2017/12/02追記

>以降の説明の画面はVersion1.00のものです。

>Version1.10でアイコンのデザインが一部変更になってます。

>しかし、見てわかる程度と判断して、そのままにしています。

 

ニューラルネットワークコンソールを立ち上げます。

f:id:arakan_no_boku:20170928224515j:plain

 

LeNet.sdcprj を選択して開き、ACTION>Export>Python Code(NNabla) を選びます。

f:id:arakan_no_boku:20170928224917j:plain

 

 そうすると、pythonコードがクリップボードにコピーされます。

f:id:arakan_no_boku:20170928234054j:plain

 

これを、先程立ち上げておいたIDLEエディタに貼り付けて、名前をつけて保存します。

f:id:arakan_no_boku:20170928235013j:plain

 ここからは、ニューラルネットワークコンソールのバージョンで、ちょっと内容が変わります。

 

Version1.00でエクスポートした場合

 

コピーできたのは、こんな感じのネットワーク定義です。

def network(x, y, test=False):

     # Input -> 1,28,28
     # Convolution -> 16,22,22
     with nn.parameter_scope('Convolution'):
          h = PF.convolution(x, 16, (7,7), (0,0))
     # ReLU
     h = F.relu(h, True)
     # MaxPooling -> 16,11,11
     h = F.max_pooling(h, (2,2), (2,2), True)
     # Convolution_2 -> 30,9,9
     with nn.parameter_scope('Convolution_2'):
            h = PF.convolution(h, 30, (3,3), (0,0))
     # MaxPooling_2 -> 30,4,4

     h = F.max_pooling(h, (2,2), (2,2), True)
     # Tanh_2
     h = F.tanh(h)
     # Affine -> 150
    with nn.parameter_scope('Affine'):
         h = PF.affine(h, (150,))
    # ReLU_2
    h = F.relu(h, True)
    # Affine_2 -> 10
    with nn.parameter_scope('Affine_2'):
         h= PF.affine(h, (10,))

    # Softmax
    h = F.softmax(h)
    # CategoricalCrossEntropy -> 1
    #h = F.categorical_cross_entropy(h, y)
    return  h

 

上記は、エクスポートした結果に3つほどの作業を施してます。 

ひとつは、h = F.categorical_cross_entropy(h, y) のコメントアウトです。 

ニューラルネットワークコンソールからエクスポートすると、これもついてきてしまうのですが、これを残しておくと推論が正しくできません。 

 

ふたつめは、nn.parameter_scope の部分の nn. がなくて、parameter_scope になっている場合は、nn. を書き足します。 

 

みっつ目は、最後の「return」です。 

y=network(x)のように使いたいので、ここは、「return」のままではなく、を書き足して、「return h」としておきます。 

しないと、左辺で受けた「y」がNonTypeだ・・といって、エラーになります。 

 

あと、以下のimportを追加で書き込む必要があります。

import nnabla as nn
import nnabla.functions as F
import nnabla.parametric_functions as PF
import nnabla.solvers as S
from nnabla.utils.data_iterator import data_iterator_csv_dataset 

 一応、ここまではOKです。

 

Version1.10以降でエクスポートした場合 

 

エクスポートした状態がこれです。

import nnabla as nn
import nnabla.functions as F
import nnabla.parametric_functions as PF

def network(x, y, test=False):
    # Input -> 1,28,28
    # Convolution -> 16,22,22
    with nn.parameter_scope('Convolution'):
        h = PF.convolution(x, 16, (7,7), (0,0))
    # ReLU
    h = F.relu(h, True)
    # MaxPooling -> 16,11,11
    h = F.max_pooling(h, (2,2), (2,2), True)
    # Convolution_2 -> 30,9,9
    with nn.parameter_scope('Convolution_2'):
        h = PF.convolution(h, 30, (3,3), (0,0))
    # MaxPooling_2 -> 30,4,4
    h = F.max_pooling(h, (2,2), (2,2), True)
    # Tanh_2
    h = F.tanh(h)
    # Affine -> 150
    with nn.parameter_scope('Affine'):
        h = PF.affine(h, (150,))
    # ReLU_2
    h = F.relu(h, True)
    # Affine_2 -> 10
    with nn.parameter_scope('Affine_2'):
        h = PF.affine(h, (10,))
    # Softmax
    h = F.softmax(h)
    # CategoricalCrossEntropy -> 1
    h = F.categorical_cross_entropy(h, y)
return h

 

Version1.00で不足していたimportと、nn.の追加、および return hにするの三ヶ所は修正されています。 

なので、Version1.10で必要な加工は、「 h = F.categorical_cross_entropy(h, y)」を コメントアウトするために、「#h = F.categorical_cross_entropy(h, y)」にするだけです。 

あと、importは以下の2行だけ追加します。

import nnabla.solvers as S
from nnabla.utils.data_iterator import data_iterator_csv_dataset 

 

推論するためのデータ・セットの読み込みを記述する

 

推論するためには、データ・セットを読み込まないと話になりません。 

これも、ニューラルネットワークコンソールで用意されているデータ・セットをそのまま使ってみます。 

読み込むためのAPIは、リファレンスで、まさにそのためのAPIですと、言わんばかりのものを見つけました。 

nnabla.utils.data_iterator.data_iterator_csv_dataset(uri, batch_size, shuffle, rng=None, normalize=True, with_memory_cache=True, with_file_cache=True, cache_dir=None, epoch_begin_callbacks=, epoch_end_callbacks=)

 

これだけあればいけるんじゃないの!・・ということで、早速試してみます。 

ニューラルネットワークコンソールをインストールしたフォルダに行きます。 

そこの「sample_dataset\MNIST」フォルダの下にある「mnist_test.csv」を使うことにします。 

ニューラルネットワークコンソールのDATASETタブのこれですね。

f:id:arakan_no_boku:20170929231243j:plain

 これをフルパスで以下のように指定してみます。

test_data = data_iterator_csv_dataset("C:\\ncon\\samples\\sample_dataset\\MNIST\\mnist_test.csv",1,shuffle=False)

c:\\ncon は、ニューラルネットワークコンソールをインストールしたフォルダです。 

引数の1は、ループの中でカウントして100件毎にログを出したかったので、バッチサイズを1件にするためにしています。 

ShuffleはFalseにしておきます。 

学習の時はTrueですが、今はTrueにする意味がないので。 

あと、\ は、\\ とエスケープしないと、エラーになるので忘れないように・・と。 

 

学習済パラメータをロードする記述を記述する

 

次はパラメータです。 

学習済パラメータのLOADの方法は練習を兼ねて、前回の記事でチュートリアルを参考にやってみました。

arakan-pgm-ai.hatenablog.com

 これにならってやります。 

LeNetのプロジェクトがあるフォルダに行きます。 

自分の環境だと、以下の場所にあります。

C:\ncon\samples\sample_project\image_recognition\MNIST

 このフォルダの「LeNet.files」というフォルダの下に学習済パラメータが保存されています。 

ニューラルネットワークコンソールのEDUCATIONタグで一番成績がよかった番号と同じ名前のフォルダを探します。

f:id:arakan_no_boku:20170929232022j:plain

 その下にある「parameters.h5」を、これもフルパスで指定すれば良さそうです。 

こんな感じです。

nn.parameter.load_parameters("C:\\ncon\\samples\\sample_project\\image_recognition\\MNIST\\LeNet.files\\20170923_103005\\parameters.h5");

 

推論を実行する部分を記述する 

 

推論をする部分のコードを書かないといけません。 

データ読み込みを行い、学習済パラメータを読み込み、それらを使って推論する。 

その部分を書くとこんな感じになります。

#テスト用データの読み込み

test_data = data_iterator_csv_dataset("C:\\ncon\\samples\\sample_dataset\\MNIST\\mnist_test.csv",1,shuffle=False)

#一致件数カウンターのクリア

mch = 0

#ネットワークの構築
nn.clear_parameters()
x = nn.Variable*1
t = nn.Variable*2
y = network(x,t)

#学習済パラメータの読み込み

nn.parameter.load_parameters("C:\\ncon\\samples\\sample_project\\image_recognition\\MNIST\\LeNet.files\\20170923_103005\\parameters.h5");

#1件ずつデータを読み、推論して結果をカウントしていく

for i in range(test_data.size):
    x.d, t.d = test_data.next()
    y.forward()
    if t.d == y.d.argmax(axis=1) :
        mch += 1

print("Accuracy:{}".format(mch / test_data.size)) 

 簡単に補足します。  

まず、以下のように書いてある部分。

f:id:arakan_no_boku:20171003193954j:plain

これは、要するにData部のshapeとLabel部のshapeです。 

MNISTなので、1,28,28で、バッチサイズが1 ということです。 

パラメータの読み込みの前に、パラメータ領域をクリアしていますが、この処理の置き場所が最大のポイントです。。

nn.clear_parameters()

 

上記のように、ネットワークを構築する前におかないとダメです。 

これを例えば、学習済パラメータのロード処理の直前とかに置いてしまうと、まったく学習されていない結果に悩むことになります。 

 

後は、データを1件ずつ読み込んで、forward()して、結果を比較してマッチしてたらカウントして結果を表示するだけです。 

これで保存して、F5キーを押して実行します。

f:id:arakan_no_boku:20171001170852j:plain

 

99.04%!いけました

 

2017/12/02追記

>上記の結果は、ニューラルネットワークコンソールとNNablaのバージョンによって若干差異があるようです。

 

上記は、convolutionの初期値とかがデフォルトのままにしている(エクスポートでは初期値設定とかは出力されないみたいなので)ことを考えれば、ほぼパラメータは正しく認識できてます。 

あとは、上記のネットワーク構築時に、初期値とかを適切に設定してやればよさそうですね。 

とりあえず、ニューラルネットワークコンソールで学習した結果のパラメータと、データ・セットがそのままNNablaで使えるのがわかったのは大きいですねえ。

 

2017/12/02追記

ニューラルネットワークコンソールのVersion1.10でアイコンのデザインが変更になっている主な箇所です。

f:id:arakan_no_boku:20171130202351j:plain

 

f:id:arakan_no_boku:20171115215731j:plain

*1:1,1,28,28

*2:1,1