1)该文章整理自网上的大牛和机器学习专家无私奉献的资料,具体引用的资料请看参考文献。
2)本文仅供学术交流,非商用。所以每一部分具体的参考资料并没有详细对应。如果某部分不小心侵犯了大家的利益,还望海涵,并联系博主删除。
3)博主才疏学浅,文中如有不当之处,请各位指出,共同进步,谢谢。
4)此属于第一版本,若有错误,还需继续修正与增删。还望大家多多指点。大家都共享一点点,一起为祖国科研的推进添砖加瓦。
深度学习入门笔记(九):深度学习数据处理 讲过,深度学习可能存在着数据 过拟合 问题,即存在 高方差。常见的解决方法有两个:一个是 正则化;另一个是 更多数据,更多数据 是一个非常可靠的方法,但是可能无法时时刻刻准备足够多的训练数据,或者获取更多训练数据的成本很高,正则化 则没有这些问题,它通常有助于避免过拟合或减少网络误差。
解决过拟合的方法——深度学习100问之神经网络中解决过拟合的几种方法。
下面就用逻辑回归来讲讲正则化的作用原理:
需要先确定的是,我们的目标是求成本函数 J J J 的最小值。如果想要在逻辑回归成本函数中加入正则化,只需添加参数 λ λ λ,也就是正则化参数,然后用 λ 2 m \frac{\lambda}{2m} 2mλ 乘以 w w w 范数的平方,即为正则化项。其中 ∥ w ∥ 2 2 \left\| w \right\|_2^2 ∥w∥22 是向量参数 w w w 的欧几里德范数(2范数)的平方,等于 w j w_{j} wj( j j j 值从1到 n x n_{x} nx)平方的和,也可表示为 w T w w^{T}w wTw,此方法称为 L 2 L2 L2 正则化。(详细的可以看这个 深度学习100问之深入理解Regularization(正则化))
细心的你会发现一个问题,为什么只正则化参数 w w w?为什么不再加上参数 b b b 呢?
其实是可以这么做的,只是我们习惯性的省略不写,因为 w w w 通常是一个高维参数矢量,已经可以表达 高偏差 问题。 w w w 可能包含有很多参数,并且我们不可能拟合所有参数,而 b b b 只是单个数字,所以 w w w 几乎可以涵盖所有参数,如果非要加了参数 b b b,其实也没太大影响,因为 b b b 只是众多参数中的一个,简单来说就是,可以但是没必要。
L 2 L2 L2 正则化是最常见的正则化类型,但不是唯一的正则化类型,你可能还听说过 L 1 L1 L1 正则化。顾名思义, L 1 L1 L1 正则化加的就不是 L 2 L2 L2 范数,而是正则项 λ m \frac{\lambda}{m} mλ 乘以 ∑ j = 1 n x ∣ w ∣ \sum_{j= 1}^{n_{x}}{|w|} ∑j=1nx∣w∣, ∑ j = 1 n x ∣ w ∣ \sum_{j =1}^{n_{x}}{|w|} ∑j=1nx∣w∣ 也被称为参数 w w w 向量的 L 1 L1 L1 范数,无论分母是 m m m 还是 2 m 2m 2m,它都是一个比例常量。
那么两个正则化的区别是什么呢?我们在 深度学习100问之深入理解Regularization(正则化) 中说过,这里就直接拿出来用了。
现在人们在训练网络时,越来越倾向于使用 L 2 L2 L2 正则化了,当然还是那句话,凡事无绝对,还是要看你的问题是什么!!!
最后一个细节,是关于 λ \lambda λ 作为正则化参数的设置,通常在进行超参数设置的时候,我们使用验证集或交叉验证集来调参,尝试各种各样的数据,寻找最好的参数。不过一般经验下,要考虑训练集之间的权衡,把参数设置为较小值,这样既可以避免过拟合,又不会矫枉过正。
顺便说一下,为了方便写代码,很多人会删掉 a a a,写成 l a m b d lambd lambd,因为在 Python 编程语言中, λ \lambda λ 是一个保留字段,即关键字,所以在编写代码时,为了避免冲突,不能使用一样的两个字符。
这就是在逻辑回归函数中实现 L 2 L2 L2 正则化的过程,那么如何在神经网络中实现 L 2 L2 L2 正则化呢?
先来看一下公式的具体参数:神经网络的成本函数包含从 W [ 1 ] W^{[1]} W[1], b [ 1 ] b^{[1]} b[1] 到 W [ l ] W^{[l]} W[l], b [ l ] b^{[l]} b[l] 的所有参数,字母 L L L 是神经网络所含的层数,正则项为 λ 2 m ∑ 1 L ∣ W [ l ] ∣ 2 \frac{\lambda }{2m}{{\sum\nolimits_{1}^{L}{| {{W}^{[l]}}|}}^{2}} 2mλ∑1L∣W[l]∣2。第一个求和符号其值 i i i 从1到 n [ l − 1 ] n^{[l - 1]} n[l−1],第二个其 J J J 值从1到 n [ l ] n^{[l]} n[l],因为 W W W 是一个 n [ l ] × n [ l − 1 ] n^{[l]}\times n^{[l-1]} n[l]×n[l−1] 的多维矩阵, n [ l ] n^{[l]} n[l] 表示 l l l 层单元的数量, n [ l − 1 ] n^{[l-1]} n[l−1] 表示第 l − 1 l-1 l−1 层隐藏单元的数量。
公式最前面的 矩阵范数 被称作 弗罗贝尼乌斯范数,用下标 F F F 标注,这个公式是线性代数中的另一种写法,和矩阵 L 2 L2 L2 范数是一个意思,表示一个矩阵中所有元素的平方和。
那么应该如何使用这个范数实现梯度下降呢?
按照之前学过的反向传播(深度学习入门笔记(七):深层神经网络),再加上正则项,即 d W + λ m W [ l ] dW + \frac {\lambda}{m}W^{[l]} dW+mλW[l],然后计算 W [ l ] W^{[l]} W[l],就等于 W [ l ] W^{[l]} W[l] 减去学习率 α \alpha α 乘以 backprop 再加上 λ m W [ l ] \frac{\lambda}{m}W^{[l]} mλW[l]。
从这个关于正则项的反向传播中可以看出,不论 W [ l ] W^{[l]} W[l] 是什么,我们都试图让它变得更小。因此正则化也被称为 权重衰减,就像一般的梯度下降一般。
为什么正则化有利于预防过拟合呢?为什么它可以减少方差问题?
我们通过例子来直观体会一下:
左图是 高偏差,右图是 高方差,中间是 Just Right,这几张图在 深度学习入门笔记(九):深度学习数据处理 中讲到过,现在来看下一个庞大的深度拟合神经网络。
我知道这张图不够大,深度也不够,但你可以想象这是一个非常大非常深的过拟合神经网络,就像这样的,够庞大够复杂了吧!?!
现在有一个代价函数 J J J,含有两个参数 W W W 和 b b b;然后添加 正则项,它可以避免数据权值矩阵过大,这就是 弗罗贝尼乌斯范数,那么为什么压缩 L 2 L2 L2 范数,或者 弗罗贝尼乌斯范数 或者参数,可以减少过拟合?
直观上理解就是,如果正则化 λ \lambda λ 设置得足够大,根据 L2 范数的效用,权重矩阵 W W W 被设置为接近于0的值,也就是把多隐藏单元的权重设为0,因为在有意义的前提下乘以0的数都等于0,于是基本上很多隐藏单元的影响都被消除了。如果是这种情况,即使是一个很大很深的网络在被大大简化了之后也会变成一个很小的网络,小到如同一个逻辑回归单元,可是深度却还是很大,它会使这个网络从 过度拟合 的状态(下面的右图)更接近 高偏差 状态(下面的左图),但是 λ \lambda λ 会存在一个中间值,于是会有一个接近 Just Right 的中间状态。
理论上, λ \lambda λ 增大, W W W 会接近于0,从而在直觉上认为大量隐藏单元被完全消除了,但是实际中是不会发生这种情况的!实际中是该神经网络的所有隐藏单元依然存在,但是它们的影响变得更小了。
再通过一个例子来感受一下正则化为什么可以预防过拟合?
假设用下面这样的双曲线来做网络的激活函数。
用 g ( z ) g(z) g(z) 表示 t a n h ( z ) tanh(z) tanh(z),可以发现:
现在你应该摒弃这个直觉了,因为如果正则化参数 λ \lambda λ 很大,激活函数的参数就会相对较小。换句话说,如果 W W W 很小,相对来说, z z z 也会很小。
特别是,如果 z z z 的值最终在这个范围内,都是相对较小的值,那么 g ( z ) g(z) g(z) 大致呈线性,每层几乎都是线性的,就会和线性回归函数一样。
但是在 深度学习入门笔记(六):浅层神经网络 中第二节讲过,如果每层都是线性的,那么整个网络就是一个线性网络,即使是一个非常深的深层网络,因具有线性激活函数的特征,最终也只能计算线性函数,因此,它不适用于非常复杂的决策,以及 过度拟合 数据集的非线性决策边界,如同 过度拟合高方差 的情况。
总结一下:
如果正则化参数变得很大,参数 W W W 就会变得很小,此时忽略 b b b 的影响, z z z也会相对变小,从而使得激活函数也就是曲线函数 t a n h tanh tanh 会相对呈线性性质,整个神经网络就会计算线性函数的值,因为这个线性函数非常简单,并不是一个极复杂的高度非线性函数,从而就不会发生过拟合的情况。
如果正则化参数很小,参数 W W W 就会相对很大, z z z也会相对变大,从而使得激活函数也就是曲线函数 t a n h tanh tanh 会相对呈非线性性质,整个神经网络就会变成一个极复杂的高度非线性函数,从而很容易发生 过拟合 的情况。
除了 L 2 L2 L2 正则化之外,还有一个非常实用的正则化方法——Dropout(随机失活),来一起看看它的工作原理。
这就是精简后的 dropout 过程,下面我们来看一下代码实现的过程。方法有几种,接下来要讲的是最常用的方法,即 inverted dropout(反向随机失活),出于完整性考虑,用一个三层( l = 3 l=3 l=3)网络来举例说明,因此编码中会有很多涉及到3的地方,只举例说明如何在某一层中实施 dropout。
首先要定义向量 d d d, d [ 3 ] d^{[3]} d[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。它的作用是生成随机矩阵,比如 d [ 3 ] d^{[3]} d[3],在这里的对应值为1的概率是0.8,对应为0的概率是0.2。
接下来要做的就是从第三层中获取激活函数,这里叫它 a [ 3 ] a^{[3]} a[3], a [ 3 ] a^{[3]} a[3] 等于上面的 a [ 3 ] a^{[3]} a[3] 乘以 d [ 3 ] d^{[3]} d[3],即 a3 =np.multiply(a3,d3)
(这里是元素相乘,也可写为 a3 *=d3
)。它的作用就是让 d [ 3 ] d^{[3]} d[3] 中所有不等于0的元素相乘并输出,而等于0的则不输出。
如果用 python 实现该算法的话, d [ 3 ] d^{[3]} d[3] 则是一个布尔型数组,值为 true 和 false,而不是1和0,不过乘法运算依然有效,因为 python 会把 true 和 false 翻译为1和0。
最后,向外扩展 a [ 3 ] a^{[3]} a[3],用它除以0.8,或者除以 keep-prob 参数。
为什么要扩展呢?
有的同学可能第一次看到的时候比较蒙,下面我解释一下为什么要这么做,保留和删除神经元的概率分别为80%和20%,这意味着最后的输出维度会受到影响,减少了20%,所以需要 /0.8
,恢复维度。不过如果 keep-prop 设置为1,那么就不存在 dropout,因为它会保留所有节点。我们讲到的 反向随机失活(inverted dropout) 方法会通过除以 keep-prob 的方法来确保输出的期望值和维度保持不变。
Dropout 可以随机删除网络中的神经单元,那么它为什么可以通过正则化发挥如此大的作用呢?
第一个直观上理解,为 不要依赖于任何一个特征,因为该单元的输入可能随时被清除。
第二个直观上理解,实施 dropout 的时候,其中一个要选择的参数是 keep-prob,它代表每一层上保留单元的概率。所以不同层的 keep-prob 也是可以变化的。
举个例子:第一层,矩阵 W [ 1 ] W^{[1]} W[1] 是7×3,第二个权重矩阵 W [ 2 ] W^{[2]} W[2] 是7×7,第三个权重矩阵 W [ 3 ] W^{[3]} W[3] 是3×7,以此类推, W [ 2 ] W^{[2]} W[2] 是最大的权重矩阵,因为 W [ 2 ] W^{[2]} W[2] 拥有最大参数集,即7×7。为了预防矩阵的过拟合,对于这一层,我认为这是第二层,它的 keep-prob 值应该相对较低,假设是0.5。对于其它层,过拟合的程度可能没那么严重,它们的 keep-prob值可能高一些,这里是0.7。如果在某一层,我们不必担心其过拟合的问题,那么 keep-prob 则可以设置为1。
如果想尝试对某些层施行更多正则化,从技术上讲,也可以对输入层应用 dropout,我们有机会删除一个或多个输入特征,虽然现实中通常不这么做,keep-prob 的值为1,是非常常用的输入值,也可以用更大的值,或许是0.9。但是消除一半的输入特征是不太可能的,如果我们遵守这个准则,keep-prob 会接近于1,即使你对输入层应用 dropout。
注意:keep-prob 的值是1,意味着保留所有单元,并且不在这一层使用 dropout。
总结一下,如果担心某些层比其它层更易发生 过拟合:
补充:
在计算机视觉领域实施 dropout 有很多成功的案例,因为视觉中的输入量非常大,像素太多,以至于没有足够的数据,易发生 过拟合。故而有些计算机视觉研究人员非常喜欢用它,几乎成了默认的选择,但是,要牢记,dropout 是一种正则化方法,它有助于减少 过拟合,因此除非算法 过拟合,不然最好不要使用 dropout !!!
听起来好像 dropout 几乎是完美的一个正则方法,其实并不然,dropout 一大缺点就是代价函数 J J J 不再被明确地定义。由于在每次迭代过程中,都会随机地移除一些节点,所以自然的,代价函数 J J J 是变化的。也就是说,如果想重复检查梯度下降的性能,实际实验上其实是很难的。
除了 L 2 L2 L2 正则化和 随机失活(dropout) 正则化,还有几种方法可以减少神经网络中的 过拟合。我们在 深度学习100问之神经网络中解决过拟合的几种方法 中详细地讲过了,如果需要,可以自行查看,这里就简单地说一下。
第一种常用的方法叫作 数据增强,通过增加训练集数据,减小 过拟合。
常用的 数据增强 方法有:
还有另外一种常用的方法叫作 early stopping,代表在验证集上损失函数开始上升之前,提早停止训练神经网络,这样就可以保存一个好的模型。但是它也有一个缺点,就是不能独立地处理这两个问题——优化代价函数和解决过拟合。
因为提早停止梯度下降,也就是停止了优化代价函数 J J J,因为现在不再尝试降低代价函数 J J J,所以代价函数 J J J 的值可能不够小,同时又希望不出现过拟合,也就没有采取不同的方式来解决这两个问题,而是用一种方法同时解决两个问题,这样做的结果是网络的目标,也就是我们要考虑的东西变得更复杂。