賢くなりたいトイプードルの日記

データサイエンス系の話をメインにしていきます

機械学習入門(2:ニューラルネットワーク)

前回の記事ではパーセプトロンと活性化関数を学んだ。この2つを基礎として、今回の記事ではニューラルネットワークの基礎を学んでいく。

ニューラルネットワークとは

ニューラルネットワークは以下のようにノードがつながっていくイメージ。

たとえば入力を隠れ層(1層)のノードa1にわたす際、入力X1に重みを乗算したものと、入力X2に重みを乗算したものを加算し、それにバイアスを加え、活性化関数を通して出力を求める。

このように、次の層の各ノードに伝播させる時、入力の値すべてに重みを乗算してバイアスを加算し、活性化関数を通す。各ノードからノードへの重みとバイアスの値はそれぞれ異なることに注意。

ニューラルネットワークの実装

では実装していきます。まず変数の説明から。

  • X: 入力。ここではx1とx2の配列で1行2列
  • A1: 第一層の出力。ここではa1、a2、a3の配列。1行3列
  • W1: 第一層の重み。A1の要素(3つ)に対してXの要素(2つ)が関係するので、2行3列
  • B1: 第一層のバイアス。ここではb1、b2、b3の配列。1行3列
  • Z1: A1を活性化関数にかけた後の出力。1行3列
import numpy as np

X = np.array([1.0, 0.3])
W1 = np.array([[0.1, 0.5, 0.7], [0.2, 0.6, 0.8]])
B1 = np.array([0.1, 0.3, 0.5])

A1 = np.dot(X, W1) + B1
print(A1)

[0.26 0.98 1.44]

ちょっと分かりづらいので、a1、a2、a3それぞれを示す。

$$ { a }_{ 1 }=1.0\times 0.1+0.3\times 0.2+0.1=0.26 $$

$$ { a }_{ 2 }=1.0\times 0.5+0.3\times 0.6+0.3=0.98 $$

$$ { a }_{ 3 }=1.0\times 0.7+0.3\times 0.8+0.5=1.44 $$

この3つの出力をそれぞれシグモイド関数にかける

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

Z1 = sigmoid(A1)

print(Z1)

[0.56463629 0.72710822 0.80845465]

これで第1層の出力が全て得られました。これを第2層に伝播させます。

W2 = np.array([[0.1, 0.2], [0.3, 0.4], [0.4, 0.8]])
B2 = np.array([0.1, 0.3])

A2 = np.dot(Z1, W2) + B2
print(A2)

[0.69797795 1.35053427]

Z2 = sigmoid(A2)
print(Z2)

[0.66773931 0.79421696]

さらにもう一層伝播させ、シグモイド関数にかけずにそのままの値を出力します。そのままの値を返すidentify_functionを定義しています。

W3 = np.array([[0.2, 0.3], [0.4, 0.5]])
B3 = np.array([0.2, 0.5])

A3 = np.dot(Z2, W3) + B3
print(A3)

[0.65123465 1.09743027]

def identify_function(x):
    return x

Z3 = identify_function(A3)
print(Z3)

[0.65123465 1.09743027]

ニューラルネットワークの効率化

上に書いたような処理を、入力したら一度に出力されるような形にする。

def init_network():
    network = {}
    network['W1'] = np.array([[0.1, 0.5, 0.7], [0.2, 0.6, 0.8]])
    network['B1'] = np.array([0.1, 0.3, 0.5])
    network['W2'] = np.array([[0.1, 0.2], [0.3, 0.4], [0.4, 0.8]])
    network['B2'] = np.array([0.1, 0.3])
    network['W3'] = np.array([[0.2, 0.3], [0.4, 0.5]])
    network['B3'] = np.array([0.2, 0.5])
    
    return network

def forward(network, X):
    W1, W2, W3 = network['W1'], network['W2'], network['W3']
    B1, B2, B3 = network['B1'], network['B2'], network['B3']
    
    A1 = np.dot(X, W1) + B1
    Z1 = sigmoid(A1)
    A2 = np.dot(Z1, W2) + B2
    Z2 = sigmoid(A2)
    A3 = np.dot(Z2, W3) + B3
    
    Y = identify_function(A3)
    
    return Y

呼び出す時は、

network = init_network()
X = np.array([1.0, 0.3])
Y = forward(network, X)

print(Y)

[0.65123465 1.09743027]

init_networkメソッドのインスタンスとXをforwardメソッドの引数に入れて、出力を得ています。

たった3行で出力が得られるようになりました。

まとめ

順方向に伝播するモデルを構築できました。

ただ、今回の実装では重みとバイアスを適当に決めているだけなので、入力に対応する出力は人間にとって価値のあるものではありません。

人間にとって価値のあるモデルにするためには、得られた出力から逆に伝播させ、重みとバイアスを適正な値に調整する必要があります。

次の記事では、重みとバイアスの値を更新する方法について書いていきます。

人工知能プログラミングを10秒ではじめよう