0%

深度学习--part3

在part2中讲到了学习算法,这部分讲一讲和此相关的一些算法和参数相关的设定问题。
神经网络的学习的目的是找到使损失函数的值尽可能小的参数。这是寻找最优参数的问题,解决这个问题的过程称为最优化 ( optimization )。遗憾的是,神经网络的最优化问题非常难。这是因为参数空间非常复杂,无法轻易找到最优解(无法使用那种通过解数学式一下子就求得最小值的方法)。而且,在深度神经网络中,参数的数量非常庞大,导致最优化问题更加复杂。

SGD

在part2中,为了找到最优参数,我们将参数的梯度(导数)作为了线索。使用参数的梯度,沿梯度方向更新参数,并重复这个步骤多次,从而逐渐靠近最优参数,这个过程称为随机梯度下降法 ( stochastic gradient descent ),简称SGD。
令需要更新的权重参数记为$W$,损失函数关于$W$的梯度记为$\frac{\partial L}{\partial W}$,$\eta$为学习率(一般取0.01或0.001这些事先决定好的值) 。用数学式可以将SGD的更新方式写为:

用python实现如下:

1
2
3
4
5
6
7
8
class SGD:
def __init__(self, lr=0.01):
self.lr = lr # 学习率

def update(self, params, grads):
for key in params.keys():
# 参数 params和 grads都是字典型变量,分别保存权重参数和梯度
params[key] -= self.lr * grads[key]

SGD是朝着梯度方向只前进一定距离的简单方法,在学习过程中,会不断的调用它。使用这个SGD类,可以按如下方式进行神经网络的参数的更新:
1
2
3
4
5
6
7
8
9
10
network = TwoLayerNet(...)
optimizer = SGD() # 进行参数优化的方法实体,参数的更新由 optimizer负责完成

for i in range(10000):
...
x_batch, t_batch = get_mini_batch(...) # mini-batch
grads = network.gradient(x_batch, t_batch)
params = network.params
optimizer.update(params, grads)
...

同理,之后的一些学习方法,只需要将optimizer替换为对应的方法实体即可。
SGD虽然实现简单,但是在解决某些问题时会低效,比如如下函数:

函数是向$x$轴方向延伸的“碗”状函数,等高线呈向$x$轴方向延伸的椭圆状:

如果用图表示梯度的话,则如下图所示。这个梯度的特征是,$y$轴方向上大,$x$轴方向上小。换句话说,就是$y$轴方向的坡度大,而$x$轴方向的坡度小。这里需要注意的是,虽然上式最小值在$(x, y) = (0, 0)$处,但是图中的梯度在很多地方并没有指向$(0, 0)$。

由于函数的形状非均向,梯度的方向并没有指向最小值的方向,SGD在搜索时会非常低效。搜索路径会呈“之”字型移动。如下图:

Momentum

Momentum是“动量”的意思,和物理有关。用数学式表示Momentum方法,令需要更新的权重参数记为$\mathit{W}$,损失函数关于$\mathit{W}$的梯度记为$\frac{\partial L}{\partial \mathit{W}}$,$\eta$为学习率,$\mathit{v}$代表物理上的速度如下所示。

上式表示了物体在梯度方向上受力,在这个力的作用下,物体的速度增加这一物理法则。如图所示,Momentum方法给人的感觉就像是小球在地面上滚动。

式中有 $\alpha v$ 这一项。在物体不受任何力时,该项承担使物体逐渐减速的任务($\alpha$设定为0.9之类的值),对应物理上的地面摩擦或空气阻力。
python实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CLass Momentum:
def __init__(self, lr=0.01, momentum=0.9):
self.lr = lr
self.momentum = momentum
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.momentum * self.v[key] - self.lr * grads[key]
params[key] += self.v[key]

更新路径如下图。和SGD相比,我们发现“之”字形的“程度”减轻了。这是因为虽然$x$轴方向上受到的力非常小,但是一直在同一方向上受力,所以朝同一个方向会有一定的加速。反过来,虽然$y$轴方向上受到的力很大,但是因为交互地受到正方向和反方向的力,它们会互相抵消,所以$y$轴方向上的速度不稳定。因此,和SGD时的情形相比,可以更快地朝$x$轴方向靠近,减弱“之”字形的变动程度。