《深度学习入门》的cpp实现-ch06: 与学习相关的技巧

上一篇文章中,我们使用随机正态分布初始化神经网络的权重,SGD的方法更新参数,对于学习率、batch大小、权重衰减率等超参数选择了固定值,这篇文章我们会探讨对于这些做法的改进方案。
由于这一节涉及到众多方法, 并且要求网络结构复杂,不能逐一进行"cpp实现",理解原理并掌握即可(所以请把这篇文章当作读书笔记看待)。
参数的更新
SGD
SGD公式如下 $$W \gets W - \eta \frac{\partial L}{\partial W} $$
SGD原理简单,并且在代码上容易实现,但是针对某些参数空间, 解决问题的效率比较低下,比方说对于 $$f(x,y) = \frac{1}{20}x^2 + y^2$$

梯度特征是,在y轴方向上很大,在x轴方向上很小。虽然在(x,y)=(0,0)处梯度最小,但是很多地方梯度并没有指向(0, 0)。
对于这种函数形状非均向(anisotropic),比如呈延伸状,搜索的路径就会非常低效,根本原因是梯度的方向并没有指向最小值的方向。

Momentum
Momentum是“动量”的意思,公式如下
$$v \gets \alpha v - \eta \frac{\partial L}{\partial W}$$
$$W \gets W + v$$
η 表示学习率; v对应物理上的速度; αv 在物体不受任何力时,该项承担使物体逐渐减速的任务,对应物理上的地面摩擦或空气阻力。
在 Momentum 公式中,W 保存了过往梯度的矢量和。如果在一个方向上,梯度方向变化反复,那么经过多次,在一个方向上的力会被抵消;如果在一个方向上,梯度很小,但是持续不变,那么经过多次,在这个方向上的力就会有一个“加速度”。

和 SGD 相比,我们发现“之”字形的“程度”减轻了。这是因为虽然 x 轴方向上受到的力非常小,但是一直在同一方向上受力,所以朝同一个方向会有一定的加速。反过来,虽然 y 轴方向上受到的力很大,但是因为交互地受到正方向和反方向的力,它 们会互相抵消,所以 y 轴方向上的速度不稳定。
AdaGrad
在神经网络的学习中,学习率过小, 会导致学习花费过多时间;学习率过大,则会导致学习发散而不能正确进行。
AdaGrad 会为参数的每个元素适当地调整学习率,与此同时进行学习 (AdaGrad 的 Ada 来自英文单词 Adaptive,即“适当的”的意思)。
$$h \gets h + \frac{\partial L}{\partial W} \odot \frac{\partial L}{\partial W} $$ $$ W \gets W - \eta \frac{1}{\sqrt{h}} \frac{\partial L}{\partial W} $$
在更新参数时,通过乘以
1/sqrt(h),就可以调整学习的尺度。这意味着,参数的元素中变动较大(被大幅更新)的元素的学习率将变小。也就是说,可以按参数的元素进行学习率衰减,使变动大的参数的学习率逐渐减小。

Adam
Adam 是 2015 年提出的新方法。它的理论有些复杂,直观地讲,就是融合了 Momentum 和 AdaGrad 的方法。


权重初始值
随机的初始化值
当深度学习网络发生过拟合时,是由于权重参数过大导致的。如果要抑制过拟合,在设定权重初始值的时候就选择比较小的值。
那么如果将权重初始值设定为0,这并不是一个好主意。将权重参数设置为0,或者设置为一样的值,会导致后面层神经元会在前向推理时传递相同的值,意味着在反向传播的时候,所有权重参数会进行相同的更新。这使得神经网络拥有许多不同的权重的意义丧失了。为了防止“权重均一化”(严格地讲,是为了瓦解权重的对称结构),必须随机生成初始值。
隐藏层的激活值的分布
- 当使用第一层网络权重标准差为1的高斯分布

这里使用激活函数sigmoid函数是S型函数,随着输出不断地靠近0(或者靠近1),它的导数的值逐渐接近0。因此,偏向0和1的数据分布会造成反向传播中梯度的值不断变小,最后消失。这个问题称为梯度消失(gradient vanishing)。
- 将权重的标准差设为 0.01,进行相同的实验

这次的激活值分布有所偏向,说明多个神经元会输出近似相同的值,有些神经元的存在是没有意义的,整个神经网络会表现力受限
** Xavier 初始值 在 Xavier Glorot 的论文中, 为了使各层的激活值呈现出具有相同广度的分布, 提出前一层的节点数越多,要设定为目标节点的初始值的权重尺度就越小。
如果前一层的节点数为 n,则初始值使用标准差为1/sqrt(n)的高斯分布

** He 初始值
当激活函数使用 ReLU 时,一般推荐使用 ReLU 专用的初始值,也被称为"He初始值"。
当前一层的节点数为 n 时,He 初始值使用标准差为sqrt(2/n)的高斯分布。和 Xavier初始值相比,因为 ReLU 的负值区域的值 为 0,为了使它更有广度,所以需要 2 倍的系数。
Batch Normalization
Batch Norm的思路是调整各层的激活值分布使其拥有适当广度,有以下优点:
- 可以使学习快速进行(可以增大学习率)。
- 不那么依赖初始值(对于初始值不用那么神经质)。
- 抑制过拟合(降低Dropout等的必要性)。

正则化
过拟合
发生过拟合的原因,主要有以下两个:
- 模型拥有大量参数、表现力强。
- 训练数据少。
权值衰减
很多过拟合原本就是 因为权重参数取值过大才发生的。权值衰减通过在学习的过程中对大的权重进行惩罚,来抑制过拟合。为损失函数加上权重的平方范数(L2范数)。这样一来,就可以抑制权重变大。
Dropout
Dropout 是一种在学习的过程中随机删除神经元的方法。训练时,随机选出隐藏层的神经元,然后将其删除。被删除的神经元不再进行信号的传递。
训练时,每传递一次数据,就会随机选择要删除的神经元。然后,测试时,虽然会传递所有的神经元信号,但是对于各个神经元的输出,要乘上训练时的删除比例后再输出

class Dropout:
def __init__(self, dropout_ratio=0.5):
self.dropout_ratio = dropout_ratio
self.mask = None
def forward(self, x, train_flg=True):
if train_flg:
# 训练时, 随机选择神经元, 并且将选择神经元序号保存
self.mask = np.random.rand(*x.shape) > self.dropout_ratio
return x * self.mask
else:
# 推理时,将每个值乘以保留系数
return x * (1.0 - self.dropout_ratio)
def backward(self, dout):
# 反向传播时,使用mask选择保留的神经元进行
return dout * self.mask
可以将 Dropout 理解为,通过在学习过程中随机删除神经元,从而每一次都让不同的模型进行学习。
##超参数的验证
调整超参数时,必须使用超参数专用的确认数据。用于调整超参 数的数据,一般称为验证数据(validation data)
超参数的范围只要“大致地指定”就可以了。所谓“大致地指定”,是指像0.001到1000这样,以“10的阶乘”的尺度指定范围(也表述为“用对数尺度(logscale)指定”)。




