Pythonによるパーセプトロンのスクラッチ実装

パーセプトロン:ニューラルネットワークの基本単位

パーセプトロンは、最も単純な人工ニューロンであり、すべての現代的なニューラルネットワークの基礎となる構成要素です。入力値を受け取り、重みとバイアスを適用し、結果が特定の閾値を超えるかどうかに基づいてバイナリ(はい/いいえ)の出力を生成します。本質的に、パーセプトロンは、間違いに基づいてパラメータを反復的に調整することで、データを2つのカテゴリに分離することを学習する線形分類器です。

パーセプトロンがどのように決定を下すか

パーセプトロンは、信号の重みを付けることで、「発火」すべきかどうかを判断し、生物学的なニューロンを模倣します。決定プロセスは、特定の数学的公式に従います:

output = 1 if (weight * input + bias) > 0, else 0

決定関数の主要な構成要素

  • Input (x): 評価されるデータポイント。
  • Weight (w): 特定の入力にどれだけの重要性を割り当てるかを決定します。多入力システムでは、異なる重みによってモデルが特定の要因を他の要因よりも優先させることが可能になります。
  • Bias (b): 決定境界をシフトさせる値。入力がゼロの場合や、理想的な境界が原点に中心がない場合に、モデルが決定を下せるようにします。
  • Classifier: 結合された公式 (weight * input + bias) > 0 は、決定関数として知られています。

決定境界とバイアスの役割

決定境界は、パーセプトロンが出力を0から1に切り替える正確なポイントです。数学的には、これは weight * input + bias = 0 となる場所で発生します。入力を求める式を解くと、境界は -bias / weight に位置します。

バイアスが不可欠な理由

バイアスがないと、決定境界は常にゼロに固定されてしまいます。これは、モデルがカテゴリ間の境界線がちょうど0にある問題しか解決できないことを意味します。

例えば、学生が試験に合格したかどうかを判断するモデル(合格点が50点の場合)を訓練する場合、バイアスなしのパーセプトロンは失敗します。すべての試験のスコアは正の値であるため、正の重みは全員を「合格」とし、負の重みは全員を「不合格」とします。バイアスがあれば、境界をゼロから実際の閾値(例:50)へとスライドさせることができ、モデルがそのようなタスクで100%の精度を達成することを可能にします。

学習プロセス:訓練、エポック、および学習率

パーセプトロンは、誤った予測を行ったときに重みとバイアスを調整することで学習します。この反復プロセスは訓練と呼ばれます。

更新ルール

予測が間違っているとき、モデルは誤差(result - prediction)を計算し、パラメータを正しい方向へ押し進めます:

  • weight += learning_rate * error * input
  • bias += learning_rate * error

訓練パラメータ

  • Epoch: 訓練データセット全体を1回走査すること。 複数の重みとバイアスが正しい値に収束するために、通常は複数のエポックが必要となります。
  • Learning Rate: 調整の大きさを制御する倍率。学習率が小さいと安定しますが遅くなり、学習率が大きいとオーバーシュート(行き過ぎ)や不安定さにつながる可能性があります。

データ正規化によるパフォーマンスの最適化

データ正規化は、訓練前に入力値を小さく一貫した範囲(例:0から1)に縮小するプロセスです。これは2つの理由から重要です:

  1. 安定性: 大きな入力値は重みの更新において極端な変動を引き起こし、訓練中に「急激な動き」を引き起こす可能性があります。正規化は、よりスムーズな収束を保証します。
  2. 公平性: 複数の入力が使用される場合、スケールの大きい特徴量(例:数千単位の給与)は、スケールの小さい特徴量(例:バイナリの0または1)を数学的に圧倒してしまい、モデルが小さくても潜在的に重要な特徴量を無視してしまう原因となります。

Pythonによる実装

以下は、数値が正か負かを学習するように設計されたパーセプトロンの完全な実装です。

import random

learning_rate = 0.1
EPOCHS = 100

weight = random.uniform(-1, 1)
bias   = random.uniform(-1, 1)

# Training data: positive numbers are True, negative numbers are False
data  = [(i * 0.1, True)  for i in range(1, 501)]
data += [(i * 0.1, False) for i in range(-500, 0)]
random.shuffle(data)

for epoch in range(EPOCHS):
    for value, result in data:
        prediction = (weight * value + bias) > 0
        if prediction != result:
            error = result - prediction          # +1 or -1
            weight += learning_rate * error * value
            bias   += learning_rate * error

decision_boundary = -bias / weight
print(f"weight = {weight:.3f}")
print(f"bias   = {bias:.3f}")
print(f"decision boundary = {decision_boundary:.3f}")

技術的視点と背景

パーセプトロンは今日では単純なソフトウェア構成要素ですが、ハードウェアと理論の両立において深い根源を持っています。

"In the early days of machine learning... networks like this was often implemented and trained in hardware [such as ADALINE]. Nowadays, that 'small brain' would likely be built on a breadboard using op-amps instead."

さらに、パーセプトロンは、明示的な IF 文に依存する従来のソフトウェアから、プログラマーがコードを書き込むのではなく、モデルがデータを通じて IF ロジックを自ら発見する機械学習への移行を象徴しています。

Sources