深度学习debug&调参技巧

1.超参数设置

  • 初始调参阶段记得关闭L2、Dropout等用来调高模型泛化能力的超参数,它们很可能极大的影响loss曲线,干扰重要超参数的选取。
  • 学习率:最重要的参数。
    • 调参时按照指数规律设置几组可能的学习率:[1, 0.1, 0.01, 0.001, 0.0001, 0.00001],最后从这几组学习率中选择loss下降的又快又深的曲线所对应的学习率即可。其中0.01、0.1是常用学习率值。
    • 学习过程中如果优化过程中loss不下降,就对学习率减半。
    • 如果用带动量的sgd的话,最佳学习率取决于动量,而动量又取决于学习率。
    • 计算出来每次更新时的梯度向量的模与当前参数向量的模的比值。
      • 如果这个比值在10^-3量级附近的话说明学习率还可以。
      • 如果数量级太小,则网络更新不动,需要增大学习率。
      • 数量级太大则每次更新对网络的修改太大,网络很容易发生不稳定,需要降低学习率。
    • 尝试warmup
  • 学习率衰减策略:
    • 衰减策略一般用multistep方式。
    • 方案1:直接从第一步起就开始线性衰减
    • 方案2:cosine learning rate
    • 方案3:cyclic learning rate
    • 衰减开始的时机:验证集metric值达到最高时的前若干步。
    • 衰减量级(线性衰减or指数衰减):常用指数衰减(更敏感)。
    • 衰减速率:衰减系数设0.1,0.3,0.5。最少衰减为初始学习率的0.5才行(除非你的衰减间隔真的很短),常见为0.5。
    • 衰减的周期:接近训练结束,应该衰减100倍左右。
  • batchsize:
    • 64是个不错的默认值
    • batchsize和lr应该是正比关系:二者应该成线性调节,当前batch size为b、lr为r模型表现不错,若要使用更大的batch size b’,那么可以设置初始lr为r×b/b’。
    • 数据集不均衡的话建议使用更大一点的值
    • 调节参考:https://mp.weixin.qq.com/s/67Gr9NXNxaI9uq05tY3Vkw
  • weight decay:
    • 一般取0.005。不对bias参数做正则化。
    • 复杂的数据集需要较少的正则化,因此设置为较小的权重衰减值,例如10^-410^-510^-60
    • 浅层结构需要更多的正则化,因此设置更大的权重衰减值,例如10^-210^-310^-4
  • 初始化:
    • 权重初始化无脑用Xavier,也可以尝试Kaiming He(ReLU激活时用)、MSRA。
    • 偏置初始化置0。
    • word embedding用uniform。
    • 将所有residual blocks中的最后一个batch norm层的γ和β参数设置为0,也即residual block的输出和输入相等,可以使模型在初始阶段更容易训练。
  • 优化器:
    • 先用Adam,优化不动换带动量的SGD。
    • adam的初始学习率设为0.0003是个比较合适的值。
    • SGD超参数比较多,需要更多经验。动量值一般取0.9-0.99。常用0.99、0.97、0.95、0.9。
    • 效果上来说,如果超参数调的好,带动量的SGD更优!
    • 要做梯度裁剪,要做梯度归一化。

3.tricks

  • 使用固定的随机种子,来保证运行代码两次都获得相同的结果,消除差异因素。
  • 计算机使用64-bit或者32-bit浮点精度(FP64/FP32)做训练,英伟达的部分显卡针对FP16做了定制化优化,能够达到更快的计算速度,比如V100。

3.1数据预处理

  • 数据预处理:zero-center&normalize归一化、shuffle。
  • 数据增强:最常见的是random crop,random flip (herizontal, vertical),骚一点可以加random hue,random contrast,color space shift等等。
  • 对于图像分类,每个类别需要 1000 张图像甚至更多才能训练出置信的结果。

3.2网络结构

  • 激活函数:cnn先试ReLU,再试PReLU,其余可以试swish。rnn用tanh。
  • dropout值首选0.5,备选0.7。
  • embedding的维度从128开始调节。
  • 用3 * 3卷积核,两个3 * 3的卷积核堆叠能获得5 * 5卷积核的感受野并且参数比5 * 5卷积核少,所以是大量推荐使用的。
  • 适当使用1 * N卷积。因为1 * N卷积可以减少计算量,并且在某个方向强调感受野,也就是说假如要对一个长方形形状的目标进行分类。
  • BN层尽量加上,加快收敛。一般顺序是 Conv - BN - Relu。少在太小批次中使用bn层。因为BN从每个batch中求得均值和方差才能进行操作。
  • filter数量2^n。第一层的filter数量不要太少. 否则根本学不出来(底层特征很重要).
  • 模型一般不要超过40层。
  • 下采样在网络前几层的密度大一些,(这样能尽可能用微弱精度损失换取速度提升) 越往后下采样的密度应该更小,最终能够下采样的最大深度,以该层的感受野以及数据集中最大的有意义物体尺寸决定(自然不可能让最大有意义的物体在某一层被下采样到分辨率小于1,但是网络依然可以work,只不过最后几层可能废弃了(要相信cnn的学习能力,因为最大不了它也能学出单位卷积,也就是只有中心元素不为0的卷积核),更准确的说这是最大感受野的极限,最大感受野应该覆盖数据集中最大有意义的物体)。
  • 深度决定了网络的表达能力,网络越深学习能力越强。宽度决定了网络在某一层学到的信息量。感受野决定了网络在某一层看到多大范围,一般说来最后一层一定至少要能看到最大的有意义的物体,更大的感受野通常是无害的。在达到相同感受野的情况下,多层小卷积核的性能一定比大卷积核更好。
  • 只对权重正则化,不对偏置正则化:每一层的权值的作用是调节每一层超平面的方向(因为w就是其法向量),因此只要比例一致,不会影响超平面的形状的。但是,我们必须注意到,每一层中的偏置是调节每一层超平面的平移长度的,如果你对偏置进行了正则,那么我们的b可能就会变得很小,或者很稀疏,这样就导致你的每一层的超平面只能局限于很小的一个范围内,使得模型的容量大大减少,可能会导致欠拟合。

3.3损失设计

  • 如果分类精度不够是样本不均衡造成的,考虑使用focal loss。
  • 多任务情况下, 各loss想法限制在一个量级上, 或者最终限制在一个量级上, 初期可以着重一个任务的loss

3.监控与可视化

  • 两方面做记录(可视化):tensorboard、logging到本地。保证每个流程都有监控。
  • 需要可视化的项目:数据输入、模型不同层权重的分布变化、每一层输出的分布变化、模型不同层权重的分布变化、loss变化、metric指标变化、learning rate变化。
  • 数据输入:检查馈送到网络的输入数据是否正确。避免混淆了图像的宽度和高度。显示若干batch的数据就行。同时确保label和输入是对应的。
  • 每一层输出:
    • 每一层cnn输出:应该具备sparse和localized的特点。如果训练出的模型,用于预测某张图片时,发现在卷积层里的某个feature map的activation matrix可视化以后,基本跟原始输入长得一样,基本就表明出现了一些问题,因为这意味着这个feature map没有学到多少有用的东西。
    • 最后一层logits输出的预测结果会越来越趋近于样本的one-hot vec。
    • 一个好的激活值标准差大约在 0.5 到 2.0 之间。明显超过这一范围可能就代表着激活值消失或爆炸。
  • 每一层模型权重:
    • 可视化显示出来是smooth的。
    • 观察loss比观察评估指标更重要。
    • 参数更新的大小(权重和偏差)应该是 1-e3数量级,更新值应该有一个高斯分布。
    • 对于权重,一些时间之后这些柱状图应该有一个近似高斯的(正常)分布。
    • 对于偏差,这些柱状图通常会从 0 开始,并经常以近似高斯(这种情况的一个例外是 LSTM)结束。

4.debug

  • 将各个参数的设置部分集中在一起。如果参数的设置分布在代码的各个地方,那么修改的过程想必会非常痛苦。
  • 从简单模型开始调通,一点一点丰富模型结构。
  • 先上小规模训练数据, 模型往大了放, 只要不爆显存, 能用256个filter你就别用128个. 直接奔着过拟合去。没错, 就是训练过拟合网络, 连测试集验证集这些都可以不用.
  • tf框架debug可以用tfdbg

  • 注意loss function和metric function是不同的,loss function是优化器直接优化的。
  • loss/metric分析:
    • 训练集曲线不下降:
      • 学习率太低、太高
      • 初始化方案有问题
      • 模型结构有问题(梯度爆炸or消失大量神经元失活、大量神经元失活)
      • 梯度没有在整个模型传播
      • 过分正则化导致模型欠拟合
      • 损失函数错误
      • 数据或者标签有误(没有归一化、没有shuffle)
    • 训练集曲线上升:如果loss 大于三倍的初始loss,基本就是要炸了。
      • 学习率太高
      • 数据有问题(如:本身有问题,数据处理有问题、data和label是否一致等等)
      • 网络结构设计不当(如:softmax使用在错误维度)
      • 损失函数设计错误(如:符号错误)
    • 训练集曲线震荡:
      • batch size 过小
      • 显示的训练间隔太小
      • 学习率大
      • 数据输入不对。
    • 训练集曲线下降,接近线性:学习率比较小。因为一般而言,loss 曲线下降斜率是逐渐缓和的。
    • 训练集曲线下降,验证集曲线不变or上升:过拟合。
    • 训练集曲线下降,测试集震荡:
      • 训练集和测试集分布存在差异(如:数据增强做的太过分了)
      • 学习率大
      • LOSS的计算是基于单个batch
      • 网络是否存在欠拟合的可能
    • 训练集和验证集上曲线重合:欠拟合。
    • 训练和验证集都是0:
      • 任务太简单,是极端情况。
      • logits输出值太大,softmax算出的结果接近于0。这种情况一般是由于初始化导致的。
    • Nan:
      • 计算原始输入值有Nan:之前产生的Nan或者0,有可能会被传递下去,造成后面都是Nan。
      • 除0问题:除数的值是0。检查一下神经网络中有可能会有除法的地方,分母可以加一个eps=1e-8。特别是softmax中,exp(x)值为inf时会产生0输出。
      • 开根号问题:保证根号下>=0, 可以加一个eps=1e-8.
      • 梯度过大,造成更新后的值为Nan。特别是RNN,在序列比较长的时候,很容易出现梯度爆炸的问题。
      • 初始参数值过大:有可能出现Nan问题。输入和输出的值,最好也做一下归一化。
      • 学习率设置太高:在前100轮迭代中出现Nan。

参考

  • https://mp.weixin.qq.com/s?__biz=MzU0NTAyNTQ1OQ==&mid=2247484020&idx=3&sn=6882d32290f44d15cb353ea92849356b&chksm=fb727ea8cc05f7be022025b20d6e8e71170eb5eb1ce383d526aabe8b92a759fd426e9cd114b3#rd
  • https://blog.ailemon.me/2019/02/26/solution-to-loss-doesnt-drop-in-nn-train/
  • http://zhuanlan.zhihu.com/p/20767428
  • https://blog.csdn.net/qq_21950671/article/details/99065122
  • https://blog.csdn.net/LoseInVain/article/details/83021356
  • https://blog.csdn.net/LoseInVain/article/details/83021356
  • https://zhuanlan.zhihu.com/p/63841572