与我交流
与我交流

# 2.2 model.fit()内部:剖析例 1 中的梯度下降

# 2.2.1 梯度下降的优化

在上一节中,我们建立了一个简单的模型并将其拟合到一些训练数据中,在给定文件大小的情况下,我们可以做出合理准确的下载时间预测。它不是最令人印象深刻的神经网络,但其工作原理与我们将要构建的大型,复杂得多的系统完全相同。我们发现将其拟合 10 次并不是很好,但是拟合 200 次就产生了较好的模型[40]。让我们更详细地了解模型训练下到底发生了什么。

output = kernel * input + bias

最初,这些赋值为较小的随机值(称为随机初始化)。当然,当 kernel 和 bias 是随机的时,kernel * input + bias不会产生任何有用的信息。利用我们的想象力,根据这些参数的变化,我们可以描绘出平均绝对误差的值。当它们逼近我们在图 2.4 中看到的直线的斜率和截距时,损耗将变小,反之,损耗将变得更糟。所有可调参数函数的损耗称为损耗面。

# 图 2.5 损失表面以等高线图形式显示了针对模型的可调参数显示的损失。通过这种鸟瞰图,我们可以看到对于低损耗{bias:0.08,kernel:0.07}(标有白色“ X”)是一种合理的选择。我们很难测试所有不同的参数来构建这样的地图,但是可以这样做的话,优化将非常容易。只需选择与最低损耗相对应的参数即可!
figure2.5

这是一个很小的示例,并且我们只有两个可调参数和一个目标值,因此可以将损耗表面显示为 2D 等高线图,如图 2.5 所示。该损失表面具有良好的碗形,碗的底部的全局最小值表示最佳配置参数。但是,总的来说,深度学习模型的损失面要比这种模型复杂得多。它将具有两个以上的维度,并且可能具有许多局部最小值,即,点低于附近的任何点,但不是最低的。

我们看到该损失表面的形状像一个碗,其最佳值(最低值)在{bias:0.08,kernel:0.07}附近。这符合我们的数据形成的直线,即使文件大小接近零,下载时间也约为 0.10 秒。我们模型的随机初始化从一个随机参数开始,类似于该图中的随机位置,从中我们可以计算出初始损失。接下来,我们根据反馈信号逐渐调整参数。这种逐渐的调整,也称为训练,是“机器学习”中的“学习”。这是在训练循环中发生的,如下图 2.6 所示。

# 图 2.6 此流程图描述了该训练循环通过梯度下降来更新模型。
figure2.6
  1. 绘制一批训练样本x和相应的目标y_true。 “批处理”只是将许多输入示例作为张量组合在一起。批处理中示例的数量称为“批处理大小”。在实际深度学习中,通常将其设置为2的幂,例如128和256。将示例分批处理,以利用GPU的并行处理能力并使梯度的计算值更稳定(有关详细信息,请参见下面的2.2.2节)。

  2. 在 x 上运行网络(称为正向传递的步骤),以获得预测 y_pred。

  3. 计算批次上的网络损失,以衡量 y_true 和 y_pred 之间的差距。回想一下,当调用 model.compile()时指定了损失函数。

  4. 慢慢减少批次损失的方式更新网络中的系数(参数)。各个系数的详细更新由优化器管理,这是我们在 model.compile()调用期间指定的另一个选项。 如果您可以在每一步上减少损失,那么最终您将获得一个损失较少的网络。网络“学习”后将其输入映射到正确的结果。看起来像魔术,但是当简化为这些基本步骤时,它被证明很简单。

唯一困难的部分是步骤 4,如何确定应增加的系数,应减少的系数以及降低多少?我们可以简单地猜测和测试,仅接受实际上损失减少的更新。这样的算法可能适用于像这样的简单问题,速度非常慢。对于较大的问题,当我们优化数百万个系数时,便变得很难。更好的方法是利用网络中使用的所有操作都是可微的这一事实,根据网络参数来计算损耗的梯度。

什么是“梯度”?除了可以精确地定义它之外,我们还可以将其直观地描述如下: “一个方向,如果将系数在该方向上移动一点,您将在所有可能的方向中最快地增加损失函数。” 即使此定义不是太过严谨的定义,但仍有很多需要解开的内容,因此让我们尝试将其分解。

  • 首先,梯度是一个向量。它具有与系数相同数量的元素。它代表系数值空间中的一个方向。如果模型的系数由两个数字组成(如我们的简单线性回归网络中的情况),则梯度为 2D 向量。深度学习模型通常具有数千或数百万个维度,并且这些模型的梯度是具有数千或数百万个元素的向量(方向)。
  • 其次,梯度取决于当前的系数值。换句话说,不同的系数值将产生不同的梯度。从图 2.5 可以清楚地看出,下降最快的方向取决于您在损耗面上的位置。此位置在左边,我们必须向右走。在底部附近,我们必须向上,依此类推。
  • 最后,梯度的数学定义指定了损失函数沿其增加的方向。当然,在训练神经网络时,我们希望减少损失。这就是为什么我们必须沿与梯度相反的方向移动系数的原因。

打个比方,在山脉中远足。想象一下,我们希望旅行到最低海拔的地方。以此类推,我们可以通过沿东西轴和南北轴定义的任何方向移动来更改高度。我们应该将上面的第一个要点解释为,鉴于脚下的坡度,我们的海拔梯度是最陡峭的向上方向。第二点很明显,指出最陡峭的向上方向取决于我们当前的位置。最后,如果我们想降到低海拔,我们应该朝着与梯度相反的方向走。

该训练过程恰当地称为梯度下降。还记得清单 2.4 中用配置优化器指定模型优化器:“ sgd” 吗?现在应该清楚随机梯度下降的梯度下降部分。“随机”部分仅表示我们在每个梯度下降步骤中从训练数据中抽取随机样本以提高效率,而不是在每个步骤中都使用每个训练数据样本。随机梯度下降是对计算效率的梯度下降的简单修改。

现在,我们有了工具来更完整地说明优化的工作原理,以及为什么 200 次训练后的下载时间估算模型好于 10 次。图 2.7 说明了梯度下降算法如何沿着我们的损失表面向下的路径找到适合我们训练数据的系数配置。图 2.7A 中的等高线图显示了与以前相同的损耗面,只是将其放大了一点,这里覆盖了路径,便是梯度下降算法。路径从随机初始化开始;即图像上的随机位置。因为我们事先不知道最佳选择,所以我们必须随机选择一个地方开始!沿路径调用了其他几个兴趣点,这些位置说明了与欠拟合和良好拟合模型相对应的位置。图 2.7B 展示了模型损失随步长变化的曲线图。图 2.7C 说明了使用相应系数配置所拟合的模型。

简单的线性回归模型是整本书中唯一的模型,我们可以奢侈地生动地形象地看到梯度下降过程。当我们稍后遇到更复杂的模型时,请记住,梯度下降的本质保持不变:只是迭代地降低复杂的高维曲面的坡度,希望我们最终能在损耗非常低的地方结束。

# 图 2.7 A. 使用梯度下降采取 200 次适中的步骤将参数设置引导到局部最优位置。注释突出显示了 20、100 和 200 次训练后的初始系数值。B.损失随时间变化的图,突出显示了对应的损失。C. 在训练 10、20、100 和 200 次后,由拟合模型体现的 sizeMB 与 timeSec 的函数。在此重复此操作,以使读者可以轻松比较损失表面位置和模型输出。请参阅 codepen.io/tfjs-book/pen/JmerMM 以使用此代码。
figure2.7

在最初的工作中,我们使用了默认的步长大小(由默认的学习率决定),但是在仅循环训练 10 次有限数据时,没有足够的步长来达到最佳值。200 步就足够了。通常,如何设置训练次数?有一些有用的经验法则,我们将在本书的学习过程中进行介绍,但是没有一成不变的法则。如果我们训练次数太多,那么我们将无法在合理的时间内达到最佳参数。相反,如果我们使用太少的训练,我们将跳过最小值,甚至有可能比我们开始的地方遭受更大的损失。我们模型的参数需要在最优值附近剧烈波动,而不是直接采用快速方法。图 2.8 说明了当我们的梯度步幅太大时会发生什么。在更极端的情况下,这将导致参数值发散并变为 Infinity ,这反过来又会在系数中生成 NaN (非数字)值,从而完全破坏了模型。

# 图 2.8 学习率过高时,梯度步长会过大,新参数可能会比旧参数差。这可能会导致振荡行为或某些其他不稳定性,从而导致不确定性或 NaN。您可以尝试将 CodePen 代码中的学习率提高到 0.5 或更高,以查看此行为。
figure2.8

# 2.2.2 反向传播:内部梯度下降

上面,我们解释了更新系数的步长大小如何影响梯度下降的过程。但是,我们尚未讨论如何计算更新方向。方向对于神经网络的学习过程至关重要。它们由相对于系数的梯度确定,并且用于计算梯度的算法称为反向传播。反向传播技术是 1960 年代发明的,是神经网络和深度学习的基础之一。在本节中,我们将使用一个简单的示例来说明反向传播的工作原理。请注意,本节适用于希望了解反向传播的读者。对于只希望通过 TensorFlow.js 应用该算法的读者来说,这是不必要的,因为这些机制都很好地隐藏在 tf.Model.fit()API 下。这些读者可以跳过本节,继续阅读第 2.3 节。

简单的线性模型y’ = v * x。 其中 x 是输入要素,y'是预测输出。v 是反向传播期间要更新的模型的唯一系数参数。假设我们使用平方误差作为损失函数,那么 loss,v,x 和 y(实际目标值)之间具有以下关系: loss = square(y’ - y) = square(v * x - y) 让我们假设以下具体值:两个输入分别为 x = 2 和 y = 5 ,系数值为 v = 0 。然后可以将损失计算为 25。这在下图中逐步显示。图 2.9 面板 A 中的每个灰色方块代表一个输入(即 x 和 y )。每个白框都是一个操作。共有三个操作。连接操作的边缘(以及将可调系数 v 与第一个操作连接的边缘)标记为 e1 ,e2 和 e3 。

# 图 2.9 通过仅具有一个可更新系数(v )的简单线性模型说明了反向传播算法。A :模型的正向传递:损失值是根据系数(v )和输入(x 和 y )计算的。B :反向传递,从 loss 到 v 逐步计算相对于 v 的损耗梯度。
figure2.9

反向传播的重要步骤: “假设其他所有内容(在这种情况下,即 x 和 y)保持不变,则如果 v 增加单位数量,我们将获得多少损失值变化”该量称为“相对于 v 的损耗梯度”。为什么我们需要这个梯度?因为一旦有了它,我们就可以在与其相反的方向上改变 v ,这样我们就可以减少损失值。注意,我们不需要相对于 x 或 y 的损耗梯度,因为 x 和 y 不需要更新:它们是输入数据,并且是固定的。

该梯度是从损耗值开始逐步返回变量 v 的,逐步进行计算,如上面面板 B 的图 2.9 所示。进行计算的方向就是将该算法称为“反向传播” 的原因。让我们逐步进行操作。下面讨论的每个步骤都对应图中的粗箭头。

  • 在标记为损失的边缘处,我们从 1 的梯度值开始。这很简单:“ 损失的增加对应于损失本身的单位增加”。

  • 在标记为 e3 的边缘,我们计算相对于 e3 的损耗梯度。因为该运算是一个平方,并且根据基本演算,我们知道 x 2 相对于 x 的导数(即,单变量情况下的梯度)为 2 _ x ,我们得到的梯度值为 2 _ -5 = -10 。将值-10 与之前(即 1)的梯度相乘,以获得边缘 e3 :-10 上的梯度。这是损失的增加量,如果将 e3 的值增加 1。正如您可能已经观察到的,我们使用的规则是从损失相对于一条边的梯度到下一条边的一个方面,即前一个梯度与当前节点上计算的梯度相乘。该规则有时称为链式规则。

  • 在边缘 e2 处,我们计算 e3 相对于 e2 的梯度。由于这是一个简单的加法运算,因此无论 y 或-y 的值如何,梯度都仅为 1。将此 1 与边缘 e3 上的梯度相乘,我们得到边缘 e2 上的梯度,即-10。

  • 在边缘 e1 处,我们计算 e2 相对于 e1 的梯度。这里的运算是 x 与 v 之间的乘积,即 x _ v 。因此,相对于 e1 (即相对于 v )的 e2 的梯度为 x ,即 2。将 2 的值与边缘 e2 上的梯度相乘得出最终梯度:2 _ -10 = -20 。

到目前为止,我们已经获得了相对于 v 的损耗梯度,即-20。为了应用梯度下降,我们需要将该梯度的负值与学习率相乘。假设学习率为 0.01。然后我们得到一个梯度更新:

-(-20) * 0.01 = 0.2

这是我们将在此步骤中应用于 v 的更新:
v = 0 + 0.2 = 0.2

如您所见,因为我们有 x = 2 和 y = 5,并且要拟合的函数是 y'= v * x,所以 v 的最佳值为 5/2 = 2.5。经过一步训练,v 的值从 0 变为 0.2。换句话说,系数 v 稍微接近所需值。在随后的训练步骤(忽略训练数据中的任何噪声)下,它将越来越近,这将基于与上述相同的反向传播算法。

上面的示例有意简化了,因此很容易理解。即使该示例捕获了反向传播的本质,但在实际的神经网络训练中发生的反向传播在以下方面与它有所不同:

  1. 除了提供简单的训练示例(在本例中为 x = 2 和 y = 5 )之外,通常会同时提供许多输入示例的批处理。所有单个示例的损耗值的算术平均值得出梯度的损耗值。
  2. 通常,需要更新更多元素。因此,会涉及矩阵演算,而不是像上面那样做简单的一元导数。
  3. 不只计算一个变量的梯度,而是通常涉及多个变量。下图(图 2.10)显示了一个示例,该示例用于稍微复杂的线性模型,其中有两个要优化的变量。除了 k 之外,模型还有一个偏差项:y'= k * x + b 。这里有两个要计算的梯度,一个用于 k,一个用于 b。反向传播的两条路径都从损失开始。它们共享一些共同的边缘,并形成树状结构。 数字。
# 图 2.10 显示了从 loss 到两个可更新的系数(k 和 b)的反向传播。
figure2.10

希望对反向传播的数学和算法有更深入了解的读者可以参考信息框2.2中的链接。

信息框 2.2

有关梯度下降和反向传播的进一步阅读

优化神经网络背后的微分运算肯定很有趣,并且可以洞悉这些算法的运行方式,但是除了基础知识外,对于机器学习从业人员也绝对不是必需条件,就像理解 TCP / IP 协议的复杂性一样,但对于了解如何构建 Web 应用程序而言并非至关重要。我们邀请好奇的读者探索以下出色的资源,以加深对网络中基于梯度的优化数学的理解。

反向传播演示滚动讲解插图https://google-developers.appspot.com/machine-learning/crash-course/backprop-scroll/

斯坦福 CS231 讲座 4-反向传播的课程讲义 http://cs231n.github.io/optimization-2/

安德烈· 卡帕蒂(Andrej Karpathy)的《哈克神经网络指南》http://karpathy.github.io/neuralnets/

在这一点上,您应该对从训练数据中拟合简单模型过程中会发生的事情有一个很好的了解,因此,让我们从解决微小的下载时间预测问题,延伸到使用 TensorFlow.js 解决一些更具挑战性的事情。在下一部分中,我们将构建一个模型,从多个输入中准确预测房地产价格。

上次更新: 11/15/2020, 1:05:56 PM