本章介绍一些学习过程中的技术细节。
参数更新
把寻找最优参数的问题看成一个最优化问题。神经网络的最优化问题非常难,因为参数空间非常复杂,参数数量大,无法轻易找到解析解(且这种寻求总体解析解的方法不具有拓展性)。
SGD
为了找到最优参数,我们将参数的梯度作为线索,沿着梯度的反方向更新参数(以学习率为更新步伐)。由于参数的初始值是随机设定的(正态分布的初始值),这个方法称为随机梯度下降法,SGD。
数学式
python代码
class SGD:
def __init__(self, lr=0.01):
self.lr = lr
def update(self, params, grads):
for key in params.keys():
params[key] -= self.lr * grads[key]
缺点
在解决有些问题时,SGD可能没有效率。例如,
在俯视图中等间距地标出梯度,
可见,梯度在方向上的分量非常小,这将导致SGD得花很长时间才能在方向上移动到原点处。如图,
怎么改进这个方法呢?
Momentum
Momentum是“动量”的意思。先来看一下数学式,
数学式
python代码
class Momentum:
def __init__(self, lr=0.01, friction=0.9):
self.lr = lr
self.friction = friction
self.v = None
def update(self, params, grads):
if self.v is None:
self.v = {}
for key, val in params.items():
self.v[key] = np.zeros_like(val)
for key in params.keys():
self.v[key] = self.friction * self.v[key] - self.lr * grads[key]
params[key] += self.v[key]
物理意义
可见,矩阵的引入赋予了参数更新一定的惯性。整个过程好像许多初始位置不同的台球在有摩擦有凹陷的台球桌上的运动,可以想象,最后台球都会静止在某个局部极小值处。
由于方向的向心速度可以叠加,小球更快地到达了原点。
权重的初始值
不能把权重都设为零
首先,把权重都设为零是有动因的:权重过大容易发生过拟合。某些权重过大,相当于把输入数据中的某些细节看得过于重要,但一般来说,如果待识别对象真的有那么明显、绝对、单一的特征,我们就可以利用传统算法解决而不必求助于深度学习了。所以,把初始权重都设置为零是不是一个好主意呢?
不是的。举一个具体的例子,我们使用仿射变换层叠加sigmoid再叠加仿射变换层再叠加sigmoid…的网络结构。如果把权重都设为零,则导致第一仿射层输出全为零,紧接着的sigmoid层输出全为0.5,后续各层情况依此类推。问题出现了,由于同一层中的神经元是完全一样的,所以它们对loss的影响(损失函数对这些地位一样的参数的导数)是一样的,而在这种情况下它们的输入又都是一样的,所以导数是一样的。这样,这些权重在后续的学习(反向传播更新参数)过程中只能同步变化——这可不妙,它们表现得和一个神经元一样,严重降低了神经网络的表现力(在这个极端的例子中,变得毫无表现力)。
隐藏层的激活值分布
那么我们该怎样设置权重的初始值呢?一个合理的想法是,将权重设置为在一个小范围内的正态分布。用随机取输入数据做实验发现,如果权重分布的范围过大,激活值(激活函数的输出,下一层的输入)会向0, 1集中。如图,
造成反向传播中梯度的值(随着层数加深)不断变小,最后消失。这个问题称为梯度消失(gradient vanishing)。
相反,如果过于初始权重过于集中,激活值也会集中(在0.5附近),导致大量参数拥有同样的梯度,降低网络表现力。
Xavier证明,初始值使用标准差为
的分布最好。