だれも書いていない決まり
予約アプリが、土曜日は混むと店に知らせてきました。アプリを作った人は「暖かくて乾いた週末なら」という決まりを書いていません。アプリは意味のない数から始め、過去の日を1日ずつ見て、その数を直していったのです。
この直していくことが、ニューラルネットワークにとっての「学ぶ」です。手で追える計算です。
このレッスンのゴール
メモ
小さなニューラルネットワークがどう学ぶかを説明できる。
キーワード
| ことば | いみ | れい |
|---|---|---|
| ニューロン | 重みをかけた入力とバイアスを足し、答えを決めるもの | |
| 重み | 1つの入力がどれだけ効くか | |
| バイアス | 決め方をずらすために足す数 | |
| 学習率 | 1回の直しの大きさ | 0.1 |
| エポック | 学習データのすべての行を1回通すこと | |
| 隠れ層 | 入力と答えのあいだにあるニューロンの列 |
学生が入力するセルは、このプレビューでは空白です。
パーセプトロンは足してから決める
パーセプトロンは、ニューロン1つです。各入力にその重みをかけて足し、バイアスを足します。
合計が0より大きければ1、ここでは「busy」と答えます。そうでなければ0、「quiet」です。大きなプラスの重みは、その入力を busy の側へ押します。マイナスの重みは quiet の側へ押します。
例: 2日をニューロンに通す
| 日 | 入力: 気温、雨、週末 | 重み 0.5、-0.9、0.4 とバイアス -1 | 合計 | 答え |
|---|---|---|---|---|
| 暖かい土曜 | 2.0、0.0、1 | 1.0 + 0 + 0.4 - 1 | 0.4 | 1、busy |
| 寒くて雨の火曜 | 0.5、1.0、0 | 0.25 - 0.9 + 0 - 1 | -1.65 | 0、quiet |
学生が入力するセルは、このプレビューでは空白です。
1回の直しを手で
# 忙しかった1日: 気温は10度単位、雨は cm、週末は1です。
day = [2.0, 0.0, 1]
target = 1
weights = [0.0, 0.0, 0.0]
bias = 0.0
rate = 0.1
total = bias
for n in range(3):
total = total + weights[n] * day[n]
guess = 0
if total > 0:
guess = 1
error = target - guess
for n in range(3):
weights[n] = round(weights[n] + rate * error * day[n], 2)
bias = round(bias + rate * error, 2)
print("予測:", guess, "誤差:", error)
print("重み:", weights, "バイアス:", bias)
# 新しい重みとバイアスを、4行目と5行目に写してください。
# もう一度実行します。誤差は0になりましたか。
学ぶとは重みを直すこと
学習は、重みを0にして始めます。行ごとに、パーセプトロンが予測します。誤差は、本当の答えから予測を引いたもので、1、0、-1 のどれかです。
各重みは、学習率 × 誤差 × その入力だけ動きます。すべての行を1回通すのがエポックです。決めた回数のエポックが終わるか、1エポックでほとんど何も変わらなくなったら、学習を止めます。
1年分でパーセプトロンを学ばせる
docker exec jupyter python3 steps/perceptron.pyここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
1本の直線では足りないことがある
パーセプトロンは、入力を1本のまっすぐな境界で分けます。そのように分けられない形もあります。
よく知られた例が XOR です。2つの入力のうち、ちょうど片方だけが1のときに答えが1になります。1の2行を片側に、0の2行を反対側に置く直線は、1本では引けません。下のノートブックのセルで試します。
例: XORの4行
| 1つ目の入力 | 2つ目の入力 | 答え |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
学生が入力するセルは、このプレビューでは空白です。
ノートブックに入力: AND、OR、XOR
from sklearn.linear_model import Perceptron
from sklearn.neural_network import MLPClassifier
X = [[0, 0], [0, 1], [1, 0], [1, 1]]
# 同じ4つの入力に、3通りの答えをつけます。
for name, y in [["AND", [0, 0, 0, 1]], ["OR", [0, 1, 1, 1]], ["XOR", [0, 1, 1, 0]]]:
print(name, Perceptron(random_state=0).fit(X, y).score(X, y))
# 隠れ層にニューロンを4つ置きます。
net = MLPClassifier(hidden_layer_sizes=(4,), solver="lbfgs", max_iter=1000, random_state=1).fit(X, [0, 1, 1, 0])
print(net.score(X, [0, 1, 1, 0]))
層と、たくさんの小さな直し
隠れ層は、入力と答えのあいだに並ぶニューロンの列です。それぞれが自分の境界を引き、最後のニューロンがそれらを組み合わせます。XOR はこうして解けます。
ネットワークは、かくっと切りかわる決め方ではなく、なめらかな決め方を使います。だから、誤差が減る向きへ、各重みを少しずつ動かせます。誤差は答えの側から各層へ送り返されます。これを誤差逆伝播法と呼びます。
3つの大きさのネットワーク
docker exec jupyter python3 steps/network.pyここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
よくある間違い
注意
ネットワークは大きいほどよいと考えがちです。ここでは隠れユニットが2つでも8つでも32でも、どれも 0.87 でした。3つの入力の形が単純だからです。ニューロンを増やすと時間がかかり、ノイズまで学ぶことがあります。小さく始め、テストデータで効くとわかったときだけ大きくします。
さらに挑戦: 学習率を変える
ノートブックで、perceptron.py のパーセプトロンを3つの学習率で学ばせてください。eta0=0.01、eta0=1、eta0=10 を使い、回数と正解率を毎回書きとめます。
そのうえで、学習率がモデルによって大事だったりそうでなかったりする理由を、二文で説明しましょう。ヒント: 重みとバイアスをすべて同じ数でかけると、決め方はどうなりますか。