勾配降下法によるコスト関数の最小化とADALINEモデルの実装
概要
https://gyazo.com/255fa6f4233958c7ed62ddb224951018
勾配降下法の動き
https://gyazo.com/10c2baea69866e084097b83658ae3fc7
コスト関数の偏微分
https://gyazo.com/d686225fe5e9c767f361509137778e0a
ADALINE, 勾配降下法の実装
code: Python
import matplotlib.pyplot as plt
class AdalineGD(object):
"""ADAptive LInear NEuron 分類器.
パラメータ
------------
eta : float
学習率(0.0より大きく1.0以下の値)
n_iter : int
トレーニングデータのトレーニング回数
random_state : int
重みを初期化するための乱数シード
属性
-----------
w_ : 1次元配列
適合後の重み
cost_ : リスト
各エポックでの誤差平方和のコスト関数
"""
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):
""" トレーニングデータに適合させる
パラメータ
----------
トレーニングデータ
n_samplesはサンプルの個数、n_featuresは特徴量の個数
目的変数
戻り値
-------
self : object
"""
rgen = np.random.RandomState(self.random_state)
self.w_ = rgen.normal(loc=0.0, scale=0.01, size=1 + X.shape1) self.cost_ = []
for i in range(self.n_iter):
net_input = self.net_input(X)
# Please note that the "activation" method has no effect
# in the code since it is simply an identity function. We
# could write output = self.net_input(X) directly instead.
# The purpose of the activation is more conceptual, i.e.,
# in the case of logistic regression (as we will see later),
# we could change it to
# a sigmoid function to implement a logistic regression classifier.
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):
"""1ステップ後のクラスラベルを返す"""
return np.where(self.activation(self.net_input(X)) >= 0.0, 1, -1)
code: Python
import pandas as pd
import numpy as np
# 1-100行目の目的変数の抽出(今回は2値分類問題にしたいため、setosaとversicolorの2つの花だけを使用)
# Iris-setosaを-1、Iris-versicolorを1に変換
y = np.where(y == 'Iris-setosa', -1, 1)
# 1-100行目の1、3列目の抽出
X = df.iloc[0:100, 0, 2].values fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(10, 4))
ada1 = AdalineGD(n_iter=10, eta=0.01).fit(X, y)
ax0.plot(range(1, len(ada1.cost_) + 1), np.log10(ada1.cost_), marker='o') ax0.set_ylabel('log(Sum-squared-error)') ax0.set_title('Adaline - Learning rate 0.01') ada2 = AdalineGD(n_iter=10, eta=0.0001).fit(X, y)
ax1.plot(range(1, len(ada2.cost_) + 1), ada2.cost_, marker='o') ax1.set_ylabel('Sum-squared-error') ax1.set_title('Adaline - Learning rate 0.0001') plt.show()
https://gyazo.com/ce86f52a5317d876bfeba93c3f914dfc
結果として得られたコスト関数のグラフからわかるように、2種類の問題が発生しています。左のグラフは、選択した学習率が大きすぎると、大局的最小値を超えてしまうため、コスト関数を最小にするどころか、エポックごとに誤差平方和が増えています。右のグラフを見ると、コストが減少していることがわかります。ただし、学習率は非常に小さいため、アルゴリズムをコストの大局的最小値に収束させるには、相当な数のエポックが必要になるでしょう。
https://gyazo.com/66940c0435b27b42a9de3dfe44d1bae7
特徴量のスケーリングを通じて勾配降下法を改善する
ここでは、標準化というスケーリング手法を用います。このスケーリング手法は、データに標準正規分布の特性を与えます。この特性は、勾配降下法による学習をより素早く収束させるのに役立ちます。標準化は、各特徴量の平均をずらして中心が0になるようにし、各特徴量の標準偏差を1にします。
$ x'_j = \frac{x_j - \mu_j}{\sigma_j}
標準化が勾配降下法による学習に役立つ理由の1つは、次の図に示すように、ステップ数が少なくなることです。
https://gyazo.com/4d9978fcaedeecf7608a7386eb919a1f
code: Python
# standardize features
X_std = np.copy(X)
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')
# 決定領域のプロット
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))
# 各特徴量を1次元配列に変換して予測を実行
# 予測結果を元のグリッドポイントのデータサイズに変換
Z = Z.reshape(xx1.shape)
# グリッドポイントの等高線のプロット
plt.contourf(xx1, xx2, Z, alpha=0.3, cmap=cmap)
# 軸の範囲の設定
plt.xlim(xx1.min(), xx1.max())
plt.ylim(xx2.min(), xx2.max())
# クラスごとにサンプルをプロット
for idx, cl in enumerate(np.unique(y)):
alpha=0.8,
label=cl,
edgecolor='black')
ada = AdalineGD(n_iter=15, eta=0.01)
ada.fit(X_std, y)
plot_decision_regions(X_std, y, classifier=ada)
plt.title('Adaline - Gradient Descent')
plt.legend(loc='upper left')
plt.tight_layout()
# plt.savefig('images/02_14_1.png', dpi=300)
plt.show()
plt.plot(range(1, len(ada.cost_) + 1), ada.cost_, marker='o')
plt.xlabel('Epochs')
plt.ylabel('Sum-squared-error')
plt.tight_layout()
plt.show()
https://gyazo.com/3539164839c32ee03db98c9be4dab585
https://gyazo.com/a90578919ea816c7c3ab1f5a4f479ac1