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

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

Neural Network Consoleで設計したニューラルネットワークモデルを、keras/tensorflowに書きかえる。/使い方・番外編

Tensorflow.jsでブラウザでディープラーニングを実行したい。

それには、keras/tensorflowで学習済モデルを作らないといけない。

でも、Neural Network Consoleでグラフィカルに設計を検討するのも捨てがたい。

じゃあ、Neural Network Consoleのモデルを、keras/tennsorflowに書き換えてしまえばいいじゃないか・・。

今回は、そういう発想の話です。

f:id:arakan_no_boku:20181020211946j:plain

 

Neural Network Consoleからkerasへ

 

さて。

モデルの置き換えをしていきます。

Neural Network Consoleから「Neural Network Libraries」のソースとして、モデルをエクスポートして、それを元にする手もありますが、今回はやめときました。

とりあえず、Neural Network Consoleのサンプルプロジェクトの「LeNet」をkerasに書き写してみます。

LeNetのモデルはこうです。

f:id:arakan_no_boku:20181021230320j:plain

 

keras側のベースになるソーステンプレート

 

前回、Neural Network ConsoleのデータセットCSVを直接読み込む処理を作りました。

arakan-pgm-ai.hatenablog.com

せっかくなので、それを使う前提でテンプレート枠を以下のように用意します。

import tensorflow as tf
import nncdata as nd

nnc = nd.NncData()
x_train, x_test, y_train, y_test = nnc.load_nnc_image('./mnist_test.csv',width=28,height=28)

model = tf.keras.models.Sequential()

model.compile(loss='categorical_crossentropy',
              optimizer=tf.keras.optimizers.Adam(),
              metrics=['accuracy'])

model.fit(x_train, y_train, epochs=10,batch_size=64)
sc = model.evaluate(x_test, y_test)
print("acc=",sc[1])

 

テンプレートをCONFIGにあわせて修正する 

 

最初にするのは、上記テンプレートの修正です。

Neural Network ConsoleのCOFIGタブにあわせておこないます。

まず、「Global Config」です。

f:id:arakan_no_boku:20181021232405j:plain

上記のMax_Epochと、BatchSizeの値にあわせて、以下のように書き換えます。

model.fit(x_train, y_train, epochs=10,batch_size=64)

 

次にOptimizerです。

f:id:arakan_no_boku:20181021232615j:plain

上記の、Updaterの選択肢にあわせて、以下のように書き換えます。

model.compile(loss='categorical_crossentropy',
optimizer=tf.keras.optimizers.Adam(),
metrics=['accuracy'])

ちなみに、他の選択肢の場合も、だいたい同じ名前に書き換えれば大丈夫です。

kerasに用意されている主なオプティマイザーを以下に示します。

  • tf.keras.optimizers.SGD()
  • tf.keras.optimizers.RMSprop()
  • tf.keras.optimizers.Adagrad()
  • tf.keras.optimizers.Adadelta()
  • tf.keras.optimizers.Adamax()

注意が必要なのは、「Nesterov」と「 Momentum」が、Neural Network Consoleで選択されている場合です。

kerasではこの2つは、SGD()に含まれていて、パラメータを指定することで、等価な動きをするようになってますので、そのままの名前では置き換えできません。

 

Inputはテンプレートのデータ取り込み部分を変更する

 

keras/tensorflowでは、最初の「Input」はモデルに含めません。

以下のようなデータ取り込み(前処理)部で対応します。

nnc = nd.NncData()
x_train, x_test, y_train, y_test = nnc.load_nnc_image('./mnist_test.csv',width=28,height=28)

学習用のデータセットCSVとデータの用意。

それと対照データのサイズにあわせて、変更します。

なお、データセットCSVとデータは、kerasのプログラムと同じフォルダに置くと、あれこれ悩まなくてすむので、そうしてます。

 

モデルを上から順番に置き換えていく

 

kerasでは、以下の宣言でまずモデルの枠を作ります。

model = tf.keras.models.Sequential()

あとは、model.add(・・・)で、必要なレイヤーをペタペタ貼り付けていくだけです。

GUIであるなし・・の違いはありますが、Neural Network Consoleと似た感覚で積み上げていくことができます。

あとはNeural Network Consoleの各レイヤーを、kerasではどう書けばよいのかというだけの問題ですね。

最初のConvolution+Relu

まず最初のレイヤーから。

f:id:arakan_no_boku:20181021235337j:plain

そして、Convolutionのプロパティがこうです。

f:id:arakan_no_boku:20181021235857j:plain

これを、kerasで書くとこうなります。

model.add(tf.keras.layers.Conv2D(16, kernel_size=(7, 7), activation='relu', input_shape=(28, 28, 1)))

Conv2Dレイヤーで対応します。

OutMapsの値を第一パラメータ、KernekShapeの値と、inputのサイズも転記します。

なお、inputがNeural Network Consoleでは「1,28,28」とチャネル数(1ならグレースケール、3ならカラー等)が前に来てますが、kerasだと「28,28,1」のように後ろになるので注意します。

なお、ReLuのような活性化関数のレイヤーは、上記例のように「activation」オプションで名前を書いておけば、レイヤーとして設定する必要はありません。 

一つ目のMaxPooling

f:id:arakan_no_boku:20181022000742j:plain

これをkerasで書くとこうなります。

model.add(tf.keras.layers.MaxPooling2D(pool_size=(2, 2)))

 オプションの「pool_size」の値には、上記の「shape:2,2」と書かれている部分の値を転記します。 

二つ目のConvolution

f:id:arakan_no_boku:20181022001129j:plain

パラメータはこうです。

f:id:arakan_no_boku:20181022001155j:plain

これを、kerasで書くとこうです。

model.add(tf.keras.layers.Conv2D(30, kernel_size=(3, 3))

OutMapsとKernelShapeについては、一つ目と同じように転記します。

ただ、inputのサイズは一番最初のレイヤー以外はkerasが自動計算してくれるので、転記する必要はありません。

また、この2つ目は後ろにReLuがつかないので、activationオプションもありません。 

二つ目のMaxPooling

f:id:arakan_no_boku:20181022001649j:plain

これは、ほぼ一つ目と同じです。

model.add(tf.keras.layers.MaxPooling2D(pool_size=(2, 2)))

 

後続の活性化関数とAffine層をまとめて

残りも同じ要領なので、まとめていきます。

f:id:arakan_no_boku:20181022211435j:plain

活性化関数「Tanh」はこうです。

model.add(tf.keras.layers.Activation('tanh'))

こんな感じで単独で活性化関数を書くときは、「Activation」レイヤーを使います。

この後は、「Affine」レイヤーが来ます。

f:id:arakan_no_boku:20181022212211j:plain

AffineとReLUの組み合わせは、kerasでは以下のように書きます。

model.add(tf.keras.layers.Dense(150, activation='relu'))

第一引数の「150」は、 Neural Network Consoleでの「OutShape」つまり、ボックスの横に表示されている数字です。

activationに名称を指定することで、ReLUレイヤーも併せてかけます。

ちなみに、ReLUのところが、sigmoidだったら。

model.add(tf.keras.layers.Dense(150, activation='sigmoid'))

になるわけで、「tanh」、「softmax」なんかも同様です。

だから、続く

f:id:arakan_no_boku:20181022220230j:plain

という組み合わせは、以下のように書きます。

model.add(tf.keras.layers.Dense(10, activation='softmax'))

 

損失関数はmodel.addじゃないぞ

ここまでは比較的ワンパターンなのですが、最後の損失関数。

f:id:arakan_no_boku:20181022220513j:plain

ここだけは、ちょっと違います。

これだけは、model.compileの「loss=」のパラメータとして指定するわけです。

今回の場合は以下のようになります。

model.compile(loss='categorical_crossentropy',
optimizer=tf.keras.optimizers.Adam(),
metrics=['accuracy'])

 

まとめてみます

ここまでのモデルのソースの部分をまとめてみます。

model = tf.keras.models.Sequential()
model.add(tf.keras.layers.Conv2D(16, kernel_size=(7, 7), activation='relu', input_shape=(28, 28, 1)))
model.add(tf.keras.layers.MaxPooling2D(pool_size=(2, 2)))
model.add(tf.keras.layers.Conv2D(30, kernel_size=(3, 3)))
model.add(tf.keras.layers.MaxPooling2D(pool_size=(2, 2)))
model.add(tf.keras.layers.Activation('tanh'))
model.add(tf.keras.layers.Dense(150, activation='relu'))
model.add(tf.keras.layers.Dense(10, activation='softmax'))
model.compile(loss='categorical_crossentropy',
              optimizer=tf.keras.optimizers.Adam(),
              metrics=['accuracy'])

 

これが、 Neural Network Consoleの以下のモデルに対応します。

f:id:arakan_no_boku:20181022221821j:plain

これでも良いのですが、

model.add(tf.keras.layers.Activation('tanh'))
model.add(tf.keras.layers.Dense(150, activation='relu'))

の間に「Flatten()」をはさんで以下のようにしても良いです。

model.add(tf.keras.layers.Activation('tanh'))
model.add(tf.keras.layers.Flatten())
model.add(tf.keras.layers.Dense(150, activation='relu'))

 

このFlatten()は「入力を平滑化する」と説明には書かれてます。

つまり、tanhの出力「30,4,4」を、Affineに対応するDenseに渡す時に「30×4×4」の480に変換してくれる・・感じです。

実際には、Flatten()をはさまなくても、Denseの最初で平滑化されるので、問題はないのですが、この方が明快なので、kerasのサンプルには、よく出てきます。

なので、今回も最後の全体ソースでは、Flattenをはさんだ書き方にしてます。 

 

最後にモデルを組み込んだ全体のソース

 

最終的に全体は、こんな感じになります。

import tensorflow as tf
import nncdata as nd

nnc = nd.NncData()
x_train, x_test, y_train, y_test = nnc.load_nnc_image('./mnist_test.csv',width=28,height=28)

model = tf.keras.models.Sequential()
model.add(tf.keras.layers.Conv2D(16, kernel_size=(7, 7), activation='relu', input_shape=(28, 28, 1)))
model.add(tf.keras.layers.MaxPooling2D(pool_size=(2, 2)))
model.add(tf.keras.layers.Conv2D(30, kernel_size=(3, 3)))
model.add(tf.keras.layers.MaxPooling2D(pool_size=(2, 2)))
model.add(tf.keras.layers.Activation('tanh'))
model.add(tf.keras.layers.Flatten())
model.add(tf.keras.layers.Dense(150, activation='relu'))
model.add(tf.keras.layers.Dense(10, activation='softmax'))
model.compile(loss='categorical_crossentropy',
              optimizer=tf.keras.optimizers.Adam(),
              metrics=['accuracy'])

model.fit(x_train, y_train, epochs=10,batch_size=64)
sc = model.evaluate(x_test, y_test)
print("acc=",sc[1])

これを実行してみます。 

f:id:arakan_no_boku:20181022224502j:plain

10カテゴリの分類なのに、データ画像を7000枚位しか用意できなかったので、若干過学習気味ですけど、まあ、ちゃんと動いてるっぽいですね。

Neural Network Consoleとkerasは、レイヤーをペタペタ積み上げてモデルを作る感覚がよく似てます。

だから、読み替えだけの話になってくるので、そこさえわかれば手作業で移植しても、それほど手間はかからないんじゃないか・・と思ってやってみました。

結果。

思っていたより、スムース・・かつ、簡単でした。

いい感じです。