前回まで2次元畳み込み層を実現するConv2Dに着目してみました.
今回は全結合層のDenseです.
旧来の階層型ニューラルネットとはこのDenseのみで構成されていたわけで,その意味では最も基礎にあたると言えるでしょうね.
それで,まず最初はこのDenseを1層だけ用いた場合を考えてみます.コードは下記.
from keras import Model
from keras.layers import Input, Dense
import numpy as np
input_data = Input(shape=(2))
output_data = Dense(units=1, activation='sigmoid')(input_data)
model = Model(inputs=input_data, outputs=output_data)
model.summary()
summaryの出力結果は以下です.
入力は2つ,出力が1つなので,パラメータはそれらを接合する重み2つとバイアス1つ,合計3つとなります(Trainable params: 3と表示されています)
さて,ここでANDを学習させたいと思います.つまり,入力が(1,1)のときのみ1を出力し,(0,0),(1,0),(0,1)のとき0を出力するように学習させます.
で,そのデータですが,下記のように作りましたが,Pythonらしくない酷いコードですがご勘弁を💦
x = np.array([[0,0],[0,1],[1,0],[1,1]]) # AND input
y = np.array([0,0,0,1])[:,None] # AND output
x_train = np.zeros((1000,2)) # 1000個のデータ数
y_train = np.zeros((1000,1))
i=0
for n in range(1000):
x_train[n] = x[i]
y_train[n] = y[i]
i = (i + 1)%4
そして学習させます
model.fit(x=x_train, y=y_train, batch_size=10, epochs=100, validation_split=0.2, verbose=1)
w = model.layers[1].get_weights()
w0=w[0]
w1=w[1][0]
print(w0)
print(w1)
結果はこちらのようになりました.
w[0]ですが,入力をx0, x1とすると,それらに対する重みであるw0, w1は,w[0]=[[w0],[w1]]のような形で縦に入ってます.
今回は出力を1つに設定してますが,もし出力を2つに設定した場合を考えます.それぞれy0, y1とした場合,全結合なのでx0-y0, x0-y1, x1-y0, x1-y1の結合の重みがあり,それらをw00, w01, w10, w11とします.すると,w[0]=[[w00, w01],[w10, w11]]という形で値が入ります.
したがって,このw[0]の縦方向と横方向の違いは間違えやすそうです.
3つ目のw[1][0]=-3.7はバイアスの値なので,特に問題ありません.1つの出力に対し1つのバイアスがあります(設定でなくすことも出来ますが,デフォルトはありです)
それでは,この重みの値を用いて分離平面をプロットしてみます.
w = model.layers[1].get_weights()
w0 = w[0]
a = -w0[0]/w0[1]
b = -w[1][0]/w0[1]
x1 = np.arange(-0.5,1.5,0.1)
y1 = a*x1+b
import matplotlib.pyplot as plt
plt.plot(x[:,0],x[:,1],'o')
plt.plot(x1, y1)
今回はDense層が1つだけなので,式で書くと\(y=f(w_0x_0+w_1x_1+b)\)であり,関数\(f\)はシグモイド関数\(f(u)=1/(1+e^{-u})\)です.
これは大雑把に言えば\(u>0\)で出力1,\(u<0\)で出力0となることを連続微分可能な関数で表現したもの.
つまり,\(u=0\)の直線が出力\(y\)が0か1の境界線ということになります.これを図示します.
直線の式 \(w_0x_0+w_1x_1+b=0\)を変形すると,\(x_1 = -(w_0/w_1)x_0 – (b/w_1) \)となるので,傾きが\(-(w_0/w_1)\),切片が\(- (b/w_1) \)となり,それを\(x_0\)の範囲を-0.5から1.5までの範囲で描いています.
結果はこちら
このようになりました.オレンジが\(u=0\)の分離直線となります.
これより上の領域が\(u>0\)なので出力\(y=1\),下の領域が\(u<0\)なので出力\(y=0\)となります.
図より,\(y=1\)となるのは,\(x0=1, x=1\)のときのみで,他は\(y=0\).学習結果が正しいことが分かりました.
次はDenseを階層構造にした場合を考えてみたいと思います.