たくさんの内積を、まとめて計算する
入力から隠れ層、隠れ層から出力層へと、順番に計算を進めていくことを 順伝播(じゅんでんぱ)と呼びます。前のレッスンの計算を、今度はコードで書いてみましょう。
隠れ層には、ニューロンAとBの2つがありました。どちらも「重みと入力の内積+バイアス」を計算します。ニューロンの数が増えると、内積をたくさん計算することになるので、NumPyではこれを まとめて 計算します。
まず、重みがすべて違う数の例で、並べ方を確かめましょう。重みは、列ごとに1つのニューロン の分を縦に並べます。
import numpy as np
x = np.array([1, 2]) # 入力 x1=1, x2=2
W = np.array([[3, 5], # 1行目: x1に掛ける重み(Aは3、Bは5)
[2, 6]]) # 2行目: x2に掛ける重み(Aは2、Bは6)
x @ W # → [7, 17]
1列目 (3, 2) がニューロンAの重み、2列目 (5, 6) がニューロンBの重みです。行は「どの入力に掛けるか」、列は「どのニューロンの分か」を表します。
- ニューロンA: 1×3+2×2=7
- ニューロンB: 1×5+2×6=17
同じ並べ方で、前のレッスンのXORのネットワークを書くと次のようになります。この例では、AもBも重みがどちらも1なので、1がすべての位置に並びます。
import numpy as np
x = np.array([1, 0]) # 入力(スイッチ1=オン、スイッチ2=オフ)
# 列ごとに1つのニューロンの重みを並べる(1列目がA、2列目がB)
W1 = np.array([[1, 1],
[1, 1]])
b1 = np.array([-0.5, -1.5]) # Aのバイアス、Bのバイアス
x @ W1 # → [1, 1] : Aの内積、Bの内積
x @ W1 + b1 # → [0.5, -0.5]
@ は、「入力と、各列(各ニューロンの重み)との内積を、列の数だけまとめて計算する」 記号です。1列目との内積 1×1+0×1=1 がニューロンAの分、2列目との内積がニューロンBの分になります。バイアスを足すと、前のレッスンの表と同じ z=0.5, −0.5 が得られます。
この計算は、高校・大学の数学では 行列の積 と呼ばれます。名前は難しそうですが、中身は第4章の内積をまとめて行っているだけです。
XORを解くネットワークをNumPyで動かす
@ を使うと、4通りの入力すべてを一度に計算することもできます。入力を1行に1件ずつ並べた表(4行×2列)にすれば、各行ごとの計算がまとめて行われます。
import numpy as np
def step(z):
return (z > 0).astype(int) # 0より大きければ1、そうでなければ0
X = np.array([[0, 0],
[0, 1],
[1, 0],
[1, 1]]) # 4件の入力
W1 = np.array([[1, 1], [1, 1]]) # 隠れ層(A, B)の重み
b1 = np.array([-0.5, -1.5])
W2 = np.array([1, -1]) # 出力層の重み(Aに+1、Bに-1)
b2 = -0.5
H = step(X @ W1 + b1) # 隠れ層 → [[0, 0], [1, 0], [1, 0], [1, 1]]
out = step(H @ W2 + b2) # 出力層 → [0, 1, 1, 0]
step は、「0より大きければ1、そうでなければ0」の活性化関数です。z > 0 は、配列の各要素が0より大きいかどうかを True(はい)/False(いいえ)で返します。.astype(int) は、それを True なら1、False なら0の整数に変換します。例えば [0.5, -0.5] は [True, False] を経て [1, 0] になります。
隠れ層 H の各行が、それぞれの入力に対するニューロンA・Bの出力です。出力 out は [0, 1, 1, 0] で、XORの答えと一致します。
たった2行で、ネットワーク全体の計算ができている 点に注目してください。層が増えても、「前の層の出力 @ 重み + バイアス → 活性化関数」を層の数だけ繰り返すだけです。何百層もあるディープラーニングのモデルも、順伝播の計算はこの繰り返しです。
よくある誤解と振り返り
誤解:「@(行列の積)は、高度な数学を理解していないと使えない?」
@ がやっていることは、「表の各行」と「重みの各列」の内積を、すべての組み合わせについて計算することです。1つ1つは、第4章で手計算した内積と同じです。慣れるまでは、「行と列の数が合っているか」だけを意識すれば十分です(入力が2つなら、重みの表は2行でなければなりません)。
なぜまとめて計算するのか
for文で1つずつ内積を計算しても、答えは同じです。それでもまとめて計算するのは、NumPyやGPU(画像処理用の計算装置)が、まとめた計算を非常に速く処理できるからです。ディープラーニングが実用的な速さで動くのは、この「まとめて計算する」工夫のおかげです。
振り返り
- 順伝播とは、入力から出力へ、層ごとに順番に計算を進めること
- 各層の計算は「前の層の出力
@ 重み + バイアス → 活性化関数」
@ は、たくさんの内積をまとめて計算する記号(数学では行列の積と呼ぶ)
ここまでは、XORを解く重みを人間が考えて与えていました。次のレッスンでは、この重みを データから自動で学習する 仕組みを見ていきます。