导语:印度深度学习专家 Rishabh Shukla 对开发深度神经网络的经验总结。 本文为印度深度学习专家、创业者 Rishabh Shukla 在 GitHub 上发表的长博文,总结了他过去的开发经验,旨在给新入门的开发者提供指导。雷锋网做了不改变原意的编译。
在深度学习领域,为了高效训练深度神经网络,有些实践方法被过来人强烈推荐。
在这篇博文中,我会覆盖几种最常使用的实践方法,从高品质训练数据的重要性、超参数(hyperparameters)到更快创建 DNN(深度神经网络) 原型模型的一般性建议。这些推荐方法中的大多数,已被学术界的研究所证实,并在论文中展示了相关实验、数学证据,比如 Efficient BackProp(Yann LeCun et al.) 和 *** Practical Recommendations for Deep Architectures(Yoshua Bengio)***。
许多 ML 开发者习惯把原始训练数据直接扔给 DNN——为什么不这么做呢?既然任何 DNN (大多数人的假设)仍然能够给出不错的结果,不是吗?但是,有句老话叫“给定恰当的数据类型,一个简单的模型能比复杂 DNN 提供更好、更快的结果”。虽然这有一些例外,但在今天,这句话仍然没有过时。因此,不管你是在计算机视觉( CV),自然语言处理(NLP)还是统计建模(Statistical Modelling)等领域,想要对原始数据预处理,有几个方法可以得到更好的训练数据:
获取越大的数据库越好。DNN 对数据很饥渴,越多越好。
去除所有包含损坏数据的训练样本,比如短文字,高度扭曲的图像,假输出标签,包含许多虚值(null values)的属性。
Data Augmentation(数据扩张)——生成新样例。以图像为例,重新调节,增加噪声等等。
激励函数是所有神经网络的核心部分之一。
激励函数把渴望已久的非线性(non-linearity)加入了模型。多年来,Sigmoid 函数 一直是多数人倾向的选择。但是,Sigmoid 函数不可避免地存在两个缺陷:1. 尾部 sigmoids 的饱和,进一步导致梯度消失。2. 不以 0 为中心(输出在 0 到 1 之间)。
一个更好的替代选择是 Tanh 函数。数学上来说,Tanh 只是调整、平移过的 Sigmoid 函数:tanh(x) = 2*sigmoid(x) - 1。虽然 Tanh 仍旧存在梯度消失的缺陷,但好消息是:Tanh 以 0 为中心。因此,把 Tanh 作为激励函数能更快地收敛(converge)。我发现使用 Tanh 通常比 Sigmoid 效果更好。
你还可以探索其他选择,比如 ReLU, SoftSign 等等。对于一些特定任务, 它们能够改善上述问题。
保留超出最优数量的隐藏单元,一般是比较保险的做法。这是因为任何正则化方法( regularization method)都会处理好超出的单元,至少在某种程度上是这样。在另一方面,保留比最优数量更少的隐藏单元,会导致更高的模型欠拟合(underfitting)几率。
另外,当采用无监督预训练的表示时(unsupervised pre-trained representations,下文会做进一步解释),隐藏单元的最优数目一般会变得更大。因此,预训练的表示可能会包含许多不相关信息(对于特定任务)。通过增加隐藏单元的数目,模型会得到所需的灵活性,以在预训练表示中过滤出最合适的信息。
选择隐层的最优数目比较直接。正如 Yoshua Bengio 在 Quora 中提到的:
“你只需不停增加层,直到测试误差不再减少。”
永远用小的随机数字初始化权重,以打破不同单元间的对称性(symmetry)。但权重应该是多小呢?推荐的上限是多少?用什么概率分布产生随机数字?
当使用 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 是下一层的。
这或许是最重要的超参数之一,调节着学习过程。如果学习率设置得太小,你的模型很可能需要 n 年来收敛。设置得太大,再加上不多的初始训练样本,你的损失可能会极高。一般来说,0.01 的学习率比较保险
相比固定学习率,在每个周期、或每几千个样例后逐渐降低学习率是另一个选择。虽然这能更快地训练,但需要人工决定新的学习率。一般来说,学习率可以在每个周期后减半。几年前,这种策略十分普遍。
幸运的是,我们现在有了更好的、基于动能(momentum based)的方法,来调整学习率。这取决于误差函数的曲率。另外,既然有些参数有更快、或更慢的学习速率;它或许能帮助我们针对模型中的单独参数,设定不同的学习率。
最近有大量关于优化方法的研究,导致了自适应学习率(adaptive learning rates)。目前我们有许多选择,从老式动能方法( Momentum Method ),到 Adagrad、Adam (个人最爱)、 RMSProp 等等。;类似于 Adagrad 或 Adam 的方法,能替我们省去人工选择初始学习率的麻烦;给定合适的时间,模型会开始平滑地收敛。当然,选择一个特别合适的初始学习率仍然能起到帮助作用。
网格搜索(Grid Search )在经典机器学习中十分普遍。但它在寻找 DNN 的最优超参数方面一点也不高效。这主要是由于 DNN 尝试不同超参数组合所耗费的时间。随着超参数不断增长,网格搜索需要的计算性能会指数级增长。
有两种解决办法:
取决于你之前的经验,你可以人工对部分常见超参数调参,比如学习率、隐层数目。
采用随机搜索(random search),或者随机采样代替网格搜索,来选择最优超参数。
超参数组合通常在期望范围之内、从均匀分布中被选择出来。加入之前获得的知识来进一步缩小搜寻空间,也是有可能的(比如,学习率不应该太大也不应该太小)。大家发现,随机搜索比网格搜索高效地多。
随机梯度下降( Stochastic Gradient Descent )的老方法也许对于 DNN 不是那么有效率(有例外)。最近,有许多研究聚焦于开发更灵活的优化算法,比如 Adagrad、Adam,、AdaDelta,、RMSProp 等等。在提供自适应学习率之外,这些复杂的方法还对于模型的不同参数使用不同的学习率,通常能有更平滑的收敛。把这些当做超参数是件好事,你应该每次都在训练数据的子集上试试它们。
即便是运行最先进的深度学习模型,使用最新、最强大的计算硬件,内存管理仍然在字节(byte)级别上进行。所以,把参数保持在 64, 128, 512, 1024 等 2 的次方永远是件好事。这也许能帮助分割矩阵和权重,导致学习效率的提升。当用 GPU 运算,这变得更明显。
不管你进行的是 NLP(自然语言处理)、计算机视觉还是语音识别等任务,无监督预训练永远能帮助你训练监督、或其他无监督模型:NLP 中词向量就(Word Vectors)无所不在;你可以用 ImageNet 的数据库,使用无监督方式对你的模型预训练,或是对于两个类别的监督分类;或是更大频域的音频样本,来在扬声器消崎模型(speaker disambiguation model)中使用该信息。
训练一个模型的主要目的是学习合适的参数,即产生输入到输出的最优映射。这些参数利用每个训练样本进行调参,不管你决定使用 batch, mini-batch 还是随机学习。当采用随机学习方法时,学习每个训练样本后权重的梯度都会进行调参,向梯度加入噪音(随机学习中“随机”的由来)。这样做的结果十分理想,比如说,训练中加入的噪音使得模型更不容易过拟合。
但是,随机学习方法也许效率不高。如今的计算设备有非常可观的运算能力,随机学习很可能会浪费其中的一大部分。如果我们能计算矩阵相乘,那么为什么要限制自己,重复单个矢量组之间的乘法呢?因此,为了更高的吞吐率和更快的学习,我推荐使用 mini-batch 而不是随机学习。
但是,选择适当的 batch 规模同样重要。所以我们能保留一些噪音(相比大规模 batch),与此同时更高效地利用计算性能。一般来说,包含 16 个到 128 个样例的 batch(2 的幂)是不错的选择。通常,一旦你发现了更重要的超参数(通过随机搜索或是人工搜索),batch 规模就会确性下来。但是,有些场景中模型得到训练数据流(比如网络学习),那么采用随机学习就是不错的选择。
这来自于信息理论(Information Theory)——“学习到一件不太可能发生的事却发生了,比学习一件很可能发生的事已经发生,包含更多的信息。”同样的,把训练样例的顺序随机化(在不同周期,或者 mini-batch),会导致更快的收敛。如果模型看到的很多样例不在同一种顺序下,运算速度会有小幅提升。
如果有数百万的参数需要学习,正则化就是避免 DNN 过拟合的必须手段。你也可以继续使用 L1/L2 正则化,但 Dropout 是检查 DNN 过拟合的更好方式(雷锋网按:Dropout 是指随机让网络某些隐层节点的权重不工作,不工作的那些节点可以暂时认为不是网络结构的一部分,但是它的权重会保留下来)。执行 Dropout 很容易,并且通常能带来更快地学习。0.5 的默认值是一个不错的选择,当然,这取决于具体任务。如果模型不太复杂,0.2 的 Dropout 值或许就够了。
在测试阶段,Dropout 应该被关闭,权重要调整到相应大小。只要对一个模型进行 Dropout 正则化,多一点训练时间,误差一定会降低。
“对深度学习模型进行多个周期的训练,会得到更好的模型”——我们经常听到这句话。但多少周期才是“多”呢?其实,这里有一个简单的策略:继续按照一个固定的样例数或者周期训练模型,比如两万个样例或者一个周期。在每批样例之后,比较测试误差(test error)和训练误差(train error),如果它们的差距在缩小,那么继续训练。另外,记得在每批训练之后,保存模型的参数,所以训练好之后你可以从多个模型中做选择。
训练深度学习模型有上千种出差错的方式。我猜大家都遇到过这样的场景:模型已经训练了几个小时或者好几天,然而在训练完成之后,才意识到某个地方出问题了。为了不让你自己神经错乱,一定要对训练过程作可视化处理。比较显而易见的措施是保存或打印损失值、训练误差、测试误差等项目的日志。
在此之外,一个很好的措施是采用可视化库(visualization library ),在几个训练样例之后、或者周期之间,生成权重柱状图。这或许能帮助我们追踪深度学习模型中的一些常见问题,比如梯度消失与梯度爆发(Exploding Gradient)。
谢天谢地,对于快速创建原型模型,我们已经有了相当不错的库,比如 Theano, Tensorflow, Keras 等等。几乎所有这些深度学习库支持 GPU 计算和自动微分法。所以,你不需要深入研究核心 GPU 编程技术(除非你想——这绝对很有意思)。你也不需要写自己的微分代码——在非常复杂的模型上这相当费劲(但若需要,你应该有能力去做)。 Tensorflow还提供了分布式计算的支持——如果你是土豪的话.
先谈定义,卷积神经网络是一种特殊的神经网络,其中至少包含一个卷积层。在典型的CNN网络结构中,输入一张图像,经由一系列卷积层、非线性激活层、池化层和全连接层后,可输出相应的类别标签。
卷积神经网络的特别之处在于加入了卷积层。
在经典的神经网络中,整张图片会被传入网络中来训练各网络层权值。当输入为简单居中的图像时,如Mnist手写数字图,网络识别效果较优,但是当输入变为更为复杂多变的图像时,如跳上窗户的小猫,此时网络识别效果不佳甚至无法辨识。
加入更多隐含层学习输入图像的抽象特征,可能会有所帮助,但是这样会增加神经元的数目,大大增加训练所需的计算资源和占用过多的内存,这是不切实际的。
而CNN识别目标的过程,是先寻找诸如边缘、线段和曲线等相关低级特征,然后使用多个卷积层来构建更多抽象的高级特征。
在卷积层的学习过程中,CNN网络通过共享多个卷积核(或特征检测器)的权值,来学习每张图片的局部信息,并用于构建抽象特征图谱。卷积核共享特性大大降低了训练网络所需的参数量。 由于经过训练的检测器可以通过卷积层重复用来组合地检测图片中的抽象特征,因此卷积神经网络更适用于复杂的图像识别任务。
在深度神经网络中,调整超参数组合并非易事,因为训练深层神经网络十分耗时,且需要配置多个参数。 接下来,我们简单列举几个影响CNN网络的关键超参数。
学习率是指在优化算法中更新网络权重的幅度大小。
学习率可以是恒定的、逐渐降低的、基于动量的或者是自适应的,采用哪种学习率取决于所选择优化算法的类型,如SGD、Adam、Adagrad、AdaDelta或RMSProp等算法。
在卷积神经网络的学习过程中,小批次会表现得更好,选取范围一般位于区间[16,128]内。 还需要注意的是,CNN网络对批次大小的调整十分敏感。
激活函数具有非线性,理论上可以使模型拟合出任何函数。通常情况下,rectifier函数在CNN网络中的效果较好。当然,可以根据实际任务,选择其他类型的激活函数,如Sigmoid和Tanh等等。
增加隐含层数目以加深网络深度,会在一定程度上改善网络性能,但是当测试错误率不再下降时,就需要寻求其他的改良方法。增加隐含层数目也带来一个问题,即提高了训练该网络的计算成本。
当网络的单元数设置过少时,可能会导致欠拟合,而单元数设置过多时,只要采取合适的正则化方式,就不会产生不良影响。
在网络中,通常会使用小随机数来初始化各网络层的权重,以防止产生不活跃的神经元,但是设置过小的随机数可能生成零梯度网络。一般来说,均匀分布方法效果较好。
作为一种常用的正则化方式,加入Dropout层可以减弱深层神经网络的过拟合效应。该方法会按照所设定的概率参数,在每次训练中随机地不激活一定比例的神经单元。该参数的默认值为0.5。
手动调整超参数是十分费时也不切实际。接下来介绍两种搜索最优超参数的常用方法。
卷积层,来更好地了解CNN网络是如何学习输入图像的特征。
可视化有两种直接方式,分别是可视化激活程度和可视化相关权重。在网络训练过程中,卷积层的激活情况通常会变得更为稀疏和具有局部特性。当不同输入图像的激活图都存在大片未激活的区域,那么可能是设置了过高的学习率使得卷积核不起作用,导致产生零激活图像。
性能优良的神经网络通常含有多个明显而平滑的卷积器,且没有任何干扰特征。若在权重中观察到相关干扰特征,可能原因是网络未被充分训练,或是正则化强度较低导致了过拟合效应。
