2.3 自适应线性神经元和学习收敛
本节将讨论另外一种单层神经网络(NN):自适应线性神经元(Adaline)。Adaline是在Frank Rosenblatt提出感知器算法几年之后,由Bernard Widrow及其博士生Tedd Hoff联合提出的,它可以被视为对前者的优化和改进[3]。
Adaline算法特别有趣,因为它说明了定义和最小化连续代价函数的关键概念。这为理解诸如逻辑回归、支持向量机和回归模型等更高级的分类机器学习算法奠定了基础,我们将在以后的章节中讨论这些问题。
Adaline算法的规则(也被称为Widrow-Hoff规则)与Frank Rosenblatt的感知器之间的关键差异在于,Adaline算法规则的权重基于线性激活函数更新,而感知器则是基于单位阶跃函数。Adaline的线性激活函数ϕ(z)是净输入的等同函数,即
ϕ(wTx)=wTx
尽管线性激活函数可用于学习权重,但是我们仍然使用阈值函数进行最终的预测,这与前面看到的单位阶跃函数类似。
感知器与Adaline算法的主要区别如图2-9所示。
图 2-9
图2-9说明,在连续评估正确的分类标签与线性激活函数之后,Adaline算法通过比较实际标签与线性激活函数的连续有效输出以计算模型误差,并更新权重。与之相反,感知器则是比较实际分类标签与预测分类标签。
2.3.1 通过梯度下降最小化代价函数
监督机器学习算法的一个关键组成部分是在学习过程中优化的目标函数。该目标函数通常是我们想要最小化的代价函数。对Adaline而言,可以把学习权重的代价函数J定义为计算结果与真正分类标签之间的误差平方和(SSE):
从下面的段落中可以看到,添加只是为了方便,它使与权重参数相关的代价函数或者损失函数的梯度推导更容易。与单位阶跃函数相反,这种连续线性激活函数的主要优点是代价函数变得可分。代价函数的另外一个优点是凸起,因此,可以用被称为梯度下降的简单而强大的优化算法来寻找权重,最小化代价函数以分类鸢尾花数据集样本。
如图2-10所示,可以把梯度下降背后的主要逻辑描述为走下坡路直到抵达局部或全局代价最小为止。每次迭代都向梯度相反的方向上迈出一步,步幅由学习速率以及梯度斜率来决定。
图 2-10
采用梯度下降方法,现在我们可以通过在代价函数J(w)的梯度▽J(w)的相反方向上迈出一步来更新权重:
w:=w+Δw
其中,把权重变化Δw定义为负的梯度乘以学习速率η:
Δw=-η▽J(w)
要计算代价函数的梯度,我们需要分别用每个权重wj来计算代价函数的偏导数:
这样就可以把权重wj的更新表达为:
因为同时更新所有的权重,所以Adaline的学习规则就成为:
w:=w+Δw
平方差的导数
如果熟悉微积分,与第j个权重相对应的SSE代价函数的偏导数可以计算如下:
尽管Adaline的学习规则看起来与感知器一样,但应该注意的是当z(i)=wTx(i)时,ϕ(z(i))为实数而不是整数型分类标签。此外,权重更新是基于训练数据集中所有样本进行计算的,而不是在每个样本之后逐步更新权重,这也就是为什么这种方法被称为批量梯度下降。
2.3.2 用Python实现Adaline
因为感知器的算法规则与Adaline非常相近,本章将在前面的感知器实现的基础上修改fit
方法,通过梯度下降最小化代价函数来更新权重。
不像感知器那样在每次训练模型后都更新权重,我们根据整个训练数据集来计算梯度,调用self.eta * errors.sum()
计算偏置单元(零权重),调用self.eta * X.T.dot(errors)
计算从1到m的权重,这里X.T.dot(errors)
是特征矩阵与误差向量的矩阵相乘。
请注意,activation
方法对代码没有影响,因为它只是一个标识函数。在这里,我们添加激活函数(通过activation
方法来计算)来说明信息是如何通过单层神经网络流动的:从输入数据、净输入、激活到输出。
第3章将学习具有非同一性、非线性激活函数的逻辑回归分类器。我们将会看到逻辑回归模型与Adaline关系密切,两者之间唯一的区别在于激活函数和代价函数。
与感知器类似,我们把所收集的代价存储在self.cost_
列表,以检验训练后的算法是否收敛。
矩阵乘法
矩阵乘法与向量点积非常相似,把矩阵中的每行当成单一的行向量来计算。这种向量化的方法代表了更紧凑的表达方法,可以用NumPy做更为有效的计算。例如:
请注意,在前面的等式中,我们用一个向量乘以一个矩阵,数学上对此并无定义。然而,记住本书前面的约定,向量可以被表达为3×1的矩阵。
在实践中,我们经常需要通过实验找到可以达到最优收敛的最佳学习速率η。所以选择η=0.1和η=0.0001两个不同的学习速率,把代价函数与迭代次数的关系在图中画出,以便观察Adaline实现从训练数据中学习的情况。
感知器超参数
学习速率η(eta
)和迭代次数n_tier
是感知器和Adaline学习算法的超参数。第6章会分析各种不同的技术,以自动寻找确保分类模型可以获得最佳性能所需要的不同超参数值。
下述代码将根据两种不同的学习速率,画出代价与迭代次数之间的关系图:
从图2-11中绘制的代价函数图可以看到,存在着两种不同类型的问题。左图显示选择学习速率太大将会出现的情况。因为所选的全局最小值太低,以至于代价函数无法最小化,结果误差经过每次迭代变得越来越大。另一方面,从右图可以看到代价在降低,但所选的学习速率η=0.0001太小,以至于算法需要经过多次迭代才能收敛到全局最低代价。
图 2-11
图2-12说明了如果改变某个特定权重参数的值来最小化代价函数J时会发生的情况。左图显示如果选择一个好的学习速率,代价会逐渐降低,向全局最小的方向发展。然而,右图显示如果选择的学习速率太大,将会错过全局最小值。
图 2-12
2.3.3 通过特征缩放改善梯度下降
本书中的许多机器学习算法,都需要通过某种形式的特征缩放来优化性能,第3章和第4章将对此做详细的讨论。
梯度下降是从特征缩放受益的众多算法之一。本节将用一种称为标准化的特征缩放方法,它可以使数据具有标准正态分布的特性:零均值和单位方差。此标准化过程有助于促进梯度下降学习更快地收敛。但它不会使原始数据集呈正态分布。标准化会使每个特征的均值以零为中心,并且每个特征的标准差为1(单位方差)。例如,对第j个特征的标准化,我们可以简单地用每个训练样本值减去均值μj,然后再除以标准差σj:
这里xj是包含所有n个训练样本的第j个特征值的向量,该标准化技术将应用于数据集的每个特征j。
优化器必须遍历几个步骤才能发现好的或者最优解(全局代价最小),这是标准化有助于梯度下降学习的原因之一,如图2-13所示,两个子图将代价平面表示为二元分类问题中两个模型权重的函数。
图 2-13
用NumPy内置的mean
和std
方法可以很容易地实现标准化:
标准化完成之后,将再次训练Adaline,然后在学习速率η=0.01的条件下,可以看到它经过几轮迭代后完成了收敛:
执行代码后应该可以看到图2-14所示的决策区域以及代价下降情况。
图 2-14
从图2-14可以看到,在学习速率η=0.01的情况下,Adaline经过训练已经开始收敛。然而,即使所有的样本都分类正确了,SSE仍然保持非零。
2.3.4 大规模机器学习与随机梯度下降
在上一节中,我们学习了如何基于整个训练数据集来计算代价梯度,从相反方向来最小化代价函数,这就是为什么这种方法有时也称为批量梯度下降。假设现在有一个拥有数百万个数据点的非常大的数据集,这在许多机器学习应用中并不少见。在这种情况下,运行批量梯度下降的计算成本巨大,因为向全局最小值的方向每迈出一步,都需要重新评估整个训练数据集。
随机梯度下降(SGD)算法是批量梯度下降算法的一种常用替代方法,它有时也称为迭代或在线梯度下降法。该方法并不是基于所有样本x(i)的累积误差之和来更新权重:
而是逐渐更新每个训练样本的权重:
η(y(i)-ϕ(z(i)))x(i)
虽然随机梯度下降可以看作梯度下降的近似,但因为需要更频繁地更新权重,所以通常收敛得更快。因为要根据单个训练实例来计算每个梯度,所以误差平面比梯度下降噪声更大,当然这也有优势,因为如果采用非线性代价函数,随机梯度下降更容易逃脱浅度局部极小值,这从本书第12章可以看到。要通过随机梯度下降获得满意的结果,很重要的一点是将训练数据以随机顺序呈现出来,同时要对训练数据集重新洗牌以防止迭代循环。
在训练中调整学习速率
在随机梯度下降的实现中,固定的学习速率η经常被随时间下降的自适应学习速率所取代,例如:
其中C1和C2为常数,要注意随机梯度下降并没有到达全局最小值,而是在一个非常靠近这个点的区域。用自适应学习速率可以把代价进一步最小化。
随机梯度下降的另外一个优点是它可以用于在线学习。在线学习中模型可以在数据到达时实时完成训练。这对累积大量数据的情况特别有用(例如网络应用中的用户数据)。采用在线学习的方法,系统可以立即适应变化,而且在存储空间有限的情况下,可以在更新模型后丢弃训练数据。
小批量梯度下降
批量梯度下降和随机梯度下降之间的折中就是所谓的小批量学习。小批量学习可以理解为对训练数据的较小子集采用批量梯度下降,例如,每次32个训练样本。小批量梯度下降的优点是可以通过更频繁的权重更新,实现快速收敛。此外,小批量学习允许利用线性代数概念中的向量化操作(例如,通过点积实现加权求和)取代随机梯度下降中训练样本上的for
循环,进一步提高学习算法的计算效率。
因为我们已经采用梯度下降实现了Adaline学习规则,所以只需要对学习算法做一些调整以让其通过随机梯度下降更新权重。在调用fit
方法的过程中,将在每个样本训练之后更新权重。此外,将在实现在线学习时调用额外的partial_fit
方法,不再重新初始化权重。为了检验算法在训练后是否收敛,每次迭代都将计算训练样本的平均代价。而且还将增加一个选项,在每次迭代开始之前,对训练数据重新洗牌以避免在优化代价函数时重复循环。通过random_state
参数,允许为反复训练定义随机种子:
AdalineSGD
分类器中使用的_shuffle
方法的工作方式如下:通过调用np.random
中的permutation
函数生成范围从0~100的唯一数组成的随机序列。然后以这些数字作为索引来对特征矩阵和分类标签向量进行洗牌。
可以调用fit
方法来训练AdalineSGD
分类器,用plot_decision_regions
把训练结果以图形表示出来:
通过执行前面的示例代码可以得到两张图,如图2-15所示。
图 2-15
如图2-15所示,平均代价降低得非常快,在15次迭代后,最终的决策边界看起来与批量梯度下降的Adaline结果类似。如果要更新模型,例如,要实现流式数据的在线学习,可以对单个训练样本直接调用partial_fit
方法,比如ada_sgd.partial_fit(X_std[0, :], y[0])
。