脳とニューラルネットワークは似ている、と言われるけど本当なのか。パーセプトロンとヘブ則を実装して確かめてみた
約13分で読めます

脳とニューラルネットワークは似ている、と言われるけど本当なのか。パーセプトロンとヘブ則を実装して確かめてみた


「脳の構造とニューラルネットワークって似てますよね」という話を、割とよく耳にする。ニューロンが電気信号でつながっている脳と、ノードが重みでつながっているニューラルネットワーク。確かに図で見ると似ている。実際、ニューラルネットワークの最初期のモデルは、生物のニューロンをかなり直接的に模倣する形で作られた。

ただ、この「似ている」という話をどこまで信じていいのか、正直あやふやなまま今まで来てしまった。似ているのは見た目の構造だけなのか、学習の仕組みまで似ているのか。せっかくなので、ニューラルネットワークの起源であるパーセプトロンと、生物学的なニューロンの学習モデルとして提案されたヘブの学習則を、実際にPythonで実装して比較してみることにした。

パーセプトロンは、もともと脳のモデルだった

パーセプトロンは1958年、心理学者のFrank Rosenblattが提案した。ルーツをさらに遡ると、1943年にMcCullochとPittsが提案した「ニューロンは入力の重み付き和がある閾値を超えたら発火する」という単純なモデルにたどり着く。パーセプトロンは、このモデルに「学習則」を組み合わせたものだ。

やっていることは非常にシンプルな二値分類器だ。入力ベクトル xx に対して、

y^={1(wx+b0)1(それ以外)\hat{y} = \begin{cases} 1 & (w \cdot x + b \geq 0) \\ -1 & (\text{それ以外}) \end{cases}

という予測をする。重み ww とバイアス bb を学習で調整していく。

パーセプトロン学習則とヘブ則、何が違うのか

ここからが今回の本題だ。パーセプトロンの重み更新には、パーセプトロン学習則が使われる。

ww+η(yy^)xw \leftarrow w + \eta (y - \hat{y}) \, x

予測が間違っていたときだけ、正解の方向に重みを補正する。誤差信号 (yy^)(y - \hat{y}) がゼロなら、何も更新しない。

一方、脳のニューロンの学習モデルとして知られるヘブの学習則(1949年、Donald Hebbが提唱)は、もっと単純だ。「同時に発火したニューロン同士の結合が強まる」というルールで、数式にすると次のようになる。

ww+ηyxw \leftarrow w + \eta \, y \, x

見た目はほとんど同じだが、決定的な違いがある。ヘブ則には誤差の概念がない。 正しく分類できていようが間違っていようが関係なく、入力とラベルの相関があるたびに、常に重みを強化し続ける。パーセプトロン学習則は「間違えたときだけ直す」フィードバック型、ヘブ則は「一致したら常に強める」相関の蓄積型、と言い換えられる。

実験1:精度は同じでも、重みの中身は全く違う

線形分離可能な2次元データ(2つのガウス分布のクラスタ、各500件)を作り、パーセプトロン学習則とヘブ則、それぞれで40エポック学習させてみた。

def perceptron_train(X, y, epochs, lr=0.1):
    w = np.zeros(X.shape[1])
    b = 0.0
    for epoch in range(epochs):
        for xi, yi in zip(X, y):
            pred = 1 if (w @ xi + b) >= 0 else -1
            if pred != yi:                # 誤分類したときだけ更新(誤差信号あり)
                w += lr * yi * xi
                b += lr * yi
    return w, b

def hebbian_train(X, y, epochs, lr=0.1):
    w = np.zeros(X.shape[1])
    b = 0.0
    for epoch in range(epochs):
        for xi, yi in zip(X, y):
            w += lr * yi * xi             # 発火の相関だけで常に更新(誤差信号なし)
            b += lr * yi
    return w, b

結果は次の通り。

perceptron: final_acc=0.9950  final_||w||=0.48
hebbian:    final_acc=0.9950  final_||w||=11084.69

分類精度はどちらも99.5%で、ほぼ同じ。ここだけ見ると「じゃあヘブ則でも十分じゃないか」と思ってしまう。しかし重みベクトルのノルム(大きさ)を見ると、パーセプトロンが0.48で落ち着いているのに対し、ヘブ則は11084.69、実に2万3000倍という差がついていた。

重みベクトルのノルムの推移を示す折れ線グラフ。perceptron学習則(青)は0.48付近でほぼ横ばいなのに対し、ヘブ則(緑)はエポックが進むごとに直線的に増え続け、40エポック後には11,085に達している

これは正直、意外だった。パーセプトロンは一度正しく分類できるようになると、その点についてはもう更新しない。だから重みはある値で落ち着く。ところがヘブ則は「正しく分類できているかどうか」を一切見ていないので、すでに正解している点についても、律儀に毎エポック重みを足し続ける。 一度身につけた「正しさ」を検証する仕組みがないので、同じ方向にひたすら重みを積み増し続け、際限なく発散していく。

見た目の精度という結果だけを見れば同じでも、その内側で起きていることは全く違う。ヘブ則は、正しい・間違っているに関係なく相関があったものをひたすら強化し続ける。これは人間で言えば、一度「これは正しい」と思い込んだことについて、それを裏付ける情報が入ってくるたびに、検証もせずにどんどん確信を強めていく状態に近い。表面上の「精度」は変わらないまま、内側の「確信度」だけが際限なく膨らんでいく。確証バイアスの数理モデルを、期せずして目の当たりにした気分だった。

実験2:単純パーセプトロンには解けない問題がある

似ている・似ていないの話から少し離れて、パーセプトロンそのものの限界も確認しておきたい。有名な話として、単層のパーセプトロンはXOR問題を解けない。

X_xor = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
y_xor = np.array([-1, 1, 1, -1])   # 0 XOR 0=0, 0 XOR 1=1, 1 XOR 0=1, 1 XOR 1=0

この4点は、1本の直線では絶対に分離できない。実際にパーセプトロン学習則で学習させると、精度がどう動くか見てみる。

単層パーセプトロンをXORデータで学習させたときの精度の推移を示す折れ線グラフ。0.5と0.75の間を行ったり来たりし続け、1.0に到達しない

精度は0.5と0.75の間を行ったり来たりするだけで、1.0に到達することは一度もない。4点のうち最大3点まではどうにか合わせられるが、残り1点はどうやっても間違えてしまう。これが1969年にMinskyとPapertが指摘した、単層パーセプトロンの限界だ。

解決策としてよく知られているのが、隠れ層を追加する方法だ。隠れ層を1つ持つ多層パーセプトロン(MLP)を、誤差逆伝播法で学習させてみる。

def mlp_train(X, y01, hidden=4, epochs=20000, lr=1.0):
    W1 = rng.normal(0, 1, size=(X.shape[1], hidden))
    b1 = np.zeros(hidden)
    W2 = rng.normal(0, 1, size=(hidden, 1))
    b2 = np.zeros(1)
    for epoch in range(epochs):
        h = sigmoid(X @ W1 + b1)
        out = sigmoid(h @ W2 + b2).ravel()
        # ここから誤差逆伝播で W1, b1, W2, b2 を更新
        ...
    return W1, b1, W2, b2
2-layer MLP on XOR: final predictions = [0.0114 0.9869 0.9843 0.0178]  targets = [0. 1. 1. 0.]
2-layer MLP final loss = 0.000217

予測値が [0.01, 0.99, 0.98, 0.02]、正解の [0, 1, 1, 0] にほぼ一致している。損失も0.0002まで下がった。

隠れ層を1つ追加したMLPの損失(MSE)の推移を示す折れ線グラフ。学習開始直後に急速に下がり、ゼロに近い値まで滑らかに収束している

実験3:層を足さなくても、特徴量を1つ足せば解ける

ここで、ちょっと意外な検証もしてみた。「XORが解けないのは層が足りないからだ」という説明はよく見かけるが、本当に層を足すことだけが解決策なのだろうか。

XORの本質は x1x_1x2x_2 の非線形な組み合わせにある。だったら、その組み合わせを表す特徴量を先に計算して渡してしまえばいいのではないか。x1×x2x_1 \times x_2 という項を1つ追加してみる。

X_xor_aug = np.column_stack([
    X_xor[:, 0], X_xor[:, 1], X_xor[:, 0] * X_xor[:, 1]
])
w_aug, b_aug, acc_aug, _ = perceptron_train(X_xor_aug, y_xor, epochs=200, lr=0.1)
x1*x2を足した単層パーセプトロン on XOR: final_acc = 1.0000
weights (x1, x2, x1*x2) = [0.1  0.1 -0.3], bias = -0.1

隠れ層もバックプロパゲーションも使わず、単層のパーセプトロンのまま、掛け算という特徴量を1つ足しただけで、精度100%に到達した。

素のx1, x2だけで学習させた場合(0.5と0.75の間で振動し続ける)と、x1*x2を足しただけの場合(学習開始直後から精度1.0で完全に水平)を重ねた折れ線グラフ

青い線(素のx1, x2だけ)はさっきと同じように振動し続けているのに、橙色の線(x1*x2を足しただけ)は最初から一直線に精度1.0を保っている。「解けない問題に対して、モデルを複雑にする(層を足す)」のと、「問題の見方を変える(特徴量を工夫する)」のは、どちらも有効な解決策になり得るということが、数字ではっきり出た。

これも人生っぽい話だと思う。今のやり方でどうしても解決しない問題に直面したとき、取れる道は一つではない。がむしゃらに努力量を増やす(層を足す、MLPで殴る)のも一つの手だし、そもそもの問題の捉え方を変えて、今まで見えていなかった軸(x1*x2のような)を見つける方が早い場合もある。しかも今回のように、後者の方がずっと軽量に済むことすらある。

で、結局「脳と似ている」は本当なのか

ここまでの実験を踏まえて、最初の疑問に戻る。

パーセプトロンの学習則(誤差信号で正す)と、生物学的なヘブ則(相関を常に強める)は、似ているようで全く別の性質を持っていた。現代のニューラルネットワークが実際に使っている誤差逆伝播法は、パーセプトロン学習則の延長線上にある誤差信号ベースの学習だ。一方、脳のニューロンが実際にどうやって学習しているかというと、少なくともヘブ則に近い、局所的な相関に基づく仕組みが有力視されている。脳が誤差逆伝播法のような形で学習しているという証拠は、今のところ乏しいというのが、神経科学の中でも議論になっている点らしい(biological plausibility問題と呼ばれる)。

つまり、「ニューラルネットワークの構造は脳から着想を得ている」というのは歴史的に正しいが、「今のニューラルネットワークの学習方法は脳の学習方法に似ている」というのは、少なくとも額面通りには受け取れない。構造の比喩と、学習アルゴリズムの中身は、分けて考えたほうがよさそうだ。

そして今回手を動かしてみて一番印象に残ったのは、実は脳の話より、ヘブ則の「検証なき強化」が持つ危うさと、XORの「別の解決策がある」という話の方だった。相関があるものを無批判に強化し続けると、見かけの結果は変わらないまま内側の確信だけが膨らんでいく。解けない問題にぶつかったときは、力業(層を足す)だけでなく、視点を変える(特徴量を変える)という選択肢もある。どちらも、ニューラルネットワークの話でありながら、そのまま自分の日々の考え方にも刺さる内容だった。

まとめ

  • パーセプトロン学習則は「間違えたときだけ直す」誤差信号ベースの学習、ヘブ則は「一致したら常に強める」相関ベースの学習で、数式は似ているが性質は大きく違う
  • 同じデータで学習させると、精度はほぼ同じ(99.5%)でも、ヘブ則の重みノルムはパーセプトロンの2万3000倍(11084.69 対 0.48)まで発散する
  • 単層パーセプトロンはXORを解けず精度は0.5〜0.75で頭打ちになるが、隠れ層を足したMLPは損失0.0002まで収束して解ける
  • 層を足さなくても、x1×x2x_1 \times x_2 という特徴量を1つ足すだけで、単層パーセプトロンのままXORが精度100%で解ける
  • 「脳とニューラルネットワークは似ている」は構造の比喩としては正しいが、学習アルゴリズムのレベルでは今のニューラルネットワークの主流(誤差逆伝播法)と脳の学習(ヘブ則的な相関学習に近いと言われる)は、かなり性質が異なる