ヤマカサのプログラミング勉強日記

プログラミングに関する日記とどうでもよい雑記からなるブログです。

Python機械学習プログラミング 達人データサイエンティストによる理論と実践 part. 1

Python機械学習プログラミング 達人データサイエンティストによる理論と実践

第2章

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

入力  \boldsymbol{x} に対応する重み  \boldsymbol{w} の線形結合  z =  \boldsymbol{w}^{ \mathrm{T}}  \boldsymbol{x} を総入力と呼びます.

重みの更新は,訓練データ   \boldsymbol{x}^{(i)} 毎に行います.

 w_{j} \leftarrow w_{j} + \Delta w_{j}

 \Delta w_{j} = \eta (y^{(i)} - \hat{y}^{(i)}) x_{j}^{(i)}

perceptron.py
import numpy as np


class Perceptron(object):
    """パーセプトロンの分類機
    Parameters
    ------
    eta : float
        学習率 (0.0より大きく1.0以下の値) パーセプトロンの収束が保証されるのは学習率が十分に小さいとき
    n_iter : int
        訓練データの訓練回数
    random_state : int
        重みを初期化するための乱数シード

    Attributes
    ------
    w_ : 1次元配列
        適合後の重み
    errors_ : list
        各エポックでのご分類の数
    """

    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):
        """
        訓練データに適合させる

        Parameters
        ------
        X : list, shape = [n_examples, n_features]
            訓練データ
        y : list, shape = [n_examples]
            目的変数

        Return
        ------
        self : object
        """
        rgen = np.random.RandomState(self.random_state)
        self.w_ = rgen.normal(loc=0.0, scale=0.01, size=1 + X.shape[1])
        self.error_ = []

        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
                errors += int(update != 0.0)
            self.error_.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)

アイリスの分類

アイリスと聞くと光彩を思い浮かべましたが,どうやら花のようです.

アヤメ属 - Wikipedia

import os
import pandas as pd
from matplotlib import pyplot as plt
import numpy as np

path = os.path.join('D:\\Book\\python-machine-learning-book-3rd-edition-master\\ch02\\', 'iris.data')

print(path)
df = pd.read_csv(path, header=None, encoding='utf-8')
print(df.tail())

y = df.iloc[0:100, 4].values
y = np.where(y == 'Iris-setosa', -1, 1)
X = df.iloc[0:100, [0, 2]].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='x', label='versicolor')

plt.xlabel('sepal length [cm]')
plt.ylabel('petal length [cm]')
plt.legend(loc='upper left')

plt.show()

Iris-setosa, Iris-versicolorの花びらとがく片の散布図

散布図から分かるようにこのデータをもとにパーセプトロンによる分類を行うことができそうです.線形分離可能なことが視覚的に分かるということです.

学習結果

ppn = perceptron.Perceptron(eta=0.1, n_iter=10)
ppn.fit(X, y)
plt.plot(range(1, len(ppn.errors_) + 1), ppn.errors_, marker='o')
plt.xlabel('Epochs')
plt.ylabel('Number of updates')
plt.show()

エポック数と誤分類

感想

同じデータを繰り返して学習する必要あるのかなと思ったけど,線形分類不可能なときは重みが収束しないので回数を決める必要があるみたいですね.