从感知器到Adaline
从感知器到Adaline:时尚演变的回顾
确立正确的基础
介绍
在之前的一篇文章中,我试图解释可能有史以来存在的最基本的二元分类器,Rosenblatt的感知器。理解这个算法具有教育价值,并且可以作为初级机器学习课程的很好介绍。这个算法可以在一个下午内从零开始编码,可以激发兴趣、成就感和深入研究更复杂主题的动力。然而,作为一个算法,它还有很多不足之处,因为只有在类别是线性可分的情况下才能保证收敛,而这种情况经常不是真实情况。
在本文中,我们将继续探索分类概念的旅程。从Rosenblatt的感知器到adaline是一个自然的过渡。我们只需要将阶跃激活函数更改为线性函数。这个小改变导致了一个可以被稳健最小化的连续损失函数。这使我们能够引入许多在机器学习中有用的概念,如向量化和优化方法。
在未来的文章中,我们还将涵盖激活和损失函数的进一步微妙变化,从adaline到逻辑回归,逻辑回归在日常实践中已经是一个有用的算法。以上所有算法本质上都是单层神经网络,可以轻松扩展为多层神经网络。从这个意义上说,本文将引领读者通过这个演变阶段,为探索更高级的概念打下基础。
我们需要一些公式。我使用在线的LaTeX公式编辑器开发公式的LaTeX代码,然后使用谷歌浏览器插件Maths Equations Anywhere将公式渲染为图像。这种方法的唯一缺点是LaTeX代码不会被保存,以防需要再次渲染。为此,我在本文末尾提供了公式列表。如果您对LaTex不熟悉,这可能具有教育意义。掌握正确的符号表示方法是机器学习过程中的一部分。
- Meet One-2-3-45++:一项创新的人工智能方法,可将一张图片转化为约一分钟内的详细3D纹理网格
- 中国的这篇AI论文介绍了“Monkey”:一种新颖的人工智能方法,可提高大型多模态模型中的输入分辨率和上下文关联性
- 《人文释放克劳德2.1:通过扩展上下文窗口和提高准确性,革新企业人工智能》
自适应线性神经元分类器(adaline)
那么什么是adaline算法?Adaline是一个二元分类器,就像感知器一样。通过使用一组特征[x₁, .. , xₘ]的输入值进行预测,其中m是特征的数量。输入值与权重[w₁, .. , wₘ]相乘,并添加偏差以获得净输入z = w₁x₁ + .. + wₘxₘ + b。净输入被传递给线性激活函数σ(z),然后使用阶跃函数进行预测,就像感知器一样:

与感知器的一个关键区别是,线性激活函数用于学习权重,而阶跃函数仅用于最后进行预测。这听起来可能是一个小事,但它具有重要的意义。线性激活函数是可微分的,而阶跃函数不是!上面的0.5阈值不是铁定的。通过调整阈值,我们可以根据我们的用例调整精确度和召回率,即基于误报和漏报的成本。
在adaline的情况下,线性激活函数简单地是标识函数,即σ(z) = z。需要在训练过程中最小化的目标函数(也称为损失函数)为

其中w是权重

而b是偏移量。求和是针对训练集中的所有示例。在某些实现中,损失函数还包括一个1/2系数以方便计算。这个系数在计算损失函数相对于权重和偏移量的梯度时被抵消掉,并且除了将学习速率缩放2倍以外,并没有其他影响。在本文中,我们不使用1/2系数。
对于每个示例,我们计算计算结果和真实类别标签之间的平方差异

注意,输入向量被理解为形状为(1, m)的矩阵,即作为我们特征矩阵x的一行,其形状为(n, m),稍后我们会看到。
训练无非就是一个优化问题。我们需要调整权重和偏移量,使得损失函数最小化。与任何最小化问题一样,我们需要计算目标函数相对于自变量(在本例中为权重和偏移量)的梯度。损失函数相对于权重wⱼ的偏导数如下

最后一行引入了重要的矩阵表示法。特征矩阵x的形状为(n, m),我们取其第j列的转置,即形状为(1, n)的矩阵。真实类别标签y是形状为(n, 1)的矩阵。所有样本的净输出z也是形状为(n, 1)的矩阵,在激活之后不会改变,激活被认为适用于其中的每个元素。上述公式的最终结果是一个标量。你能猜到我们如何使用矩阵表示法来表示相对于所有权重的梯度吗?

其中特征矩阵的转置形状为(m, n)。这一操作的最终结果是一个形状为(m, 1)的矩阵。这种表示法很重要。我们将使用这个矩阵乘法来替代循环,使用numpy。在神经网络和GPU时代,应用向量化的能力是至关重要的!
那么关于损失函数相对于偏移量的梯度呢?

上面的横线表示其下方向量的均值。同样,使用numpy进行均值计算是一项向量化的操作,即不需要使用循环来实现求和。
有了梯度后,我们可以使用梯度下降优化方法来最小化损失。权重和偏移量项通过以下方式进行迭代更新

其中η是适当选择的学习率。过小的值可以延迟收敛,而过大的值则可能完全阻止收敛。需要进行一些试验,就像对机器学习算法的参数进行调整一样。
在上述实现中,我们假设权重和偏移量是根据所有示例同时进行更新的。这被称为全批量梯度下降,是一种极端情况。另一种极端情况是在每个训练示例之后更新权重和偏移量,这被称为随机梯度下降(SGD)。实际上还存在某种中间情况,即小批量梯度下降,其中权重和偏移量是基于示例的子集进行更新的。通过这种方式通常可以更快地达到收敛,也就是说,我们不需要对整个训练集运行那么多次迭代,同时向量化仍然(至少部分)可行。如果训练集非常大(或者模型非常复杂,如现在在自然语言处理中使用的变换器),全批量梯度下降可能根本不是一个选项。
替代公式和闭式解法
在我们继续用Python实现adaline之前,我们将进行一个快速的话题转变。我们可以将偏置b吸收到权重向量中,如下所示:

这样,训练集中所有样本的净输出变为:

意味着特征矩阵已经在前面填充了一列全为1的列,导致其形状为(n, m+1)。关于组合权重集的梯度变为:

原则上,我们可以推导出闭式解法,因为在最小值处所有梯度都将为零

实际上,由于奇异点或计算不够准确,上述等式中的矩阵的逆可能不存在或无法计算。因此,在实际中,这样的闭式解法既不在机器学习中使用,也不在一般的数值方法中使用。不过,了解到adaline类似于线性回归,并且具有闭式解法仍然是有用的。
用Python实现adaline
我们的实现将使用小批量梯度下降。然而,这个实现是灵活的,允许使用随机梯度下降和全批量梯度下降两种极端来优化损失函数。我们将通过改变批量大小来观察收敛行为。
我们使用一个封装了fit和predict函数的类来实现adaline,这符合通常的scikit-learn API风格。
在初始化时,adaline分类器设置小批量梯度下降的批量大小。如果批量大小被设置为None,则使用整个训练集(全批量梯度下降),否则使用批次方式使用训练集(小批量梯度下降)。如果批量大小为1,我们实际上就转变为了随机梯度下降。在每次通过训练集之前,训练集都会被洗牌以避免重复的循环,但这只对使用小批量梯度下降时有效。算法的精髓在于_update_weights_bias函数,它对训练集进行完整遍历并返回相应的损失。该函数应用了用前一节中的导数分析计算出的梯度的梯度下降。注意numpy的matmul和dot函数的使用,它们避免了使用显式循环。如果批量大小设置为None,则完全没有循环,实现是完全向量化的。
在实践中使用adaline
我们进行必要的导入并创建一个合成数据集,就像之前的感知器文章中所做的那样
产生

与之前的文章唯一的区别是我们微调了高斯均值和协方差,使得这些类不是线性可分的,我们期望adaline能够克服这一点。此外,两个独立变量故意具有不同的尺度,以讨论特征缩放的重要性。
让我们尝试拟合第一个模型并可视化收敛过程。在拟合之前,我们对特征进行归一化,使得它们都具有零均值和单位标准差。
这产生了收敛图

Adaline慢慢收敛,但损失函数不会变为零。为了验证训练的成功,我们使用与之前文章相同的方法可视化决策边界
这产生

由于训练集中的两个类别不是线性可分的,我们使用了线性决策边界,因此有一些分类错误的点。尽管如此,算法收敛得很好。解决方案是确定性的。通过足够多次地通过训练集,我们得到的权重和偏置是数值相等的,无论它们的初始值如何。
小批量与完整批量梯度下降
上述数值实验使用了完整批量梯度下降,这在一定程度上解释了收敛速度较慢。我们将使用与之前相同的数据集和随机状态,但这次我们将使用不同的批次大小来拟合adaline分类器,范围从20到400,即我们训练集中的示例数量。
这产生

我们可以清楚地看到,批量大小越小,收敛速度越快,但也会出现一些振荡。这些振荡可能会在较大的学习率下破坏收敛。如果我们将学习率加倍到0.002,这一点变得明显

进一步增大学习率将最终阻止较小的批量大小的收敛。使用更大的学习率,即使是完整批量梯度下降也无法收敛,因为我们会超过全局最小值。
结论
Adaline是对感知器的重要改进。通过最小化连续的损失函数来获得权重和偏置,该损失函数是凸的(因此没有局部最小值)。只要学习率足够小,即使类别不是线性可分的,算法也会收敛。在使用梯度下降的任何变体时,收敛速度受到特征缩放的影响。在这篇文章中,我们使用简单的标准化方法,将每个特征的均值移动到零,同时调整其方差为单位方差。通过这种方式,可以选择一个对所有权重和偏置均有效的学习率,这意味着可以在较少的迭代中获得全局最小值。
在深入研究更复杂的主题(如支持向量机和多层神经网络)之前,了解如何使用向量化构建二分类器是非常重要的。在日常实践中,人们会使用提供了先进分类算法的scikit-learn,该算法允许非线性决策边界,并支持高效且系统化的超参数调整和交叉验证。然而,从头开始构建简单的二分类器可以提供深入的理解,增加信心,并产生归属感。虽然从头开始构建一切当然是不现实的,但深入理解这些较简单的算法为掌握现成的库中包含的更高级算法提供了必要的技能和洞察力,使其不再那么难以理解。
文章中使用的LaTeX公式代码
文章中使用的公式可以在下方的gist中找到,以便您需要再次渲染它们。




