無から始める Keras のもくじはこちら
前回のおさらい
前回は雑なデータセットを作って雑に学習させた。
入力は 5 次元で、要素がそれぞれ 0 以上 1 未満。出力は 1 次元で、入力の要素の和が 2.5 以下だったら -1、2.5 より大きかったら 1。いわゆる「2 クラス分類」をする。
データセットは以下のように作った。出力の方は 0 or 1 にしてから 2 をかけて -1 している(-1 か 1 になる)。
data = np.random.rand(250,5)
labels = (np.sum(data, axis=1) > 2.5) * 2 - 1
モデルは以下のようにした。活性化関数には -1 から 1 を値域としてとれる tanh を使って、損失には符号に対してよく利くヒンジ損失を使った。
model = Sequential([Dense(20, input_dim=5, activation='tanh'), Dense(1, activation='tanh')])
model.compile('adam', 'hinge', metrics=['accuracy'])
Sequential モデルとは
第 1 回でも述べたように、Sequential モデルは単純に、今の層のそれぞれのノードに、前の層の全ノードから矢印を引っ張ってくるイメージである。
確かにこれでもニューラルネットワークとして機能するのだが、ちょっとおもしろくない。
これを解決するのが Functional API である。なんか 2 つのニューラルネットワークの出力を入れてみたり、レイヤーを共有してみたりと、結構複雑なことができるようになる。
Functional API とは
前回と同じ形のニューラルネットワークを作ってみよう。
同じ形なので作るのが無意味な雰囲気がするが、そこは練習である。
Functional API は簡単に言えば「矢印のつなぎ方をぐちゃぐちゃにできる」ものである。Sequential モデルでは A → B → C という形でしかできなかった。
でも Functional API では
A
↘
C → D → E
↗
B
みたいなことができるようになる。今回はやらないけど。
Functional API を使う
入力層
まずは入力の層。
from keras.layers import Input, Dense
from keras.models import Model
inputs = Input(shape=(5,))
この Input という名前の層なのだが、残念ながらドキュメントがない。
shape
は入力の次元を表している。
(5,)
という表記は Python を知らないとなんやねんという感じだが、ただ単純に 5 という要素だけもつ Tuple(配列みたいなもの)である。カンマがあるので複数要素があるように見えるが、要素は 1 個である。カンマがないとただの数値と見分けがつかないからなのだが、本当にクソ記法だと思う。Python はこういうところが嫌い。
隠れ層
x = Dense(20, activation='tanh')(inputs)
ノード数が20で、入力が inputs
であるような層。
dense は「密な」という意味だが、前の層のすべてのノードから、この層のすべてのノードに対して、すべての矢印を引っ張るので、そういう意味で密なんだと思う。
出力層
predictions = Dense(1, activation='tanh')(x)
こちらは隠れ層とほぼ同じ。説明はいらないでしょう。
Functional API の意味付け
Functional(関数型)というのはどういうことか。
よく見ると Dense
はそのインスタンスを関数呼び出ししていることがわかる。
ドキュメントには、レイヤーのインスタンスを関数呼び出しすると、テンソルが返ると書いてある。
そもそもニューラルネットワークというのは、おおよそテンソル積(もっと単純化すれば行列積)である。
入力ベクトルに行列をかけて、さらに行列をかけて……というのがそもそもの本質になる。
1 回層を通り抜けることが、1 回行列をかけるのとだいたい同じ意味になる。
その行列(テンソル)をかけ合わせるイメージを、関数呼び出しと照らし合わせている。
関数呼び出しをすると、入力したテンソルが、指定した意味合いをもって変形されて、またテンソルとなって出力される。そういう感じのイメージでとらえるとわかりやすい気がする。
まったく厳密なことを言っていないのでアレだけど。
ちなみに Input
レイヤーだけはそもそもテンソルが返ってくる(ので関数呼び出しがいらない)。
今回のプログラムまとめ
import numpy as np
from keras.layers import Input, Dense
from keras.models import Model
data = np.random.rand(250,5)
labels = (np.sum(data, axis=1) > 2.5) * 2 - 1
inputs = Input(shape=(5,))
x = Dense(20, activation='tanh')(inputs)
predictions = Dense(1, activation='tanh')(x)
model = Model(input=inputs, output=predictions)
model.compile('adam', 'hinge', metrics=['accuracy'])
model.fit(data, labels, nb_epoch=150, validation_split=0.2)
test = np.random.rand(200, 5)
predict = np.sign(model.predict(test).flatten())
real = (np.sum(test, axis=1) > 2.5) * 2 - 1
print(sum(predict == real) / 200.0)
書き方以外は第 2 回とまったく同じなので、ほぼ同じ精度が出ると思う。