Posted at

パーセプトロン – Python機械学習第二章学習メモ

More than 1 year has passed since last update.

元記事はこちら

最近Python機械学習を読み進めているのですが、その学習メモです。


パーセプトロン


用語


  • 入力値:$ x $

  • 重みベクトル:$ w $

  • 総入力:$ z = w_1x_1 + ... + w_mx_m = w^Tx $

  • しきい値:$ \theta $

  • 活性化関数(単位ステップ関数、ヘビサイド関数):$ \phi $

    (mathjaxのせいか数式が表示されない。githubだといける)

    $$ \phi = \begin{eqnarray}\left{\begin{array{l}1(z\geq\theta) \-1(z<\theta)\end{array}\right.\end{eqnarray} $$


  • 学習率:$ \eta $



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


  1. 重みを0または値の小さい乱数で初期化

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


    1. 出力値$ \hat{y} $を計算する

    2. 重みを更新する




各重みの更新法

$$ w_j := w_j + \Delta w_j $$


誤差の算出

$$ \Delta w_j = \eta(y^{(i)} - \hat{y}^{(i)})x_j^{(i)} $$

$ y^{(i)} $:本当のクラスラベル

$ \hat{y}^{(i)} $:予測されたクラスラベル


予測が当たった場合

重みは更新されない。

$$ \Delta w_j = \eta(1 - 1)x_j^{(i)} = 0 $$

$$ \Delta w_j = \eta(-1 - (-1))x_j^{(i)} = 0 $$


予測が当たらなかった場合

重みが更新される。

$$ \Delta w_j = \eta(-1 - 1)x_j^{(i)} = \eta(-2)x_j^{(i)} $$

$$ \Delta w_j = \eta(1 - (-1))x_j^{(i)} = \eta(2)x_j^{(i)} $$


インデックス0の場合

$ w_0 $ のみ違う計算式となる

$$ \Delta w_0 = \eta(-1 - output^{(i)}) $$

$$ \Delta w_1 = \eta(1 - output^{(i)})x_1^{(i)} $$


パーセプトロンの実装

import numpy as np

class Perceptron(object):
"""
eta: 学習率(0.0<eta<1.0)
n_iter: トレーニング回数
"""

def __init__(self, eta=0.01, n_iter=10):
self.eta = eta
self.n_iter = n_iter

def fit(self, X, y):
"""
X: トレーニングデータ(pandas, 行にサンプル、列に特徴量)
Y: 目的変数(pandas)
"""

self.w_ = np.zeros(1 + X.shape[1])
self.errors_ = []

for ite in range(self.n_iter):
#print('w:', self.w_)
errors = 0
for xi, target in zip(X, y):
#print('xi:', xi)
#重みw1,...,wmの更新
predict = self.predict(xi)
update = self.eta * (target - predict)
#print('target:', target, ' predict:', predict, ' update:', update)
self.w_[1:] += update * xi
# 重みw0の更新
self.w_[0] += update
# 重みの更新が0でない場合は誤分類としてカウント
error = int(update != 0.0)
#print('error:', error)
errors += error
# 反復回数ごとの誤差を格納
print(ite, ': ', errors)
self.errors_.append(errors)
return self

def net_input(self, X):
"""総入力を計算"""
result = np.dot(X, self.w_[1:]) + self.w_[0]
#print('w_', self.w_)
#print('net_input_result:', result)
return result

def predict(self, X):
"""1ステップ後のクラスラベルを返す"""
return np.where(self.net_input(X) >= 0.0, 1, -1)


トレーニングデータの可視化コード

import pandas as pd

import matplotlib.pyplot as plt
import numpy as np

# irisデータを読み込み
df = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data', header=None)
print(df.tail())

# 100行目までを抽出
y = df.iloc[0:100, 4].values
# 正解ラベルの変換
y = np.where(y == 'Iris-setosa', -1,1)
# 1行目と3行目を抽出する。
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('ガクの長さ[cm]')
plt.ylabel('花弁の長さ[cm]')
plt.legend(loc='upper left')
plt.show()


実行結果

       0    1    2    3               4

145 6.7 3.0 5.2 2.3 Iris-virginica
146 6.3 2.5 5.0 1.9 Iris-virginica
147 6.5 3.0 5.2 2.0 Iris-virginica
148 6.2 3.4 5.4 2.3 Iris-virginica
149 5.9 3.0 5.1 1.8 Iris-virginica


トレーニング

ppn = 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 misclassifications')
plt.show()


実行結果

0 :  2

1 : 2
2 : 3
3 : 2
4 : 1
5 : 0
6 : 0
7 : 0
8 : 0
9 : 0


学習結果のプロット

from matplotlib.colors import ListedColormap

def plot_decision_regions(X, y, classifier, resolution = 0.02):
# マーカーとカラーマップの準備
markers = ('s', 'x', 'o', '^','v')
colors = ('red', 'blue', 'lightgreen', 'grey', 'cyan')
cmap = ListedColormap(colors[:len(np.unique(y))])

#決定領域のプロット
#X[:, 0]とやるとPandasのdataframeから各要素の0番目を取得できる
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())

#クラスごとにサンプルをプロット
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('sepal length')
plt.xlabel('petal length')
plt.legend(loc='upper left')
plt.show()


実行結果