神经网络的驱动器(引擎):基于梯度的优化
如前所见,我们首个神经网络的各层这样来转化数据:
output = relu(dot(W, input) + b)
在此, W 和 b 是张量,是层的属性。它们被叫做层的权重或者可训练的参数。权重 W 是核心属性,b 是偏置属性。权重包含的信息,用于网络训练数据的学习。
起初,权重矩阵填充的是些微小的随机数,叫做随机初始化。当然,在 W 和 b 是随机数时,不能指望这时的 relu( dot( W, input ) + b ) 产生有用的东西。所产生的只是训练的起点。接下来,根据反馈信号,逐渐调整权重。这一逐渐调整的过程,叫做训练。训练,正是机器学习的基本内容。
训练是个反复循环的过程,按以下步骤进行:
- 输入一批训练样本 x 和相对应的目标 y。
- 针对 x 运行网络(这一步叫做“前进”),以获得预测目标 y_pred。
- 测算 y_pred 和 y 之间的差值,算出这批数据的网络损失。
- 更新网络权重,使得这批数据的网络损失有所减小。
最终得到的是训练数据损失很小的网络,预测的目标 y_pred 和预期的目标 y 之间的差值很小。这时的网络已经“学会”把输入数据,与正确的目标关联起来。
上面的第1、2、3步足够简单。难点是第4步,更新网络权重。如果给出单独的权重系数,你如何计算它是变大还是变小,并且变化值是多少?
网络的各种运算操作可以微分化。据此,解决上述问题比较好的办法,是对网络系数相关损失的梯度变化进行运算。你可以往梯度变化相反方向改变系数值,以此减少损失。
张量的导数:梯度
梯度是张量运算的导数。若函数的输入是多维张量,则函数导数概念的泛化产生出梯度概念。
设想有输入的向量 x,矩阵 W,目标 y 和损失函数。
你可用 W 计算预测目标 y_pred ,计算 y_pred 与期待目标 y 之间的差值(损失):
y_pred = dot(W, x)
loss_value = loss(y_pred, y)
如果输入的 x 和 y 固定不变,可把“损失”看作 W 函数的映射:
loss_value = f(W)
假设 W 的当前值是 W0,则 W0 的函数 f 的导数是张量的梯度 ( f )( W0 ) 。
W 与 W0 形状相同。
每个系数的梯度 ( f )( W0 )[ i, j ] 表示 loss_value 改变的大小和方向。当你改变 W0 [ i, j ] 时,可以看到这些。
张量的梯度 ( f )( W0 ) 是当 W = W0 时函数 f( W ) 的梯度。
如前所见,单个系数的函数 f( x) 的导数,可看作曲线 f 的斜率。与此相似,可把梯度 ( f )( W0 ) 看作函数 f( W ) 在W0处的曲率。
随机梯度下降
可导函数,理论上能分析发现它的极小值,导数为 0 的那个点位。因此,你必须找到导数趋于 0 的那些点位,并确定其中那一个的函数值最小。
这一理论用于神经网络,意味着从若干权重的组合中,发现最小的损失函数。梯度方程式 ( f )( W ) = 0 可解此题。
梯度方程式,是 N 个变量构成的多项式。N 是网络系数的个数。
当 N = 2 或 N = 3 时,或可使用梯度方程式;但实际的神经网络参数从不少于数千个,甚至经常达到几千万个,梯度方程式难以招架对付。
你可以使用本节开头的“四步算法”:根据随机批量数据当前产生的误差,一点一点地修改参数。因为你所用的是可微分函数,可以计算它的梯度,以较高效率实现第 4 步算法。如果你朝着梯度相反方向更新权重,损失会一点点地逐次减少:
- 输入一批训练样本 x 及其相应的目标 y 。
- 针对 x 运行网络,以得到预期值 y_pred 。
- 算出这批数据的损失值,即 y_pred 和 y 之间的差距。
- 算出网络反向传播的参数的损失梯度。
- 朝着梯度相反方向少量移动参数值。例如, W -= step * gradient ,把损失减少一点。
在此,step 合理取值很重要。如果它的值太小,迭代次数太多,甚至因某个极小值卡住。如果它的值太大,也不能达到减少损失的目的。