パーセプトロンのまとめ

# はじめに

パーセプトロンを一から実装した時のまとめ

# パーセプトロンの理論

まず、2 つのクラスを`1(陽性)`と`-1(陰性)`に分けます。
そうすると活性化関数$φ$が定義できます。
この活性化関数は特定の入力$x$に対応する重みベクトル$w$の線形結合を引数とします。

```math
Z = \sum_{i=1}^{n}w_ix_i
\quad
w = \begin{pmatrix}
w_1 \\
w_2 \\
\vdots \\
w_n
\end{pmatrix}
\quad
x = \begin{pmatrix}
x_1 \\
x_2 \\
\vdots \\
x_n
\end{pmatrix}
```

活性化関数には単位ステップ関数を使います

```math
φ(z) = \left\{
\begin{array}{ll}
1 & (z \gt \theta) \\
-1 & (z \lt \theta)
\end{array}
\right.
```

$\theta$はしきい値です

## パーセプトロンの学習規則

1. 重み$w$を 0 または小さな乱数で初期化する

2. トレーニングサンプル$x^{(i)}$毎に以下の手順を実行する

   1. 出力$\breve{y}$を計算する
   2. 重み$w$を更新する

重みベクトル$w$の各重み$w_i$は同時に更新され以下の式で表されます。

```math
w_i := w_i + \Delta w_i
```

$\Delta w_i$の計算は以下のようにされます。

```math
\Delta w_i = \eta(y_i - \breve{y_i})x_i^{(i)}
```

$\eta$は学習率で$0<\eta<1$です。
$y_i$はトレーニングクラスラベルで$\breve{y_i}$は予測クラスラベルです。

# 実装

以下の環境で実装しました。
実装した後に、実際にアヤメのデータセットを使って訓練していきます。

- python 3.6

- JupyterLab 0.35.4

使用したモジュールは`numpy`です。
データセットを入手して処理するための`pandas`と可視化するために`matplotlib`を使います。

##コード

```python
class Perceptron(object):
    """
    パーセプトロン分類機

    eta:学習率
    n_iter:トレーニング回数
    ーーーー
    属性
    w_ :一次元配列
        適合後の重み
    errors_ :リスト
        各エポックでの誤分類数
    """
    def __init__(self,eta = 0.01,n_iter = 10):
        self.eta = eta
        self.n_iter = n_iter

    def fit(self,X,y):
        """
        トレーニングデータに適応させる
        パラメータ
        ----------
        X:トレーニングデータ（配列のようなデータ構造）、shape = {n_samples,n_features}
        y:配列のようなデータ構造,shape={n_samples}、目的変数

        戻り値
        self:object
        """
        self.w_ = np.zeros(1 + X.shape[1])
        self.errors_ = []

        for _ in range(self.n_iter):   #トレーニングデータの回数分反復
            errors = 0
            for xi,target in zip(X,y):  #Xとyのデータを同時に取り出す
                #重みWn(n=>1)の更新   △w = 学習率*(答え-予測結果)*X
                update = self.eta * (target - self.predict(xi))
                self.w_[1:] += update * xi
                #重みW0の更新
                self.w_[0] += update
                #重みの更新が0出ない場合に誤分類としてカウント
                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]
    #1ステップ後のクラスラベルを返す
    def predict(self,X):
        return np.where(self.net_input(X) >= 0.0,1,-1)
```

## モデルの訓練

最初にデータの入手をします。

```python
#Irisデータセットの入手
import pandas as pd

df = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data',header=None)
```

次にデータのプロットをしました。

```python
#グラフの描画
import matplotlib.pyplot as plt
#1-100番目まで目的変数の抽出
y = df.iloc[0:100,4].values
#Iris-setosa を-1　iris-verginica を1に変換
y = np.where(y == 'Iris-setosa',-1,1)
#1-100行目の1,3列めの抽出
X = df.iloc[0:100,[1,3]].values
#グラフのプロット
plt.scatter(X[:50,0],X[:50,1],color = 'red',marker = 'o',label = 'setosa')
plt.scatter(X[50:100,0],X[50:100,1],color = 'blue',marker = '*',label='versicolor')
#軸ラベルの設定
plt.xlabel('speal length [cm]')
plt.ylabel('petal length [cm]')
#凡例の表示
plt.legend(loc = 'upper left')

plt.show()
```

これのような画像が出力されるはずです。

![dataGraph.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283552/4bc2cee7-9ddc-047a-08d5-3f4e2ca574c3.png)


次に、実際に実装したモデルのトレーニングをします。トレーニングした後に間違えたデータの個数をエポック毎にプロットしました。

```python
ppn = Perceptron(eta = 0.1,n_iter = 10)
#モデルの適合
ppn.fit(X,y)
plt.plot(range(1,len(ppn.errors_)+1),ppn.errors_,marker='*')
#軸ラベルの設定
plt.xlabel("epochs")
plt.ylabel("Number of missclassifications")
plt.show()
```

2回で収束したことがわかりました。

![loss.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283552/1bada91f-0535-70bb-9eec-76923e046a2b.png)

次に、パーセプトロンが学習した境界線をプロットする関数を実装して、実際に表示しました。

```python
from matplotlib.colors import ListedColormap

def plot_decision_regions(X,y,classifier,resolution = 0.02):
    #マーカーとカラーマップの用意
    markers = ('s','x','o','^','v')
    colors = ('red','blue','lightgreen','gray','cyan')
    cmap = ListedColormap(colors[:len(np.unique(y))])
    #決定領域のプロット
    x1_min,x1_max = X[:,0].min() - 1,X[:,0].max() + 1
    x2_min,x2_max = X[:,1].min() - 1,X[:,1].max() + 1
    #グリッドポイントの作成
    xx1,xx2 = np.meshgrid(np.arange(x1_min,x1_max,resolution),
                         np.arange(x2_min,x2_max,resolution))
    #特徴量を一次元配列に変換して予測を実行する
    Z = classifier.predict(np.array([xx1.ravel(),xx2.ravel()]).T)
    #予測結果をグリッドポイントのデータサイズに変換
    Z = Z.reshape(xx1.shape)
    #グリッドポイントの等高線のプロット
    plt.contourf(xx1,xx2,Z,alpha = 0.4,cmap = cmap)
    #軸の範囲の設定
    plt.xlim(xx1.min(),xx1.max())
    plt.ylim(xx2.min(),xx2.max())
    #クラスごとにサンプルをプロット
    #matplotlibが1.5.0以下ならc = cmapをc=colors[idx]に変更
    for idx,cl in enumerate(np.unique(y)):
        plt.scatter(x=X[y==cl,0],y=X[y == cl,1],alpha=0.8,c = cmap(idx),marker=markers[idx],label=cl)


plot_decision_regions(X,y,classifier=ppn)
plt.xlabel('spal length[cm]')
plt.ylabel('etal length[cm]')
#凡例の設定
plt.legend(loc = 'upper left')
plt.show()

```

以下の画像のようになりました。

![result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283552/96bafec3-8783-56d0-f2bd-23d8e012dd9c.png)


# 参考にしたもの

[[第 2 版]Python 機械学習プログラミング 達人データサイエンティストによる理論と実践](https://www.amazon.co.jp/Python-%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-%E9%81%94%E4%BA%BA%E3%83%87%E3%83%BC%E3%82%BF%E3%82%B5%E3%82%A4%E3%82%A8%E3%83%B3%E3%83%86%E3%82%A3%E3%82%B9%E3%83%88%E3%81%AB%E3%82%88%E3%82%8B%E7%90%86%E8%AB%96%E3%81%A8%E5%AE%9F%E8%B7%B5-impress-gear/dp/4295003379/ref=sr_1_fkmrnull_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=2TJ6S0GGNADB2&keywords=python+%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0+%E9%81%94%E4%BA%BA%E3%83%87%E3%83%BC%E3%82%BF%E3%82%B5%E3%82%A4%E3%82%A8%E3%83%B3%E3%83%86%E3%82%A3%E3%82%B9%E3%83%88%E3%81%AB%E3%82%88%E3%82%8B%E7%90%86%E8%AB%96%E3%81%A8%E5%AE%9F%E8%B7%B5&qid=1555231770&s=gateway&sprefix=python+%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92%2Caps%2C263&sr=8-1-fkmrnull)