nprogram’s blog

気ままに、プログラミングのトピックについて書いていきます

単純パーセプトロンの学習

はじめに

単純パーセプトロンを学習していきます。

パーセプトロンで論理演算のANDを実装することを考えます。 x1,x2:入力層 y:出力層 w1,w2:入力層と出力層のユニット(ニューロン)の結合の強さを表す係数 (結合係数) b:バイアス θ:ステップ関数

ちなみにここでいうバイアスというのは偏りのことです

f:id:nprogram:20190331233357p:plain

このネットワークは,2入力,1出力です。

いつも1が入力されてくるユニットを付け加えて3入力1出力のネットワークとみなすとプログラムしやすくなります。

その場合、以下のような図になります。

f:id:nprogram:20190331233404p:plain

次に、ステップ関数を定義します。

ステップ関数とは、入力が0を超えたら1を出力し、それ以外は0を出力する関数です。

def step(x):
    if x > 0 :
        return 1
    else :
        return 0


def And(x1, x2):
    x = np.array([1, x1, x2])
    
    # 結合係数 w1 = 0.5 w2 = 0.5 バイアス b = -0.7
    b = -0.7
    
    w = np.array([b, 0.5, 0.5])
    
    # 以下は「tmp = np.sum(x * w)」と同一
    tmp = np.sum(np.dot(x, w))
    
    return step(tmp)


print(And(0,0))
print(And(1,0))
print(And(0,1))
print(And(1,1))
0
0
0
1

今度は学習によって結合係数とバイアスを求めます

今はANDになるように、結合係数(w1,w2)やバイアス(b)は手動で与えましたが、学習によって決めます。

def step(x):
    if x > 0 :
        return 1
    else :
        return 0


# 各点での学習を50回繰り返す
epoch = 50

# 学習の進み方を調節するパラメータ
eta = 0.1

# 係数の初期設定 (b, w1, w2)
w = np.array([0.0, 0.0, 0.0])

print(f"w = {type(w)}")

# 1つ前のステップでの係数を入れておくための変数
w_prev = w

# 学習の書くステップでの係数を追加していくためのnumpy配列
ws = w

# 学習データ
x_train = [[1,0,0], [1,0,1], [1,1,0], [1,1,1]]

# 教師ラベル
y_train = [0, 0, 0, 1]

for i in range(epoch):
    for x, y in zip(x_train, y_train):
        output = step(np.sum(w*x))
        
        # b, w1, w2の係数を決めます
        for j in range(len(w)):
            w[j] = w[j] + (y-output) * x[j] * eta
        
    # 各ステップでの係数を追加したnumpy配列を作る
    ws = np.append(ws, w)
    
    # print(w)
    # 1つ前のステップの係数を新しく求められた係数に置き換える
    w_prev = w

# 各ステップの係数を貯めたwvecsは1次元のnumpy配列にしていたので
# 各ステップ毎の多次元配列になるように整形する
ws = ws.reshape(-1,3)

print(ws)
[[ 0.1  0.1  0.1]
 [ 0.1  0.1  0.1]
 [ 0.   0.2  0.1]
 [-0.1  0.2  0.1]
 [-0.1  0.2  0.2]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]
 [-0.2  0.2  0.1]]

上記の学習で求めたバイアス、結合係数をAndの演算結果を求める式に適用します。

def And2(x1, x2):
    x = np.array([1, x1, x2])
    
    # バイアス b = -0.2
    # 結合係数 w1 = 0.2
    # 結合係数 w2 = 0.1
    b = -0.2
    w1 = 0.2
    w2 = 0.1
    
    w = np.array([b, w1, w2])
    
    # 以下は「tmp = np.sum(x * w)」と同一
    tmp = np.sum(np.dot(x, w))
    
    return step(tmp)


print(And2(0,0))
print(And2(1,0))
print(And2(0,1))
print(And2(1,1))
0
0
0
1

参考リンク