
在上一课中,我们探讨了神经网络(neural networks)的结构。现在,让我们谈谈网络如何通过查看大量标记的训练数据来学习。核心思想是一种称为梯度下降(gradient descent)的方法,这不仅构成了神经网络学习的基础,也是许多其他机器学习方法的核心。
作为一个提醒,我们的目标是识别手写数字。这是一个经典例子——神经网络的“Hello World”。

这些数字被渲染到一个28x28像素的网格上,其中每个像素都有一个介于0.0到1.0之间的灰度值。这784个值决定了网络输入层神经元的激活值。

图像中的每个像素值成为网络第一层中特定神经元的激活值。
每一层的神经元激活值基于前一层所有激活值的加权和,再加上一个称为“偏置”的数字。然后,我们对这个总和应用一个非线性函数,例如 sigmoid函数(sigmoid)或 ReLU。

通过这种方式,值从一层传播到下一层,完全由权重和偏置决定。输出层中十个神经元中最亮的那个就是网络判断图像所代表的数字的选择。

输入层的神经元激活值基于图像的像素值。其余各层的激活值则取决于前一层的激活值。
考虑到每层有16个神经元的两层隐藏层的选择,这个网络总共有13,002个可以调整的权重和偏置,正是这些值决定了网络的具体功能。
我们设计这种分层结构的初衷是:或许第二层可以检测边缘,第三层可以识别诸如环和线之类的图案,而最后一层则将这些图案组合起来以识别数字:

我们希望这种分层结构能够允许将问题分解为更小的步骤:从像素到边缘,再到环和线,最终到数字。
网络是如何学习的
使机器学习与其他计算机科学不同的地方在于,你不需要明确编写执行特定任务的指令。在这种情况下,你实际上从未编写过识别数字的算法。相反,你编写的是一个算法,它可以接收大量手写数字的示例图像以及它们是什么的标签,并调整网络的13,002个权重和偏置,使其在这些示例上表现更好。
我们输入的带有标签的图像统称为“训练数据”。

我们希望这种分层结构能够使网络学到的知识不仅适用于训练数据,还能推广到训练数据之外的图像。为了测试这一点,在训练网络后,我们可以向它展示更多它从未见过的标记数据,并观察它对这些数据分类的准确性。

这一过程需要大量数据。幸运的是,MNIST数据库(http://yann.lecun.com/exdb/mnist/)背后的团队整理了一个包含数万张手写数字图像的集合,每张图像都标有对应的数字标签,我们可以免费使用这些数据。
我需要指出,虽然将机器描述为“学习”可能会引发争议,但一旦你了解了它的工作原理,这种过程就不再像某种疯狂的科幻设想,而更像是一个微积分练习。归根结底,它只是寻找一个特定函数的最小值。

记住,我们网络的行为由其所有的权重和偏置(weights and biases)决定。权重表示一层中每个神经元与下一层中每个神经元之间连接的强度。而每个偏置则表明其对应的神经元倾向于活跃还是不活跃。

一开始,将所有的权重和偏置初始化为随机数。由于网络此时的行为是随机的,所以在给定的训练示例上表现会非常糟糕。
例如,如果你输入一张3的图像,输出层看起来就像一团乱麻:

那么,如何通过编程识别计算机表现糟糕并帮助其改进呢?
为此,你需要定义一个损失函数(a cost function),这相当于告诉计算机:“不,表现糟糕,输出层的激活值应该大多数接近0,而第三个神经元的激活值应为1。你给我的结果完全是垃圾!”

用更数学的方式来说,将每个输出激活值与期望值之间的差值平方相加。我们将这个结果称为单个训练示例的“损失”。

当网络能够自信地正确分类这张图像时,损失会很小;但如果网络表现得毫无头绪,损失就会很大。

但我们不仅关心网络在单个图像上的表现。要真正衡量其性能,我们需要考虑所有数万个训练示例的平均损失。这个平均值是我们衡量网络有多糟糕以及计算机应该有多内疚的指标。
这个函数,轻描淡写地说,是相当复杂的。还记得网络本身是一个函数吗?它有784个输入(像素值)、10个输出和13,002个参数。

损失函数又增加了一层复杂性。损失函数的输入是这13,002个权重和偏置,它输出一个单一数字,描述这些权重和偏置有多糟糕。损失函数的定义基于网络在所有数万张标记训练数据上的行为。换句话说,这些训练数据构成了损失函数的大量参数。


仅仅告诉计算机它表现有多糟糕并没有太大帮助。你希望告诉计算机如何调整那13,002个权重和偏置以改进性能。
为了便于理解,与其努力想象一个有13,002个输入的损失函数,不如想象一个简单的函数,它以一个数字作为输入,输出另一个数字。

如何找到使这个损失函数值最小化的输入?
学过微积分的人会知道,可以通过求解斜率为零的点来显式地找到这个最小值。然而,对于非常复杂的函数来说,这种方法并不总是可行的,对于我们这个由大量参数定义的、有13,002个输入的函数来说,这种方法显然不可行。

一种更灵活的策略是:从一个随机输入开始,确定朝哪个方向调整输入可以使输出值降低。具体来说,就是计算出你当前位置的函数斜率。如果斜率为负,向右移动;如果斜率为正,向左移动。

在每个点检查新的斜率并不断重复这一过程,你将逐渐接近函数的某个局部最小值。
可以想象一个球滚下山坡的场景:

对于这个简化的单输入损失函数,你可能会落入许多不同的山谷之一。这取决于你从哪个随机输入开始,而且无法保证你找到的局部最小值就是损失函数的最小可能值。
此外,注意如果你的步长与斜率本身成比例,那么当斜率在接近最小值时变缓,你的步长也会越来越小,这样可以防止你越过最小值。

增加一点复杂性,想象一个有两输入一输出的函数。你可以将输入空间视为xy平面,而将损失函数表示为该平面上方的一个曲面。

不再询问函数的斜率,而是询问在输入空间中应该朝哪个方向移动,以最快地降低函数的输出值。再次想象一个球滚下山坡。
在这个高维空间中,用单一数字来描述“斜率”已经没有意义。相反,我们需要用一个向量来表示最陡峭的上升方向。
如果你熟悉多变量微积分,就会知道这个向量被称为“梯度”,它指示了你应该朝哪个方向移动以最快地增加函数值。
很自然地,取这个向量的负方向就得到了最快降低函数值的方向。此外,梯度向量的长度表示这个最陡峭斜坡的坡度有多陡。

对于那些不熟悉多变量微积分的人来说,可以查看我在可汗学院(Khan Academy)(https://www.khanacademy.org/math/multivariable-calculus)关于这个主题的一些工作。不过说真的?对于我们目前的需求而言,重要的是在理论上存在一种方法可以计算这个向量,它既能告诉你“下坡”的方向,也能告诉你坡度有多陡。即使你对细节不太清楚,也没关系。
因此,最小化这个函数的算法是计算这个梯度方向,沿着下坡方向迈出一步,然后重复这个过程。这个过程被称为“梯度下降”。

记住,我们的损失函数是专门设计用来衡量网络在分类训练示例上的糟糕程度。因此,调整权重和偏置以降低损失将提升网络的性能!
在实践中,每一步都将表现为

,其中常数 η 被称为学习率。学习率越大,你的步长就越大,这意味着你可能更快地接近最小值,但也存在越过最小值并在其周围大幅震荡的风险。
对于一个有13,002个输入的函数,其基本思想与两个输入的函数相同。我仍然展示一个有两个输入的函数的图像,因为对一个13,002维的输入进行微调很难直观理解。但让我给你一个非空间的方式来思考它。
想象将整个网络的所有权重(weights)和偏置(biases)组织成一个包含13,002个条目的大型列向量。此时,损失函数的负梯度(the negative gradient)也将是一个包含13,002个条目的向量。

负梯度(negative gradient)是这个极其高维的输入空间中的一个向量方向,它告诉你对这13,002个数字进行哪些微调可以导致损失函数最快地下降。
负梯度向量的每个分量告诉我们两件事。首先,符号当然告诉我们输入向量的对应分量应该向上还是向下调整。但更重要的是,这个梯度中各分量的相对大小告诉我们哪些变化更重要。

在我们的网络中,调整一个权重可能比调整其他某些权重对损失函数的影响更大。请看下图,并假设网络应该将其输入分类为“3”。

在我们的网络中,某些连接比其他连接对训练数据的影响更大。你可以将我们庞大的损失函数的梯度理解为,它编码了每个权重和偏置的相对重要性。也就是说,哪些变化能带来最大的效益。
因此,如果损失函数是原始神经网络函数之上的一个复杂层,那么它的梯度则是另一个复杂层,告诉我们对所有这些权重和偏置进行哪些微调可以最快地改变损失函数的值。

我主要想让你理解的是,当我们提到网络“学习”时,我们指的是通过调整权重和偏置来最小化损失函数,从而提高网络在训练数据上的性能。
翻译自:https://www.3blue1brown.com/lessons/gradient-descent