在深度学习中,为了有效地训练深度神经网络,有一些值得我们强烈推荐的做法。在本文中,我将介绍一些最常用的方法,从高质量训练数据的重要性,到超参数的选择,再到更快的做出 DNN 原型的通用技巧。这些方法的大多数由学术界和工业界的研究验证过,并且在诸如 Yann LeCun 等人写的《Efficient BackProp》和 Yoshua Bengio 的《Practical Recommendations for Deep Architectures》中也给出了数学和实验证明。
你会注意到,我没有在这篇文章中提到任何数学证明。在这里建议的所有要点,更多地应该被看作是 DNN 的最佳训练方法中获得的总结。为了更深入地了解,我强烈建议您最后看一遍上面提到的研究论文和后面提到的参考文献。
训练数据
许多机器学习从业者习惯于在任何深度神经网络(DNN)中使用原始(raw)训练数据。为什么不这样做呢,任何 DNN(大概)仍会给出好的结果,对吧?但是,说出「给出正确的数据类型,一个十分简单的模型会比一个复杂的 DNN 更快地给出更好的结果」(虽然,这可能会有例外)并不是很守旧派的。因此,无论你是在做计算机视觉,还是在做自然语言处理或统计建模等,你都应该尝试预处理您的原始数据。你可以采取以下几个方法来获得更好的训练数据:
尽可能大的数据集(DNN 对数据的需求是相当大的:越多越好)
删除所有具有损坏数据的训练样本(短文本、高失真图像、虚假输出标签、具有大量空值的特征等)
数据增强——创建新样本(如果是图像,可以重新缩放、添加噪声等)
选择适当的激活函数
激活函数在任何神经网络中都是重要组成部分之一。激活将大家非常期望的非线性效果引入到了模型中。多年来,sigmoid 激活函数一直是最好的选择。但是 sigmoid 函数本质上有两个缺陷:
1.sigmoid 尾部的饱和(会进一步导致梯度消失问题)
2.sigmoid 不是以 0 为中心的。
一个更好的选择是 tanh 函数——在数学上,tanh 只是一个重新缩放和移位的 sigmoid,tanh(x) = 2*sigmoid(x) - 1。虽然 tanh 仍然可能遭受梯度消失问题,但好消息是 tanh 是以零为中心的。因此,使用 tanh 为激活函数可以更快地收敛。我使用中也发现使用 tanh 作为激活函数通常比使用 sigmoid 函数好。
你可以根据具体任务进一步探索其它选择,如已经表现出可以改善一些问题的 ReLU,SoftSign 等函数。
隐含单元和隐含层的数量
使用比最佳隐含单元数更多的数量通常是安全的。因为,任何正则化方法在一定程度上都可以处理多余的单元。而另一方面,使用比最佳隐含单元数更少的数量时,发生欠拟合的概率更高一些。
此外,当采用无监督学习预训练的表示(pre-trained representations,在后面部分中描述)时,隐含单元数的最佳数量通常要更大一些。因为,在各种表示中(对于特定的监督任务),预训练表示可能会包含大量的无关信息。通过增加隐含单元的数量,模型将具有足够支持从预训练表示中过滤出最合适的信息的灵活性。
选择最佳隐含层数是相对简单的。正如 Yoshua Bengio 在 Quora 上提到的:「你只需要继续添加层,直到测试错误不再改善为止」。
权重初始化
始终使用小随机数(random numbers)初始化权重,以打破不同单元之间的对称性。但是权重应该多小呢?推荐的上限是多少?使用什么概率分布来生成随机数?此外,当使用 sigmoid 激活函数时,如果权重被初始化为非常大的数,则 sigmoid 函数将会饱和(尾部区域),导致死亡神经元(dead neurons)。如果权重非常小,则梯度也会很小。因此,最好在中间范围选择权重,并且使它们围绕平均值均匀分布。
幸运的是,目前已经有很多关于初始权重的适当值的研究,这对于有效的收敛是非常重要的。为了初始化得到均匀分布的权重,uniform distribution 可能是最好的选择之一。此外,如论文(Glorot and Bengio, 2010)所示,具有更多传入连接(fan_in)的单元应具有相对较小的权重。
由于所有这些深入的实验,现在我们有一个测量公式,可以直接用于权重初始化;例如从~ Uniform(-r, r) 范围获得权重,对于 tanh 作为激活函数的时候,r=sqrt(6/(fan_in+fan_out));而对于 sigmoid 作为激活函数的时候,r=4*(sqrt(6/fan_in+fan_out)),其中 fan_in 是上一层的大小,fan_out 是下一层的大小。