你应该知道的神经网络调试技巧
作者 | Russell Stewart
译者 | zhwhong(@zhwhong_shsf)
整理 | AI100(rgznai100)
以下建议主要针对神经网络的初学者。这些建议主要基于我在行业应用和斯坦福大学里为神经网络初学者提供建议所获得的经验。
神经网基本上比大多数程序更难调试,因为大多数神经网络错误不会导致类型错误或运行时错误。它们只是导致神经网络难以收敛。特别是当你刚接触这个的时候,它会让人非常沮丧!但是一个有经验的神经网络训练者将能够系统地克服这些困难,尽管存在着大量似是而非的错误消息:性能错误:你的神经网络没有训练好。对于缺乏经验的人来说,这种信息是令人生畏的。但对有经验的,这是一个非常好的错误信息。这意味着样板代码已经偏离了正确道路,是时候去深挖一下原因了!
➤如何应对NaN
到目前为止,我从学生那里得到的最常见的第一个问题是,“为什么我出现了 NaNs ?”。有时候,这个问题的答案很复杂。但大多数情况是,NaNs 在前100轮迭代中就出现了,这时候这个答案就非常简单:你的学习率(learn rate)设置的太高了。当学习率非常高时,在训练的前100轮迭代中就会出现NaNs。尝试不断的把学习率除以3,直到在前100轮迭代中不再出现NaNs。一旦这样做起作用了,你就会得到一个很好的初始学习率。根据我的经验,最好的有效学习率一般在你得到NaNs的学习率的1-10倍以下。
如果你是在超过100轮迭代之后才出现的NaNs,还有2个其他的常见原因。
如果你训练的是RNN,请确保使用的是“梯度剪裁(clip gradient)”,这可以把全局的梯度二范数(L2)限制在一定的范围内。RNN倾向于在训练早期产生梯度,其中10%或者更少的batch会出现学习尖峰,这些尖峰上的梯度值非常大。如果没有限制幅度,这些尖峰就可能导致NaNs。
如果你自己编写了任何自定义的layer,那么这个问题很可能是由这些自定义的layer中一些除零错误引发的。还有一个众所周知的产生NaNs的layer就是softmax层。 softmax的计算在分子和分母中都含有指数函数exp(x),当inf除以inf时就可能会产生NaNs。所以要确保你使用的是一个稳定版本的softmax实现。
➤当神经网络不再学习的时候怎么办?
当你不再碰到NaNs的时候,很可能就会遇到这样一种情况,你的网络顺利地训练了几千轮,但是训练的loss值却在前几百个回合后不再减小。如果你是初次构建代码库的话,基本上不会说需要等待超过2000轮迭代。这不是因为所有网络都能在2000次迭代内开始学习,而是因为你在编码中引入bug的几率很高,与其等待长时间的迭代,不如早早的进入调试模式。现在你应该不断缩小问题的范围,直到你的网络可以在2000次迭代内开始学习。幸运的是,有2个不错的维度来降低复杂度:
把训练集的样本量减小到10。任何一个可用的网络通常都能在几百次迭代后过拟合十个样本。但是很多编码bug则会阻止这种情况发生。如果你的网络仍然不能过度拟合训练集的10个样本,请再次确认数据和标签是否是正确对应的。尝试将batch size设为1来检查batch计算中的错误。在代码中加入一些log输出以确保是以你期望的方式运行的。一般来说,通过暴力排查总会找到这些错误。一旦网络可以拟合10个样本了,继续尝试拟合100个。如果现在可以正常训练了但不如预期,则可以进入下一步了。
解决你感兴趣的问题的最简单版本。如果你正在做句子翻译,尝试首先为目标语言构建一个语言模型。上一步成功了,只给出三个源语言的单词,尝试着去预测翻译的第一个词。如果你打算从图像中检测物体,训练回归网络之前试着去分类图像中有多少个物体。在获得一个确保网络可以解决的好的子问题,以及花费最少的时间来使用代码挂接数据之间存在着平衡点。创造力可以起到帮助作用。
一个为新想法扩展网络的小技巧就是慢慢地缩小上述两步中所做的简化。这是坐标上升法的一种形式,而且十分有用。一开始,你可以证明这个网络可以记住少量的样本,然后可以证明它在一个简化版的子问题中可以在验证集上具有泛化能力。慢慢提升难度,稳步前进。这并不像第一次Karpathy的风格那么有趣,但至少它是有用的。有些时候你会发现有些问题本身十分困难,难以在2000次迭代内完成学习。这很棒!但是它很少需要以前那种难度级别问题迭代次数的十倍以上。如果真需要这么多次迭代,可以尝试寻找一个中间的复杂度。
➤调整超参数
现在网络已经开始学习东西了,你可能会觉得非常不错。但你可能发现它不能解决这个问题中最困难的版本。超参数的调整就是其中的关键。也许有人仅仅下载了一个CNN包然后在上面跑自己的数据集,并告诉你超参数的调整并不会带来改变。你要认识到他们在用已有的框架解决已有的问题。如果你使用新架构解决新问题,则必须调试超参数来获得一个良好的配置。最好是为你的特定问题读一些超参数教程,但为了完整性我会在这里列出一些基本的想法:
可视化是关键。不要害怕花时间在整个训练过程中去写一些好用的可视化工具。如果你的可视化方法还是简单观察终端中的loss值变化,那你该考虑一下升级了。
权值初始化很重要。一般来说,大一点幅度的初始权值会好一些,但太大了就会导致NaN。因此初始权值需要和学习率一起调整。
确保权值看起来是“健康的”。要了解这是什么意思,我推荐用ipython notebook打开现有网络的权值。花一些时间来熟悉在标准数据集(如ImageNet或Penn Tree Bank)上训练的成熟网络中的组件的权值直方图应该是什么样子。
神经网络不是输入尺度不变的,尤其当它使用SGD训练而不是其他的二阶方法训练时,因为SGD不是一个尺度不变的方法。在确定缩放尺度之前,花点时间来尝试多次缩放输入数据和输出标签。
在训练结束之前减小学习率总能带来提升。最佳的decay策略是:在k个epoch后,每n个epoch之后将学习率除以1.5,其中k > n。
使用超参数配置文件。虽然在你开始尝试不同的值之前把超参数放在代码中也是ok的。我通过命令行参数加载的方式使用json文件,就像 Russell91/TensorBox 中一样,但是具体的形式并不重要。避免总是要去重构你的代码,因为那将是超参数加载的糟糕问题。重构引入了bugs,花费你的训练周期,这种情况能够被避免直到你有一个你觉得不错的网络。
随机地搜索超参数,如果可以的话。随机搜索可以产生你想不到的超参数组合, 并且能减少很大工作量一旦你已经训练形成了对于给定超参数会带来什么样的影响的直觉。
➤总结
调试神经网络可能比调试传统程序更费精力,因为几乎所有错误都被投射到整个网络表现的单一维度。尽管如此,二分查找仍然起作用。通过交替
1)调整问题的难度,和2)使用少量的训练样本,你可以快速解决最初的问题。然后超参数调整和长时间的等待就可以解决你剩下的问题了。
原文地址
http://russellsstewart.com/blog/0
译文地址
http://www.jianshu.com/p/ba2ac29810e6
AI100好评如潮的精品课《XGBoost从基础到实战》由资深讲师一步步带领大家从XGBoost的安装到一行行Python代码实现各类算法任务,不需要有很强的数学基础,直接从实战入手,学员们纷纷表示获益良多。快加入我们开启你的XGBoost探索之路!