吴恩达深度学习
吴恩达深度学习
目录
深度学习的实践层面
调试 - 方差、偏差
参数VS超参数(Parameters vs Hyperparameters)
作用
- 想要你的深度神经网络起很好的效果,你还需要规划好你的参数以及超参数。
什么是超参数?
- 超参数需要你来设置,这些数字实际上控制了最后的参数和的值,所以它们被称作超参数(Hyperparameters)
例如
- Learning rate (学习率)
- Iterations(梯度下降法循环的数量)
- (隐藏层数目)
- (隐藏层单元数目)
- Choice of activation function(激活函数的选择)
- 其他,如momentum、mini batch size、regularization parameters等等
一些经验
预先很难确切知道,究竟超参数的最优值应该是什么
走Idea—Code—Experiment—Idea这个循环,尝试各种不同的参数,实现模型并观察是否成功,然后再迭代
在你开发途中,很有可能学习率的最优数值或是其他超参数的最优值是会变的
即使你每天都在用当前最优的参数调试你的系统,你还是会发现最优值过一年就会变化,因为电脑的CPU或是GPU、网络等可能会变化很大今天的深度学习应用领域,还是很经验性的过程。经常试试不同的超参数,勤于检查结果,看看有没有更好的取值,你将会得到设定超参数的直觉
训练,验证,测试集(Train / Dev / Test sets)
一些补充
要确保验证集和测试集的数据来自同一分布
现代深度学习的另一个趋势是越来越多的人在训练和测试集分布不匹配的情况下进行训练,
假设你要构建一个用户可以上传大量图片的应用程序,目的是找出并呈现所有猫咪图片,可能你的用户都是爱猫人士,训练集可能是从网上下载的猫咪图片,而验证集和测试集是用户在这个应用上上传的猫的图片,
就是说,训练集可能是从网络上抓下来的图片。而验证集和测试集是用户上传的图片。
结果许多网页上的猫咪图片分辨率很高,很专业,后期制作精良,而用户上传的照片可能是用手机随意拍摄的,像素低,比较模糊,这两类数据有所不同,针对这种情况,根据经验,我建议大家要确保验证集和测试集的数据来自同一分布
不需要无偏估计的情况
就算没有测试集也不要紧,测试集的目的是对最终所选定的神经网络系统做出无偏估计,如果不需要无偏估计,也可以不设置测试集。
所以如果只有验证集,没有测试集,我们要做的就是,在训练集上训练,尝试不同的模型框架,在验证集上评估这些模型,然后迭代并选出适用的模型。
因为验证集中已经涵盖测试集数据,其不再提供无偏性能评估。
当然,如果你不需要无偏估计,那就再好不过了。
偏差,方差(Bias /Variance)
一些补充
当所有分类器都不适用时,如何分析偏差和方差呢?
比如,图片很模糊,即使是人眼,或者没有系统可以准确无误地识别图片,在这种情况下,最优误差会更高,那么分析过程就要做些改变了,我们暂时先不讨论这些细微差别,重点是通过查看训练集误差,我们可以判断数据拟合情况,至少对于训练数据是这样,可以判断是否有偏差问题,然后查看错误率有多高。当完成训练集训练,开始使用验证集验证时,我们可以判断方差是否过高,从训练集到验证集的这个过程中,我们可以判断方差是否过高。
机器学习基础(Basic Recipe for Machine Learning)
调试 - 正则化
正则化(Regularization)
一些补充
为什么只正则化参数?为什么不再加上参数 呢?
你可以这么做,只是我习惯省略不写,
因为通常是一个高维参数矢量,已经可以表达高偏差问题,可能包含有很多参数,我们不可能拟合所有参数,而只是单个数字,所以几乎涵盖所有参数,而不是,
如果加了参数,其实也没太大影响,因为只是众多参数中的一个,所以我通常省略不计,如果你想加上这个参数,完全没问题。
① 正则化与正则化
公式
选用
正则化是最常见的正则化类型。人们在训练网络时,越来越倾向于使用正则化。
你们可能听说过正则化,正则化,加的不是范数,而是正则项乘以,
也被称为参数向量的范数,无论分母是还是,它都是一个比例常量。如果用的是正则化,最终会是稀疏的,也就是说**向量中有很多0**,有人说这样有利于压缩模型,因为集合中参数均为0,存储模型所占用的内存更少。
实际上,虽然正则化使模型变得稀疏,却没有降低太多存储内存,所以我认为这并不是正则化的目的,至少不是为了压缩模型。
范数(Norm)
这节需要向量范数、矩阵范数 (matrix norm) 的基础,我印象中我大学好像没学这玩意?
另外找了一些资料来学习:详见数学相关的笔记
平方范数
- 我们称为范数平方,这个矩阵范数(即平方范数),被定义为矩阵中所有元素的平方求和
- 通常我们需要使用范数的平方来计算正则化后的成本函数
欧几里德范数(2-范数)
公式
正则化
叫这个名字是因为这里用了欧几里德范数 (2-范数)
后面的部分为。是乘以,后者是的欧几里德范数的平方(或2-范数的平方)
弗罗贝尼乌斯范数(Frobenius Norm,简称 F-范数)
公式:
它表示一个矩阵中所有元素的平方和正则化
p.s. 话说这章的原笔记有点操蛋,我开始时甚至不知道正则化用的到底是 欧几里德范式 还是 弗罗贝尼乌斯范式。话也说得很谜语,不过一般当成是后者算了。
该矩阵范数被称作“弗罗贝尼乌斯范数”,用下标标注”,鉴于线性代数中一些神秘晦涩的原因,我们不称之为“矩阵范数”,而称它为“弗罗贝尼乌斯范数”,
矩阵范数听起来更自然,但鉴于一些大家无须知道的特殊原因,按照惯例,我们称之为“弗罗贝尼乌斯范数”
② dropout 正则化(Dropout Regularization)
除了正则化,还有一个非常实用的正则化方法 —— Dropout(随机失活)
原理
我们复制这个神经网络,dropout会遍历网络的每一层,并设置消除神经网络中节点的概率。
假设网络中的每一层,每个节点都以抛硬币的方式设置概率,每个节点得以保留和消除的概率都是0.5,设置完节点概率,我们会消除一些节点,然后删除掉从该节点进出的连线,最后得到一个节点更少,规模更小的网络,然后用backprop方法进行训练。
实现(反向随机失活)
如何实施dropout呢?方法有几种,接下来我要讲的是最常用的方法,即 inverted dropout(反向随机失活)。
出于完整性考虑,我们用一个三层()网络来举例说明。编码中会有很多涉及到3的地方。我只举例说明如何在某一层中实施dropout。
a3 # 未正则化的原矩阵
①
首先要定义向量,表示网络第三层的dropout向量,然后看它是否小于某数,我们称之为keep-prob
keep-prob是一个具体数字,上个示例中它是0.5,而本例中它是0.8,它表示保留某个隐藏单元的概率。它的作用就是生成随机矩阵
是一个矩阵,中的对应值为1的概率都是0.8,对应为0的概率是0.2
d3 = np.random.rand(a3.shape[0],a3.shape[1]) < keep_prob
②
接下来要做的就是从第三层中获取激活函数,这里我们叫它,含有要计算的激活函数
等于上面的乘以,即让中0元素与中相对元素归零。
a3 =np.multiply(a3,d3)
# 也可写为 a3*=d3
补充:如果用python实现该算法的话,则是一个布尔型数组,值为true和false,而不是1和0,乘法运算依然有效,python会把true和false翻译为1和0
③
最后,我们向外扩展,用它除以0.8,或者除以keep-prob参数。
a3 /= keep_prob
Q:为什么要这么做?
A:我们的预期是,减少20%,也就是说中有20%的元素被归零,
为了不影响的期望值,我们需要用,它将会修正或弥补我们所需的那20%,的期望值不会变
理解 dropout
Dropout可以随机删除网络中的神经单元,他为什么可以通过正则化发挥如此大的作用呢?
输入可能失活的问题
直观上理解:不要依赖于任何一个特征,因为该单元的输入可能随时被清除。输入可能会失活
因此该单元通过这种方式传播下去,并为单元的四个输入增加一点权重,通过传播所有权重,dropout将产生收缩权重的平方范数的效果
从技术上讲,我们也可以对输入层应用dropout,我们有机会删除一个或多个输入特征,但是现实中我们通常不这么做
Dropout 与 L2正则化
(这tm怎么感觉说的话都差不多)
dropout将产生收缩权重的平方范数的效果,并完成一些预防过拟合的外层正则化,这和之前讲的正则化类似;
实施dropout的结果是它会压缩权重,对不同权重的衰减是不同的,它取决于激活函数倍增的大小。
和我们之前讲过的正则化类似,实施dropout的结果是它会压缩权重,并完成一些预防过拟合的外层正则化。
事实证明,dropout被正式地作为一种正则化的替代形式,对不同权重的衰减是不同的,它取决于倍增的激活函数的大小。
总结一下,dropout的功能类似于正则化,与正则化不同的是,被应用的方式不同,dropout也会有所不同,甚至更适用于不同的输入范围。
Keep-prob值的设置
keep-prob,它代表每一层上保留单元的概率。不同层的keep-prob也可以变化
例如:
第一层,矩阵是7×3,第二个权重矩阵是7×7,第三个权重矩阵是3×7,以此类推
- 对于第二层:是最大的权重矩阵,因为拥有最大参数集 (7×7) 。为了预防矩阵的过拟合,对于这一层的keep-prob值相对较低,比如是0.5
- 对于其它层:过拟合的程度可能没那么严重,它们的keep-prob值可能高一些,比如是0.7
- 对于在某一层:我们不必担心其过拟合的问题,那么keep-prob可以为1。这也意味着保留所有单元,并且不在这一层使用dropout
总结:
- 如果你觉得某一层比其他层更容易发生过拟合,这一层就应该设置更低的留存率。缺点是,在交叉验证时会有更多的超参数
- 另一种方案是对一些层使用dropout(留存率相同)而另一些层不使用,那么就只有一个超参数,就是keep-prob
dropout 与 计算机视觉
最早对dropout技术的成功,应用于计算机视觉领域。
原因:
计算视觉中的输入量非常大,输入太多像素,以至于没有足够的数据,所以dropout在计算机视觉中应用得比较频繁,有些计算机视觉研究人员非常喜欢用它,几乎成了默认的选择。
但要牢记一点,dropout是一种正则化方法,它有助于预防过拟合,因此除非算法过拟合,不然我是不会使用dropout的,所以它在其它领域应用得比较少,主要存在于计算机视觉领域,因为我们通常没有足够的数据,所以一直存在过拟合,这就是有些计算机视觉研究人员如此钟情于dropout函数的原因。
缺点
dropout一大缺点就是:代价函数不再被明确定义,每次迭代,都会随机移除一些节点,如果再三检查梯度下降的性能,实际上是很难进行复查的。
定义明确的代价函数每次迭代后都会下降,因为我们所优化的代价函数实际上并没有明确定义,或者说在某种程度上很难计算,所以我们失去了调试工具来绘制这样的图片。
解决方案:
我通常会关闭dropout函数,将keep-prob的值设为1,运行代码,确保J函数单调递减。然后打开dropout函数,希望在dropout过程中,代码并未引入bug。我觉得你也可以尝试其它方法,虽然我们并没有关于这些方法性能的数据统计,但你可以把它们与dropout方法一起使用。
③ 其他正则化方法(Other regularization methods)
数据扩增
(笔记者:我知道这是减低过拟合的方法,但原来这是属于正则化方法的范畴的么,怎么理解 “正则化” 的含义)
我们可以通过添加这类图片来增加训练集。例如,水平翻转图片、随意裁剪图片等
并把它添加到训练集。这虽然不如我们额外收集一组新图片那么好,但这样做节省了获取更多猫咪图片的花费
对于光学字符识别,我们还可以通过添加数字,随意旋转或扭曲数字来扩增数据
数据扩增可作为正则化方法使用,实际功能上也与正则化相似。
Early stopping(早终止法)
运行梯度下降时,我们可以绘制训练误差,或只绘制代价函数的优化过程,在训练集上用0-1记录分类误差次数。呈单调下降趋势,如图。
你会发现,验证集误差通常会先呈下降趋势,然后在某个节点处开始上升
early stopping作用原理是:在中间点停止迭代过程,我们得到一个值中等大小的弗罗贝尼乌斯范数。
与正则化相似,选择参数w范数较小的神经网络,但愿你的神经网络过度拟合不严重。
缺点:
但对我来说early stopping的主要缺点就是你不能独立地处理这两个问题,因为提早停止梯度下降,也就是停止了优化代价函数,因为现在你不再尝试降低代价函数,所以代价函数的值可能不够小,同时你又希望不出现过拟合,
你没有采取不同的方式来解决这两个问题,而是用一种方法同时解决两个问题,这样做的结果是我要考虑的东西变得更复杂。如果不用early stopping,另一种方法就是正则化,训练神经网络的时间就可能很长。我发现,这导致超级参数搜索空间更容易分解,也更容易搜索,但是缺点在于,你必须尝试很多正则化参数的值,这也导致搜索大量值的计算代价太高。
优点:
只运行一次梯度下降,你可以找出的较小值,中间值和较大值,而无需尝试正则化超级参数的很多值。
如果你还不能完全理解这个概念,没关系,下节课我们会详细讲解正交化,这样会更好理解。
选用:
虽然正则化有缺点,可还是有很多人愿意用它。
吴恩达老师个人更倾向于使用正则化,尝试许多不同的值,假设你可以负担大量计算的代价。而使用early stopping也能得到相似结果,还不用尝试这么多值。
该如何使用该范数实现梯度下降呢?
我们额外增加的正则化项,既然已经增加了这个正则项,现在我们要做的就是给加上这一项,然后计算这个更新项,使用新定义的,它的定义含有相关参数代价函数导数和,以及最后添加的额外正则项,这也是正则化有时被称为 “权重衰减” 的原因。
不过一般不用那个名字,之所以叫它“权重衰减”是因为这两项相等,权重指标乘以了一个小于1的系数。
为什么正则化有利于预防过拟合呢?(Why regularization reduces overfitting?)
特别是,如果的值最终在这个范围内,都是相对较小的值,大致呈线性,每层几乎都是线性的,和线性回归函数一样。
第一节课我们讲过,如果每层都是线性的,那么整个网络就是一个线性网络,即使是一个非常深的深层网络,因具有线性激活函数的特征,最终我们只能计算线性函数,因此,它不适用于非常复杂的决策,以及过度拟合数据集的非线性决策边界,如同我们在幻灯片中看到的过度拟合高方差的情况。
调试 - 梯度
归一化输入(Normalizing inputs)
训练神经网络,其中一个加速训练的方法就是归一化输入。假设一个训练集有两个特征,输入特征为2维,归一化需要两个步骤:
零均值
归一化方差
我们希望无论是训练集和测试集都是通过相同的和定义的数据转换,这两个是由训练集得出来的。
第一步:略,就是算平均值然后位移
第二步是归一化方差。注意特征的方差比特征的方差要大得多,我们要做的是给方差赋值,,这是节点 的平方,是一个向量,它的每个特征都有方差。
我们把所有数据除以向量,最后变成上图形式。此时和的方差都等于1。
补充:提示一下,你必须要用同样的方法调整测试集,而不是在训练集和测试集上分别预估 和
非归一化的缺点:
我们为什么要这么做呢?为什么我们想要归一化输入特征,回想一下右上角所定义的代价函数。
如果你使用非归一化的输入特征,代价函数会像下图这样,有点像狭长的碗一样
如果你在上图这样的代价函数上运行梯度下降法,你必须使用一个非常小的学习率。因为如果是在这个位置,梯度下降法可能需要多次迭代过程,直到最后找到最小值。
归一化的好处:
如果你归一化特征,代价函数平均起来看更对称,函数是一个更圆的球形轮廓。
不论从哪个位置开始,梯度下降法都能够更直接地找到最小值,你在梯度下降法中可以使用较大步长,而不需要像在非归一化图中那样反复执行。
当然,实际上是一个高维向量,因此用二维绘制并不能正确地传达并直观理解。但总地直观理解是代价函数会更圆一些,而且更容易优化
选用:
实际上如果假设特征范围在0-1之间,的范围在-1到1之间,范围在1-2之间,它们是相似范围,所以会表现得很好。
当它们在非常不同的取值范围内,如其中一个从1到1000,另一个从0到1,这对优化算法非常不利。
但是仅将它们设置为均化零值,假设方差为1,就像上一张幻灯片里设定的那样,确保所有特征都在相似范围内,通常可以帮助学习算法运行得更快。如果输入特征处于不同范围内,可能有些特征值从0到1,有些从1到1000,那么归一化特征值就非常重要了。
如果输入特征处于相似范围内,那么归一化就不是很重要了。
但总归执行这类归一化并不会产生什么危害,我通常会做归一化处理,虽然我不确定它能否提高训练或算法速度。
梯度消失 / 梯度爆炸(Vanishing / Exploding gradients)
训练神经网络,尤其是深度神经所面临的一个问题就是梯度消失或梯度爆炸,也就是你训练神经网络的时候,导数或坡度有时会变得非常大,或者非常小,甚至于以指数方式变小,这加大了训练的难度。
实际上,在很长一段时间内,它曾是训练深度神经网络的阻力,虽然有一个不能彻底解决此问题的解决方案,但是已在如何选择初始化权重问题上提供了很多帮助。
产生原因
(为了节约幻灯片上的空间,我画的神经网络每层只有两个隐藏单元,但它可能含有更多)
我希望你得到的直观理解是,权重只比单位矩阵略大/小一点,深度神经网络的激活函数将爆炸式增长/指数级递减
梯度爆炸:
假设每个权重矩阵,它是1.5倍的单位矩阵(从技术上来讲,最后一项有不同维度,我们先忽略,只看其余的权重矩阵)
输出,即
如果对于一个深度神经网络来说值较大,那么的值也会非常大,实际上的值将爆炸式增长
梯度消失:
假设每个权重矩阵,它是0.5倍的单位矩阵(从技术上来讲,最后一项有不同维度,我们先忽略,只看其余的权重矩阵)
输出,即
如果对于一个深度神经网络来说值较小,那么的值也会非常小,实际上的值将指数级递减
L非常大的情景:
最近Microsoft对152层神经网络的研究取得了很大进展,在这样一个深度神经网络中,
如果激活函数或梯度函数以与相关的指数增长或递减,它们的值将会变得极大或极小,从而导致训练难度上升,
尤其是梯度指数小于时,梯度下降算法的步长会非常非常小,梯度下降算法将花费很长时间来学习。
神经网络的权重初始化(Weight Initialization for Deep NetworksVanishing / Exploding gradients)
解决 “梯度消失 / 梯度爆炸” 的一个技巧是:更细致地随机化你的神经网络
步骤:
原理:
,,暂时忽略
为了预防值过大或过小,你可以看到越大,你希望越小,因为是的和,如果你把很多此类项相加,希望每项值更小,最合理的方法就是设置,表示神经元的输入特征数量。
代码:
实际上,你要做的就是设置某层权重矩阵,
其中就是我喂给第层神经单元的数量(即第层神经元数量)。总而言之:
就是将随机初始化时,对每一层的权重矩阵设置一定的权重。这会降低了梯度消失和爆炸问题
其它变体函数
一篇由Herd等人撰写的论文曾介绍过,对于几个其它变体函数,如tanh激活函数,常量1比常量2的效率更高。即方差设置为,而不是
- 如果使用Relu激活函数,可以用公式或代码,
- 如果使用tanh激活函数,可以用公式或代码,也叫Xavier初始化。
- Yoshua Bengio和他的同事还提出另一种方法,你可能在一些论文中看到过,它们使用的是公式。
梯度检验(Gradient checking)
梯度的数值逼近(Numerical approximation of gradients)
本节内容:
在实施backprop时,有一个测试叫做梯度检验,它的作用是确保backprop正确实施。
因为有时候,你虽然写下了这些方程式,却不能100%确定,执行backprop的所有细节都是正确的。
为了逐渐实现梯度检验,我们首先说说如何计算梯度的数值逼近。下节课,我们再讨论如何在backprop中执行梯度检验,以确保backprop正确实施。
原理:
我们先画出函数,标记为,。
假设,在 右侧设置,在左侧设置。设置,即我们通过这个绿色大三角形同时考虑了这两个小三角形。所以我们得到的不是一个单边公差而是一个双边公差
我们写一下数据算式,
结论:即使用双边误差的方法更逼近导数,其结果更精准
逼近误差:
双边误差的方法比单边公差精确度高得多:
对于一个非零的,双边误差的逼近误差可以写成,大写符号的含义是指逼近误差其实是一些常量乘以
然而如果用单边误差逼近,误差就是。当小于1时,实际上比大很多,如果,所以在执行梯度检验时,我们使用双边误差,即,而不使用单边公差,因为它不够准确。
梯度检验(Gradient checking)
梯度检验帮我们节省了很多时间,也多次帮我发现backprop实施过程中的bug,接下来,我们看看如何利用它来调试或检验backprop的实施是否正确。
步骤:
假设你的网络中含有下列参数,和……和
① 向量化
首先,把所有参数转换成一个巨大的向量数据。
比如所有矩阵转换成向量,之后首尾连接,得到一个巨型向量
并得到一个的代价函数② 向量化
接着,同理把和……和转换成一个新的向量,得到,它与具有相同维度
③ 梯度检验(Gradient checking)
现在的问题是和代价函数的梯度或坡度有什么关系?这就是实施梯度检验的过程
④ 双边误差验证
④① 双边误差
首先,我们要清楚是超参数的一个函数,你也可以将函数展开为,
不论超级参数向量的维度是多少,为了实施梯度检验,你要做的就是循环执行,从而对每个也就是对每个组成元素计算的值,我使用双边误差,只对增加,其它项保持不变,对另一边做同样的操作,只不过是减去,其它项全都保持不变:
④② 验证
我们了解到这个值,
是代价函数的偏导数,然后你需要对i的每个值都执行这个运算,最后得到两个向量,得到的逼近值,它们具有相同维度(同),你要做的就是验证这些向量是否彼此接近
④③ 如何界定是否接近
我一般做下列运算
分子:计算这两个向量的距离的欧几里得范数。
注意这里()没有平方。它是误差平方之和,然后求平方根,得到欧式距离,然后用向量长度归一化,使用向量长度的欧几里得范数。分母:只是用于预防这些向量太小或太大,分母使得这个方程式变成比率。
我们实际执行这个方程式,可能为,使用这个取值范围内的,
- 如果方程式值为或更小,这就很好,这就意味着导数逼近很有可能是正确的,它的值非常小。
- 如果方程式值在范围内,我就要小心了,也许这个值没问题,但我会再次检查这个向量的所有项,确保没有一项误差过大,否则说明可能有bug
- 如果方程式值是,我就会担心是否存在bug
这时应该仔细检查所有项,看是否有一个具体的值,使得与$ d\theta[i]$大不相同,并用它来追踪一些求导计算是否正确,
经过一些调试,最终结果会是这种非常小的值(),那么,你的实施可能是正确的。
梯度检验应用的注意事项(Gradient Checking Implementation Notes)
- 不要在训练中使用梯度检验,它只用于调试
- 因为他太慢了,计算所有值的是一个非常漫长的计算过程
- 如果算法的梯度检验失败,要检查所有项
- 如果与的值相差很大,我们要做的就是查找不同的i值,看看是哪个导致与的值相差这么多
- 在实施梯度检验时,如果使用正则化,请注意正则项
- 如果代价函数,记住一定要包括这个正则项
- 梯度检验不能与dropout同时使用
- 因为每次迭代过程中,dropout会随机消除隐藏层单元的不同子集,难以计算dropout在梯度下降上的代价函数。
因此dropout可作为优化代价函数的一种方法,但是代价函数J被定义为对所有指数极大的节点子集求和。 - 而在任何迭代过程中,这些节点都有可能被消除,所以很难计算代价函数。你只是对成本函数做抽样,用dropout,每次随机消除不同的子集,很难用梯度检验来双重检验dropout的计算,所以我一般不同时使用梯度检验和dropout。
- 因为每次迭代过程中,dropout会随机消除隐藏层单元的不同子集,难以计算dropout在梯度下降上的代价函数。
- 比较微妙的一点,现实中几乎不会出现这种情况
- 当和接近0时,梯度下降的实施是正确的,在随机初始化过程中……,但是在运行梯度下降时,和变得更大。
可能只有在和接近0时,backprop的实施才是正确的。但是当和变大时,它会变得越来越不准确。
你需要做一件事,我不经常这么做:就是在随机初始化过程中,运行梯度检验,然后再训练网络。和会有一段时间远离0,如果随机初始化值比较小,反复训练网络之后,再重新运行梯度检验。
- 当和接近0时,梯度下降的实施是正确的,在随机初始化过程中……,但是在运行梯度下降时,和变得更大。