目次
はじめに
リライトで目次を追加しました。
内容は、2018年10月時点のままで変更していません。
keras/tensorflowの構文も当時のバージョンで動作確認をしており、tensorflow1.0でしか動かないと思います。
Neural Network Consoleモデルからkerasへ書き換え
Neural Network Consoleで設計したモデルの「keras/tennsorflowモデル」への書き換えをやってみます。
サンプルとして、Neural Network Consoleのサンプルプロジェクトの「LeNet」をkerasに書き写してみようと思います。
書き換え対象のLeNetのモデルはこうです。
keras側のベースになるソーステンプレート
テンプレート枠を以下のように用意します。
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」です。
上記のMax_Epochと、BatchSizeの値にあわせて、以下のように書き換えます。
model.fit(x_train, y_train, epochs=10,batch_size=64)
次にOptimizerです。
上記の、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とデータは、kerasのプログラムと同じフォルダに置くと、あれこれ悩まなくてすむので、そうしてます。
モデルを上から順番に置き換えていく
kerasでは、以下の宣言でまずモデルの枠を作ります。
model = tf.keras.models.Sequential()
あとは、model.add(・・・)で、必要なレイヤーをペタペタ貼り付けていくだけです。
GUIであるなし・・の違いはありますが、Neural Network Consoleと似た感覚で積み上げていくことができます。
あとはNeural Network Consoleの各レイヤーを、kerasではどう書けばよいのかというだけの問題ですね。
最初のConvolution+Relu
まず最初のレイヤーから。
そして、Convolutionのプロパティがこうです。
これを、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
これをkerasで書くとこうなります。
model.add(tf.keras.layers.MaxPooling2D(pool_size=(2, 2)))
オプションの「pool_size」の値には、上記の「shape:2,2」と書かれている部分の値を転記します。
二つ目のConvolution
パラメータはこうです。
これを、kerasで書くとこうです。
model.add(tf.keras.layers.Conv2D(30, kernel_size=(3, 3))
OutMapsとKernelShapeについては、一つ目と同じように転記します。
ただ、inputのサイズは一番最初のレイヤー以外はkerasが自動計算してくれるので、転記する必要はありません。
また、この2つ目は後ろにReLuがつかないので、activationオプションもありません。
二つ目のMaxPooling
これは、ほぼ一つ目と同じです。
model.add(tf.keras.layers.MaxPooling2D(pool_size=(2, 2)))
後続の活性化関数とAffine層をまとめて
残りも同じ要領なので、まとめていきます。
活性化関数「Tanh」はこうです。
model.add(tf.keras.layers.Activation('tanh'))
こんな感じで単独で活性化関数を書くときは、「Activation」レイヤーを使います。
この後は、「Affine」レイヤーが来ます。
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」なんかも同様です。
だから、続く
という組み合わせは、以下のように書きます。
model.add(tf.keras.layers.Dense(10, activation='softmax'))
損失関数はmodel.addではない
ここまでは比較的ワンパターンなのですが、最後の損失関数。
ここだけは、ちょっと違います。
これだけは、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の以下のモデルに対応します。
これでも良いのですが、
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])
これを実行してみます。
10カテゴリの分類なのに、データ画像を7000枚位しか用意できなかったので、若干過学習気味ですけど、まあ、ちゃんと動いてるっぽいですね。
Neural Network Consoleとkerasは、レイヤーをペタペタ積み上げてモデルを作る感覚がよく似てます。
だから、読み替えだけの話になってくるので、そこさえわかれば手作業で移植しても、それほど手間はかからないんじゃないか・・と思ってやってみました。
結果。
思っていたより、スムース・・かつ、簡単でした。
いい感じです。