(续我的上一篇博客)最早训练神经网络的时候完全什么技巧都不懂,能成功运行开源代码,并且看到loss下降就放心跑着了。随着对网络越来越多的接触,发现从数据集(train/val/test)的准备到训练超参数(例如args或者config文件)的设置都逐渐会根据经验去进行设计了。从来没有细细梳理过每一个训练小trick和其背后的依据,因此以边梳理边学习的态度,根据个人和网站上大家的总结对深度网络的训练进行了整理。

目录

  • 数据集的准备
  • 超参数及其他网络设置
    • 1. batch size
    • 2. learning rate
    • 3. optimizer
    • 4. activation
    • 5. 模型参数初始化
    • 6. 防止网络过拟合
    • 7. 训练前的检查工作
    • 8. 训练中的监控方法
    • 总结

数据集的准备

本部分内容参见我的上一篇博客链接:深度网络的训练经验总结(数据篇)

超参数及其他网络设置

介绍一个入门级的网站:PlayGround是一个在线演示、实验的神经网络平台,是一个入门神经网络非常直观的网站。这个图形化平台非常强大,将神经网络的训练过程直接可视化。同时也能让我们对Tensorflow有一个感性的认识。链接:http://playground.tensorflow.org

1. batch size

  batch size,即批量大小,是单次训练选择的样本数量。GD -> SGD -> mini-batch SGD,逐步在训练中引入了batch size,这个参数的好处是(1)解决了将数据一次性输入网络引起的内存爆炸;(2)在一个epoch里多次更新权重,加速了收敛;(3)可以跳出局部最优。但是设置batch size时仍要考虑空间和时间的限制,batch size对训练的影响具体表现在:过小,则会导致梯度变化较大,loss震荡,网络不易收敛;过大,则梯度已经非常准确,loss震荡较小,容易陷入局部最优。论文《Revisiting Small Batch Training for Deep Neural Networks》中表明,最好的训练结果都是在batch size处于2~32之间,一般都是2的幂次(如4,8,16)。综上所述,在一定范围内调大batch size,可以提高内存利用率和训练速度,虽然一个epoch时间变少,但是迭代更新权重次数少了,需要增大epoch来达到更高的精度,二者可以找到一个平衡。

1 epoch = 完成一次全部样本的训练 = 训练集样本数 / batch_size
1 iteration = 完成一次batch size大小的样本的训练 = 一次前向传播 + 一次反向传播
另,在一篇博客中有如下trick:当模型训练到尾声,想更精细化地提高成绩(比如论文实验/比赛到最后),有一个有用的trick,就是设置batch size为1,即做纯SGD,慢慢把error磨低。

2. learning rate

  learning rate,即学习率,简单来讲就是模型在利用反向传播的误差更新权重时学习的速率 α \alpha α, θ j = θ j − α Δ J ( θ ) Δ θ j \theta_j = \theta_j - \alpha \frac{\Delta J(\theta)}{\Delta \theta_j} θj​=θj​−αΔθj​ΔJ(θ)​ 。这个参数在训练神经网络时至关重要,它通过控制权重更新的幅度来决定训练的速度和精度,合适的学习率能够使目标函数在合适的时间内收敛到局部最优值。lr对训练的影响如下:过小,可以确保不会错过任何局部极小值,也意味着要花费更长的时间来进行收敛;过大,梯度会在极小值附近来回震荡,可能无法收敛或者loss爆炸。

  从上图可以看出,如果在整个训练过程中不调整学习率的话,则不能适应训练的各个阶段。所以,学习率的设置一般是采用一种退火的方式,先从一个比较高的学习速率开始,然后慢慢地在训练中降低学习速率。初始学习率通常设置在0.01~0.001,如果是迁移学习,那么学习率会在 1 0 − 4 10^{-4} 10−4之下。然而,在网络开始训练的时候,给它的初始化权重一般都是随机赋值的,权重更新的梯度很大,如果用一开始就用很大的学习率(也会跟batch size有一定关系),很可能造成训练中数值不稳定。所以,这里有一个trick是进行学习率的预热,最开始训练的时候用比较小的学习率,然后训练过程稳定之后切回到最初的初始学习率。这个预热过程让我想到了在训练网络之前找到最佳学习率的过程,网上有一种方法是利用pytorch的fastai包,通过每一次迭代提高学习率(线性或指数)来找到loss从下降到上升的转折点,如下图所示,然而这种方式我从来没有使用过。

常用的学习率的退火方式有:

  • 轮数衰减(step decay):随着每N次迭代,学习率下降一定的百分比。
  • 指数衰减(exponential decay):学习率按训练轮数增长指数差值递减, l r = α d e c a y g l o b a l _ s t e p s d e c a y _ s t e p s ∗ l r 0 lr = \alpha_{decay}^{\frac{global\_steps}{decay\_steps}}*lr_0 lr=αdecaydecay_stepsglobal_steps​​∗lr0​,衰减率 α d e c a y \alpha_{decay} αdecay​一般可以设置为0.9。
  • 分数衰减(1/t decay):按照公式递减, l r = l r 0 1 + α d e c a y ∗ g l o b a l _ s t e p s d e c a y _ s t e p s lr = \frac{lr_0}{1+ {\alpha_{decay}} * {\frac{global\_steps}{decay\_steps}}} lr=1+αdecay​∗decay_stepsglobal_steps​lr0​​。

  然而,当梯度下降到某一位置时,训练损失会很难得到改善。这个时候损失有可能并没有达到局部最低点,而是到达了鞍点。可以参看论文《Cyclical Learning Rates for Training Neural Networks》和《Stochastic Gradient Descent with Warm Restarts》,利用学习率周期变化的方法,在每一个周期的时候重新初始化学习率。此外,在下面的optimizer中讲到自适应学习率,来实现调整学习率的更多可能性。

3. optimizer

  optimizer,即优化器,用来更新网络的训练参数以达到最小损失。其中,学习率作为优化器的重要参数已经在上面总结了,下面将主要对比不同优化器中梯度下降的算法和超参的设置。在tensorflow和pytorch中都有封装好的优化器函数,分别通过tf.train.optimizer_name和torch.optim.optimizer_name来调用。主要的梯度下降算法可以分为三类,如下表所示:

类别 算法 描述 缺点
梯度下降法 GD
(Gradient Descent)
是最原始的算法,每输入一个样本都要更新一次参数。 每次下降的方向不一定正确,训练速度慢,容易陷入局部最优。
BGD
(Batch Gradient Descent)
每次更新与整个训练集样本的损失函数的梯度有关,即输入所有样本更新一次参数,可以缩短训练时间。 计算量太大,也容易收敛到局部极小值,不能解决鞍点问题。
SGD
(Stochastic Gradient Descent)
从训练样本中随机选取一个样本/小批量(mini-batch)的样本,更新一次参数。 仍没能克服局部最优解的问题,而且batch size可能带来的问题上面已经讨论过了。
动量优化法 Momentum 引入一个带有历史梯度信息的Momentum(动量)来加速SGD(进行两个方向矢量的合成),从而加快收敛并减小震荡。动力超参数取0.9。 容易盲目跟从下坡的速度,走到坡底的时候速度不会慢下来,从而可能会冲上另一个坡。
NAG
(Nesterov Accelerated Gradient)
相对于Momentum的改进在于,按照上一步的速度先走一小步,再根据当前的梯度再走一步。同样,动力超参数取0.9。 希望可以根据参数的重要性而对不同的参数进行不同程度的更新。
自适应学习率优化算法 AdaGrad 缩放每个参数反比于其所有梯度历史平均值总和的平方根。具有代价函数最大梯度的参数相应地有个快速下降的学习率,而具有小梯度的参数在学习率上有相对较小的下降。 随着迭代次数增多,学习率会越来越小,最终会趋近于0。
RMSProp 做了一个梯度平方的滑动平均,避免了分母由于时间积累越来越大。超参数常用的值是[0.9,0.99,0.999]。 已经被证明是一种有效且实用的深度神经网络优化算法。
Adam 除了像RMSProp一样存储了过去梯度的平方的指数衰减平均值 ,也像momentum一样保持了过去梯度的指数衰减平均值。超参数β1=0.9,β2=0.999,ϵ=10e^(−8)。 Adam一般来说是收敛最快的优化器,被用的较为频繁。

参考链接:
[1] 机器学习:各种优化器Optimizer的总结与比较
[2] 如何选择优化器 optimizer

4. activation

  activation,即激活函数,它的输入和输出维度相同,好处是给神经元引入了非线性因素,具有更强的特征提取能力。下表列出了常用激活函数:

激活函数 公式 优点 缺点
Sigmoid σ ( x ) = 1 1 + e − x \sigma(x)=\frac{1}{1+e^{-x}} σ(x)=1+e−x1​ 将一个实数输入转化到0-1之间的输出。 在梯度反向传递时容易导致梯度消失,并且输出不是均值为0的,幂运算的求解较为耗时。
Tanh t a n h ( x ) = e x − e − x e x + e − x tanh(x)=\frac{e^{x}-e^{-x}}{e^{x}+e^{-x}} tanh(x)=ex+e−xex−e−x​ 它将输入数据转化到-1到1之间。 仍然存在Sigmoid的其他两个问题。
ReLU r e l u ( x ) = m a x ( 0 , x ) relu(x)=max(0,x) relu(x)=max(0,x) 将大于0的部分保留,将小于0的部分变成0。在正区间解决了梯度消失问题,斜率恒等于1也避免了梯度爆炸,加快了收敛速度,计算方法更加简单。 输出不是zero-centered,神经元失活。注意初始化参数和学习率设置来避免上述问题。
Leaky ReLU r e l u ( x ) = m a x ( α x , x ) relu(x)=max(\alpha x,x) relu(x)=max(αx,x) Leaky ReLU有ReLU的所有优点,并且不会有神经元失活问题 实际操作当中,并没有完全证明Leaky ReLU总是好于ReLU。

5. 模型参数初始化

  根据上面激活函数的选择,我们可以看到参数初始化的方法也会对网络的训练造成一定程度的影响,主要体现在收敛速度和精度上。初始化参数时最容易想到的就是用零初始化,然而,经过前向传播和反向传播后,参数在不同维度上经过相同的更新,迭代的结果是不同维度的参数是一样的,这严重地影响了模型的性能。在初始化时,参数的值一般在什么范围呢?如果参数过小,在反向传播时梯度的变化也很小,对于深度网络来说,会产生梯度弥散(消失)问题,降低收敛速度;过大,则很容易想到对于sigmoid和tanh激活函数会饱和,梯度容易消失,relu会带来梯度爆炸。我们在随机初始化参数时,常用的方法是(1)标准初始化:均匀随机初始化(参数范围是 [ − 1 n i n , 1 n i n ] [-\dfrac{1}{\sqrt{n_{in}}}, \dfrac{1}{\sqrt{n_{in}}}] [−nin​ ​1​,nin​ ​1​], n i n n_{in} nin​是输入神经元的个数)和高斯随机初始化),这种方法适合tanh激活函数;(2)Xavier初始化:参数范围在 [ − 6 n i n + n o u t , 6 n i n + n o u t ] [-\frac{\sqrt 6}{\sqrt {n_{in}+ n_{out}}},\frac{\sqrt 6}{\sqrt {n_{in}+ n_{out}}}] [−nin​+nout​ ​6 ​​,nin​+nout​ ​6 ​​], n i n n_{in} nin​和 n o u t n_{out} nout​是输入和输出神经元的个数;(3)He初始化: N ( 0 , 2 n i n ) N(0,\sqrt{\frac{2}{n_{in}}}) N(0,nin​2​ ​), U ( − 6 n i n , 6 n i n ) U(-\sqrt{\frac{6}{n_{in}}},\sqrt{\frac{6}{n_{in}}}) U(−nin​6​ ​,nin​6​ ​),适合relu函数使用。通常偏置项初始化为0,或比较小的数,如:0.01。
  上述描述可能太不直观,我平常训练网络也没有专门去考虑参数值的范围,常常在tensorflow中直接用一些初始化代码;而在pytorch中,有自己默认初始化参数方式,所以在定义好网络结构以后,不进行参数初始化也是可以的。

init_zeros=tf.zeros_initializer()#常量初始化用来初始化偏置
#初始化为正态分布,标准和截断
init_random = tf.random_normal_initializer(mean=0.0, stddev=1.0, seed=None, dtype=tf.float32)
init_truncated = tf.truncated_normal_initializer(mean=0.0, stddev=1.0, seed=None, dtype=tf.float32)
#初始化为均匀分布
init_uniform = tf.random_uniform_initializer(minval=0, maxval=10, seed=None, dtype=tf.float32)
#初始化为变尺度正态和均匀分布
init_variance_scaling_normal = tf.variance_scaling_initializer(scale=1.0,mode="fan_in",                                                        distribution="normal",seed=None,dtype=tf.float32)
init_variance_scaling_uniform = tf.variance_scaling_initializer(scale=1.0,mode="fan_out",                                                       distribution="uniform",seed=None,dtype=tf.float32)
#初始化所有变量
sess.run(tf.global_variables_initializer())

6. 防止网络过拟合

  • 正则化(Regularization):对于权重过大的部分进行惩罚,也就是直接在损失函数中增加权重的1范数或者2范数量级。L1正则化更适用于特征选择,而L2 正则化更适用于防止模型过拟合,也可以将L1正则化和L2正则化结合起来。
  • Dropout:以一定概率随机地“临时丢弃”一部分神经元。
  • 批量归一化(Batch Normalization):采用BN算法后,可以移除dropout和正则化这两项了参数,或者可以选择更小的L2正则约束参数。BN具有提高网络泛化能力的特性,解决在训练过程中,中间层数据分布发生改变的情况。引入了可学习重构参数γ、β,让我们的网络可以学习恢复出原始网络所要学习的特征分布。

7. 训练前的检查工作

  为保险起见,每次刚开始训练代码时,我都会观察前几个迭代loss变化的趋势,来避免运行了好一阵子,才发现训练其实并不正确,也是心累。那么loss可能的变化趋势代表哪些存在的问题呢?在训练刚开始时最经常会遇到的问题如下:

  • Loss 震荡:
    (1)输入数据存在问题;
    (2)batch size过小;
    (3)初始学习率过大;
    (4)网络是否学不到东西:可以尝试去拟合一个小数据集来判断。
    (5)分析网络各结构是否正确。
  • Loss 变为Nan:
    (1)被除数的值是无穷大,或者就是除数的值是0;
    (2)初始学习率过大;
    (3)初始参数值过大,没有进行归一化;
    (4)梯度过大,造成更新后的值为Nan。

8. 训练中的监控方法

这里推荐可视化工具:tensorflow平台使用tensorboard,pytorch平台可以使用visdom

总结

  经过这次整理过程还是学到了不少之前没有注意到的细节,虽然深度神经网路的训练很玄学,但是细细想来还是有一些道理的,也有训练从无到有带来的快乐。训练网络可能只是实验的一个开端,后续各种评价指标进行性能的度量,才步入了实验对比改进的正轨。总之,细节很多,还需要耐着性子去慢慢磨,希望越来越自如吧。知乎上面还有其他的一些调参技巧,附上链接https://www.zhihu.com/question/25097993。

深度网络的训练经验总结(参数篇)相关推荐

  1. 笔记 | 百度飞浆AI达人创造营:深度学习模型训练和关键参数调优详解

    笔记 | 百度飞浆AI达人创造营:深度学习模型训练和关键参数调优详解 针对特定场景任务从模型选择.模型训练.超参优化.效果展示这四个方面进行模型开发. 一.模型选择 从任务类型出发,选择最合适的模型. ...

  2. 深度学习模型训练和关键参数调优详解

    深度学习模型训练和关键参数调优详解 一.模型选择 1.回归任务 人脸关键点检测 2.分类任务 图像分类 3.场景任务 目标检测 人像分割 文字识别 二.模型训练 1.基于高层API训练模型 加载数据集 ...

  3. 深度网络的训练过程与方法

    第一步:自下而上的无监督学习 (这一步是网络参数初始化得过程,区别于传统神经网络初值随机初始化,深度学习模型通过无监督学习输入数据进行初始化,因此这个初值更接近全局最优,从而能够取得更好的效果.) 逐 ...

  4. 深度网络学习调研报告

     深度网络学习调研报告 目录 1.前言...............................................3 1.1课题研究的背景及意义................. ...

  5. 利用多 GPU 加速深度学习模型训练

    01 - 前言 深度学习模型通常使用 GPU 训练,因为 GPU 具有相比 CPU 更高的计算能力,以 Tesla V100 为例,使用 Tensor Core 加速的半精度浮点计算能力达到 125 ...

  6. ​深度学习-关于图像分类的经典10篇文章

    本文介绍十年来最佳图像分类论文,来帮助你快速学习计算机视觉 前言 计算机视觉是一门将图像和视频转换成机器可理解信号的学科,有了这些信号,程序员可以基于这种高阶进一步控制机器的行为.在计算机视觉任务中, ...

  7. 深度网络的过拟合问题讨论

    问题背景 最近做深度学习实验的时候遇到了一个很棘手的问题,那就是大名鼎鼎的"过拟合",直观地表现在图中是长这个样子的,分析来讲就是说深度网络在拟合训练集的时候是可以很好地实现,Lo ...

  8. 常见的五种神经网络(4)-深度信念网络(下)篇之深度信念网络的原理解读、参数学习

    该系列的其他文章: 常见的五种神经网络(1)-前馈神经网络 常见的五种神经网络(2)-卷积神经网络 常见的五种神经网络(3)-循环神经网络(上篇) 常见的五种神经网络(3)-循环神经网络(中篇) 常见 ...

  9. 【深度学习】Weight Normalization: 一种简单的加速深度网络训练的重参数方法

    前言:为什么要Normalization 深度学习是一种在给定数据的情况下,学习求解目标函数最小化或者最大化的模型.在深度网络中,模型参数往往包含了大量的weights和biases.在求解优化模型的 ...

最新文章

  1. 【12.16】VC++调用Word OLE进行自动化生成报表
  2. ARP协议详解之ARP动态与静态条目的生命周期
  3. 22543!Windows 11 新预览版发布
  4. nginx fastcgi python_Nginx + webpy 和FastCGI搭建webpy环境
  5. db2 查杀死锁进程
  6. jsp页面执行java语法,获取的值在页面调用
  7. 根据线程名获取线程及停止线程
  8. [Linux 性能检测工具]IOSTAT
  9. SSRF攻击实例解析
  10. 计算机网络技术期末应用试卷,中专学校2016年《计算机网络技术与应用》期末考试题(考试卷与答案)...
  11. 去除 火狐浏览器自动给域名前加 www.
  12. 智能寻迹(循迹)小车项目思路 + 代码
  13. 【Unity】Transform—用代码设置父子关系
  14. mysql rpl_MySQL半同步复制rpl_semi_sync_master_timeout测试
  15. 位图(bitset)的使用【STL】
  16. 线性模型是否真的能给出一个很好的解释?
  17. Win7(Win2003)下安装Node.js(版本号:v0.11.0)提供下载
  18. 面试题:构造方法中可不可以有return语句呢?
  19. 浅谈Asterisk的语音编码(codec)
  20. Linux自动挂载失败,linux – 在启动期间自动挂载单独分区时的systemd / udev依赖性失败...

热门文章

  1. 通过rvm 安装 ruby
  2. 原生JS获取body
  3. 基于STM32F103 HAL库 MB85RS128 驱动程序
  4. 用理想低通滤波器在频率域实现低通滤波、高通滤波。
  5. 8.dfs--王子救公主(遍历迷宫,王子和公主访问过同一位置,即成功救公主)
  6. 计算点到道路的距离_在ArcMap中完成
  7. 安装使用Animate动画库【Animate.css下载安装教程】
  8. 操作DOM对象(重点)
  9. natapp内网穿透速学教程
  10. 1822 - Failed to add the foreign key constraint. Missing index for constraint ‘fk_tno_course‘ in the