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

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

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

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

前回の復習

人口ニューロン

重み $\boldsymbol{w} = [w_{1}, \cdots , w_{m}]^{\mathrm{T}}$,訓練データ $\boldsymbol{x} = [x_{1}, \cdots , x_{m}]^{\mathrm{T}}$ の線形結合である総入力 $z = \boldsymbol{w}^{\mathrm{T}} \boldsymbol{x}$ を引数に持つ決定関数を

\begin{align} \phi(z) = \begin{cases} 1 & (z \geq \theta) \cr -1 & (z < \theta) \end{cases} \end{align}

とします.$\theta$ はしきい値です.

上記を簡略化するために,$w_0 = -\theta$, $x_{0} = 1$ として,総入力を $z = w_{0}x_{0} + \boldsymbol{w}^{\mathrm{T}} \boldsymbol{x}$ とし,

\begin{align} \phi(z) = \begin{cases} 1 & (z \geq 0) \cr -1 & (z < 0) \end{cases} \end{align}

とします.

ソースコード

update = self.eta * (target - self.predict(xi))

この部分はパーセプトロンの更新式において,$η(y - \hat{y})$ に該当します.

第2章

ADALINE

コスト関数 $J(\cdot)$ として,

\begin{align} J(\boldsymbol{w}) = \dfrac{1}{2} \sum_{i}(\hat{y} - \phi(z)) \end{align}

を最小化する $\boldsymbol{w}$ を勾配降下法によって求めます.

更新式は,

\begin{align} \boldsymbol{w} \leftarrow \boldsymbol{w} + \Delta \boldsymbol{w} \end{align}

\begin{align} \Delta \boldsymbol{w} = -η \Delta J(\boldsymbol{w}) \end{align}

であり,

\begin{align} \dfrac{\partial J}{\partial w_{i}} = - \sum_{i} (\hat{y} - \phi (z)) \end{align}

です.

adaline.py

import numpy as np

class AdalineGD(object):

    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):
        rgen = np.random.RandomState(self.random_state)
        self.w_ = rgen.normal(loc=0.0, scale=0.01, size=1 + X.shape[1])
        self.cost_ = []

        for i in range(self.n_iter):
            net_input = self.net_input(X)
            output = self.activation(net_input)
            errors = (y - output)
            self.w_[1:] += self.eta * X.T.dot(errors)
            self.w_[0] += self.eta * errors.sum()
            cost = (errors**2).sum() / 2.0
            self.cost_.append(cost)
        return self

    def net_input(self, X):
        return np.dot(X, self.w_[1:]) + self.w_[0]

    def activation(self, X):
        return X

    def predict(self, X):
        return np.where(self.activation(self.net_input(X)) >= 0.0, 1, -1)
fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(10, 4))

ada1 = adaline.AdalineGD(n_iter=10, eta=0.01).fit(X, y)
ax[0].plot(range(1, len(ada1.cost_) + 1), np.log10(ada1.cost_), marker='o')
ax[0].set_xlabel('Epochs')
ax[0].set_ylabel('log(Sum-squared-error)')
ax[0].set_title('Adaline - Learning rate 0.01')

ada2 = adaline.AdalineGD(n_iter=10, eta=0.0001).fit(X, y)
ax[1].plot(range(1, len(ada2.cost_) + 1), ada2.cost_, marker='o')
ax[1].set_xlabel('Epochs')
ax[1].set_ylabel('Sum-squared-error')
ax[1].set_title('Adaline - Learning rate 0.0001')

plt.show()

学習率によるコスト関数の値

学習率を適切に選ぶのは難しいみたいです.

最小二乗法

ディープラーニングは最小二乗法であるとかいう記事が炎上してたと見たことがあります.それはともかく,コスト関数見ると勾配降下法を使わなくても最小二乗法で解けるので,pythonで試してみました.

import os
import pandas as pd
import numpy as np


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

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

y = df.iloc[0:100, 4].values
y = np.where(y == 'Iris-setosa', -1, 1)
X = df.iloc[0:100, [0, 2]].values

T = np.dot(X.T, X)
T = np.dot(np.linalg.inv(T), X.T)
para = np.dot(T, y)
print(para)


def phi(X, v):
    if np.dot(X, v) >= 0: return 1
    else: return -1


cnt = 0
cost = 0.0

for xi, target in zip(X, y):
    if target != phi(xi, para): cnt += 1
    cost += (target - np.dot(xi, para)) * (target - np.dot(xi, para)) / 2.0

print(cnt)
print(cost)

$J(\boldsymbol{w}^{*}) = 2.604$

となり,勾配降下法よりもコストが小さいです.まあ,当たり前なんですが.