Adaline 和梯度下降法
本文介绍了基于梯度下降法 (Gradient Descent,GD) 的自适应线性神经元 (Adaptive Linear Neuron,Adaline),以及对应的算法实现。
Adaline 原理
Adaline 是 1960 年由 Bernard Widrow 和 Tedd Hoff 提出的,被认为是对 Rosenblatt 感知器的改进。Adaline 算法定义了最小化成本函数的核心概念,为之后一些更先进的机器学习算法 (如逻辑回归和支持向量机) 奠定了基础。
Adaline 规则和 Rosenblatt 感知器的不同之处在于在调整权重的激活函数不同,前者采用线性函数,后者采用单位阶跃函数。在 Adaline 中,。
Adaline 在使用线性激活函数更新权重之外,采用单位阶跃的量化器来预测类标签,如下图所示:
批量梯度下降法
定义在学习过程中需要优化的目标函数,是监督式学习的关键步骤。目标函数通常是需要最小化的成本函数。对于 Adaline,我们定义权重的成本函数 为预测类标签和实际类标签之间的方差总和 (Sum of Squared Errors,SSE)。
线性激活函数的优点是,成本函数可微并且是凸函数 (局部最小值为全局最小值)。
这里采用一种称为梯度下降的算法来找出最小化成本函数对应的权重,以便对 Iris 数据子集的样本进行分类。如下图所示,可以将梯度下降类比 成下山的过程,直到达到局部最小值。每次训练相当于在斜坡上行走一步,其中步长由学习率和斜率决定:
梯度下降法的思想是,如果函数在某点可微且有定义,那么函数在该点沿着梯度相反的方向下降最快,下降系数用学习率 表示:
每次训练后,权重更新为:
权重的每次更新是基于训练集中的所有样本 (而并非单个样本),因此这种最原始的梯度下降法也被称为批量梯度下降法 (Batch Gradient Descent,BGD)。
基于批量梯度下降法的 Adaline 实现
下面代码中,采用了面向对象的方法,定义 Adaline 为 Python 类。
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(X) >= 0.0, 1, -1)
对算法训练过程进行绘图:
import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_csv(
"https://archive.ics.uci.edu/ml/achine-learning-databases/iris/iris.data",
header=None,
)
X = df.iloc[:100, [0, 2]].values
y = df.iloc[:100, 4].values
y = np.where(y == "Iris-setosa", -1, 1)
fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(8, 4))
ada1 = 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 = 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.tight_layout()
plt.show()
从上图中的左子图可以观察到,如果学习率太大,由于每次训练都越过了局部最小值,成本函数得不到最小化,反而错误会在学习过程中一直增加。右子图中,成本函数能够逐渐降低,但由于选择了非常小的学习率 ,以至于算法需要很长时间才能收敛。
下图中的左子图说明了成本函数随权重的调整而最小化,右子图说明了学习率太大所带来的问题:
许多机器学习算法需要某些方法才能实现最佳性能,梯度下降法是从特征缩放 (feature scaling) 中受益的许多算法之一。这里我们将使用一种称为标准化 (standardization) 的特征缩放方法,能够将数据变换成服从标准正态分布 (平均值为 0,标准差为 1)。
例如,对第 个特征,只需要对每个训练样本减去样本均值