# パーセプトロン
Created: October 15, 2020 2:26 PM
Tags: algorithm
> そのアルゴリズムは、最適な重み係数を自動的に学習した後、入力信号と 掛け合わせ、ニューロンが発火するかどうかを判断するものだった。
> より形式的には、人工ニューロンの概念を二値分類タスクとして捉えることができる。
$\phi(z)$: 決定関数
> 決定関数(decision function)である 𝜙(z) を定義できる。この関数は、特定の入力値 $x$ と対応する重みベ クトル $w$ の線形結合を引数として受け取る。
$z=w_{1}x_{1} + ... + w_{m}x_{m}$: 総入力( net input)
> サンプル x(i) に対する総入力 z が、指定されたしきい値 𝜃 よりも大きい場合は 1 のク ラスを予測し、それ以外の場合は -1 のクラスを予測する。
パーセプトロンでは、 $\phi(z)$ は単位ステップ関数である。
$\phi(z) = \begin{cases}
1 & (z \geq \theta) \\
-1 & (z \lt \theta)
\end{cases}$
$w_{0} = -\theta, x_{0} = 1$とすると
$z = w_{0}x_{0} + w_{1}x{1} + ... + w_{m}x_{m} = \bm{w^{T}x} \\
\phi(z) = \begin{cases}
1 & (z \geq 1) \\
-1 & (z \le -1)
\end{cases}$
負のしきい値(重み $w_{0} = - \theta$ を通常はバイアスユニット(bias unit)と呼ぶ。
総入力 $z = \bm{w^{T}x}$ がパーセプトロンの決定関数によって二値出力のいずれかに押し込まれる。
Rosenblatt によるパーセプトロンの初期の学習規則を次の手順にまとめることができる。
1. 重みを0または値の小さい乱数で初期化する
2. トレーニングサンプル $\bm{x}^{(i)}$ごとに次の手順を実行する。
1. 出力値 $\hat{y}$ を計算する
2. 重みを更新する
出力値は、先に定義した単位ステップ関数によって予測されるクラスラベル。重みベクトル $\bm{w}$ の各重み $w_{j}$ は同時に更新する。
$w_{j} := w_{j} + \Delta w_{j}$
$\Delta w_{j} = \eta (y^{(i)} - \hat{y}^{(i)})x^{(i)}_{j}$
$\eta$ : 学習率 ($0.0 < \eta \le 1.0$)
$y^{(i)}$ : i番目のトレーニングサンプルの真のクラスラベル
$\hat{y}^{(i)}$ : 予測されたクラスラベル
$\Delta w_{j}$ は真のクラスラベルと予測されたクラスラベルが一致する場合は0になる。予測が間違っていた場合は目的とする正または負のクラスの方向に向かうように重みが計算される。
> パーセプトロンの収束が保証されるのは、2 つのクラスが線形分離可能で、学習率が十分に小さ い場合に限られることに注意しよう。2 つのクラスを線形の決定境界で分離できない場合は、デー タセットに対するトレーニングの最大回数(エポック)や誤分類の最大数を設定して対応する。そう した措置をとらない場合、パーセプトロンはいつまでも重みを更新し続けることになる。
![[%E3%83%8F%E3%82%9A%E3%83%BC%E3%82%BB%E3%83%95%E3%82%9A%E3%83%88%E3%83%AD%E3%83%B3/ScreenShot_2020-10-15_at_3.20.16_PM.png]]
以下はパーセプトロンのPythonによる実装である。
```python
import numpy as np
class Perceptron(object):
"""
eta: float
学習率
n_iter: int
トレーニングの回数
random_state: int
ランダムシード
w_: 一次元配列
適用後の重み
errors: リスト
各エポックでの誤分類(更新)の数
"""
def __init__(self, eta=0.01, n_iter=50, random_state=1):
self.eta = eta
self.n_iter = n_iter
self.random_state = random_state
def fit(self, X, y):
"""
X: トレーニングデータ
y: 目的変数
"""
rgen = np.random.RandomState(self.random_state)
self.w_ = rgen.normal(loc=0.0, scale=0.01, size=1 + X.shape[1]) # 標準偏差 0.01 の正規分布
self.errors_ = []
for _ in range(self.n_iter):
errors = 0
for xi, target in zip(X, y): # 同時に重みを更新
update = self.eta * (target - self.predict(xi))
self.w_[1:] += update * xi
self.w_[0] += update # バイアスユニット x_[0]は常に1
errors += int(update != 0.0) #誤分類のカウント
self.errors_.append(errors)
return self
def net_input(self, X):
"""総入力を計算する"""
return np.dot(X, self.w_[1:]) + self.w_[0]
def predict(self, X):
"""1ステップ後のクラスラベルを返す"""
return np.where(self.net_input(X) >= 0.0, 1, -1)
```
[[一対全(One-versus-All [[OvA)]]]] の手法により、パーセプトロンアルゴリズムを [[多クラス分類]] に拡張できる。
> しかし、 パーセプトロンの最大の課題の1つは収束性である。パーセプトロンの学習規則が収束するのは、2 つのクラスを線形超平面 ※31 によって分割できる場合である。これは Frank Rosenblattによって数学的に証明されている。そうした線形の決定境界によってクラスを完全に分割できない場合は、 エポックの最大値を設定しない限り、重みはいつまでも更新され続けることになる。
> 線形超平面は、定数項を含めて特徴量の線形結合で表される。特徴量が 1 つのときは定数、特徴量が 2 つの ときは 2 次元の直線になる。
以上、[[📖Python機械学習プログラミング 達人データサイエンティストによる理論と実践]] 2.1節からの引用
---