深度学习的实践层面
- 1.1 训练,验证,测试集
- 偏差,方差(Bias /Variance)
- 机器学习基础
- 正则化 Regularization
- dropout 正则化(Dropout Regularization)
- 其他正则化方法(Other regularization methods)
- 归一化输入(Normalizing inputs)
- 梯度消失/梯度爆炸(Vanishing / Exploding gradients)
- 神经网络的权重初始化(Weight Initialization for Deep NetworksVanishing / Exploding gradients)
- 梯度检验(Gradient checking)
1.1 训练,验证,测试集
假设这是训练数据,我用一个长方形表示,我们通常会将这些数据划分成几部分,一部分作为训练集,一部分作为简单交叉验证集,有时也称之为验证集,方便起见,我就叫它验证集(dev set),其实都是同一个概念,最后一部分则作为测试集。
接下来,我们开始对训练集执行算法,通过验证集或简单交叉验证集选择最好的模型,经过充分验证,我们选定了最终模型,然后就可以在测试集上进行评估了,为了无偏评估算法的运行状况。
在机器学习发展的小数据量时代,常见做法是将所有数据三七分,就是人们常说的70%训练集,30%测试集。如果明确设置了验证集,也可以按照60%训练集,20%验证集和20%测试集来划分。这是前几年机器学习领域普遍认可的最好的实践方法。
在大数据时代,我们现在的数据量可能是百万级别,那么验证集和测试集占数据总量的比例会趋向于变得更小。
在机器学习中,如果只有一个训练集和一个验证集,而没有独立的测试集,遇到这种情况,训练集还被人们称为训练集,而验证集则被称为测试集
偏差,方差(Bias /Variance)
高偏差(high bias)------“欠拟合”(underfitting)
高方差(high variance)------拟合(overfitting):拟合一个非常复杂的分类器,比如深度神经网络或含有隐藏单元的神经网络,可能就非常适用于这个数据集,但是这看起来也不是一种很好的拟合方式分类器。
机器学习基础
下图为在训练神经网络用到的基本方法:
初始模型训练完成后,首先要知道算法的偏差高不高,如果偏差较高,试着评估训练集或训练数据的性能。如果偏差的确很高,甚至无法拟合训练集,就要选择一个新的网络。如果网络足够大,通常可以很好的拟合训练集。
评估方差------查看验证集性能。如果方差高,最好的解决办法就是采用更多数据,也可以尝试通过正则化来减少过拟合。但是,如果能找到更合适的神经网络框架,有时它可能会一箭双雕,同时减少方差和偏差。
正则化 Regularization
过拟合问题(高方差)的两个解决方法:①正则化,②准备更多的数据。
L2正则化
逻辑回归函数中的正则化
其中
∣
∣
w
∣
∣
2
2
\left|| w \right||2^2
∣∣w∣∣22是
w
w
w的欧几里德范数的平方,等于
w
j
w_{j}
wj(
j
j
j 值从1到
n
x
n_{x}
nx)平方的和,也可表示为
w
T
w
w^{T}w
wTw,也就是向量参数
w
w
w 的欧几里德范数(2范数)的平方。因为这里用了欧几里德范数,被称为向量参数
w
w
w的
L
2
L2
L2范数。
可以不再加上参数 b b b,原因: w w w通常是一个高维参数矢量,已经可以表达高偏差问题, w w w可能包含有很多参数,我们不可能拟合所有参数,而 b b b只是单个数字,所以 w w w几乎涵盖所有参数,而不是 b b b,如果加了参数 b b b,其实也没太大影响,因为 b b b只是众多参数中的一个。
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,它都是一个比例常量。用 L 1 L1 L1正则化, w w w最终会是稀疏的( w w w向量中有很多0)。
在Python编程语言中, λ \lambda λ是一个保留字段,编写代码时,我们写成 l a m b d lambd lambd。
神经网络中的 L 2 L2 L2正则化
正则项为 λ 2 m ∑ 1 L ∣ W [ l ] ∣ 2 \frac{\lambda }{2m}{{\sum\nolimits_{1}^{L}{| {{W}^{[l]}}|}}^{2}} 2mλ∑1L∣W[l]∣2
∣ ∣ W [ l ] ∣ ∣ 2 {||W^{\left[l\right]}||}^{2} ∣∣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层隐藏单元的数量。
用backprop计算出 d W dW dW的值,backprop会给出 J J J对 W W W的偏导数,实际上是 W [ l ] W^{[l]} W[l],把 W [ l ] W^{[l]} W[l]替换为 W [ l ] W^{[l]} W[l]减去学习率乘以 d W dW dW,即给 d W dW dW加上这一项 λ m W [ l ] \frac {\lambda}{m}W^{[l]} mλW[l],然后计算这个更新项。
如果正则化参数变得很大,参数 W W W很小, z z z也会相对变小,此时忽略 b b b的影响, z z z会相对变小,实际上, z z z的取值范围很小,这个激活函数,也就是曲线函数 t a n h tanh tanh会相对呈线性,整个神经网络会计算离线性函数近的值,这个线性函数非常简单,并不是一个极复杂的高度非线性函数,不会发生过拟合。
如果你使用的是梯度下降函数,在调试梯度下降时,其中一步就是把代价函数 J J J设计成这样一个函数,在调试梯度下降时,它代表梯度下降的调幅数量。可以看到,代价函数对于梯度下降的每个调幅都单调递减。如果你实施的是正则化函数,请牢记, J J J已经有一个全新的定义。如果你用的是原函数 J J J,也就是这第一个项正则化项,你可能看不到单调递减现象,为了调试梯度下降,请务必使用新定义的 J J J函数,它包含第二个正则化项,否则函数 J J J可能不会在所有调幅范围内都单调递减。
dropout 正则化(Dropout Regularization)
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表示保留某个隐藏单元的概率,此处keep-prob等于0.8,它意味着消除任意一个隐藏单元的概率是0.2,它的作用就是生成随机矩阵。
-
从第三层中获取激活函数,这里我们叫它 a [ 3 ] a^{[3]} a[3], 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)
. -
向外扩展 a [ 3 ] a^{[3]} a[3],用它除以0.8,或者除以keep-prob参数。
原因:为了不影响 z [ i ] z^{\lbrack i]} z[i]的期望值。这将会修正或弥补我们所需的那20%, a [ i ] a^{[i]} a[i]的期望值不会变。
反向随机失活(inverted dropout)方法通过除以keep-prob,确保 a [ i ] a^{[i]} a[i]的期望值不变。
不同的训练样本,清除不同的隐藏单元也不同。实际上,如果通过相同训练集多次传递数据,每次训练数据的梯度不同,则随机对不同隐藏单元归零.
在测试阶段,我们并未使用dropout, 因为在测试阶段进行预测时,我们不期望输出结果是随机的,如果测试阶段应用dropout函数,预测会受到干扰。
Inverted dropout函数在除以keep-prob时可以记住上一步的操作,目的是确保即使在测试阶段不执行dropout来调整数值范围,激活函数的预期结果也不会发生变化,所以没必要在测试阶段额外添加尺度参数,这与训练阶段不同。
l = k e e p − p r o b l=keep-prob l=keep−prob
理解 dropout(Understanding Dropout)
直观上理解:不要依赖于任何一个特征,因为该单元的输入可能随时被清除,因此该单元通过这种方式传播下去,并为单元的四个输入增加一点权重,通过传播所有权重,dropout将产生收缩权重的平方范数的效果,和之前讲的
L
2
L2
L2正则化类似;实施dropout的结果实它会压缩权重,并完成一些预防过拟合的外层正则化;
L
2
L2
L2对不同权重的衰减是不同的,它取决于激活函数倍增的大小。
总结一下,dropout的功能类似于 L 2 L2 L2正则化,与 L 2 L2 L2正则化不同的是应用方式不同会带来一点点小变化,甚至更适用于不同的输入范围。
事实证明,dropout被正式地作为一种正则化的替代形式, L 2 L2 L2对不同权重的衰减是不同的,它取决于倍增的激活函数的大小。
dropout的功能类似于 L 2 L2 L2正则化,与 L 2 L2 L2正则化不同的是,被应用的方式不同,dropout也会有所不同,甚至更适用于不同的输入范围。
如果担心某些层更容易发生过拟合,可以把某些层的keep-prob值设置得比其它层更低,或者在一些层上应用dropout,而有些层不用dropout,应用dropout的层只含有一个超参数,就是keep-prob。缺点是为了使用交叉验证,要搜索更多的超参数。
dropout一大缺点就是代价函数 J J J不再被明确定义,每次迭代,都会随机移除一些节点,如果再三检查梯度下降的性能,实际上是很难进行复查的。
其他正则化方法(Other regularization methods)
除了 L 2 L2 L2正则化和随机失活(dropout)正则化,还有几种方法可以减少神经网络中的过拟合:
数据扩增
通过随意翻转,裁剪图片,随机选取放大后的部分图片,我们可以增大数据集,额外生成假训练数据。和全新的,独立的数据相比,这些额外的假的数据无法包含像全新数据那么多的信息,但代价几乎为零,除了一些对抗性代价。以这种方式扩增算法数据,进而正则化数据集,减少过拟合比较廉价。
对于光学字符识别,还可以通过添加数字,随意旋转或扭曲数字来扩增数据,把这些数字添加到训练集,它们仍然是数字。
early stopping
术语early stopping代表提早停止训练神经网络。
运行梯度下降时,我们可以绘制训练误差,或只绘制代价函数 J J J的优化过程。
当你还未在神经网络上运行太多迭代过程的时候,参数 w w w接近0,因为随机初始化 w w w值时,它的值可能都是较小的随机值,所以在你长期训练神经网络之前 w w w依然很小,在迭代过程和训练过程中 w w w的值会变得越来越大,比如在这儿,神经网络中参数 w w w的值已经非常大了,所以early stopping要做就是在中间点停止迭代过程,我们得到一个 w w w值中等大小的弗罗贝尼乌斯范数,与 L 2 L2 L2正则化相似,选择参数w范数较小的神经网络。
early stopping的主要缺点就是不能独立地处理这优化代价函数和预防过拟合。
Early stopping的优点是,只运行一次梯度下降,你可以找出 w w w的较小值,中间值和较大值,而无需尝试 L 2 L2 L2正则化超级参数 λ \lambda λ的很多值。
归一化输入(Normalizing inputs)
两个步骤:
- 零均值: μ = 1 m ∑ i = 1 m x ( i ) \mu = \frac{1}{m}\sum_{i =1}^{m}x^{(i)} μ=m1∑i=1mx(i),它是一个向量, x x x等于每个训练数据 x x x减去 μ \mu μ,意思是移动训练集,直到它完成零均值化。
- 归一化方差,注意特征
x
1
x_{1}
x1的方差比特征
x
2
x_{2}
x2的方差要大得多,我们要做的是给
σ
\sigma
σ赋值,
σ
2
=
1
m
∑
i
=
1
m
(
x
(
i
)
)
2
\sigma^{2}= \frac{1}{m}\sum_{i =1}^{m}{({x^{(i)})}^{2}}
σ2=m1∑i=1m(x(i))2,这是节点
y
y
y 的平方,
σ
2
\sigma^{2}
σ2是一个向量,它的每个特征都有方差,把所有数据除以向量
σ
2
\sigma^{2}
σ2,最后变成上图形式。
为什么要归一化输入特征:
J ( w , b ) = 1 m ∑ i = 1 m L ( y ^ ( i ) , y ( i ) ) J(w,b)=\frac{1}{m}\sum\limits_{i=1}^{m}{L({{{\hat{y}}}^{(i)}},{{y}^{(i)}})} J(w,b)=m1i=1∑mL(y^(i),y(i))
如果你使用非归一化的输入特征,代价函数会是一个非常细长狭窄的代价函数,然而如果你归一化特征,代价函数平均起来看更对称。
如果在细长狭窄的的代价函数上运行梯度下降法,必须使用一个非常小的学习率。但如果函数是一个更圆的球形轮廓,那么不论从哪个位置开始,梯度下降法都能够更直接地找到最小值,可以在梯度下降法中使用较大步长,而不需要像在左图中那样反复执行。
如果输入特征处于不同范围内,可能有些特征值从0到1,有些从1到1000,那么归一化特征值就非常重要了。如果特征值处于相似范围内,那么归一化就不是很重要了。
梯度消失/梯度爆炸(Vanishing / Exploding gradients)
简单起见,假设使用激活函数
g
(
z
)
=
z
g(z)=z
g(z)=z,也就是线性激活函数,我们忽略
b
b
b,假设
b
[
l
]
b^{[l]}
b[l]=0。
输出
y
=
W
[
l
]
W
[
L
−
1
]
W
[
L
−
2
]
…
W
[
3
]
W
[
2
]
W
[
1
]
x
y=W^{[l]}W^{[L -1]}W^{[L - 2]}\ldots W^{[3]}W^{[2]}W^{[1]}x
y=W[l]W[L−1]W[L−2]…W[3]W[2]W[1]x,
z
[
1
]
=
W
[
1
]
x
z^{[1]} =W^{[1]} x
z[1]=W[1]x,
a
[
1
]
=
g
(
z
[
1
]
)
a^{[1]} = g(z^{[1]})
a[1]=g(z[1])
所有这些矩阵数据传递的协议将给出 y ^ \hat y y^而不是 y y y的值。
假设每个权重矩阵 W [ l ] = [ 1.5 0 0 1.5 ] W^{[l]} = \begin{bmatrix} 1.5 & 0 \\ 0 & 1.5 \\ \end{bmatrix} W[l]=[1.5001.5], y = W [ 1 ] [ 1.5 0 0 1.5 ] ( L − 1 ) x y= W^{[1]}\begin{bmatrix} 1.5 & 0 \\ 0 & 1.5 \\ \end{bmatrix}^{(L -1)}x y=W[1][1.5001.5](L−1)x,假设所有矩阵都等于它,如果对于一个深度神经网络来说 L L L值较大,那么 y ^ \hat{y} y^的值也会非常大, 将爆炸式增长。
相反的,如果权重是0.5,
W
[
l
]
=
[
0.5
0
0
0.5
]
W^{[l]} = \begin{bmatrix} 0.5& 0 \\ 0 & 0.5 \\ \end{bmatrix}
W[l]=[0.5000.5],它比1小,这项也就变成了
0.5
L
{0.5}^{L}
0.5L,矩阵
y
=
W
[
1
]
[
0.5
0
0
0.5
]
(
L
−
1
)
x
y= W^{[1]}\begin{bmatrix} 0.5 & 0 \\ 0 & 0.5 \\ \end{bmatrix}^{(L - 1)}x
y=W[1][0.5000.5](L−1)x, 激活函数的值将以指数级下降,它是与网络层数数量
L
L
L相关的函数,在深度网络中,激活函数以指数级递减。
补充:梯度爆炸/消失
梯度为偏导数构成的向量。
损失函数收敛至极小值时,梯度为0(接近0),损失函数不再下降。我们不希望在抵达极小值前,梯度就为0了,也不希望下降过程过于震荡,甚至不收敛。梯度消失与梯度爆炸分别对应这2种现象:
梯度消失(vanishing gradients):指的是在训练过程中,梯度(偏导)过早接近于0的现象,导致(部分)参数一直不再更新,整体上表现得像损失函数收敛了,实际上网络尚未得到充分的训练。
梯度爆炸(exploding gradients):指的是在训练过程中,梯度(偏导)过大甚至为NAN(not a number)的现象,导致损失剧烈震荡,甚至发散(divergence)。
在梯度(偏导)计算中,主要的影响因素来自激活函数的偏导、当前层的输入(前一层的输出)、以及权重的数值等,这些因子连续相乘,带来的影响是指数级的。
神经网络的权重初始化(Weight Initialization for Deep NetworksVanishing / Exploding gradients)
我们来看看只有一个神经元的情况,然后才是深度网络。
Xavier initialization(tanh)
z = w 1 x 1 + w 2 x 2 + … + w n x n z = w_{1}x_{1} + w_{2}x_{2} + \ldots +w_{n}x_{n} z=w1x1+w2x2+…+wnxn, b = 0 b=0 b=0,暂时忽略 b b b,为了预防 z z z值过大或过小,你可以看到 n n n越大,你希望 w i w_{i} wi越小,因为 z z z是 w i x i w_{i}x_{i} wixi的和,如果你把很多此类项相加,希望每项值更小,最合理的方法就是设置 w i = 1 n w_{i}=\frac{1}{n} wi=n1, n n n表示神经元的输入特征数量,实际上,你要做的就是设置某层权重矩阵 w [ l ] = n p . r a n d o m . r a n d n ( shape ) ∗ np.sqrt ( 1 n [ l − 1 ] ) w^{[l]} = np.random.randn( \text{shape})*\text{np.}\text{sqrt}(\frac{1}{n^{[l-1]}}) w[l]=np.random.randn(shape)∗np.sqrt(n[l−1]1) n [ l − 1 ] n^{[l - 1]} n[l−1]是第 l − 1 l-1 l−1层神经元数量。
MSRA/He initialization(Relu)**
W = np.random.randn(input_layer_neurons,hidden_layer_neurons)* sqrt(2/input_layer_neurons)
所有这些公式只是给出初始化权重矩阵的方差的默认值,如果想添加方差,方差参数则是另一个你需要调整的超级参数,可以给公式 np.sqrt ( 2 n [ l − 1 ] ) \text{np.}\text{sqrt}(\frac{2}{n^{[l-1]}}) np.sqrt(n[l−1]2)添加一个乘数参数。
虽然调优该参数能起到一定作用,但考虑到相比调优,其它超级参数的重要性,通常把它的优先级放得比较低。
梯度检验(Gradient checking)
梯度的数值逼近(Numerical approximation of gradients):
作用是确保backprop正确实施。
f ( θ + ε ) − f ( θ − ε ) 2 ε = ( 1.01 ) 3 − ( 0.99 ) 3 2 × 0.01 \frac {f\left( \theta + \varepsilon \right) - f(\theta -\varepsilon)}{2\varepsilon} = \frac{{(1.01)}^{3} - {(0.99)}^{3}}{2 \times0.01} 2εf(θ+ε)−f(θ−ε)=2×0.01(1.01)3−(0.99)3
在执行梯度检验时,我们使用双边误差,即 f ( θ + ε ) − f ( θ − ε ) 2 ε \frac{f\left(\theta + \varepsilon \right) - f(\theta -\varepsilon)}{2\varepsilon} 2εf(θ+ε)−f(θ−ε),而不使用单边公差,因为它不够准确。
假设你的网络中含有下列参数, W [ 1 ] W^{[1]} W[1]和 b [ 1 ] b^{[1]} b[1]…… W [ l ] W^{[l]} W[l]和 b [ l ] b^{[l]} b[l],为了执行梯度检验,首先要做的就是,把所有参数转换成一个巨大的向量数据,你要做的就是把矩阵 W W W转换成一个向量,把所有 W W W矩阵转换成向量之后,做连接运算,得到一个巨型向量 θ \theta θ,该向量表示为参数 θ \theta θ,代价函数 J J J是所有 W W W和 b b b的函数,现在你得到了一个 θ \theta θ的代价函数 J J J(即 J ( θ ) J(\theta) J(θ))。接着,你得到与 W W W和 b b b顺序相同的数据,你同样可以把 d W [ 1 ] dW^{[1]} dW[1]和 d b [ 1 ] {db}^{[1]} db[1]…… d W [ l ] {dW}^{[l]} dW[l]和 d b [ l ] {db}^{[l]} db[l]转换成一个新的向量,用它们来初始化大向量 d θ d\theta dθ,它与 θ \theta θ具有相同维度。
同样的,把 d W [ 1 ] dW^{[1]} dW[1]转换成矩阵, d b [ 1 ] db^{[1]} db[1]已经是一个向量了,直到把 d W [ l ] {dW}^{[l]} dW[l]转换成矩阵,这样所有的 d W dW dW都已经是矩阵,注意 d W [ 1 ] dW^{[1]} dW[1]与 W [ 1 ] W^{[1]} W[1]具有相同维度, d b [ 1 ] db^{[1]} db[1]与 b [ 1 ] b^{[1]} b[1]具有相同维度。经过相同的转换和连接运算操作之后,你可以把所有导数转换成一个大向量 d θ d\theta dθ,它与 θ \theta θ具有相同维度,现在的问题是 d θ d\theta dθ和代价函数 J J J的梯度或坡度有什么关系?
这就是实施梯度检验的过程,英语里通常简称为“grad check”,首先,我们要清楚 J J J是超参数 θ \theta θ的一个函数,你也可以将J函数展开为 J ( θ 1 , θ 2 , θ 3 , … … ) J(\theta_{1},\theta_{2},\theta_{3},\ldots\ldots) J(θ1,θ2,θ3,……),不论超级参数向量 θ \theta θ的维度是多少,为了实施梯度检验,你要做的就是循环执行,从而对每个 i i i也就是对每个 θ \theta θ组成元素计算 d θ approx [ i ] d\theta_{\text{approx}}[i] dθapprox[i]的值,使用双边误差,也就是
d θ approx [ i ] = J ( θ 1 , θ 2 , … θ i + ε , … ) − J ( θ 1 , θ 2 , … θ i − ε , … ) 2 ε d\theta_{\text{approx}}\left[i \right] = \frac{J\left( \theta_{1},\theta_{2},\ldots\theta_{i} + \varepsilon,\ldots \right) - J\left( \theta_{1},\theta_{2},\ldots\theta_{i} - \varepsilon,\ldots \right)}{2\varepsilon} dθapprox[i]=2εJ(θ1,θ2,…θi+ε,…)−J(θ1,θ2,…θi−ε,…)
只对 θ i \theta_{i} θi增加 ε \varepsilon ε,其它项保持不变,因为我们使用的是双边误差,对另一边做同样的操作,只不过是减去 ε \varepsilon ε, θ \theta θ其它项全都保持不变。
从上节课中我们了解到这个值( d θ approx [ i ] d\theta_{\text{approx}}\left[i \right] dθapprox[i])应该逼近 d θ [ i ] d\theta\left[i \right] dθ[i]= ∂ J ∂ θ i \frac{\partial J}{\partial\theta_{i}} ∂θi∂J, d θ [ i ] d\theta\left[i \right] dθ[i]是代价函数的偏导数,然后你需要对i的每个值都执行这个运算,最后得到两个向量,得到 d θ d\theta dθ的逼近值 d θ approx d\theta_{\text{approx}} dθapprox,它与 d θ d\theta dθ具有相同维度,它们两个与 θ \theta θ具有相同维度,你要做的就是验证这些向量是否彼此接近。
定义两个向量是否真的接近彼此:计算这两个向量的距离 d θ approx [ i ] − d θ [ i ] d\theta_{\text{approx}}\left[i \right] - d\theta[i] dθapprox[i]−dθ[i]的欧几里得范数,注意这里( ∣ ∣ d θ approx − d θ ∣ ∣ 2 {||d\theta_{\text{approx}} -d\theta||}_{2} ∣∣dθapprox−dθ∣∣2)没有平方,它是误差平方之和,然后求平方根,得到欧式距离,然后用向量长度归一化,使用向量长度的欧几里得范数。分母只是用于预防这些向量太小或太大,分母使得这个方程式变成比率,我们实际执行这个方程式, ε \varepsilon ε可能为 1 0 − 7 10^{-7} 10−7,使用这个取值范围内的 ε \varepsilon ε,如果你发现计算方程式得到的值为 1 0 − 7 10^{-7} 10−7或更小,这就很好,这就意味着导数逼近很有可能是正确的,它的值非常小。
如果它的值在 1 0 − 5 10^{-5} 10−5范围内,我就要小心了,也许这个值没问题,但我会再次检查这个向量的所有项,确保没有一项误差过大,可能这里有bug。
如果左边这个方程式结果是 1 0 − 3 10^{-3} 10−3,我就会担心是否存在bug,计算结果应该比 1 0 − 3 10^{- 3} 10−3小很多,如果比 1 0 − 3 10^{-3} 10−3大很多,我就会很担心,担心是否存在bug。这时应该仔细检查所有 θ \theta θ项,看是否有一个具体的 i i i值,使得 d θ approx [ i ] d\theta_{\text{approx}}\left[i \right] dθapprox[i]与$ d\theta[i]$大不相同,并用它来追踪一些求导计算是否正确,经过一些调试,最终结果会是这种非常小的值( 1 0 − 7 10^{-7} 10−7),那么,你的实施可能是正确的。
注意事项(Gradient Checking Implementation Notes)
-
不要在训练中使用梯度检验,它只用于调试。计算所有 i i i值的 d θ approx [ i ] d\theta_{\text{approx}}\left[i\right] dθapprox[i]是一个非常漫长的计算过程,为了实施梯度下降,你必须使用 W W W和 b b b backprop来计算 d θ d\theta dθ,并使用backprop来计算导数,只要调试的时候,你才会计算它,来确认数值是否接近 d θ d\theta dθ。完成后,你会关闭梯度检验,梯度检验的每一个迭代过程都不执行它,因为它太慢了。
-
如果算法的梯度检验失败,要检查所有项,检查每一项,并试着找出bug。如果 d θ approx [ i ] d\theta_{\text{approx}}\left[i\right] dθapprox[i]与dθ[i]的值相差很大,我们要做的就是查找不同的i值,看看是哪个导致 d θ approx [ i ] d\theta_{\text{approx}}\left[i\right] dθapprox[i]与 d θ [ i ] d\theta\left[i\right] dθ[i]的值相差这么多。举个例子,如果你发现,相对某些层或某层的 θ \theta θ或 d θ d\theta dθ的值相差很大,但是 dw [ l ] \text{dw}^{[l]} dw[l]的各项非常接近,注意 θ \theta θ的各项与 b b b和 w w w的各项都是一一对应的,这时,你可能会发现,在计算参数 b b b的导数 d b db db的过程中存在bug。反过来也是一样,如果你发现它们的值相差很大, d θ approx [ i ] d\theta_{\text{approx}}\left[i\right] dθapprox[i]的值与 d θ [ i ] d\theta\left[i\right] dθ[i]的值相差很大,你会发现所有这些项目都来自于 d w dw dw或某层的 d w dw dw,可能帮你定位bug的位置,虽然未必能够帮你准确定位bug的位置,但它可以帮助你估测需要在哪些地方追踪bug。
-
在实施梯度检验时,如果使用正则化,请注意正则项。如果代价函数 J ( θ ) = 1 m ∑ L ( y ^ ( i ) , y ( i ) ) + λ 2 m ∑ ∣ ∣ W [ l ] ∣ ∣ 2 J(\theta) = \frac{1}{m}\sum_{}^{}{L(\hat y^{(i)},y^{(i)})} + \frac{\lambda}{2m}\sum_{}^{}{||W^{[l]}||}^{2} J(θ)=m1∑L(y^(i),y(i))+2mλ∑∣∣W[l]∣∣2,记住一定要包括这个正则项。
-
梯度检验不能与dropout同时使用,因为每次迭代过程中,dropout会随机消除隐藏层单元的不同子集,难以计算dropout在梯度下降上的代价函数 J J J。