关于神经网络的一些技巧和经验

前言

神经网络在训练过程中存在太多的技巧,以至于通常很难复现论文中的效果,我在学习的过程中做了不少的实践,也就是所谓的炼丹,龙虎胎息,吐故纳新,贫道在此记录一些炼丹经验。

数据预处理

在监督学习中,数据预处理能一定程度的提升泛化能力,这个环节很重要,不过也需要根据实际情况做处理,就不多说了。

模型的改动

  • 结构的变动

    如果遇到提升网络性能的时候瓶颈,发现模型表达能力弱,那么可以根据特征输出来改动网络结构,尽量的减少特征信息的丢失,比如卷积网络feature map下采样的时候就容易丢失特征信息。

  • batch norm层的使用

    卷积网络中添加batchnorm层重新规范特征的分布,也保证了梯度的传播。

  • 正则化

    较为重要但有没什么好说的,约束参数值是目前提升泛化的最好办法。

高效的训练

  • 大batch训练

    当有足够的显存时,我们都会增加batch size来加速训练,但是batch size的选取还是很讲究的,总结如下:

    • GPU对2的幂次的batch可以发挥更佳的性能,设置成32、64、128…时往往要比设置为整10倍数时表现更优。
    • batch size的正确选择是为了在内存效率和内存容量之间寻找最佳平衡
    • batch数太小,梯度容易受噪声影响,或者相邻两次差异过大,可能会导致loss函数震荡而不收敛。
    • 随着batch size增大,处理相同的数据量的速度加快,震荡会减小,有利于收敛但慢,达到相同精度所需要的epoch数量越来越多。
    • batch size过大时,相邻间差异太小,容易陷入局部最小。
  • 优化器选择

    • 动量(momentum)

      动量借助了物理思想,在物理学上定义为质量乘以速度。想象一下在碗里滚动一个球,不会在底部停止,受惯性影响。使用动量的随机梯度下降(SGD)也就是加了一个速度项的超参数,这个参数乘上次的移动量,影响着本次的移动量改变。

      \[ \begin{array}{l} v \leftarrow \alpha v-\epsilon \nabla_{\theta}\left(\frac{1}{m} \sum_{i=1}^{m} L\left(f\left(x^{(i)} ; \theta\right), y^{(i)}\right)\right) \\ \theta \leftarrow \theta+v \end{array} \]

      • 动量移动得更快
      • 动量有机会逃脱局部极小值
      • 代价是引入了另一个超参数
    • AdaGrad(Adaptive Gradient)

      AdaGrad 不是像动量一样跟踪梯度之和,而是跟踪梯度平方之和,并使用这种方法在不同的方向上调整梯度

      • 在参数空间中更为平缓的倾斜方向会取得更大的进步
      • 梯度的平方和只会增加,会导致有效学习率过早和过量的减小
    • RMSProp(Root Mean Square Propagation)

      RMSProp 算法修改AdaGrad,改变梯度积累为指数加权的移动平均,也可以理解为添加衰减因子来控制梯度积累的大小,保证学习率在一个可控范围。

    • Adam(Adaptive Moment Estimation)

      Adam 同时兼顾了动量和 RMSProp 的优点,也就是动量直接并入了梯度一阶矩(指数加权)的估计。

    根据我的经验,Adam比较快,但是泛化能力不会太好,momentum较为慢,但是结合余弦衰减的学习率,收敛效果极好。

    炼丹需要三味真火,但水很深啊,我总觉得还需要多做些实验增加些理解,关于优化器和学习率的选择还有待探索,之后再补充。

  • 学习率问题

    • 针对batch的变化,线性增加学习率

      通常增大batch size会降低收敛率,那就需要在学习率上做些改动,根据经验batch size增加多少倍,学习率也增加多少倍。

    • 学习率热身

      如果是随机初始化的参数,一开始较大的学习率会导致不稳定,所以可以在最开始使用一个很小的学习率,然后逐步切换到默认的初始学习率。

    • 动态学习率

      我比较喜欢的是Cosine Learning Rate Decay的方法,学习率随着时间按余弦曲线变化至零,虽然没有根据情况自定义学习率那么高效,但是很省心,就基本上不用考虑学习率问题。