1.训练/开发/测试集
训练集(Training sets)、验证集(Development sets)、测试集(Test sets)的优劣对神经网络的性能有非常重要的影响。此外,构建神经网络时,还需要设置许多参数,例如神经网络的层数、每个隐藏层包含的神经元个数、学习因子(学习速率)、激活函数的选择等等。我们很难一开始就选择最佳的参数,需要通过不断地迭代更新来获得。
循环迭代的过程如下:先有个想法Idea,选择初始的参数值,构建神经网络模型结构;然后通过代码Code的形式,实现这个神经网络;最后,通过实验Experiment验证这些参数对应的神经网络的表现性能。根据验证结果,对参数进行适当的调整优化,再进行下一次的Idea->Code->Experiment循环。通过很多次的循环,不断调整参数,选定最佳的参数值,从而让神经网络性能最优化。
通常来说,最适合某个领域的深度学习网络往往不能直接应用在其它问题上。解决不同问题的最佳选择是根据样本数量、输入特征数量和电脑配置信息(GPU或者CPU)等,来选择最合适的模型。因此,应用深度学习是一个反复迭代的过程,需要通过反复多次的循环训练得到最优化参数。决定整个训练过程快慢的关键在于单次循环所花费的时间,单次循环越快,训练过程越快。而设置合适的Train/Dev/Test sets数量,能有效提高训练效率。
一般将所有的样本数据分成三个部分:Train/Dev/Test sets(训练集,(简单交叉)验证集,测试集) 。Train sets用来训练算法模型;Dev sets用来验证不同算法的表现情况,从中选择最好的算法模型;Test sets用来测试最好算法的实际表现,作为该算法的无偏估计。
最常见的方法是将所有数据三七分,即设置Train sets和Test sets的数量比例为70%和30%。如果有Dev sets,则设置比例为60%、20%、20%,分别对应Train/Dev/Test sets。但是如果数据量很大的时候,比如百万级的数据,这种比例分配就不太合适了。科学的做法是要将Dev sets和Test sets的比例设置得很低。因为Dev sets的目标是用来比较验证不同算法的优劣,从而选择更好的算法模型就行了。通常不需要所有样本的20%这么多的数据来进行验证。对于100万的样本,往往只需要10000个样本来做验证就够了。Test sets也是一样,目标是测试已选算法的实际表现,无偏估计。对于100万的样本,往往也只需要10000个样本就够了。因此,对于大数据样本,Train/Dev/Test sets的比例通常可以设置为98%/1%/1%,或者99.5%/0.25%/0.25%或者99.5%/0.4%/0.1%。样本数据量越大,相应的Dev/Test sets的比例可以设置的越低一些。
现代深度学习还有个重要的问题就是训练样本和测试样本分布上不匹配,意思是训练样本和测试样本来自于不同的分布。我们需要尽量保证Dev sets和Test sets来自于同一分布。通常我们可以对现有的训练样本做翻转、假如随机噪声等操作,来扩大训练样本的数量,从而让该模型更加强大。即使Train sets和Dev/Test sets不来自同一分布,使用这些技巧也能提高模型性能。
如果没有Test sets也是没有问题的。Test sets的目标主要是进行无偏估计。如果不需要无偏估计,可以不设置测试集。我们可以通过Train sets训练不同的算法模型,在Dev sets上进行验证,根据结果选择最好的算法模型,不再进行无偏估计。如果只有Train sets和Dev sets,有人会把这里的Dev sets称为Test sets。
2.偏差/方差
偏差(Bias)和方差(Variance)是机器学习领域非常重要的两个概念和需要解决的问题。在传统的机器学习算法中,Bias和Variance是对立的,分别对应着欠拟合和过拟合,我们常常需要在Bias和Variance之间进行权衡。而在深度学习中,我们可以同时减小Bias和Variance,构建最佳神经网络模型。
如下图所示,显示了二维平面上,high bias,just right,high variance的例子。可见,high bias对应着欠拟合,而high variance对应着过拟合。
对于输入特征是高维的情况,可以通过两个数值Train set error(训练集误差)和Dev set error(验证集误差)来理解bias和variance。
假设Train set error为1%,而Dev set error为11%,即该算法模型对训练样本的识别很好,但是对验证集的识别却不太好。这说明了该模型对训练样本可能存在过拟合,模型泛化能力不强,导致验证集识别率低。这恰恰是high variance的表现。假设Train set error为15%,而Dev set error为16%,虽然二者error接近,即该算法模型对训练样本和验证集的识别都不是太好。这说明了该模型对训练样本存在欠拟合。这恰恰是high bias的表现。假设Train set error为15%,而Dev set error为30%,说明了该模型既存在high bias也存在high variance(深度学习中最坏的情况)。再假设Train set error为0.5%,而Dev set error为1%,即low bias和low variance,是最好的情况。值得一提的是,以上的这些假设都是建立在base error是0的基础上,即最优误差(贝叶斯误差)约为0 。base error不同,相应的Train set error和Dev set error会有所变化,但没有相对变化。
一般来说,Train set error体现了是否出现bias,Dev set error体现了是否出现variance(正确地说,应该是Dev set error与Train set error的相对差值)。
模型既存在high bias也存在high variance,可以理解成某段区域是欠拟合的,某段区域是过拟合的。
3.机器学习基础
机器学习要避免出现high bias和high variance。
减少high bias的方法通常是增加神经网络的隐藏层个数、神经元个数,训练时间延长,选择其它更复杂的NN模型等。在base error不高的情况下,一般都能通过这些方式有效降低和避免high bias,至少在训练集上表现良好。
减少high variance的方法通常是增加训练样本数据,进行正则化Regularization,选择其他更复杂的NN模型等。
解决high bias和high variance的方法是不同的。实际应用中通过Train set error和Dev set error判断是否出现了high bias或者high variance,然后再选择针对性的方法解决问题。
Bias和Variance的折中tradeoff。传统机器学习算法中,Bias和Variance通常是对立的,减小Bias会增加Variance,减小Variance会增加Bias。而在现在的深度学习中,通过使用更复杂的神经网络和海量的训练样本,一般能够同时有效减小Bias和Variance。这也是深度学习之所以如此强大的原因之一。
4.正则化
如果出现了过拟合(high variance),可以采用正则化的方法来解决。减小high variance也可以采用扩大样本数的方法,但扩大训练集比较难,所以正则化更可行。
L1 regularization表达式为:
L2 regularization表达式为:
只对w进行正则化而不对b进行正则化的原因如下:一般w的维度很大,而b只是一个常数。相比较来说,参数很大程度上由w决定,改变b值对整体模型影响较小。所以,一般为了简便,就忽略对b的正则化了。
L1 regularization比L2 regularization得到的w更加稀疏,即很多w为零值。其优点是节约存储空间,因为大部分w为0。L1 regularization在解决high variance方面不如L2 regularization。而且,L1的在微分求导方面比较复杂。L2 regularization更加常用。
L1、L2 regularization中的λ
就是正则化参数(超参数的一种)。可以设置λ为不同的值,在Dev set中进行验证,选择最佳的λ。在python中,由于lambda是保留字,所以为了避免冲突,使用lambd来表示λ。
在深度学习模型中,L2 regularization的表达式为:
(所有元素的平方求和)
通常,我们把称为Frobenius范数,记为
。一个矩阵的Frobenius范数就是计算所有元素平方和再开方,如下所示:
由于加入了正则化项,梯度下降算法中的计算表达式需要做如下修改:
L2 regularization也被称做weight decay。因为加上正则项导致有个增量,在更新
的时候,会多减去这个增量,使得
比没有正则项的值要小一些。不断迭代更新,不断减小。
其中,
5.为什么正则化可以减少过拟合
从左到右,分别表示了欠拟合,刚好拟合,过拟合三种情况。
假如我们选择了非常复杂的神经网络模型,如上图左上角所示。在未使用正则化的情况下,我们得到的分类超平面可能是类似上图右侧的过拟合。但是,如果使用L2 regularization, 当λ很大时,
。
近似为零,意味着某些神经元实际的作用很小,可以忽略。从效果上来看,其实是忽略了某些神经元。将过于复杂的神经网络模型就变得非常简单化。如下图所示,整个简化的神经网络模型变成了一个逻辑回归模型。问题就从high variance变成了high bias了。
因此,选择合适大小的λ值,就能够同时避免high bias和high variance,得到最佳模型。
还有另外一个直观的例子来解释为什么正则化能够避免发生过拟合。假设激活函数是tanh函数。tanh函数的特点是在z接近零的区域,函数近似是线性的,而当|z|很大的时候,函数非线性且变化缓慢。当使用正则化,λ较大,即对权重的惩罚较大,
减小。因为
,当
减小的时候,
也会减小。则此时的
分布在tanh函数的近似线性区域。那么这个神经元起的作用就相当于是linear regression。如果每个神经元对应的权重
都比较小,那么整个神经网络模型相当于是多个linear regression的组合,即可看成一个linear network。得到的分类超平面就会比较简单,不会出现过拟合现象。但它不适用于非常复杂的决策以及过度拟合数据集的非线性决策边界。
正则化时应用之前定义的代价函数,通过修改增加了一项,目的是预防权重过大。如果使用的是梯度下降函数,在调试梯度下降时,代价函数需要对梯度下降的每个调幅单调递减;如果使用正规化函数,需要用代价函数J的新的定义公式。
6.Dropout正则化
除了L2 regularization之外,还有另外一种防止过拟合的有效方法:Dropout(随机失活)。
Dropout是指在深度学习网络的训练过程中,对于每层的神经元,按照一定的概率将其暂时从网络中丢弃。也就是说,每次训练时,每一层都有部分神经元不工作,起到简化复杂网络模型的效果,从而避免发生过拟合。
Dropout有不同的实现方法,一种常用的方法是Inverted dropout(反向随机失活)。假设对于第层神经元,设定保留神经元比例概率keep_prob=0.8,即该层有20%的神经元停止工作。
为dropout向量,设置
为随机vector,其中80%的元素为1,20%的元素为0。在python中可以使用如下语句生成dropout vector:
dl = np.random.rand(al.shape[0],al.shape[1])<keep_prob
然后,第层经过dropout,随机删减20%的神经元,只保留80%的神经元,其输出(从第
层获取的激活函数)为:
al = np.multiply(al,dl)
这里是元素相乘,作用是过滤中所有等于0的元素。
最后,还要对进行scale up处理,即:
al /= keep_prob
对进行scale up是为了保证在经过dropout后,
作为下一层神经元的输入值尽量保持不变。假设第
层有50个神经元,经过dropout后,有10个神经元停止工作,这样只有40神经元有作用。那么得到的
只相当于原来的80%。scale up后,能够尽可能保持
的期望值相比之前没有大的变化。
同时,在训练时使用了scale up保证的期望值没有大的变化,测试时就不需要再对样本数据进行类似的尺度伸缩操作了。
对于m个样本,单次迭代训练时,随机删除掉隐藏层一定数量的神经元;然后,在删除后的剩下的神经元上正向和反向更新权重w和常数项b;接着,下一次迭代中,再恢复之前删除的神经元,重新随机删除一定数量的神经元,进行正向和反向更新w和b。不断重复上述过程,直至迭代训练完成。
值得注意的是,使用dropout训练结束后,在测试和实际应用模型时,不需要进行dropout和随机删减神经元,所有的神经元都在工作。因为我们不期望预测结果是随机的。
7.理解Dropout
Dropout通过每次迭代训练时,随机选择不同的神经元,相当于每次都在不同的神经网络上进行训练,能够防止过拟合。
除此之外,对于某个神经元来说,某次训练时,它的某些输入被删除了。而在下一次训练时,又有不同的某些输入被删除。经过多次训练后,某些输入被删除,某些输入被保留。这样,该神经元不会受某个特定输入非常大的影响,影响被均匀化了。也就是说,每个输入对应的权重w不会很大。与L2 regularization类似,都是减小w。
对于同一组训练数据,利用不同的神经网络训练之后,求其输出的平均值可以减少过拟合。Dropout每次丢掉一定数量的隐藏层神经元,相当于在不同的神经网络上进行训练,这样就减少了神经元之间的依赖性,即每个神经元不能依赖于某几个其他的神经元(指层与层之间相连接的神经元),使神经网络更加能学习到与其他神经元之间的更加健壮robust的特征。
不同隐藏层的dropout系数keep_prob可以不同。一般来说,神经元越多的隐藏层,keep_out可以设置得小一些.,例如0.5;神经元越少的隐藏层,keep_out可以设置的大一些,例如0.7,如果不担心过拟合问题,可以设置为1。另外,实际应用中,不建议对输入层进行dropout,如果输入层维度很大,例如图片,那么可以设置dropout,但keep_out应设置的大一些,例如0.8,0.9。总体来说,就是越容易出现overfitting的隐藏层,其keep_prob就设置的相对小一些。
dropout是一种regularization技巧,用来防止过拟合的,最好只在需要regularization的时候使用dropout。dropout主要存在于计算机视觉领域,因为输入维度较大,通常没有足够的样本量。
dropout一大缺点是代价函数不再被明确定义,每次迭代都会随机移除一些节点。可以关闭dropout,运行代码,确保代价函数J单调递减,然后再打开dropout函数。
8.其他正则化方法
除了L2正则化和dropout方法外,还有其他一些方法可以减少过拟合。
一种方法是增加训练样本数量。但是通常成本较高,难以获得额外的训练样本。但是,我们可以对已有的训练样本进行一些处理来“制造”出更多的样本,称为data augmentation。例如图片识别问题中,可以对已有的图片进行水平翻转(训练集可以增大一倍)、垂直翻转、任意角度旋转、缩放或扩大、旋转缩放裁剪等等。在光学字符识别中,也可以将原有的数字图片进行任意旋转或者扭曲,或者增加一些noise。 通过人工合成数据的话,需要通过算法验证原图的含义没有变(例如经过水平翻转猫还是猫)
还有另外一种防止过拟合的方法:early stopping。一个神经网络模型随着迭代训练次数增加,train set error和代价函数J一般是单调减小的,而dev set error 先减小,之后又增大。也就是说训练次数过多时,模型会对训练样本拟合的越来越好,但是对验证集拟合效果逐渐变差,即发生了过拟合。因此,迭代训练次数不是越多越好,可以通过train set error和dev set error随着迭代次数的变化趋势,选择合适的迭代次数,即early stopping。
但是,通常来说,机器学习训练模型有两个目标:一是优化cost function,尽量减小代价函数J;二是防止过拟合(减小方差)。这两个目标彼此对立的。这二者之间的关系称为正交化(在一个时间做一个任务)。在深度学习中,可以同时减小Bias和Variance,构建最佳神经网络模型。但是,Early stopping通过减少训练次数来防止过拟合,但这样J就不会足够小。
L2 regularization可以实现迭代训练足够多,减小J,而且也能有效防止过拟合。L2 regularization的缺点之一是最优的正则化参数的选择比较复杂,需要尝试许多不同的值。early stopping比较简单,只需要运行一次梯度下降,就可以找出w的较小值,中间值和较大值,无需尝试那么多次。但L2 regularization还是更常用一些。
9.正则化输入
训练神经网络时,标准化输入能够加速神经网络训练。标准化输入就是对训练数据集进行归一化的操作,即将原始数据减去其均值后,再除以其方差
:
(第一步:零均值化)
(第二步:所有数据除以
)
以二维平面为例,下图展示了其归一化过程:
由于训练集进行了标准化处理,那么测试集或在实际应用时,应该使用同样的和
对其进行标准化处理。保证训练集和测试集的标准化操作一致。
对输入进行标准化操作,主要是为了让所有输入归一化同样的尺度上,梯度下降时能够更快更准确地找到全局最优解。假如输入特征是二维的,且x1的范围是[1,1000],x2的范围是[0,1]。如果不进行标准化处理,x1与x2之间分布极不平衡,训练得到的w1和w2也会在数量级上差别很大。这样导致的结果是cost function与w和b的关系可能是一个非常细长的椭圆形碗。对其进行梯度下降时,由于w1和w2数值差异很大,只能选择很小的学习因子,来避免J发生振荡。一旦
较大,必然发生振荡,J不再单调下降。如下左图所示。
如果输入特征之间的范围本来就比较接近,那么不进行标准化操作也是没有太大影响的。但是,标准化处理在大多数场合下还是值得推荐的。
10.梯度消失与梯度爆炸
梯度消失和梯度爆炸是指当训练一个层数非常多的神经网络时,计算得到的梯度可能非常小或非常大,甚至是指数级别的减小或增大。这样会让训练过程变得非常困难。
假设一个多层的每层只包含两个神经元的深度神经网络模型,如下图所示:
为了简化复杂度,便于分析,我们令各层的激活函数为线性函数,即。且忽略各层常数项b的影响,令b全部为零。那么,该网络的预测输出
为:
如果各层权重的元素都稍大于1,例如1.5,则预测输出
(
)正比于
。L越大,
呈指数型增长,即数值爆炸。相反,如果各层权重
的元素都稍小于1,例如0.5,则预测输出
正比于
。网络层数L越多,
呈指数型减小,即数值消失。
也就是说,如果各层权重都大于1或者都小于1,那么各层激活函数的输出将随着层数
的增加,呈指数型增大或减小。当层数很大时,出现数值爆炸或消失。同样,这种情况也会引起梯度呈现同样的指数型增大或减小的变化。L非常大时,梯度会非常大或非常小,引起每次更新的步进长度过大或过小,让训练过程十分困难。
11.神经网络的权重初始化
改善梯度爆炸或梯度消失问题(不能彻底解决),可以通过权重初始化的方式。
深度神经网络模型中,以单个神经元为例,该层()的输入个数为n,其输出为:
忽略常数项b。为了让z不会过大或者过小,思路是让w与n有关,且n越大,w应该越小。这样能够保证z不会过大。一种方法是在初始化w时,令其方差为。
如果激活函数为tanh,一般选择如下初始化方法(伪代码):
w[l] = np.random.randn(n[l],n[l-1])*np.sqrt(1/n[l-1])
如果激活函数为ReLU,一般令方差为,选择如下初始化方法(伪代码):
w[l] = np.random.randn(n[l],n[l-1])*np.sqrt(2/n[l-1])
此外,还有一种方法,令方差为,选择如下初始化方法(伪代码):
w[l] = np.random.randn(n[l],n[l-1])*np.sqrt(2/n[l-1]*n[l])
另外,我们可以对这些初始化方法中设置某些方差参数,作为超参数,通过验证集进行验证,得到最优方差参数,来优化神经网络,但这种参数的优先级通常比较低。
12.梯度的数值逼近
Back Propagation神经网络有一项重要的测试是梯度检查。其目的是检查验证反向传播过程中梯度下降算法是否正确。下面是近似求出梯度的方法:
利用微分思想,函数f在点处的梯度可以表示成:
其中,,且足够小。双边误差公式比单边误差公式准确。
13.梯度检验
梯度检查首先要做的是分别将这些矩阵构造成一维向量,然后将这些一维向量组合起来构成一个更大的一维向量
。这样cost function
就可以表示成
。
然后将反向传播过程通过梯度下降算法得到的按照一样的顺序构造成一个一维向量
的维度与
一致。
接着利用对每个
计算近似梯度,其值与反向传播算法得到的
相比较,检查是否一致。例如,对于第i个元素,近似梯度为:
计算完所有的近似梯度后,可以计算
与
距离来比较二者的相似度。公式如下:
一般来说,如果欧氏距离越小,例如,甚至更小,则表明
与
越接近,即反向梯度计算是正确的,没有bug。如果欧氏距离较大,例如
,则表明梯度计算可能出现问题,需要再次检查是否有bug存在。如果欧氏距离很大,例如
,甚至更大,则表明梯度下降计算过程存在bug,需要仔细检查。
14.关于梯度检验实验的注记
在进行梯度检查的过程中有几点需要注意的地方:
-
不要在整个训练过程中都进行梯度检查,仅仅作为debug使用。
-
如果梯度检查出现错误,找到对应出错的梯度,检查其推导是否出现错误。
-
注意不要忽略正则化项,计算近似梯度的时候要包括进去。
-
梯度检查时关闭dropout,检查完毕后再打开dropout。
-
随机初始化时运行梯度检查,经过一些训练后再进行梯度检查(不常用)。