【畳込みニューラルネット No.5】全結合層Denseの中身を考える

シェアする

前回まで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を階層構造にした場合を考えてみたいと思います.