Pythonを使って単純な論理ゲートであるANDゲートを考える.
def AND_gate(input1, input2):
if input1 == 1 and input2 == 1:
return 1
else:
return 0
# テスト
print(AND_gate(0, 0)) # 0
print(AND_gate(0, 1)) # 0
print(AND_gate(1, 0)) # 0
print(AND_gate(1, 1)) # 1
実行すると,
0
0
0
1
とANDゲートとして機能している.
次に,同じくANDゲートをパーセプトロンとして考えてみる.\[y = \begin{cases}0;b+w_{1}x_{1}+w_{2}x_{2} \leq 0\\1;b+w_{1}x_{1}+w_{2}x_{2}>0\end{cases}\]
バイアス $b$ は重みである $w_{1}, w_{2}$ とは別の役割を担う.つまり,出力信号が 1 を出力する度合い,すなわち,発火のし易さを調整するパラメータとしての役割を持っている.
import numpy as np
class Perceptron:
def __init__(self, input_size, lr=1, epochs=10):
self.W = np.zeros(input_size+1)
self.epochs = epochs
self.lr = lr
def activation_fn(self, x):
return 1 if x >= 0 else 0
def predict(self, x):
x = np.insert(x, 0, 1) # バイアス項の追加
z = self.W.T.dot(x)
a = self.activation_fn(z)
return a
def train(self, X, d):
for _ in range(self.epochs):
for i in range(d.shape[0]):
y = self.predict(X[i])
e = d[i] - y
self.W = self.W + self.lr * e * np.insert(X[i], 0, 1)
# データの準備
X = np.array([[0, 0],
[0, 1],
[1, 0],
[1, 1]])
d = np.array([0, 0, 0, 1]) # ANDゲートの出力
# パーセプトロンの定義と訓練
perceptron = Perceptron(input_size=2)
perceptron.train(X, d)
# テスト
print("0 AND 0 =", perceptron.predict(np.array([0, 0])))
print("0 AND 1 =", perceptron.predict(np.array([0, 1])))
print("1 AND 0 =", perceptron.predict(np.array([1, 0])))
print("1 AND 1 =", perceptron.predict(np.array([1, 1])))
ここで, __init__ は,Pythonにおいて特殊メソッドあるいはマジックメソッドとも呼ばれる特別なメソッドであり,クラスのインスタンスが生成される際に自動的に呼び出されるコンストラクタである.
self.W = np.zeros(input_size+1)
self.epochs = epochs
self.lr = lr
というのは,NumPy の zeros関数 を使用して,要素が全て 0 で,かつ,サイズが input_size+1 の1次元配列を生成し self.W に代入している.また,input_size はパーセプトロンの入力数を示し,+1でバイアス項の分を追加している.lr はパーセプトロンの重み更新の際に,どれだけ大きなステップを踏むかを制御する学習率[Learning Rate]を表している.
以下で,入力配列 x の先頭[0]にバイアス項[1]を追加[insert].
x = np.insert(x, 0, 1)
次に,重み行列 W を転置した上で,入力ベクトル x との内積を計算.これにより,各入力に対する重み付け和が計算される.つまり,この内積は,各入力の重み付けされた和を計算するために使用される.
z = self.W.T.dot(x)
そして,パーセプトロンの学習メソッド train の定義を行う.このメソッドは,与えられた入力データセット X と対応する目標出力 d を用いて,パーセプトロンの重みを学習する.
def train(self, X, d):
for _ in range(self.epochs):
for i in range(d.shape[0]):
y = self.predict(X[i])
e = d[i] - y
self.W = self.W + self.lr * e * np.insert(X[i], 0, 1)
外側のループは,指定されたエポック数(self.epochs)だけ繰り返す.各エポックにおいて,訓練データをすべて使って一通り学習を行う.
内側のループは,入力データの数(d.shape[0])だけ繰り返す.これにより,各エポックで訓練データのすべてのサンプルについて学習が行われる.
y = self.predict(X[i])
は,predict メソッドを用いて,入力データ X[i] に対する予測値 y を計算している.
次に,目標出力 d[i] と予測値 y の差分を計算し,誤差 e を求める.
e = d[i] - y
次に,誤差 e に学習率 self.lr を掛け合わせ,入力データ X[i] の先頭にバイアス項を追加したものに乗じた値を,現在の重み self.W に加算.これによって,重みを誤差に応じて更新する.
self.W = self.W + self.lr * e * np.insert(X[i], 0, 1)
パーセプトロンの学習が完了した後の最終的な重みベクトルは
[-3. 2. 1.]
となる.最初の要素がバイアス項に対応し,2番目と3番目の要素がそれぞれ入力の重みとなっている.
Mathematics is the language with which God has written the universe.