Adaline算法的规则
Adaline算法与前一篇文章提到的感知器之间的关键差异在于adaline算法规则的权重基于线性激活函数更新。而感知器则是基于单位跃阶函数。Adaline的线性激活函数是净输入函数的同等函数,即
Φ(w(T)x) = w(T) * x
尽管线性激活函数可用于学习权重,但是我们仍然使用阈值函数进行最终的预测。感知器与Adline的主要区别如下
Adaline算法通过比较实际标签与线性激活函数的连续有效输出以计算模型误差,并更新权重。与之相反感知器则是比较实际分类标签与预测分类标签。
通过梯度下降最小化代价函数
监督学习算法的一个关键组成部分是在学习过程中优化的目标函数。Adaline模型相比Rosenblatt模型,定义了代价函数(cost function),最小化代价函数是许多机器学习算法的主要思想。Adaline模型代价函数用的是均方误差(Sum of Squared Errors :SSE)。
将代价函数化成这样的线性激活函数的优点是可以微分,且凸函数,因此可以用梯度下降的优化算法来寻找权重,最小化代价函数来分类鸢尾花数据样本。寻找最小均方误差就像下山一样,每次算法循环都相当于下降一步,下降一步的歩幅取决于学习率,与图中的权值点的切线斜率相关。
采用梯度下降算法,我们可以通过在代价函数J(w)和梯度△J(w)的相反方向迈出一步来更新权重。η为学习速率。
要计算代价函数的梯度,我们需要分别用每个权重w(j)来计算代价函数的偏导数,这样权重更新的表达示为
所以Adaline的学习规则就为
w : = w + △w
尽管Adaline的学习规则看起来和感知器一样,但是Adaline算法是基于训练数据集中所有样本进行权重更新的,而不是每个样本之后逐步更新权重,因此这种方式也称为批量梯度下降。
用Python实现Adaline
import numpy as np
import os
import pandas as pd
import matplotlib.pyplot as plt
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')
cmap = ListedColormap(colors[:len(np.unique(y))])
# 确定两个特征的最小值和最大值
x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1
x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1
# 创建网格数组对,步距为resolution
# 生成数据点
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) #.T对矩阵转置
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())
# plot class examples
for idx, cl in enumerate(np.unique(y)):
plt.scatter(x=X[y == cl, 0],
y=X[y == cl, 1],
alpha=0.8,
c=colors[idx],
marker=markers[idx],
label=cl,
edgecolor='blue')
# 获取数据集
s = 'https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data'
df = pd.read_csv(s,
header=None,
encoding='utf-8')
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
然后在前面感知器实现的基础上修改对象,通过梯度下降来最小化代价函数更新权重。
errors = (y - output)
批量计算误差,根据上文的公式,使用self.w_[1:] += self.eta * X.T.dot(errors)
更新权重,cost = (errors**2).sum()
计算偏置单元,即误差平方和SSE。将所收集的代价储存在self.cost_列表中,以检验训练后算法是否收敛。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))
# 学习速率为0.01,太大以至于错过全局最小值
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('lg(Sum-squared-error)')
ax[0].set_title('Adaline - Learning rate 0.01')
# 学习速率为0.0001,太小以至于代价函数无法最小化
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.show()
从下图绘制的代价函数图可以看出,存在两种不同类型的问题。左图显示学习速率太大会出现的情况,因为所选的全局最小值太低,以至于代价函数无法最小化,结果误差经过每次迭代变得越来越大。右图则是学习速率太小,没有出现收敛的趋势,以至于多次迭代才能收敛到全局最低。
下图说明了如果改变某个特定权重参数来最小化代价函数会发生的情况,左图如果选择了一个好的学习速率,代价函数会逐渐降低,右图显示如果选择的学习速率太大,则会错过全局最小值。
学习率设置,偏大偏小都会大幅降低算法效率。接下来我们采取数据标准化(standardization)的方法。
通过特征值缩放来改善梯度下降
特征缩放是用来统一资料中的自变项或特征范围的方法,在资料处理中,通常会被使用在资料前处理这个步骤。因为在原始的资料中,各变数的范围大不相同。对于某些机器学习的算法,若没有做过标准化,目标函数会无法适当的运作。举例来说,多数的分类器利用两点间的距离计算两点的差异,若其中一 个特征具有非常广的范围,那两点间的差异就会被该特征左右,因此,所有的特征都该被标准化,这样才能大略的使各特征依比例影响距离。特征缩放(Feature Scaling)是将不同特征的值量化到同一区间的方法,也是预处理中容易忽视的关键步骤之一。除了极少数算法(如决策树和随机森林)之外,大部分机器学习和优化算法采用特征缩放后会表现更优。
这一部分我们使用标准化的特征缩放方法,他可以使数据具有正态分布的特性:零均值和单位方差。此过程有助于梯度下降学习更快地收敛。但他不会使原始数据集呈正态分布。标准化使每个特征的均值以0为中心,并且每个特征的标准差为1(单位方差)。
我们可以简单地用每个训练样本减去均值,然后除以标准差。该标准化将用于数据集的每个特征。
使用Numpy的内置函数很容易对所有特征向量标准化。
# 特征缩放改善梯度下降
X_std = np.copy(X)
X_std[:, 0] = (X[:,0]-X[:,0].mean())/X[:,0].std()
X_std[:, 1] = (X[:,1]-X[:,1].mean())/X[:,1].std()
标准化后,我们再次使用0.01的学习率进行训练分类,并绘制决策趋于以及代价下降情况。
# 绘制分类器的决策区域
plot_decision_regions(X_std, y, classifier=ada)
plt.title('Adaline - Gradient Descent')
plt.xlabel('sepal length [standardized]')
plt.ylabel('petal length [standardized]')
plt.legend(loc='upper left')
plt.tight_layout()
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()
从下图可以看出,在学习速来为0.01的情况下Adaline模型已经收敛。但是即使所有样本都分类正确了,SSE仍然保持非零。
备忘录
# .mean()求均值,.std求标准差
X_std[:, 0] = (X[:,0]-X[:,0].mean())/X[:,0].std()