1 训练/验证/测试集

机器学习/深度学习中,一般会把数据集分为训练集(training set)、验证集(validation set/development set,dev set)、测试集(test set)三部分。

训练集用于对模型进行训练;验证集用于对单一指标进行确定,如选择算法,确定参数;测试集用于对最终的模型性能进行评估。

传统机器学习中,样本量有限,可能是100,1000,10000条数据,一般把数据集按照 训练集:测试机 = 7:3 或者 训练集:验证集:测试集 = 6:2:2 的比例进行划分;但是在深度学习中,样本集规模很大,没必要划分特别大的验证集和测试集,因此一般划分比例为 训练集:验证集:测试集 = 98:1:1 或者 训练集:验证集:测试集 = 99.5:0.25:0.25 进行划分。

一定要保证验证集和测试集服从相同的数据分布。

在很多实际应用中,以计算机视觉任务为例,训练集都是很多的高清图像,但是验证集和测试集都是一些用户随意拍摄的图像,质量相对差很多。在这些情况下,一定要保证验证集和测试集符合相同的数据分别。因为,验证集的目的是根据不同算法/参数在验证集上的性能进行算法/参数的选择,如果验证集的数据分布和测试集的不一致,那么我们最终依据验证集选择的算法/参数并不能很好的匹配测试集,就会造成模型的实际应用效果较差。

很多时候没有测试集,只有训练集和验证集也无关紧要。这种情况下,在训练集上训练不同的算法/参数,在验证集上进行测试,以其在测试集上的性能优劣作为选择依据也是可以的。很多情况下,如果只有训练集/验证集,人们也一般就把验证集叫做测试集。

搭建训练/验证/测试集可以加速模型的集成,也可以更有效地衡量模型的偏差和方差,从而帮助我们更有效地选择合适的方法来优化算法。

2 偏差&方差

python数据预测的训练集和测试集的关系 测试集和训练集的比例_方差

前置条件

贝叶斯最优估计误差是1%

贝叶斯最优估计误差是1%

贝叶斯最优估计误差是15%

训练集误差

1%

15%

15%

15%

验证集误差

16%

17%

30%

17%

结论

高方差、过拟合

高偏差、欠拟合

高偏差、高方差

很接近最优模型

高偏差且高方差是模型整体结构很简单,对数据整体拟合程度较差,但同时又对部分数据拟合程度特别高。

结论:

  • 通过对比贝叶斯最优估计误差和训练集误差之间的差值,可以确定是否发生了高偏差(欠拟合);
  • 通过对比训练集误差和验证集误差之间的差值,可以确定是否发生了高方差(过拟合)。

训练模型过程中的思考思路:

训练完的模型偏差较大? ------------- Y -------------> 使用规模更大的网络
|
N
|
训练完的模型方差较大? ------------- Y ------------> 使用更多的训练数据/正则化
|
N
|
Done

3 正则化

正则化可以减少模型的方差,防止过拟合

3.1 L2正则化

logistic regression:

参数:python数据预测的训练集和测试集的关系 测试集和训练集的比例_方差_02

目标:python数据预测的训练集和测试集的关系 测试集和训练集的比例_正则化_03

添加正则化:python数据预测的训练集和测试集的关系 测试集和训练集的比例_最优估计_04正则化项

python数据预测的训练集和测试集的关系 测试集和训练集的比例_最优估计_05是正则化系数,控制正则化的强度,是一个超参数,可以通过不同值在验证集上的性能进行选择。
L2正则化:python数据预测的训练集和测试集的关系 测试集和训练集的比例_方差_06,最终权重倾向于比较平衡且接近于0,最常用;
L1正则化:python数据预测的训练集和测试集的关系 测试集和训练集的比例_正则化_07,最终权重比较稀疏,即包含很多0值。

正则化一般不考虑偏置向量,这是因为如果w相比b,参数量要大得多,若通过添加对w的正则化解决了过拟合问题,那么忽略b也可以。当然也可以添加b的正则化,只不过一般大家都忽略它。

神经网络
参数:python数据预测的训练集和测试集的关系 测试集和训练集的比例_正则化_08
目的:python数据预测的训练集和测试集的关系 测试集和训练集的比例_最优估计_09

L2正则化:python数据预测的训练集和测试集的关系 测试集和训练集的比例_正则化_10

矩阵的Frobenius范数:python数据预测的训练集和测试集的关系 测试集和训练集的比例_方差_11

反向传播:
python数据预测的训练集和测试集的关系 测试集和训练集的比例_最优估计_12

参数更新:
python数据预测的训练集和测试集的关系 测试集和训练集的比例_方差_13

因为在更新之前先对python数据预测的训练集和测试集的关系 测试集和训练集的比例_方差_14减去了python数据预测的训练集和测试集的关系 测试集和训练集的比例_最优估计_15,所以一般称L2正则化为权重衰减。

为什么正则化可以减少过拟合?

如果使用L2正则化,如果正则化强度python数据预测的训练集和测试集的关系 测试集和训练集的比例_正则化_16很大,会使得学得的参数接近于0,那么此时模型中一大部分参数接近于0,即该部分的神经元不起作用,那么原始规模很大的神经网络实际起作用的神经元在减少,模型的有效规模在减小,就降低了过拟合发生的风险。但实际上,我们的正则化强度系数不会很大,但终究使得一部分神经元的作用在弱化,降低了过拟合发生的风险。

3.2 dropout正则化

dropout随机失活,每一个神经元都以指定的概率处于失活的状态,即不参与模型的训练过程。这样,也就减少了网络的规模,降低了过拟合发生的概率。如下图所示:

python数据预测的训练集和测试集的关系 测试集和训练集的比例_最优估计_17


网络结构也就变成了:

python数据预测的训练集和测试集的关系 测试集和训练集的比例_最优估计_18


实现dropout一般采用Inverted dropout的方法。假设针对python数据预测的训练集和测试集的关系 测试集和训练集的比例_正则化_19进行dropout操作,设置python数据预测的训练集和测试集的关系 测试集和训练集的比例_正则化_20表示神经元保持激活的概率。训练过程中的实现为:

d3 = np.random.randn(a3.shape[0],a3.shape[1]) < keep_prob
a3 = a3 * d3
a3 /= keep_prob

也就是说,让python数据预测的训练集和测试集的关系 测试集和训练集的比例_正则化_19层的神经元以keep_prob的概率保持激活状态。最后a3 /= keep_prob的操作是为了保持使用dropout之后的网络层的输出期望保持不变。因为,python数据预测的训练集和测试集的关系 测试集和训练集的比例_正则化_19层的神经元以keep_prob的概率保持激活状态,那么就有python数据预测的训练集和测试集的关系 测试集和训练集的比例_正则化_23的神经元的输出值为0,若不进行最后一步的操作,则a3的期望为原始a3的keep_prob,所以为了保持期望不变需要进行a3 /= keep_prob的操作。

使用inverted dropout,测试样本不需要进行dropout操作,dropout只针对训练过程。这是因为在测试阶段,我们并不希望输出结果是随机的。如果对测试阶段也使用了dropout,那么同一个样本每次预测都会因为drop的神经元不同而造成输出结果不同,这显然不是我们希望看到的。

该如何理解dropout?
使用了dropout的神经网络,每一个神经元的输入都可能被drop,因此,神经元就不能给指定的输入特别大的权重值,也就倾向于给所有的输入比较接近的权重值,这样就起到了类似于L2正则化的效果。

一般不会对输入层应用dropout,对连接数最多的层,可以使用最大强度的dropout,即设置相对较小的keep_prob值。

在计算机视觉领域,因为没有办法收集到足够多的图像,所以更多使用dropout。但是在其他领域,若是过拟合发生的风险不大,还是不要随意使用dropout。

dropout的一个缺点是,因为随机drop了一些神经元,因此,得到的损失函数J值无法随着训练过程保证逐渐减小。因此,一般的做法是先不开启dropout进行网络训练,确保损失函数值持续下降,也就保证了目前实现的代码的正确性。然后再开启dropout减轻过拟合。

3.3 其他正则化方法

3.3.1 数据扩充

增加训练样本的数量可以减轻过拟合,但是在很多情况下,增加样本数量的代价很大,此时可以通过数据扩充的方法模拟出新的训练样本。

常用的数据扩充手段有:水平翻转、随机裁剪、旋转、颜色通道变换、噪声等手段扩充图像,虽然这样做相比添加真实的训练样本增加的额外信息量有限,但终究是在几乎不增加代价的情况下增加了额外的训练信息。

3.3.2 early stop

发生过拟合时,网络的训练误差是在持续减小的,但是验证集上的误差是先减小再增加的,因此在网络训练的中间阶段停止训练过程,就可以在验证集误差最小的情况下得到训练模型。

在网络训练的初始阶段,一般我们都初始化权重为很接近于0的值,随着训练过程的进行,权重值逐渐加大,但是early stop是在网络训练的中间过程中停止,期待此时权重值还没有增加的太大,因此,early stop也类似于L2正则化的作用,起到了防止过拟合的效果。

early stop的好处是在几乎没有增加代价的情况下进行了正则化。一般实现过程是监督模型训练过程中验证集上的模型准确度,如果在相邻个batch的训练过程中,验证集上的准确率上升幅度很小,则使用early stop停止训练过程。

4 对输入进行归一化处理

对训练集进行归一化处理也可以加速模型的训练过程。

一般用到的归一化处理手段是:减均值、除方差。减均值之后,样本集的中心点和坐标原点重合,除方差是为了使各维特征的分布范围一致。

python数据预测的训练集和测试集的关系 测试集和训练集的比例_正则化_24


对图像数据,因为本身各像素点的取值范围都为[0,255],因此图像数据一般只减均值即可。

要保持测试样本和训练样本进行了同样的归一化处理,比如测试样本是减去训练集样本的均值、除以训练集样本的方差。

为什么要对输入进行归一化处理?
如果不进行归一化处理,那么可能一个特征取值范围很大,另一个特征的取值范围很小,那么学习得到的权重也会有不同的取值范围。因此,最后得到的损失函数的形状为狭长形,对这样的函数应用梯度下降等优化算法时,必须精心设计比较小的学习率,因此收敛速度较慢;而进行归一化处理之后,各维度特征的变化幅度一样,因此学的参数的变换幅度也一致,得到的损失函数的投影为较规则的圆形,这样的损失函数应用优化算法时,可以使用较大的学习率,因此会更快收敛到最优解。

python数据预测的训练集和测试集的关系 测试集和训练集的比例_方差_25

5 梯度消失和梯度爆炸

假设某个计算图中包含一条反复与矩阵W相乘的路径,那么t步后,相当于乘以python数据预测的训练集和测试集的关系 测试集和训练集的比例_正则化_26。假设W有特征值分解python数据预测的训练集和测试集的关系 测试集和训练集的比例_正则化_27。在这种简单的情况下,可以看出:
python数据预测的训练集和测试集的关系 测试集和训练集的比例_正则化_28
此时当特征值python数据预测的训练集和测试集的关系 测试集和训练集的比例_最优估计_05大于1时会发生梯度爆炸;当特征值python数据预测的训练集和测试集的关系 测试集和训练集的比例_最优估计_05小于1时会发生梯度弥散。

梯度消失与梯度爆炸问题是指该计算图上的梯度会因为python数据预测的训练集和测试集的关系 测试集和训练集的比例_方差_31大幅度变化。梯度消失会使得我们难以知道参数朝哪个方向移动能够改进代价函数,梯度爆炸会使得学习不稳定。

梯度消失可以通过使用relu系激活函数避免发生,梯度爆炸是由于多个较大的权重连乘造成的,此时即便很小的移动步长也会造成参数值很大程度的改变。梯度爆炸可以通过启发式梯度截断进行避免。

通过合适的设置初始化参数也可以在很大程度上避免梯度消失和梯度爆炸现象的发生。

6 神经网络的权重初始化

合适的选择权重初始化策略,会使得权重值既不会太大也不会太小,避免了梯度消失和梯度爆炸的发生。

对单个神经元:

python数据预测的训练集和测试集的关系 测试集和训练集的比例_方差_32

假设总的z保持不变,那么n越大,则python数据预测的训练集和测试集的关系 测试集和训练集的比例_方差_33的值也就越小。因此,就设置python数据预测的训练集和测试集的关系 测试集和训练集的比例_正则化_34的取值方差为python数据预测的训练集和测试集的关系 测试集和训练集的比例_最优估计_35

实现上为:
python数据预测的训练集和测试集的关系 测试集和训练集的比例_方差_36

一般,

  • 如果使用relu激活函数,则初始化权重方差为:python数据预测的训练集和测试集的关系 测试集和训练集的比例_正则化_37,称之为MSRA初始化;
  • 如果使用tanh激活函数,则初始化权重方差为:python数据预测的训练集和测试集的关系 测试集和训练集的比例_方差_38,但是考虑到输入和输出的神经元数量不一致,建议初始化权重方差为:python数据预测的训练集和测试集的关系 测试集和训练集的比例_方差_39,称之为xavier初始化;

使用不同的初始化方法也可以作为一个超参数进行调节,但是该超参数的重要性比较低。

7 梯度的数值逼近及梯度校验

梯度计算:
使用双边梯度计算某一点处的梯度python数据预测的训练集和测试集的关系 测试集和训练集的比例_最优估计_40

之所以使用双边梯度,而不是使用单边梯度,是因为双边梯度的准确率更高。
例如,假设函数为python数据预测的训练集和测试集的关系 测试集和训练集的比例_最优估计_41,根据梯度计算公式我们可以知道python数据预测的训练集和测试集的关系 测试集和训练集的比例_方差_42。则假设在python数据预测的训练集和测试集的关系 测试集和训练集的比例_最优估计_43处,计算得到的梯度为3。则假如使用双边梯度且python数据预测的训练集和测试集的关系 测试集和训练集的比例_正则化_44,则计算结果为python数据预测的训练集和测试集的关系 测试集和训练集的比例_正则化_45,计算的梯度误差为0.0001。但是若使用单边误差,则计算的梯度公式为python数据预测的训练集和测试集的关系 测试集和训练集的比例_正则化_46,得到的误差为0.0301。

梯度校验:
python数据预测的训练集和测试集的关系 测试集和训练集的比例_方差_47放入向量python数据预测的训练集和测试集的关系 测试集和训练集的比例_方差_48,将python数据预测的训练集和测试集的关系 测试集和训练集的比例_方差_49放入向量python数据预测的训练集和测试集的关系 测试集和训练集的比例_正则化_50

实现过程:
python数据预测的训练集和测试集的关系 测试集和训练集的比例_正则化_51
python数据预测的训练集和测试集的关系 测试集和训练集的比例_正则化_52

最后比较这样计算得到的python数据预测的训练集和测试集的关系 测试集和训练集的比例_方差_53和通过自己的梯度计算代码得到的梯度向量python数据预测的训练集和测试集的关系 测试集和训练集的比例_正则化_50。计算公式为python数据预测的训练集和测试集的关系 测试集和训练集的比例_方差_55
一般设置上面的python数据预测的训练集和测试集的关系 测试集和训练集的比例_最优估计_56,如果最终计算得到的误差python数据预测的训练集和测试集的关系 测试集和训练集的比例_最优估计_57,那么表示自己实现的梯度计算代码精度很高;如果最终计算得到的误差python数据预测的训练集和测试集的关系 测试集和训练集的比例_正则化_58,那么需要仔细审查自己的代码;如果最终计算得到的误差python数据预测的训练集和测试集的关系 测试集和训练集的比例_方差_59,那么代码应该是存在bug的。

梯度校验实现技巧:

  • 不要在训练过程中使用梯度校验,梯度校验只是为了校验代码,梯度校验成功之后,就关闭这些代码,不要让其参与训练过程,因为梯度校验的过程很慢;
  • 如果梯度校验失败,要检查算法实现的各个步骤去修改代码的bug;
  • 在梯度校验时,同样需要将正则化项的梯度包括在内;
  • 梯度校验不能与dropout同时使用,建议关闭dropout进行双向误差的梯度校验;
  • 在网络训练的初始阶段,python数据预测的训练集和测试集的关系 测试集和训练集的比例_正则化_60,此时梯度校验是正确的,但是随着训练过程的进行,python数据预测的训练集和测试集的关系 测试集和训练集的比例_最优估计_61在增大,此时需要再次进行梯度校验。

8 作业

8.1 权重初始化

  • 不同的初始化权重会得到不同的学习结果;
  • 全零初始化不能打破神经网络的对称性,同一层的神经元学习了相同的函数;
  • 随机初始化可以打破对称性,使得不同的隐藏层神经元学习到了不同的函数;
  • 随机初始化时不要把权重设置的很大,否则会造成梯度爆炸,造成学习的不稳定;
  • 使用relu激活函数时,推荐使用MSRA权重初始化方法。

8.2 正则化

由于使用正则化限制了模型的过拟合,因此使用正则化后模型在训练集上的误差会增大,但是模型在测试集上的误差会减小,增强了模型的泛化能力;

L2正则化、dropout是最常用的两种正则化手段。

L2正则化

  • 计算损失函数时添加python数据预测的训练集和测试集的关系 测试集和训练集的比例_方差_62,python实现就是python数据预测的训练集和测试集的关系 测试集和训练集的比例_方差_63
  • 计算python数据预测的训练集和测试集的关系 测试集和训练集的比例_正则化_64导数时添加python数据预测的训练集和测试集的关系 测试集和训练集的比例_最优估计_65

dropout

只在训练阶段使用dropout,测试阶段不要使用;
使用inverted dropout;
训练阶段前向和反向计算过程都要使用dropout;

前向计算共四步骤:

  • 计算掩模 D = np.random.rand(A.shape[0],A.shape[1]);
  • 掩模按照keep_prob的概率置为1,D < keep_prob;(注:执行X = X < 0.5,则X中小于0.5的元素被置为1,大于0.5的元素被置为0);
  • 使用掩模对输出单元drop,即A = A * D;
  • 处以keep_prob保证使用/不使用dropout操作的期望相等,即A /= keep_prob,这一步就是inverted dropout。

反向计算时:

  • 在进行前向计算时,将掩模D应用于A,关闭了一些神经元,因此,反向计算时要关闭相同的神经元,即 dA *= D;
  • 前向计算时,A /= keep_prob,那么反向计算时,计算dA需要计算乘以一个常数的导数,因此 dA /= keep_prob。