第一周:深度学习的 实践层面 (Practical aspects of Deep Learning)
1.1 训练,验证,测试集(Train / Dev / Test sets)
在配置训练、验证和测试数据集的过程中做出正确决策会在很大程度上帮助大家创建高效的神经网络。训练神经网络时,我们需要做出很多决策,例如:
神经网络分多少层;每层含有多少个隐藏单元;学习速率是多少;各层采用哪些激活函数。
1.2 偏差,方差(Bias /Variance)
几乎所有机器学习从业人员都期望深刻理解偏差和方差,这两个概念
易学难精
。
另一个趋势是对偏差和方差的权衡研究甚浅,你可能听说过这两个概念,但深度学习的误差很少权衡二者,我们总是分别考虑偏差和方差,却很少谈及偏差 和方差的权衡问题,下面我们来一探究竟。
假设这就是数据集,如果给这个数据集拟合一条直线,可能得到一个逻辑回归拟合,但它并不能很好地拟合该数据,这是高偏差(high bias
)的情况,我们称为
“
欠拟合(” underfitting
)。 相反的如果我们拟合一个非常复杂的分类器,比如深度神经网络或含有隐藏单元的神经网络,可能就非常适用于这个数据集,但是这看起来也不是一种很好的拟合方式分类器方差 较高(high variance
),数据过度拟合(
overfitting
)。
在两者之间,可能还有一些像图中这样的,复杂程度适中,数据拟合适度的分类器,这个数据拟合看起来更加合理,我们称之为“
适度拟合
”
(
just right
)是介于过度拟合和欠拟合中间的一类。
在这样一个只有
𝑥
1
和
𝑥
2两个特征的二维数据集中,我们可以绘制数据,将偏差和方差可视化。在多维空间数据中,绘制数据和可视化分割边界无法实现,但我们可以通过几个指标,来研究偏
差和方差
。
假定训练集误差是
1%
,为了方便论证,假定验证集误差是
11%
,可以看出训练集设置得非常好,而验证集设置相对较差,我们可能过度拟合了训练集,在某种程度上,验证集并 没有充分利用交叉验证集的作用,像这种情况,我们称之为“
高方差
”
。
假设训练集误差是
15%
,我们把训练集误差写在首行,验证集误差是
16%
,假设该案例中人的错误率几乎为 0%
,人们浏览这些图片,分辨出是不是猫。算法并没有在训练集中得到很好训练,如果训练数据的拟合度不高,就是数据欠拟合,就可以说这种算法偏差比较高。
再举一个例子,训练集误差是
15%
,偏差相当高,但是,验证集的评估结果更糟糕,错误率达到 30%
,在这种情况下,我会认为这种算法偏差高,因为它在训练集上结果不理想,
而且方差也很高,这是方差偏差都很糟糕的情况。 再看最后一个例子,训练集误差是 0.5%
,验证集误差是
1%
,用户看到这样的结果会很 开心,猫咪分类器只有 1%
的错误率,偏差和方差都很低。
我们之前讲过,这样的分类器,会产生高偏差,因为它的数据拟合度低,像这种接近线 性的分类器,数据拟合度低。
但是如果我们稍微改变一下分类器,我用紫色笔画出,它会过度拟合部分数据,用紫色线画出的分类器具有高偏差和高方差,偏差高是因为它几乎是一条线性分类器,并未拟合数据。
这种二次曲线能够很好地拟合数据。
这条曲线中间部分灵活性非常高,却过度拟合了这两个样本,这类分类器偏差很高,因为它几乎是线性的。
而采用曲线函数或二次元函数会产生高方差,因为它曲线灵活性太高以致拟合了这两个错误样本和中间这些活跃数据。
我们讲了如何通过分析在训练集上训练算法产生的误差和验证集上验证算法产生的误差来诊断算法是否存在高偏差和高方差,是否两个值都高,或者两个值都不高,根 据法偏差和方差的具体情况决定接下来你要做的工作,下节课,我会根据算法偏差和方差
的高低情况讲解一些机器学习的基本方法,帮助大家更系统地优化算法。
1.3 机器学习基础(Basic Recipe for Machine Learning)
下图就是我在训练神经网络用到的基本方法:(尝试这些方法,可能有用,可能没用)
有两点需要大家注意:
第一点,高偏差和高方差是两种不同的情况,我们后续要尝试的方法也可能完全不同, 我通常会用训练验证集来诊断算法是否存在偏差或方差问题,然后根据结果选择尝试部分方法。举个例子,如果算法存在高偏差问题,准备更多训练数据其实也没什么用处,至少这不是更有效的方法,所以大家要清楚存在的问题是偏差还是方差,还是两者都有问题,明确这 一点有助于我们选择出最有效的方法。
第二点,在机器学习的初期阶段,关于所谓的偏差方差权衡的讨论屡见不鲜,原因是我
们能尝试的方法有很多。可以增加偏差,减少方差,也可以减少偏差,增加方差,但是在深 度学习的早期阶段,我们没有太多工具可以做到只减少偏差或方差却不影响到另一方。但在 当前的深度学习和大数据时代,只要持续训练一个更大的网络,只要准备了更多数据,那么 也并非只有这两种情况,我们假定是这样,那么,只要正则适度,通常构建一个更大的网络
便可以,在不影响方差的同时减少偏差,而采用更多数据通常可以在不过多影响偏差的同时 减少方差。这两步实际要做的工作是:训练网络,选择网络或者准备更多数据,现在我们有 工具可以做到在减少偏差或方差的同时,不对另一方产生过多不良影响。我觉得这就是深度 学习对监督式学习大有裨益的一个重要原因,也是我们不用太过关注如何平衡偏差和方差的
一个重要原因,但有时我们有很多选择,减少偏差或方差而不增加另一方。最终,我们会得 到一个非常规范化的网络。从下节课开始,我们将讲解正则化,训练一个更大的网络几乎没 有任何负面影响,而训练一个大型神经网络的主要代价也只是计算时间,前提是网络是比较 规范化的
我在课上不止一次提到了
正则化,它是一种非常 实用的减少方差的方法,正则化时会出现偏差方差权衡问题,偏差可能略有增加,如果网络足够大,增幅通常不会太高,
我们下节课让大家更好理解如何实现神经网络的正则化。
1.4 正则化(Regularization)
深度学习可能存在过拟合问题——高方差
,有两个解决方法,一个是正则化,另一个是 准备更多的数据,这是非常可靠的方法,但你可能无法时时刻刻准备足够多的训练数据或者 获取更多数据的成本很高,但正则化通常有助于避免过拟合或减少你的网络误差。
用逻辑回归来实现这些设想,求成本函数
𝐽
的最小值,它是我们定义的成本函数,参数包含一些训练数据和不同数据中个体预测的损失,𝑤
和
𝑏
是逻辑回归的两个参数,
𝑤
是一 个多维度参数矢量,𝑏
是一个实数。在逻辑回归函数中加入正则化,只需添加参数
λ
,也就是正则化参数,一会儿再详细讲。
为什么只正则化参数𝑤?为什么不再加上参数 𝑏 呢?
你可以这么做,只是我习惯省略 不写,因为𝑤
通常是一个高维参数矢量,已经可以表达高偏差问题,
𝑤
可能包含有很多参数, 我们不可能拟合所有参数,而𝑏
只是单个数字,所以
𝑤
几乎涵盖所有参数,而不是
𝑏
,如果加 了参数𝑏
,其实也没太大影响,因为
𝑏
只是众多参数中的一个,所以我通常省略不计,
如果你 想加上这个参数,完全没问题
如果用的是
𝐿1正则化,𝑤最终会是稀疏的,也就是说𝑤向量中有很多 0
,有人说这样有利于压缩模型,因为集合中参数均为 0
,存储模型所占用的内存更少。实际上,虽然
𝐿1
正则化使模型变得稀疏,却没有降低太多存储内存,所以我认为这并不是𝐿1
正则化的目的,至少不是为了压缩模型,人们在训练网络时,越来越倾向于使用𝐿2
正则化。 我们来看最后一个细节,𝜆
是正则化参数,我们通常使用验证集或交叉验证集来配置这个参数,尝试各种各样的数据,寻找最好的参数,我们要考虑训练集之间的权衡,把参数设置为较小值,这样可以避免过拟合,所以 λ
是另外一个需要调整的超级参数,顺便说一下, 为了方便写代码,在 Python
编程语言中,
𝜆
是一个保留字段,编写代码时,我们删掉
𝑎
,写 成𝑙𝑎𝑚𝑏𝑑
,以免与
Python
中的保留字段冲突,这就是在逻辑回归函数中实现
𝐿2
正则化的过程,如何在神经网络中实现𝐿2
正则化呢?
该如何使用该范数实现梯度下降呢?
用
backprop
计算出
𝑑𝑊
的值,
backprop
会给出
𝐽
对
𝑊
的偏导数,实际上是
𝑊
[𝑙]
,把
𝑊
[𝑙]
替 换为𝑊
[𝑙]
减去学习率乘以
𝑑𝑊
。
我不打算这么叫它,之所以叫它
“权重衰减”
是因为这两项相等,权重指标乘以了一个小于 1
的系数。 以上就是在神经网络中应用𝐿2正则化的过程。
1.5 为 什 么 正 则 化 有 利 于 预 防 过 拟 合 呢 ? ( Why regularization reduces overfitting?)
为什么正则化有利于预防过拟合呢?为什么它可以减少方差问题?
我们通过两个例子 来直观体会一下
现在我们来看下这个庞大的深度拟合神经网络。我知道这张图不够大,深度也不够,但你可以想象这是一个过拟合的神经网络。这是我们的代价函数𝐽
,含有参数
𝑊
,
𝑏
。我们添加正则项,它可以避免数据权值矩阵过大,这就是弗罗贝尼乌斯范数,
为什么压缩𝐿2范数,或 者弗罗贝尼乌斯范数或者参数可以减少过拟合?
直观上理解就是如果正则化
𝜆
设置得足够大,权重矩阵
𝑊
被设置为接近于
0
的值,直观理解就是把多隐藏单元的权重设为 0
,于是基本上消除了这些隐藏单元的许多影响。如果是这种情况,这个被大大简化了的神经网络会变成一个很小的网络,小到如同一个逻辑回归单元,可是深度却很大,它会使这个网络从过度拟合的状态更接近左图的高偏差状态。
但是
𝜆
会存在一个中间值,于是会有一个接近
“
Just Right
”
的中间状态。 直观理解就是𝜆
增加到足够大,𝑊会接近于 0
,实际上是不会发生这种情况的,我们尝试消除或至少减少许多隐藏单元的影响,最终这个网络会变得更简单,这个神经网络越来越
接近逻辑回归,我们直觉上认为大量隐藏单元被完全消除了,其实不然,实际上是该神经网络的所有隐藏单元依然存在,但是它们的影响变得更小了。神经网络变得更简单了,貌似这样更不容易发生过拟合,因此我不确定这个直觉经验是否有用,不过在编程中执行正则化时, 你实际看到一些方差减少的结果。
我们再来直观感受一下,正则化为什么可以预防过拟合,假设我们用的是这样的双曲线激活函数。
现在你应该摒弃这个直觉,如果正则化参数 λ 很大,激活函数的参数会相对较小,因为代价函数中的参数变大了
第一节课我们讲过,
如果每层都是线性的,那么整个网络就是一个线性网络,即使是一个非常深的深层网络,因具有线性激活函数的特征,最终我们只能计算线性函数
,因此,它不适用于非常复杂的决策,以及过度拟合数据集的非线性决策边界,如同我们在幻灯片中看 到的过度拟合高方差的情况。
总结一下,如果正则化参数变得很大,参数
𝑊
很小,
𝑧
也会相对变小,此时忽略
𝑏
的影响, 𝑧会相对变小,实际上,
𝑧
的取值范围很小,这个激活函数,也就是曲线函数
𝑡𝑎𝑛ℎ
会相对呈
线性
,整个神经网络会计算离线性函数近的值,这个线性函数非常简单,并不是一个极复杂的高度非线性函数,不会发生过拟合。
大家在编程作业里实现正则化的时候,会亲眼看到这些结果,总结正则化之前,我给大家一个执行方面的小建议,在增加正则化项时,应用之前定义的代价函数𝐽
,我们做过修改, 增加了一项,目的是预防权重过大。
如果你使用的是梯度下降函数,在调试梯度下降时,其中一步就是把代价函数
𝐽
设计成 这样一个函数,在调试梯度下降时,它代表梯度下降的调幅数量。可以看到,代价函数对于 梯度下降的每个调幅都单调递减。如果你实施的是正则化函数,请牢记,𝐽
已经有一个全新 的定义。如果你用的是原函数𝐽
,也就是这第一个项正则化项,你可能看不到单调递减现象, 为了调试梯度下降,请务必使用新定义的𝐽
函数,它包含第二个正则化项,否则函数
𝐽
可能不 会在所有调幅范围内都单调递减。
这就是
𝐿2
正则化,它是我在训练深度学习模型时最常用的一种方法。在深度学习中,还 有一种方法也用到了正则化,就是
dropout 正则化
,我们下节课再讲。
1.6 dropout 正则化(Dropout Regularization)
除了
𝐿2
正则化,还有一个非常实用的正则化方法
——“
Dropout
(随机失活)
”
,我们来看 看它的工作原理。
假设你在训练上图这样的神经网络,它存在过拟合,这就是
dropout
所要处理的,我们 复制这个神经网络,dropout
会遍历网络的每一层,并设置消除神经网络中节点的概率。假设网络中的每一层,每个节点都以抛硬币的方式设置概率,每个节点得以保留和消除的概率都是 0.5
,设置完节点概率,我们会消除一些节点,
然后删除掉从该节点进出的连线,最后得到一个节点更少,规模更小的网络
,然后用 backprop
方法进行训练。
这是网络节点精简后的一个样本,对于其它样本,我们照旧以抛硬币的方式设置概率, 保留一类节点集合,删除其它类型的节点集合。对于每个训练样本,我们都将采用一个精简后神经网络来训练它,这种方法似乎有点怪,单纯遍历节点,编码也是随机的,可它真的有效。不过可想而知,我们针对每个训练样本训练规模极小的网络,最后你可能会认识到为什么要正则化网络,因为我们在训练极小的网络。
如何实施 dropout 呢?
方法有几种,接下来我要讲的是最常用的方法,即
inverted
dropout
(反向随机失活),出于完整性考虑,我们用一个三层(
𝑙 = 3
)网络来举例说明。 编码中会有很多涉及到 3
的地方。我只举例说明如何在某一层中实施
dropout
。 首先要定义向量𝑑
,
𝑑
[3]
表示一个三层的
dropout
向量:
d3 = np.random.rand(a3.shape[0],a3.shape[1])
然后看它是否小于某数,我们称之为
keep-prob
,
keep-prob
是一个具体数字,上个示例 中它是 0.5
,而本例中它是
0.8
,它表示保留某个隐藏单元的概率,此处
keep-prob
等于
0.8
, 它意味着消除任意一个隐藏单元的概率是 0.2
,它的作用就是生成随机矩阵,如果对
𝑎
[3]
进行 因子分解,效果也是一样的。𝑑
[3]
是一个矩阵,每个样本和每个隐藏单元,其中
𝑑
[3]
中的对应
值为
1
的概率都是
0.8
,对应为
0
的概率是
0.2
,随机数字小于
0.8
。它等于
1
的概率是
0.8
, 等于 0
的概率是
0.2
。
接下来要做的就是从第三层中获取激活函数,这里我们叫它
𝑎
[3]
,
𝑎
[3]
含有要计算的激活 函数,𝑎
[3]
等于上面的
𝑎
[3]
乘以
𝑑
[3]
,
a3 =np.multiply(a3,d3)
,这里是元素相乘,也可写 为𝑎3 ∗= 𝑑3
,它的作用就是让
𝑑
[3]
中所有等于
0
的元素(输出),而各个元素等于
0
的概率 只有 20%
,乘法运算最终把
𝑑
[3]
中相应元素输出,即让
𝑑
[3]
中
0
元素与
𝑎
[3]
中相对元素归零。
如果用
python
实现该算法的话,
𝑑
[3]
则是一个布尔型数组,值为
true
和
false
,而不是 1 和
0
,乘法运算依然有效,
python
会把
true
和
false
翻译为
1
和
0
,大家可以用
python 尝试一下。
最后,我们向外扩展
𝑎
[3]
,用它除以
0.8
,或者除以
keep-prob
参数
𝑎3/= 𝑘𝑒𝑒𝑝 − 𝑝𝑟𝑜b
下面我解释一下为什么要这么做
,为方便起见,我们假设第三隐藏层上有
50
个单元或 50 个神经元,在一维上
𝑎
[3]
是
50
,我们通过因子分解将它拆分成
50 × 𝑚
维的,保留和删除它 们的概率分别为 80%
和
20%
,这意味着最后被删除或归零的单元平均有
10
(
50×20%=10
) 个,现在我们看下𝑧
[4]
,
𝑧
[4]
= 𝑤
[4]
𝑎
[3]
+ 𝑏
[4]
,我们的预期是,
𝑎
[3]
减少
20%
,也就是说
𝑎
[3]
中 有 20%
的元素被归零,为了不影响
𝑧
[4]
的期望值,我们需要用
𝑤
[4]
𝑎
[3]
/0.8
,它将会修正或弥 补我们所需的那 20%
,
𝑎
[3]
的期望值不会变,划线部分就是所谓的
dropout
方法。
它的功能是,不论
keep-prop
的值是多少
0.8
,
0.9
甚至是
1
,如果
keep-prop
设置为
1
, 那么就不存在 dropout
,因为它会保留所有节点。反向随机失活(
inverted dropout
)方法通 过除以 keep-prob
,确保
𝑎
[3]
的期望值不变。 事实证明,在测试阶段,当我们评估一个神经网络时,也就是用绿线框标注的反向随机 失活方法,使测试阶段变得更容易,因为它的数据扩展问题变少,我们将在下节课讨论。 据我了解,目前实施 dropout
最常用的方法就是
Inverted dropout
,建议大家动手实践 一下。Dropout
早期的迭代版本都没有除以
keep-prob
,所以在测试阶段,平均值会变得越 来越复杂,不过那些版本已经不再使用了。
现在你使用的是
𝑑
向量,你会发现,不同的训练样本,清除不同的隐藏单元也不同。实 际上,如果你通过相同训练集多次传递数据,每次训练数据的梯度不同,则随机对不同隐藏 单元归零,有时却并非如此。比如,需要将相同隐藏单元归零,第一次迭代梯度下降时,把 一些隐藏单元归零,第二次迭代梯度下降时,也就是第二次遍历训练集时,对不同类型的隐 藏层单元归零。向量𝑑
或
𝑑
[3]
用来决定第三层中哪些单元归零,无论用
foreprop
还是
backprop
, 这里我们只介绍了 foreprob
如何在测试阶段训练算法,在测试阶段,我们已经给出了
𝑥
,或是想预测的变量,用的是标准计数法。我用𝑎
[0]
,第
0
层的激活函数标注为测试样本
𝑥,我们在测试阶段不使用dropout
函数,尤其是像下列情况:
显然在测试阶段,我们并未使用
dropout
,自然也就不用抛硬币来决定失活概率,以及 要消除哪些隐藏单元了,因为在测试阶段进行预测时,我们不期望输出结果是随机的,如果 测试阶段应用 dropout
函数,预测会受到干扰。理论上,你只需要多次运行预测处理过程, 每一次,不同的隐藏单元会被随机归零,预测处理遍历它们,但计算效率低,得出的结果也 几乎相同,与这个不同程序产生的结果极为相似。
Inverted dropout
函数在除以
keep-prob
时可以记住上一步的操作,目的是确保即使在
测试阶段不执行
dropout
来调整数值范围,激活函数的预期结果也不会发生变化,所以没必 要在测试阶段额外添加尺度参数,这与训练阶段不同。
𝑙 = 𝑘𝑒𝑒𝑝 − 𝑝𝑟𝑜𝑏
这就是
dropout
,大家可以通过本周的编程练习来执行这个函数,亲身实践一下。 为什么 dropout
会起作用呢?下节课我们将更加直观地了解
dropout
的具体功能。
1.7 理解 dropout(Understanding Dropout)
Dropout
可以随机删除网络中的神经单元,他为什么可以通过正则化发挥如此大的作用 呢?
直观上理解:不要依赖于任何一个特征,因为该单元的输入可能随时被清除,因此该单元通过这种方式传播下去,并为单元的四个输入增加一点权重,通过传播所有权重,dropout 将产生收缩权重的平方范数的效果,和之前讲的𝐿2
正则化类似;实施
dropout
的结果实它会 压缩权重,并完成一些预防过拟合的外层正则化;𝐿2
对不同权重的衰减是不同的,它取决于
激活函数倍增的大小。 总结一下,dropout
的功能类似于
𝐿2
正则化,与
𝐿2正则化不同的是应用方式不同会带来 一点点小变化,甚至更适用于不同的输入范围。
第二个直观认识是
,我们从单个神经元入手,如图,这个单元的工作就是输入并生成一些有意义的输出。通过 dropout
,该单元的输入几乎被消除,有时这两个单元会被删除,有时会删除其它单元,就是说,我用紫色圈起来的这个单元,它不能依靠任何特征,因为特征都有可能被随机清除,或者说该单元的输入也都可能被随机清除。我不愿意把所有赌注都放在一个节点上,不愿意给任何一个输入加上太多权重,因为它可能会被删除,因此该单元将通过这种方式积极地传播开,并为单元的四个输入增加一点权重,通过传播所有权重,
dropout
将产生收缩权重的平方范数的效果,和我们之前讲过的
𝐿2
正则化类似,实施
dropout
的结果是它会压缩权重,并完成一些预防过拟合的外层正则化。
事实证明,
dropout
被正式地作为一种正则化的替代形式,
𝐿2
对不同权重的衰减是不同 的,它取决于倍增的激活函数的大小。
总结一下,
dropout
的功能类似于
𝐿2
正则化,与
𝐿2
正则化不同的是,被应用的方式不同,
dropout
也会有所不同,甚至更适用于不同的输入范围。
实施
dropout
的另一个细节是,这是一个拥有三个输入特征的网络,其中一个要选择的参数是 keep-prob
,它代表每一层上保留单元的概率。所以不同层的
keep-prob
也可以变化。 第一层,矩阵𝑊
[1]
是
7×3
,第二个权重矩阵
𝑊
[2]
是
7×7
,第三个权重矩阵
𝑊
[3]
是
3×7
,以此类推,𝑊
[2]
是最大的权重矩阵,因为
𝑊
[2]
拥有最大参数集,即
7×7
,为了预防矩阵的过拟合, 对于这一层,我认为这是第二层,它的 keep-prob
值应该相对较低,假设是
0.5
。对于其它层,过拟合的程度可能没那么严重,它们的 keep-prob
值可能高一些,可能是
0.7
,这里是0.7。如果在某一层,我们不必担心其过拟合的问题,那么
keep-prob
可以为
1
,为了表达清除,我用紫色线笔把它们圈出来,每层 keep-prob
的值可能不同。
注意
keep-prob
的值是
1
,意味着保留所有单元,并且不在这一层使用
dropout
,对于有可能出现过拟合,且含有诸多参数的层,我们可以把 keep-prob
设置成比较小的值,以便应用更强大的 dropout
,有点像在处理
𝐿2
正则化的正则化参数
𝜆,我们尝试对某些层施行更多正则化,从技术上讲,我们也可以对输入层应用
dropout,我们有机会删除一个或多个输入特征,虽然现实中我们通常不这么做,
keep-prob 的值为 1,是非常常用的输入值,也可以用更大的值,或许是 0.9。但是消除一半的输入特征是不太可能的,如果我们遵守这个准则,
keep-prob 会接近于 1,即使你对输入层应用
dropout。
总结一下,如果你担心某些层比其它层更容易发生过拟合,可以把某些层的
keep-prob值设置得比其它层更低,缺点是为了使用交叉验证,你要搜索更多的超级参数,另一种方案是在一些层上应用
dropout,而有些层不用
dropout,应用
dropout 的层只含有一个超级参数,就是
keep-prob。
结束前分享两个实施过程中的技巧,实施
dropout
,在计算机视觉领域有很多成功的第一次。计算视觉中的输入量非常大,输入太多像素,以至于没有足够的数据,所以 dropout在计算机视觉中应用得比较频繁,有些计算机视觉研究人员非常喜欢用它,几乎成了默认的选择,
但要牢记一点
,dropout
是一种正则化方法,它有助于预防过拟合,因此除非算法过拟合,不然我是不会使用 dropout
的,所以它在其它领域应用得比较少,主要存在于计算机视觉领域,因为我们通常没有足够的数据,所以一直存在过拟合,这就是有些计算机视觉研究人员如此钟情于 dropout
函数的原因。直观上我认为不能概括其它学科。
dropout 一大缺点就是代价函数𝐽不再被明确定义,每次迭代,都会随机移除一些节点, 如果再三检查梯度下降的性能,实际上是很难进行复查的。
定义明确的代价函数𝐽
每次迭代后都会下降,因为我们所优化的代价函数 J
实际上并没有明确定义,或者说在某种程度上很难计算,所以我们失去了调试工具来绘制这样的图片。我通常会关闭 dropout
函数,将
keep
prob
的值设为
1
,运行代码,确保
𝐽
函数单调递减。然后打开
dropout
函数,希望在
dropout 过程中,代码并未引入 bug
。我觉得你也可以尝试其它方法,虽然我们并没有关于这些方法性能的数据统计,但你可以把它们与 dropout
方法一起使用。
1.8 其他正则化方法(Other regularization methods)
除了
𝐿2
正则化和随机失活(
dropout
)正则化,还有几种方法可以减少神经网络中的过拟合
一.数据扩增
假设你正在拟合猫咪图片分类器,如果你想通过扩增训练数据来解决过拟合,但扩增数 据代价高,而且有时候我们无法扩增数据,但我们可以通过添加这类图片来增加训练集。例如,水平翻转图片,并把它添加到训练集。所以现在训练集中有原图,还有翻转后的这张图片,所以通过水平翻转图片,训练集则可以增大一倍,因为训练集有冗余,这虽然不如我们额外收集一组新图片那么好,但这样做节省了获取更多猫咪图片的花费。
除了水平翻转图片,你也可以随意裁剪图片,
这张图是把原图旋转并随意放大后裁剪的, 仍能辨别出图片中的猫咪。
通过随意翻转和裁剪图片,我们可以增大数据集,额外生成假训练数据。和全新的,独 立的猫咪图片数据相比,这些额外的假的数据无法包含像全新数据那么多的信息,但我们这么做基本没有花费,代价几乎为零,除了一些对抗性代价。以这种方式扩增算法数据,进而正则化数据集,减少过拟合比较廉价。
像这样人工合成数据的话,我们要通过算法验证,图片中的猫经过水平翻转之后依然是猫。
大家注意,我并没有垂直翻转,因为我们不想上下颠倒图片,也可以随机选取放大后的部分图片,猫可能还在上面。
对于光学字符识别,我们还可以通过添加数字,随意旋转或扭曲数字来扩增数据,把这 些数字添加到训练集,它们仍然是数字。为了方便说明,我对字符做了强变形处理,所以数字 4
看起来是波形的,其实不用对数字
4
做这么夸张的扭曲,只要轻微的变形就好,我做成这样是为了让大家看的更清楚。实际操作的时候,我们通常对字符做更轻微的变形处理。因为这几个 4
看起来有点扭曲。所以,数据扩增可作为正则化方法使用,实际功能上也与正则化相似。
二.early stopping
还有另外一种常用的方法叫作
early stopping
,运行梯度下降时,我们可以绘制训练误 差,或只绘制代价函数𝐽
的优化过程,在训练集上用
0-1
记录分类误差次数。呈单调下降趋 势,如图。
因为在训练过程中,我们希望训练误差,代价函数
𝐽
都在下降,通过
early stopping
,我 们不但可以绘制上面这些内容,还可以绘制验证集误差,它可以是验证集上的分类误差,或验证集上的代价函数,逻辑损失和对数损失等,你会发现,验证集误差通常会先呈下降趋势, 然后在某个节点处开始上升,early stopping
的作用是,你会说,神经网络已经在这个迭代过程中表现得很好了,我们在此停止训练吧,得到验证集误差,它是怎么发挥作用的?
当你还未在神经网络上运行太多迭代过程的时候,参数
𝑤
接近
0
,因为随机初始化
𝑤
值 时,它的值可能都是较小的随机值,所以在你长期训练神经网络之前𝑤
依然很小,在迭代过程和训练过程中𝑤
的值会变得越来越大,比如在这儿,神经网络中参数
𝑤
的值已经非常大了, 所以 early stopping
要做就是在中间点停止迭代过程,我们得到一个
𝑤
值中等大小的弗罗贝 尼乌斯范数,与𝐿2
正则化相似,选择参数
𝑤
范数较小的神经网络,但愿你的神经网络过度拟 合不严重。
术语
early stopping
代表提早停止训练神经网络,训练神经网络时,我有时会用到
early
stopping
,但是它也有一个缺点,我们来了解一下。 我认为机器学习过程包括几个步骤,其中一步是选择一个算法来优化代价函数𝐽
,我们 有很多种工具来解决这个问题,如梯度下降,后面我会介绍其它算法,例如 Momentum
,
RMSprop
和
Adam
等等,但是优化代价函数
𝐽
之后,我也不想发生过拟合,也有一些工具可 以解决该问题,比如正则化,扩增数据等等。
在机器学习中,超级参数激增,选出可行的算法也变得越来越复杂。我发现,如果我们用一组工具优化代价函数𝐽
,机器学习就会变得更简单,在重点优化代价函数
𝐽
时,你只需要留意𝑤
和
𝑏
,
𝐽(𝑤, 𝑏)
的值越小越好,你只需要想办法减小这个值,其它的不用关注。然后,预防过拟合还有其他任务,换句话说就是减少方差,这一步我们用另外一套工具来实现,这个
原理有时被称为
“
正交化
”
。思路就是在一个时间做一个任务,后面课上我会具体介绍正交化, 如果你还不了解这个概念,不用担心。 但对我来说 early stopping
的主要缺点就是你不能独立地处理这两个问题,因为提早停 止梯度下降,也就是停止了优化代价函数𝐽
,因为现在你不再尝试降低代价函数
𝐽
,所以代价函数𝐽
的值可能不够小,同时你又希望不出现过拟合,你没有采取不同的方式来解决这两个问题,而是用一种方法同时解决两个问题,这样做的结果是我要考虑的东西变得更复杂。
如果不用
early stopping
,另一种方法就是
𝐿2
正则化,训练神经网络的时间就可能很长。我发现,这导致超级参数搜索空间更容易分解,也更容易搜索,但是缺点在于,你必须尝试很多正则化参数𝜆
的值,这也导致搜索大量
𝜆
值的计算代价太高。
Early stopping
的优点是,只运行一次梯度下降,你可以找出
𝑤
的较小值,中间值和较大值,而无需尝试𝐿2
正则化超级参数
𝜆
的很多值。 如果你还不能完全理解这个概念,没关系,下节课我们会详细讲解正交化,这样会更好 理解。 虽然𝐿2正则化有缺点,可还是有很多人愿意用它。吴恩达老师个人更倾向于使用
𝐿2
正则 化,尝试许多不同的𝜆
值,假设你可以负担大量计算的代价。而使用
early stopping
也能得到 相似结果,还不用尝试这么多𝜆
值。 这节课我们讲了如何使用数据扩增,以及如何使用 early stopping
降低神经网络中的方 差或预防过拟合。
1.9 归一化输入(Normalizing inputs)
训练神经网络,其中一个加速训练的方法就是归一化输入。
假设一个训练集有两个特征, 输入特征为 2
维,归一化需要两个步骤:
1.零均值
2.归一化方差;
我们希望无论是训练集和测试集都是通过相同的
𝜇和𝜎2
定义的数据转换,这两个是由训 练集得出来的。
如果你使用非归一化的输入特征,代价函数会像这样:
这是一个非常细长狭窄的代价函数,你要找的最小值应该在这里。但如果特征值在不同范围,假如𝑥
1
取值范围从
1
到
1000
,特征
𝑥
2
的取值范围从
0
到
1
,结果是参数
𝑤1和𝑤2
值的范围或比率将会非常不同,这些数据轴应该是𝑤
1
和
𝑤
2
,但直观理解,我标记为
𝑤
和
𝑏
,代价函数就有点像狭长的碗一样,如果你能画出该函数的部分轮廓,它会是这样一个狭长的函数。
然而如果你归一化特征,代价函数平均起来看更对称,如果你在上图这样的代价函数上运行梯度下降法,你必须使用一个非常小的学习率。因为如果是在这个位置,梯度下降法可能需要多次迭代过程,直到最后找到最小值。但如果函数是一个更圆的球形轮廓,那么不论从哪个位置开始,梯度下降法都能够更直接地找到最小值,你可以在梯度下降法中使用较大 步长,而不需要像在左图中那样反复执行。
当然,实际上
𝑤
是一个高维向量,因此用二维绘制
𝑤
并不能正确地传达并直观理解,但总地直观理解是代价函数会更圆一些,而且更容易优化,前提是特征都在相似范围内,而不是从 1
到
1000
,
0
到
1
的范围,而是在
-1
到
1
范围内或相似偏差,这使得代价函数
𝐽
优化起 来更简单快速。
实际上如果假设特征
𝑥
1
范围在
0-1
之间,
𝑥
2
的范围在
-1
到
1
之间,
𝑥
3
范围在
1-2
之间, 它们是相似范围,所以会表现得很好。
当它们在非常不同的取值范围内,如其中一个从
1
到
1000
,另一个从
0
到
1
,这对优化算法非常不利。但是仅将它们设置为均化零值,假设方差为 1
,就像上一张幻灯片里设定的那样,确保所有特征都在相似范围内,通常可以帮助学习算法运行得更快。
所以如果输入特征处于不同范围内,可能有些特征值从
0
到
1
,有些从
1
到
1000
,那么归一化特征值就非常重要了。如果特征值处于相似范围内,那么归一化就不是很重要了。执行这类归一化并不会产生什么危害,我通常会做归一化处理,虽然我不确定它能否提高训练 或算法速度。
这就是归一化特征输入,下节课我们将继续讨论提升神经网络训练速度的方法。
1.10 梯度消失/梯度爆炸(Vanishing / Exploding gradients)
训练神经网络,尤其是深度神经所面临的一个问题就是
梯度消失或梯度爆炸
,也就是你训练神经网络的时候,导数或坡度有时会变得非常大,或者非常小,甚至于以指数方式变小, 这加大了训练的难度。
在深度神经网络中,激活函数将以指数级递减,虽然我只是讨论了激活函数以与
𝐿
相关的指数级数增长或下降,它也适用于与层数𝐿
相关的导数或梯度函数,也是呈指数级增长或 呈指数递减。
对于当前的神经网络,假设
𝐿 = 150
,最近
Microsoft
对
152
层神经网络的研究取得了很大进展,在这样一个深度神经网络中,如果激活函数或梯度函数以与𝐿
相关的指数增长或递减,它们的值将会变得极大或极小,从而导致训练难度上升,尤其是梯度指数小于𝐿
时,梯度下降算法的步长会非常非常小,梯度下降算法将花费很长时间来学习。 总结一下,我们讲了深度神经网络是如何产生梯度消失或爆炸问题的,实际上,在很长一段时间内,它曾是训练深度神经网络的阻力,虽然有一个不能彻底解决此问题的解决方案,但是已在如何选择初始化权重问题上提供了很多帮助。
1.11 神经网络的权重初始化(Weight Initialization for Deep Networks)
上节课,我们学习了深度神经网络如何产生梯度消失和梯度爆炸问题,最终针对该问题, 我们想出了一个不完整的解决方案,虽然不能彻底解决问题,却很有用,有助于我们为神经网络更谨慎地选择随机初始化参数,为了更好地理解它,我们先举一个神经单元初始化地例子,然后再演变到整个深度网络
1.12 梯度的数值逼近(Numerical approximation of gradients)
在实施
backprop
时,有一个测试叫做
梯度检验
,它的作用是确保
backprop
正确实施。 因为有时候,你虽然写下了这些方程式,却不能 100%
确定,执行
backprop
的所有细节都是正确的。为了逐渐实现梯度检验,我们首先说说
如何计算梯度的数值逼近
,下节课,我们将讨论如何在 backprop
中执行梯度检验,以确保
backprop
正确实施。