神经网络的“引擎”:基于梯度的优化

文章目录

      • 神经网络的训练过程
      • 随机梯度下降
      • 链式求导:反向传播算法

神经网络的训练过程

这是一个全连接层的定义:

network.add(layers.Dense(512, activation='relu', input_shape=(28 * 28,)))

它使用下述方法对输入数据进行变换:

output = relu(dot(W, input) + b)

在这个表达式中,W 和b 都是张量,均为该层的属性。它们被称为该层的权重(weight)或可训练参数(trainable parameter),分别对应kernel 和bias 属性。这些权重包含网络从观察训练数据中学到的信息。

一开始,这些权重矩阵取较小的随机值,这一步叫作随机初始化(random initialization)。当然,W 和b 都是随机的,relu(dot(W, input) + b) 肯定不会得到任何有用的表示。虽然得到的表示是没有意义的,但这是一个起点。下一步则是根据反馈信号逐渐调节这些权重。这个逐渐调节的过程叫作训练,也就是机器学习中的学习。

上述过程发生在一个训练循环(training loop)内,其具体过程如下。必要时一直重复这些步骤。

  • 抽取训练样本x 和对应目标y 组成的数据批量。
  • 在x 上运行网络[这一步叫作前向传播(forward pass)],得到预测值y_pred
  • 计算网络在这批数据上的损失,用于衡量y_predy 之间的距离。
  • 更新网络的所有权重,使网络在这批数据上的损失略微下降。

最终得到的网络在训练数据上的损失非常小,即预测值y_pred 和预期目标y 之间的距离非常小。网络“学会”将输入映射到正确的目标。第一步看起来非常简单,只是输入/ 输出(I/O)的代码。第二步和第三步仅仅是一些张量
运算的应用。难点在于第四步:更新网络的权重。

  • 一种简单的解决方案是,保持网络中其他权重不变,只考虑某个标量系数,让其尝试不同的取值。假设这个系数的初始值为0.3。对一批数据做完前向传播后,网络在这批数据上的损失是0.5。如果你将这个系数的值改为0.35 并重新运行前向传播,损失会增大到0.6。但如果你将这个系数减小到0.25,损失会减小到0.4。在这个例子中,将这个系数减小0.05 似乎有助于使损失最小化。对于网络中的所有系数都要重复这一过程。但这种方法是非常低效的,因为对每个系数(系数很多,通常有上千个,有时甚至多达上百万个)都需要计算两次前向传播(计算代价很大)。
  • 一种更好的方法是利用网络中所有运算都是可微(differentiable)的(可以被求导)这一事实,计算损失相对于网络系数的梯度(gradient),然后向梯度的反方向改变系数,从而使损失降低。

随机梯度下降

给定一个可微函数,理论上可以用解析法找到它的最小值:函数的最小值是导数为0 的点,因此你只需找到所有导数为0 的点,然后计算函数在其中哪个点具有最小值。将这一方法应用于神经网络,就是用解析法求出最小损失函数对应的所有权重值。可以通过对方程 g r a d i e n t ( f ) ( W ) = 0 gradient(f)(W) = 0 gradient(f)(W)=0 求解 W W W 来实现这一方法。这是包含N 个变量的多项式方程,其中N 是网络中系数的个数。N=2 或N=3 时可以对这样的方程求解,但对于实际的神经网络是无法求解的,因为参数的个数不会少于几千个,而且经常有上千万个。

相反,你可以使用上面总结的四步算法:基于当前在随机数据批量上的损失,一点一点地对参数进行调节。由于处理的是一个可微函数,你可以计算出它的梯度,从而有效地实现第四步。沿着梯度的反方向更新权重,损失每次都会变小一点。

  • 抽取训练样本x 和对应目标y 组成的数据批量。
  • x 上运行网络,得到预测值y_pred
  • 计算网络在这批数据上的损失,用于衡量y_predy之间的距离。
  • 计算损失相对于网络参数的梯度[一次反向传播(backward pass)]。
  • 将参数沿着梯度的反方向移动一点,比如 W − = s t e p ∗ g r a d i e n t W -= step * gradient W=stepgradient,从而使这批数据上的损失减小一点。

上述方法叫作小批量随机梯度下降(mini-batch stochastic gradient descent,又称为小批量SGD)。术语随机(stochastic)是指每批数据都是随机抽取的(stochastic 是random在科学上的同义词)。下图给出了一维的情况,网络只有一个参数,并且只有一个训练样本。

神经网络的“引擎”:基于梯度的优化_第1张图片
直观上来看,为step因子选取合适的值是很重要的。如果取值太小,则沿着曲线的下降需要很多次迭代,而且可能会陷入局部极小点。如果取值太大,则更新权重值之后可能会出现在曲线上完全随机的位置。

小批量SGD 算法的一个变体是每次迭代时只抽取一个样本和目标,而不是抽取一批数据。这叫作真SGD(有别于小批量SGD)。还有另一种极端,每一次迭代都在所有数据上运行,这叫作批量SGD。这样做的话,每次更新都更加准确,但计算代价也高得多。这两个极端之间的有效折中则是选择合理的批量大小。

上图描述的是一维参数空间中的梯度下降,但在实践中需要在高维空间中使用梯度下降。神经网络的每一个权重参数都是空间中的一个自由维度,网络中可能包含数万个甚至上百万个参数维度。为了让你对损失曲面有更直观的认识,你还可以将梯度下降沿着二维损失曲面可视化,如下图所示。但你不可能将神经网络的实际训练过程可视化,因为你无法用人类可以理解的方式来可视化1 000 000 维空间。因此最好记住,在这些低维表示中形成的直觉在实践中不一定总是准确的。这在历史上一直是深度学习研究的问题来源。

神经网络的“引擎”:基于梯度的优化_第2张图片
此外,SGD 还有多种变体,其区别在于计算下一次权重更新时还要考虑上一次权重更新,而不是仅仅考虑当前梯度值,比如带动量的SGDAdagradRMSProp 等变体。这些变体被称为优化方法(optimization method)或优化器(optimizer)。其中动量的概念尤其值得关注,它在许多变体中都有应用。动量解决了SGD 的两个问题:收敛速度和局部极小点。下图给出了损失作为网络参数的函数的曲线。

神经网络的“引擎”:基于梯度的优化_第3张图片
在某个参数值附近,有一个局部极小点(local minimum):在这个点附近,向左移动和向右移动都会导致损失值增大。如果使用小学习率的SGD 进行优化,那么优化过程可能会陷入局部极小点,导致无法找到全局最小点。使用动量方法可以避免这样的问题,这一方法的灵感来源于物理学。有一种有用的思维图像,就是将优化过程想象成一个小球从损失函数曲线上滚下来。如果小球的动量足够大,那么它不会卡在峡谷里,最终会到达全局最小点。动量方法的实现过程是每一步都移动小球,不仅要考虑当前的斜率值(当前的加速度),还要考虑当前的速度(来自于之前的加速度)。这在实践中是指,更新参数w 不仅要考虑当前的梯度值,还要考虑上一次的参数更新,其简单实现如下所示。

past_velocity = 0.
momentum = 0.1 #不变的动量因子
while loss > 0.01: #优化循环
	w, loss, gradient = get_current_parameters()
	velocity = past_velocity * momentum - learning_rate * gradient
	w = w + momentum * velocity - learning_rate * gradient
	past_velocity = velocity
	update_parameter(w)

链式求导:反向传播算法

在前面的算法中,我们假设函数是可微的,因此可以明确计算其导数。在实践中,神经网络函数包含许多连接在一起的张量运算,每个运算都有简单的、已知的导数。例如,下面这个网络 f f f 包含3 个张量运算 a a a b b b c c c,还有3 个权重矩阵 W 1 W1 W1 W 2 W2 W2 W 3 W3 W3 f ( W 1 , W 2 , W 3 ) = a ( W 1 , b ( W 2 , c ( W 3 ) ) ) f(W1, W2, W3) = a(W1, b(W2, c(W3))) f(W1,W2,W3)=a(W1,b(W2,c(W3)))根据微积分的知识,这种函数链可以利用下面这个恒等式进行求导,它称为链式法则(chain rule): ( f ( g ( x ) ) ) ′ = f ′ ( g ( x ) ) ∗ g ′ ( x ) (f(g(x)))' = f'(g(x)) * g'(x) (f(g(x)))=f(g(x))g(x)将链式法则应用于神经网络梯度值的计算,得到的算法叫作反向传播(backpropagation,有时也叫反式微分,reverse-mode differentiation)。

反向传播从最终损失值开始,从最顶层反向作用至最底层,利用链式法则计算每个参数对损失值的贡献大小。
现在以及未来数年,人们将使用能够进行符号微分(symbolic differentiation)的现代框架来实现神经网络,比如TensorFlow。也就是说,给定一个运算链,并且已知每个运算的导数,这些框架就可以利用链式法则来计算这个运算链的梯度函数,将网络参数值映射为梯度值。对于这样的函数,反向传播就简化为调用这个梯度函数。由于符号微分的出现,你无须手动实现反向传播算法。

你可能感兴趣的:(人工智能)