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

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

NNabla(Neural Network Libraries)のCNNのチュートリアルをPython3.5でやってみる。

NNabla(Neural Network Libraries)のチュートリアルで(・・だいぶ書き換えてしまってはいますが・・)CNNをやります。 

事前設定や開発環境の使い方とかは、繰り返しになるので、必要に応じて前回の記事を読んでください。 

元にしたチュートリアルは以下のページの「Convolutional Neural Network with CUDA acceleration」です。

NNabla by Examples — Neural Network Libraries 0.9.4 documentation

 ただ、CPUのみの環境なので、CUDAは関係ないし、python2用でそのままだと、python3.5ではエラーになったりするので、ちょこちょこ書き換えました。

 

例によって、ソース全体を記載します

 

#① NNabla関連モジュールのインポート
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_simple

#② NNabla関連以外のモジュールのインポート
import numpy as np
from sklearn.datasets import load_digits

#③ tiny_digits.pyから転載したデータ整形function
def data_iterator_tiny_digits(digits, batch_size=64, shuffle=False, rng=None):
    def load_func(index):
         """Loading an image and its label"""
         img = digits.images[index]
         label = digits.target[index]
         return img[None], np.array([label]).astype(np.int32)
     return data_iterator_simple(load_func, digits.target.shape[0], batch_size, shuffle, rng, with_file_cache=False)

#④ 損失グラフを構築する関数を定義する
def logreg_loss(y, t):
     loss_f = F.mean(F.softmax_cross_entropy(y, t))
     return loss_f

#⑤ トレーニング関数を定義する
def training(xt,tt,data_t,loss_t,steps,learning_rate):
     solver = S.Sgd(learning_rate)
     solver.set_parameters(nn.get_parameters()) # Set parameter variables to be updatd.
     for i in range(steps):
         xt.d, tt.d = data_t.next()
         loss_t.forward()
         solver.zero_grad() # Initialize gradients of all parameters to zero.
         loss_t.backward()
         solver.weight_decay(1e-5) # Applying weight decay as an    regularization
         solver.update()
         if i % 100 == 0: # Print for each 10 iterations
              print(str(i) +":" + str(loss.d))

#⑥ ニューラルネットを構築する関数を定義する
def network(x):
    with nn.parameter_scope("cnn"): 
         with nn.parameter_scope("conv1"):
             h = F.tanh(PF.batch_normalization(
             PF.convolution(x, 4, (3, 3), pad=(1, 1), stride=(2, 2))))
         with nn.parameter_scope("conv2"):
             h = F.tanh(PF.batch_normalization(
             PF.convolution(h, 8, (3, 3), pad=(1, 1))))
             h = F.average_pooling(h, (2, 2))
        with nn.parameter_scope("fc3"):
             h = F.tanh(PF.affine(h, 32))
        with nn.parameter_scope("classifier"):
             h = PF.affine(h,10)
    return h

#⑦ 実行開始:scikit_learnでdigits(8✕8サイズ)データを取得し、NNablaで処理可能に整形する
np.random.seed(0)
digits = load_digits(n_class=10)
data = data_iterator_tiny_digits(digits, batch_size=64, shuffle=True)

#⑧ ニューラルネットワークを構築する
nn.clear_parameters()
img, label = data.next()
x = nn.Variable(img.shape)
y = network(x)
t = nn.Variable(label.shape)
loss = logreg_loss(y, t)

#⑨ 学習する
learning_rate = 1e-1
training(x,t,data,loss,1000,learning_rate)

#⑩ 推論し、最後に正確さを求めて表示する
x.d, t.d = data.next()
y.forward()
mch = 0
for p in range(len(t.d)):
    if t.d[p] == y.d.argmax(axis=1)[p] :
         mch += 1

print("Accuracy:{}".format(mch / len(t.d)))

 

 

ソースの各部分を解説します

 

① NNabla関連モジュールのインポート

② NNabla関連以外のモジュールのインポート

③ tiny_digits.pyから転載したデータ整形function

までは、前回のままです。

 

④ 損失グラフを構築する関数を定義する

処理自体は前回と変わってませんが、関数化しただけです。 

チュートリアルがそうなっていたので。

 

⑤ トレーニング関数を定義する

処理自体は、これも前回と変わってません。 

チュートリアルにならって、関数化しただけです。 

ただ、チュートリアルとは引数を変えたりしてます。 

グローバルに関数のスコープの中で外で定義した関数を使わないようにしたいという個人的こだわりだけの理由です。

 

ニューラルネットを構築する関数を定義する

CNNのネットワークを組んでます。 

見ると、単純にレイヤーをネストして積み上げているだけです。 

例えば、Convolution(畳み込み層)+tanh(活性化関数)+average_pooling(プーリング層)の組み合わせだと、以下みたいに書いてます。

 h = F.tanh(PF.batch_normalization(
 PF.convolution(h, 8, (3, 3), pad=(1, 1))))
 h = F.average_pooling(h, (2, 2))

 とは言え、ここは手で書くことは、ほとんどないと思います。 

何故かいうと、この関数の形で「ニューラルネットワークコンソール(Neural Network Console)」からPythonコードをエクスポートできるからです。 

なるほど、ニューラルネットワークコンソールとNNabla(Neural Network Libraries)はここでつながるのか・・と、やっとわかりました。 

ニューラルネットワークコンソールでGUIで設計・テストして、良好な結果が得られたネットワークをpythonコードでエクスポートして、NNablaに組み込んで、本番で動かす・・こんな感じの想定なんでしょうね。

 

⑦ 実行開始:scikit_learnでdigits(8✕8サイズ)データを取得し、NNablaで処理可能に整形する

ここも前回と全く同じです。

 

⑧ ニューラルネットワークを構築する

ここで、前に定義した関数を使って、ニューラルネットを構築し、損失グラフを生成しています。 

ポイントは、「nn.clear_parameters()」という処理が追加されていることです。 

これは「開始前に、登録されているすべてのパラメータをクリア」する処理です 

毎回、忘れずに指定する必要がありそうです。

 

⑨ 学習する

学習率を指定して、前に定義した関数で学習を行います。

 

⑩ 推論し、最後に正確さを求めて表示する

ここは前回と同じです。

これを実行(IDLEでF5キー)した結果は、こんな感じになります。

f:id:arakan_no_boku:20170927231009j:plain

 

正確さが100%ですが・・まあ、当たり前です。 

TOYプログラムなので、学習データと同じデータで推論してますから。 

ただ、まあ、なんとなくイケてる感じですね。 

次から、ニューラルネットワークコンソールからエクスポートしたPythonコードを使って動かしてみるのも、やれそうな気がしてきました(笑)。 

関連記事

NNablaカテゴリの記事一覧はこちらです。

arakan-pgm-ai.hatenablog.com

NNablaの統合開発環境である、ニューラルネットワークコンソールカテゴリの記事一覧はこちらです

arakan-pgm-ai.hatenablog.com