shirakawa

神经网络开发食谱

新年新气象:鸽了漫长时间的公众号终于重新开张了,这次公众号的写作主题将围绕深度学习,参考了Andrej教授和一些大佬的开发经验,首先对神经网络的整体架构进行理论和技术探讨。本节将描述神经网络模型构建的开发体系进行学习。

1.阅读指南

​ 训练神经网络时会出现很多漏洞,并非我们认知上简单的调包,调参即可使用。很多情况,尽管构建了错误的网络模型(训练图像忘记检测反转图像,自回归模型将它预测的数据作为输入,或者权重,正则化等等配置);大多数情况它仍在训练,我们无法检测出它出现了什么问题。所以,成功开发神经网络最需要的就是有一套完整的体系,耐心,以及对细节的关注。

your_data = #导入你的数据集
model = SuperCrossValidator(SuoerDuper.fit, your_data, ResNet50, SGDOptimizer) #设置你的网络

开始训练神经网络我们会觉得很容易。因为许多库和框架可以让我们在短短二三十行的代码来解决我们的数据问题。这会造成一种错误的印象,很多东西是即插即用的。事实上神经网络并非如此,当我们偏离训练ImageNet 分类器时,它并非现成的技术。如果不了解该技术的工作原理,将会出现很多意想不到的失败。。。

2.无声无息失败的神经网络训练

​ 当我们错误配置代码时,通常会遇到某些异常。You plugged in an integer where something expected a string. The function only expected 3 arguments. This import failed. That key does not exist. The number of elements in the two lists isn’t equal.之类的问题。这只是训练神经网络的开始,一些代码可能在语法上正确,但是在整个网络中并不正确,这些问题很难发现。例如反向传播是一种有漏洞的抽象,试图忽略它的工作原理将无法应对它带来的问题,构建和调试的神经网络模型效果也会低得多。

例如:

  • Sigmoid上的梯度消失,非线性可能会饱和完全停止学习造成训练损失是平坦的拒绝向下。可能因为你的权重初始化太大,造成矩阵乘法输出有一个很大的范围,此时z*(1-z)sigmoid 非线性的局部梯度,从而使xw的梯度都为0

1_gkXI7LYwyGPLU5dn6Jb6Bg.png

  • ReLU:非线性ReLU它将神经元阈值设置为0。使用ReLU的全连接层的前向和后向传递的核心包括:

    z = np.maximum(0, np.dot(W, x)) #前向传递
    dW = np.outer(z > 0, x) #后向传递:W 的局部梯度
    

    如果观察它的一个神经元在前向传递中被设置为0(即z = 0 ,它将不会触发),那么它的权重将为零梯度。这就是所谓的Dying ReLUs问题,如果一个ReLU神经元不幸被初始化他将永远不会出发,或者一个神经元的权重在训练到这个机制的过程中将被一个大的更新淘汰,这个神经元将“永久性死亡“。这就像一个永久性,不可恢复的脑损伤。这些神经元在我们的整个训练集中永远不会为任何实例打开,并且将永远保持死亡状态。

  • RNN中的梯度爆炸:参考CS231n中的一个例子如下图所示:

    1_dqlX0ixpk1O3225bZ1LGnA.png

    这个RNN展开了T个时间布。当我们观察反向传播的作用时,我们会看到通过所有隐藏状态和反向传播的梯度信号总时乘以相同矩阵(递归Whh)并穿插非线性反向传播。当我们取一个属a开始乘以另一个数b(i.e. a * b * b * b * b * b * b…)。如果|b| < 1,那么这个序列要么变成0,或者|b| > 1时爆炸到无穷大。同样的事情发生在RNN的反向传播中(除了b是一个矩阵而不是一个数字以外)。

一切在语法上可能正确的,构建的神经网络效果却很差。这种问题非常苦恼,也许时因为在数据增强部分左右翻转图像时忘记翻转标签。此时我们搭建的网络仍然可以很好的工作,因为我们的网络可以在内部学习检测翻转的图像,然后它将预测值左右翻转。或者在自回归模型中将预测的事物作为输入。或者,当我们尝试剪裁梯度,却剪裁了损失导致训练期间忽略异常值等等问题,如果我们构建的模型报错了,反而是很幸运的,因为大部分时候它会训练,只不过效果很糟糕而已。。。

3.开发食谱

​ 鉴于以上发生的问题,如果我们想使用神经网络应用到一个新的问题时,我们应该构建一个流程体系。重视它的规则,从简单到复杂构建,在外面对将要发生的事情做出具体假设,通过实验验证他们或者可视化展示,知道我们发现了一些问题。如果我们直接一股脑的对未经验证的模型进行检测,势必需要很久才能找到问题所在。多说无益,开始描述整个开发过程。

3.1 数据脱敏

​ 训练神经网络的第一步时,无需接触任何神经网络代码,而是从彻底检查数据开始。这一步是非常关键的一步,往往我们在数据处理的某一个步骤会一定程度上的影响实验结果。检查数据重复性问题,损坏的图像标签,数据的不平衡性问题,考虑如何定义分类过程。需要了解样本的局部特征还是全局特征,是否可以预处理,平均化。图像的噪声问题。当我们对数据有了一定的掌握,可以通过一些方法搜索/过滤/排序我们需要的数据(标签类型)并可视化他们的分布观察各轴的异常值,他们会影响数据的质量或预处理中的一些错误。

3.2 构建完整的训练—评估框架

​ 当处理好数据后,下一阶段是构建完整的训练—评估框架,通过一系列实验验证它的可靠性。我们可以先使用一些简单模型,或非常小的网络(不容易出错的模型)训练,可视化损失,正确率,模型预测,并在此过程使用明确假设进行消融实验。

​ 此阶段的提示和技巧:

  • fix random seed:使用固定的随机种子,保证我们运行两次代码时,将获得同样的结果。
  • simplify:此阶段不需要数据增强过程,它是正则化策略无需此时出现
  • 绘制评估曲线无需在意训练时间:在绘制测试损失时,在整个数据集上评估不要批次绘制测试损失
  • verify loss 验证我们的损失例如,如果你正确地初始化你的最后一层,你应该-log(1/n_classes)在初始化时测量softmax。可以为 L2 回归、Huber 损失等导出相同的默认值。
  • init well:正确初始化最终层权重,正确设置这些将加速收敛并消除“hockey stick”损失曲线,在最初的几次迭代中,构建的网络基本上只是学习偏差。
  • human baseline:除了监控损失值等可解释和可检查指标以外,尽可能与自评估准确性进行比较。或者对测试数据进行两次注释,对于每个例子,将一个注释作为预测,第二个注释作为真实值。
  • input-indepent baseline:训练与输入无关的baseline。观察我们的模型是否学会了从输入中提取信息。
  • overfit one batch:过拟合几个batch,增加模型的容量验证我们可以达到的可实现的最小损失。
  • verify decreasing training loss:验证减少训练损失,如果数据集上欠拟合,可以适当增加容量,或者使用一些方法来处理。
  • visualize just before the net:在y_hat = model(x)(或sess.run在tf之前)将数据和标签tensor解码可视化。
  • visualize prediction dynamics:在训练过程中对固定测试批次的模型预测进行可视化。这些预测将动态的为我们提供整个训练过程,如果网络以某种方式摆动,则可能会感觉到网络无法适应数据集,从而显示出不稳定性。学习率的过高或过低也容易造成抖动问题。
  • use backprop to chart dependencies:使用反向传播来绘制依赖关系,避免造成矢量化,广播等操作的计算错误(该类型错误很难发现,网络仍会正常训练),调试方法是将损失设置很小,如实例i的所有输出总和,输入到反向传播计算,确保第i个输入得到一个非零梯度。同样的策略可以用来确保你的自回归模型在时间t只取决于1...t-1。更广泛地说,梯度给你提供了关于网络中什么取决于什么的信息。
  • generalize a special case:将模型编写成函数形式,先写出完整的训练版本然后加入循环等矢量化指令将其转化为完整的模型代码
3.3 过拟合

​ 在这个阶段,我们已经充分的掌握了整个数据集,并且有了完整的训练+评估模型,对于任何给定的模型,我们可以重复地训练。在充分认识该问题的基础上,可以很好的将结果与预测结果进行比对。此时我们可以准备更新下一个模型了。寻找一个好的模型通常有两阶段:首先得到一个足够大的模型,它可以过拟合(专注于训练损失),然后对其进行适当的正则化(放弃训练损失用以改善验证损失)。

​ 此阶段的提示和技巧:

  • picking the model:挑选合适的模型有一个很关键的道理们就是模型越简单它的效果往往越好,在搭建模型时尽量避免一些奇妙的搭建思路,最好的方法就是参考最相关的论文,并复制粘贴他们获得的良好性能的最简单架构,并在此基础上加以训练。所谓站在巨人的肩膀上即使如此。
  • adam is safe:在设置超参数时,建议使用3e-4Adam。因为Adam通常对超参的容忍度更高,对于ConvNets来说,调整好的SGD总是比Adam略胜一筹,但是最佳的学习率范围却十分小,多是针对具体问题的。通常在初始阶段(RNN和相关的序列模型)使用Adam是明智之选。
  • complexify only one at a time:如果想优化模型,建议一个一个尝试确保,每次优化都能获得预期性能提升。
  • do not trust learning rate decay defaults:最好禁用学习率衰减,手动调试避免学习率过早的自动衰减为0。
3.4 正则化

​ 到了这一阶段,需要通过放弃一些训练集的准确性进行正则化,获得一些验证准确性。

​ 此阶段的提示和技巧:

  • get more data:在任何环境下对模型正则化的最佳和首选方法就是添加更多真实的训练集,花费大量时间从小型数据集中”榨汁“是不明智的。增加更多数据是能无限提高神经网络模型性能的唯一确保方法。
  • data augment:数据增强为正则化的第二好方法。
  • creative augmentation:创意性的变形数据也是扩展数据集创造性的方法:例如,域随机化,使用模拟将数据插入场景中。CV中的图像翻转等一系列手段。
  • pretrain:对网络进行预训练。
  • stick with supervised learning:坚持监督学习而不是无监督学习(至少现在是)。
  • smaller input dimensionality:如果数据集很小,任何添加的虚假输入都会造成过拟合问题。
  • smaller model size:约束模型尺寸,消除大量参数。
  • decrease the batch size:减少batch大小,较小的batch在某种程度上对应更强的正则化。因为批次经验均值/标准差是完整均值/标准差的更近似版本。
  • drop:添加dropout(不适用于批量标准化)。
  • weight decay:增加权重衰减惩罚。
  • early stopping:停止根据测量的验证损失进行训练,从而在模型即将过拟合时得到最佳参数配比。
  • try a larger model:尝试更大的模型,往往训练效果比小训练样本要好。

​ 最后,为了确保我们的模型是有效的,可以将网络的第一层权重可视化,确保模型获得有意义的良好边缘。如果第一层看起来像噪音,那么可能有问题。同样,隐藏层出现噪音问题也可能存在问题。

3.5 调参

​ 此步骤处于网络训练循环中,为了我们的模型实现低验证损失。

​ 此阶段的提示和技巧:

  • random over grid search:为了同时调整多个超参,使用网格搜索可以确保覆盖所有参数,最好使用随即搜索,因为神经网络通常对某些参数更敏感,如果参数a很重要但更更改b无影响,此时我们宁可对a进行更彻底的采样,而不是在几个固定点多次采样。
  • hyper-parameter optimization:使用一些贝叶斯超参数优化工具箱
3.6 最终结果

​ 当我们找到了最佳参数和最好的模型架构,仍然可以通过一些方法来提高正确率

​ 此阶段的提示和技巧:

  • ensembles:模型集成可以提高准确率
  • leave it training:当网络的准确率保持一定稳定时,可以尝试使用长时间保持训练。

4. 结论

​ 参考了Andrej教授和一些大佬的调参经验,总结了搭建神经网络的所有成功要素。相信这对于我们进一步探索复杂模型,模型改进,论文复现有很大帮助。掌握好整个理论体系,有助于我们进一步的发展,在"搭积木"的道路上越走越远哈.

推荐阅读

1628081548426-51138395-7cc1-4579-a0f0-dc4195c4ea0b.png

相关文章: