应用深度学习是一个典型的迭代过程。
对于一个需要解决的问题的样本数据,在建立模型的过程中,数据会被划分为以下几个部分:
在小数据量的时代,如 100、1000、10000 的数据量大小,可以将数据集按照以下比例进行划分:
而在如今的大数据时代,对于一个问题,我们拥有的数据集的规模可能是百万级别的,所以验证集和测试集所占的比重会趋向于变得更小。
验证集的目的是为了验证不同的算法哪种更加有效,所以验证集只要足够大到能够验证大约 2-10 种算法哪种更好,而不需要使用 20% 的数据作为验证集。如百万数据中抽取 1 万的数据作为验证集就可以了。
测试集的主要目的是评估模型的效果,如在单个分类器中,往往在百万级别的数据中,我们选择其中 1000 条数据足以评估单个模型的效果。
PS:
建议
建议验证集要和训练集来自于同一个分布(数据来源一致),可以使得机器学习算法变得更快并获得更好的效果。
如果不需要用无偏估计来评估模型的性能,则可以不需要测试集。
补充:交叉验证(cross validation)
交叉验证的基本思想是重复地使用数据;把给定的数据进行切分,将切分的数据集组合为训练集与测试集,在此基础上反复地进行训练、测试以及模型选择。
“偏差-方差分解”(bias-variance decomposition)是解释学习算法泛化性能的一种重要工具。
泛化误差可分解为偏差、方差与噪声之和:
偏差-方差分解说明,泛化性能是由学习算法的能力、数据的充分性以及学习任务本身的难度所共同决定的。给定学习任务,为了取得好的泛化性能,则需要使偏差较小,即能够充分拟合数据,并且使方差较小,即使得数据扰动产生的影响小。
在欠拟合(underfitting)的情况下,出现高偏差(high bias)的情况,即不能很好地对数据进行分类。
当模型设置的太复杂时,训练集中的一些噪声没有被排除,使得模型出现过拟合(overfitting)的情况,在验证集上出现高方差(high variance)的现象。
当训练出一个模型以后,如果:
PS:偏差和方差的权衡问题对于模型来说十分重要。
最优误差通常也称为“贝叶斯误差”。
首先保证偏差降低到可接受的数值,然后检查方差有没有问题。
存在高偏差:
存在高方差:
不断尝试,直到找到低偏差、低方差的框架。
在深度学习的早期阶段,没有太多方法能做到只减少偏差或方差而不影响到另外一方。而在大数据时代,深度学习对监督式学习大有裨益,使得我们不用像以前一样太过关注如何平衡偏差和方差的权衡问题,通过以上方法可以在不增加某一方的前提下减少另一方的值。
正则化是在成本函数中加入一个正则化项,惩罚模型的复杂度。正则化可以用于解决高方差的问题。
对于 Logistic 回归,加入 L2 正则化(也称“L2 范数”)的成本函数:
由于 L1 正则化最后得到 w 向量中将存在大量的 0,使模型变得稀疏化,因此 L2 正则化更加常用。
注意,lambda在 Python 中属于保留字,所以在编程的时候,用lambd代替这里的正则化因子。
对于神经网络,加入正则化的成本函数:
因为 w 的大小为 (n[l−1], n[l]),因此:
该矩阵范数被称为弗罗贝尼乌斯范数(Frobenius Norm),所以神经网络中的正则化项被称为弗罗贝尼乌斯范数矩阵。
正则化的方法除了L2,还有dropout(随机失活)。在计算机视觉中应用的特别多。它是在神经网络的隐藏层为每个神经元结点设置一个随机消除的概率,保留下来的神经元形成一个结点较少、规模较小的网络用于训练。dropout 正则化较多地被使用在计算机视觉(Computer Vision)领域。
反向随机失活是实现 dropout 的方法。对第l层进行 dropout:
keep_prob = 0.8 # 设置神经元保留概率
dl = np.random.rand(al.shape[0], al.shape[1]) < keep_prob
al = np.multiply(al, dl)
al /= keep_prob
注意,在测试阶段不要使用 dropout,因为那样会使得预测结果变得随机。
对于单个神经元,其工作是接收输入并产生一些有意义的输出。但是加入了 dropout 后,输入的特征都存在被随机清除的可能,所以该神经元不会再特别依赖于任何一个输入特征,即不会给任何一个输入特征设置太大的权重。
因此,通过传播过程,dropout 将产生和 L2 正则化相同的收缩权重的效果。
对于不同的层,设置的keep_prob
也不同。一般来说,神经元较少的层,会设keep_prob
为 1.0,而神经元多的层则会设置比较小的keep_prob
。
总结一下,如果你担心某些层比其他层更容易发生过拟合,可以把某些层的keep_prob
值设置的比其他层更低。缺点是为了使用交叉验证,你要搜索更多的超级参数。另一种方法在一些层上应用dropout,而有的则不用。
dropout 的一大缺点是成本函数无法被明确定义。因为每次迭代都会随机消除一些神经元结点的影响,因此无法确保成本函数单调递减。因此,使用 dropout 时,先将keep_prob
全部设置为 1.0 后运行代码,确保 J(w,b)函数单调递减,再打开 dropout。
使用标准化处理输入 X 能够有效加速收敛。
第一步是零均值化,第二步是归一化方差,总结起来为:
PS:注意,训练集和测试集要使用同样的均值和方差。
在不使用标准化的成本函数中,如果设置一个较小的学习率,可能需要很多次迭代才能到达全局最优解;而如果使用了标准化,那么无论从哪个位置开始迭代,都能以相对较少的迭代次数找到全局最优解。
在梯度函数上出现的以指数级递增或者递减的情况分别称为梯度爆炸或者梯度消失。
对于导数同理。因此,在计算梯度时,根据不同情况梯度函数会以指数级递增或递减,导致训练导数难度上升,梯度下降算法的步长会变得非常小,需要训练的时间将会非常长。
利用初始化缓解梯度消失和爆炸:
因为Z=W1X1+W2X2+…WnXn+b可知,当输入的数量 n 较大时,我们希望每个 wi 的值都小一些,这样它们的和得到的 z 也较小。
为了得到较小的 wi,设置Var(wi)=1/n,这里称为 Xavier initialization。
WL = np.random.randn(WL.shape[0], WL.shape[1]) * np.sqrt(1/n)
其中 n 是输入的神经元个数,即WL.shape[1]。
这样,激活函数的输入 x 近似设置成均值为 0,标准方差为 1,神经元输出 z 的方差就正则化到 1 了。虽然没有解决梯度消失和爆炸的问题,但其在一定程度上确实减缓了梯度消失和爆炸的速度。
同理,也有 He Initialization。它和 Xavier initialization 唯一的区别是Var(wi)=2/n,适用于 ReLU 作为激活函数时。
当激活函数使用 ReLU 时,Var(wi)=2/n;当激活函数使用 tanh 时,Var(wi)=1/n。
使用双边误差的方法去逼近导数,精度要高于单边误差。
- 连接参数
现在,我们需要找到 dθ 和代价函数 J 的梯度的关系。
- 进行梯度检验
如果梯度检验值和 ε 的值相近,说明神经网络的实施是正确的,否则要去检查代码是否存在 bug。一般来说,10-7~10-5是好的,如果是高于10-3就考虑bug