2.1 深度学习的实用层面
训练、开发、测试集
假设这个长方形是训练数据,第一部分为训练集,第二部分为简单交叉验证集(或称开发集),第三部分为测试集。训练集用于训练算法,通过验证集选择最好的模型,经过充分的验证选定最好的模型(从几套拟合效果不错的算法中验证出一套最有效的),然后放在测试集上评估。
在曾经机器学习的小数据量时代,常见的作法是将数据三七分,七训练三测试,也可以六训练二验证二测试,如果是100-10000条数据量上述划分是非常合适的。但在大数据时代,验证集和测试集正在趋于变小,例如上百万的数据量不需要几十万的数据用于测试,差不多一万验证集一万测试集就够了。
例如你要训练一个识别图片中的猫的分类器,你的训练集可能是网上爬下来的分辨率较高质量较高的图片,而测试集却是生活中普通用户拍摄的低质量甚至模糊的图片,这种情况下要确保验证集和测试集的数据来自同一分布。最后,有些时候没有测试集也是可以的。
偏差、方差
第一个图数据欠拟合,是偏差高的情况。第三个是高方差过拟合的情况。为了将方差偏差可视化,我们选择了两个特征的情况,但大多数情况下,是无法可视化的。
理解方差和偏差的两个关键数据是训练集误差和开发集误差,我们假设训练集误差为1%,开发集误差为11%,可以看出这可能是在处理训练集时出现过拟合了,我们将其定义为高方差。
假设训练集的误差为15%,开发集误差为16%,(假设人工识别的误差为0%),它在训练集上的表现并不好,我们称之为高偏差(不能在训练集上有较好的识别效果),但这个算法用在开发集上的结果在可以接受的范围之内,它和训练集相比只差了1%。
假设训练集上的误差为15%(这是相当高的偏差),而在开发集上时情况变的糟糕很多,误差到达30%,我们可以判断出这个算法是高偏差并且高方差的。
假设训练集只有0.5%误差,以及1%的开发集误差,那么是非常好的低偏差低方差结果。
但我们再次强调,这种讨论是基于人工识别时0%的误差,我们称之为理想误差或贝叶斯误差(接近0%)。但如果理想误差就已经比较高,比如15%,那么第二个例子就已经非常完美了。
机器学习基础
训练神经网络的基本原则:
当训练好了最初的神经网络,首先看这个算法是否具有高偏差?实际上就是看训练集上的表现,如果欠拟合那么就需要挑选更大的神经网络来训练,或是增加梯度下降的迭代次数,或换更高级的优化算法。一般增大神经网络总是有用的,它能够更好的拟合或者过拟合训练集。最后,可以尝试其他结构的神经网络。
当偏差足够小时,再看是否具有高方差?也就是看算法在开发集上的表现,即过拟合问题。解决高方差最好的办法是取得更多数据,当你没法找到更多数据时你可以尝试正则化,最后你可以寻找其他结构的神经网络。
我们看到,高偏差和高方差的解决方法一般不一样,所以我们需要训练集和开发集来判断算法是否具有高偏差和高方差。
正则化
就像在机器学习中所说的,在代价函数J中加入正则化项以惩罚一些过大的参数,以避免算法过拟合。我们一般只惩罚w而不惩罚b,就像在机器学习笔记中不惩罚x0一样。W外面加两杠称为w的范数,我们求范数的平方也就是从1到n求w的平方和,这种方法是L2正则化。下面一行是L1正则化,它会使w最后变得稀疏,这意味着w矢量中包含很多的0(我并不清楚这会产生什么样的后果),所以一般L2使用的更多。注意:lambda是python中的保留关键字,为不产生冲突我们用lambd来表示我们的参数。
在神经网络中,你有一个代价函数J,它是所有参数的函数,在末尾加上正则化项。其中范数是w矩阵上每一个元素的平方和,因为某些我们不需要知道的原因,它被称为佛罗贝尼乌斯范数。在代价函数中加入正则化项后,反向传播的dw也需要加入一项(因为dw说的是J对w的偏导数,而J变了)。
L2正则化有时候也被称为权重衰减,
那么为什么正则化能够解决过拟合?首先是加入惩罚项后,w变小,能够使拟合出的曲线更加平滑,以更好的泛化到数据集。另一个直观的例子是,假设我们使用tanh(z)作为激活函数,假如z值很小很靠近0,那么实际上激活函数就接近线性函数。只有z值的范围更大的时候,激活函数才能展现其非线性的能力。所以如果λ过大,w过小→z过小就会造成每一层都是线性激活函数,那么即便网络很深也只计算线性函数,无法拟合出复杂的决策函数。
Dropout正则化(丢弃法)
也叫随机失活正则化,该方法需要遍历一遍神经网络中的每一个节点,并为丢弃其中的某个节点置一个概率值,即对每一层的每一个节点进行一次抛硬币,使其有50%的概率被保留(被丢弃)。决定抛弃哪些节点后,将清楚节点上所有正在进行的运算。这使得你可以训练一个小得多的神经网络。
我们介绍随机失活中最常用的一种反向随机失活。首先声明一个矢量d,d3表示第三层的失活向量,用随机数给d3赋值(暂时这么理解),当d3中的元素小于某个值(假设定位0.8)那么就保留其对应的节点。
我们看图中的代码,实际上并不是给d3赋随机值,而是用一组随机值去和0.8比较大小,再用比较结果的真假(0、1)赋给d3。第二行再将d3乘到a3上,如此就直接消去了一部分单元。最后还要将a3/0.8,这是因为当你失去了20%的节点后,再计算z3时,为了保证它的期望值不变,我们必须将a3稍微放大一些以作弥补。
那么为什么随机失活正则化有用呢?假如我们有一个输出单元,它的上一层连接着非常多的输入单元,但这些输入单元的任何一个都可能被丢弃,所以它更倾向于给每一个单元一个更加均衡的权重而不愿将赌注都压在某一个特征上,这样的效果就与L2正则化较为类似了。
有一点需要注意,我们可以对每一层设置不同的留存率,对w参数矩阵(取决于上一层和本层的节点数)更为复杂的层可以设置更低的存活率,因为这一层更有可能过拟合。如果某一层我们完全不必担心过拟合,则可以设置更高的存活率甚至是1。
由于每一次迭代时都有一些神经元随机失活,所以代价函数J会变得不够明确,所以就无法用可视的画图法调试代价函数。通常这个时候应该关闭dropout,确保代价函数是单调递减的再打开dropout,并期待不会引入新的错误。
其他正则化方法
当模型过拟合时,可以通过增加数据集的方法缓解,但有的时候就是无法得到更多的数据集。假如我们正在训练一个猫的分类器,那么我们可以将图片水平翻转来增加伪数据集,虽然不如全新的图片效果好,但是是有用的,这相当于告诉算法一张猫的图片的水平翻转仍然是一只猫。对于字符识别,仍然可以将字符扭曲变形来增加训练集。所以数据集扩增可以作为一种正则化技术。
另一种方法称为早终止法(一般不使用,下面介绍原因),在梯度下降时画一张训练集误差的曲线,同时也画出开发集误差的曲线,对比两个曲线你可以理解当不断的进行迭代时,我们的模型将不断的适应于训练集,而开发集的误差将会越来越大,这就是过拟合的过程。早终止法就是提前终止神经网络的训练,使参数值停在最适合开发集的那个点附近。但这个方法有一个缺点,吴恩达将机器学习看作几个阶段,第一是你需要一个算法,并能够最优化成本函数J,我们有很多方法做到这一点例如梯度下降。第二阶段是即便优化了J,我们仍然希望不要过拟合,一般利用正则化、扩大数据集等方法。但现在机器学习中激增了很多超参数,我们要在这些诸多的可能中做选择,已经相当复杂了,因此吴恩达认为应该变得更简单,当你在优化成本函数J时,应当只关注如何让它变得尽可能小,而避免过拟合是下一步完全不同的任务,两个任务是用两套完全不同的工具实现,这个原则我们称之为正交化。那么很明显,早终止法违反了正交化原则,它将两个任务结合在一起了,用同一个工具解决了两个问题,这意味着你要考虑的问题更为复杂,这不利于我们分解超参数的搜索空间(不易于系统性的做更多的尝试,因为太乱了)。
归一化输入
有一种加速训练神经网络的方法,就是对输入进行归一化。假设输入特征是二维的,归一化包括两个步骤,第一是减去均值,也就是均值归零,使所有散点尽可能的分布在原点附近。第二是将方差进行归一化,注意特征x1比x2的方差大很多(横向的分布更宽泛),具体就是将每个点除以其平方的平均数,现在两个特征的方差都等于1。注意!如果使用这种方法对数据集缩放,一定要对测试集和训练集使用一样的μ(平均值)和 σ2(方差)。
那么为什么要归一化?如果几个特征的尺度相差的非常大,也会造成参数范围相差较大,那么代价函数就会像一个压扁的碗,其等值线函数(二维化)看起来非常的细长,这会让梯度下降过程变的十分曲折缓慢。
梯度消失/爆炸
假设我们在训练一个层数很多的神经网络,假设激活函数是线性函数,b=0,所以按照前向传播算法,最终输出的Y帽就等于所有参数w连乘最后乘x,因为没有b,而激活函数又是它本身g(z)=z,所以w不断的迭代相乘就得到了最后的Y帽。现在我们假设每一个权重(参数)矩阵w都只比单位矩阵(由于每层的节点都是两个,所以w是2×2维的)稍大一些,那么前L-1项就可以合并成w矩阵的L-1次方,那么最终的Y就会呈指数增长爆炸,相反如果w是零点几,又会指数级减小。这件事一直是深度网络技术的一个壁垒。
神经网络的权重初始化
对于梯度消失和梯度爆炸问题,该方法虽不能完全解决,但能很好的改善。首先我们从单个神经元说起,假如它有n个输入特征,为了不让这个神经元a的值过大,所以n越大你希望参数w越小,一个合理的做法是让w乘以1/n。
(后面没听懂)
梯度的数值逼近
其实就是检验导数(梯度就是导数)值进而检验模型或者代码是否正确。提起如何近似导数值,我们可能更倾向于想到选择一段极小的差值,然后取出一个三角形算倾斜度,但这里提出,取两边的双向误差再除以两个差值比单边的更为准确,后面也将用到这种方法。
梯度检验
神经网络中有许多参数,每一层的w和b,要实现梯度检查算法首先需要将这些参数拼成一个向量,然后首尾相接连接起来变成一个巨大的θ向量。经过向量转化后,代价函数J变成了θ的函数。与之相对应的,你可以将dwdb也转化成与θ维数相同的dθ向量。
现在的问题是θ是代价函数J的梯度或者斜率吗?
实现梯度检查算法首先要写一个循环,对于θ中的每一个分量,计算θi的近似微分,让我们对θi取双向微分近似得出dθi。现在我们需要判断dθ和我们计算出的近似dθ两个向量是否大致相等,计算两个向量的欧几里得距离,也就是把两个向量的分量的差的平方之和再开方。再把结果根据向量的长度标准化,把它除以向量dθ的近似和向量dθ的欧几里得长度之和。除以这个分母是为了防止这两个向量太小或太大。实际应用中,差值可以取10-7
注意:1.不要在实现时使用梯度检查,应在调试时使用,因为计算dθ的近似十分慢。
2.如果一个算法没有通过梯度检测,就去看看具体时哪个θ相差太多,可以帮助定位漏洞。
3.如果你使用了正则化就别忘了你的正则项,
4.梯度检验不能与随机失活一起使用
5.(较少发生)梯度下降可能没有问题,w和b在初始化时是比较接近0的,但当梯度下降进行时,w和b有所增大。(没听懂)
编程题
初始化的模型将蓝色和红色的点在少量的迭代中很好地分离出来,总结一下:
- 不同的初始化方法可能导致性能最终不同
- 随机初始化有助于打破对称,使得不同隐藏层的单元可以学习到不同的参数。
- 初始化时,初始值不宜过大。
- He初始化搭配ReLU激活函数常常可以得到不错的效果。