梯度检查
通过数值梯度(numerical gradient
)和解析梯度(analytic gradient
)的比较进行梯度检查,这个过程有助于得到更准确的网络
学习Gradient checks中提到的技巧和注意事项
中心差分
使用中心差分公式(the centered difference formula
)能够更好的计算数值梯度
\[ \frac {df(x)}{dx} =\frac {f(x+h) - f(x-h)}{2h} \]
步长\(h\)是一个极小值,当前取值为\(1e^{-5}\)
相对误差比较
计算数值梯度\(f_{n}'\)和解析梯度\(f_{a}'\)的相对误差,同时除以其中最大值以消除量纲,最后得到的值能够有一个统一的评判标准
\[ \frac {|f_{n}' - f_{a}'|}{\max (|f_{n}'|, |f_{a}'|, 1e^{-8})} \]
使用\(max\)以及\(1e^{-8}\)是为了避免最大值为\(0\)以及两个值均为\(0\)的情况
评判标准
- 相对误差大于\(1e^{-2}\)通常意味着梯度可能是错误的
- 相对误差在\([1e^{-2}, 1e^{-4}]\)之间同样不太准确
- 相对误差小于\(1e^{-4}\),如果目标存在扭结(
kinks
),比如tanh
非线性或者softmax
,那么这个结果是可以接受的;否则,\(1e^{-4}\)仍然太高 - 相对误差小于\(1e^{-7}\)是最好的结果
深度网络的相对误差变得更大,所以对于10
层网络而言,其相对误差小于\(1e^{-2}\)可能就很好了,因为误差在传递过程中累积;相反,对于单个可微函数而言,\(1e^{-2}\)表明梯度不正确
使用双精度
使用双精度(double precision
)数据进行梯度检查能够得到更准确的相对误差
保持浮点数有效范围
参考:What Every Computer Scientist Should Know About Floating-Point Arithmetic
在梯度计算过程中,如果梯度的有效范围(大部分梯度的取值)太小,会造成更多的数值问题(numerical issues
)
可以通过打印原始数值/解析梯度的方式查看,也可以通过IDE
调试工具进行查看。如果数值梯度过小,比如取值在\(1e^{-10}\)左右甚至更小,可以通过放大损失函数值进行调整,理想情况下在\(1.0\)的数量级,即浮点数的指数为\(0\)
目标中的扭结
相对误差过大的一个原因在于扭结(kink
)的问题,扭结是指目标函数的不可微部分(non-differentiable parts
),比如ReLU
函数在\(x=0\)点
假定输入数据\(x=-1e^{-6}\),因为x
小于0
,所以该点的解析梯度为0
,然而如果\(h>1e^{-6}\),那么\(f(x+h)>0\),数值梯度大于0
,会得到一个较大的相对误差
查找是否出现扭结需要追踪所有激活函数\(\max(x,y)\)中winner
的身份,比如在前向计算中\(x\)或者\(y\)的值更大,但是在\(f(x+h)\)或\(f(x-h)\)的计算中winner
身份发生了改变,那么表明出现了扭结现象
一种解决技巧是使用少量的数据点进行测试,降低发生扭结的可能性
谨慎设置步长h
步长\(h\)不一定是越小越好,因为过小的步长有可能会产生数值精度问题。当梯度无法检查时,可以尝试改变步长为\(1e^{-4}\)或\(1e^{-6}\)
Gradcheck during a “characteristic” mode of operation
使用随机参数对网络进行梯度检查可能会引入病理边缘病例,并掩盖梯度的错误实现,也就是梯度看起来正确实现了,但实际上并没有
最好先训练一段时间,在损失值开始下降后再进行梯度检查,更能够保证网络的准确性
不要让正则化项影响数据
损失函数包括数据损失以及正则化损失,如果正则化损失比数据损失大,那么主要的梯度将来自于正则化项,这会掩盖数据丢失梯度的错误实现
所以梯度检查时可以先单独检查数据损失,再检查正则化损失。检查正则化损失的方式一方面可以去除数据损失相关代码,另一方面可以提高正则化强度,使正则化梯度无法被忽略
关闭随机失活/数据扩充
执行梯度检查的过程中,应该关闭网络中任何非确定性(non-deterministic
)的影响,比如随机失活(dropout
)、随机数据扩充(random data augmentation
),因为在计算数值梯度时,这些参数会明显的带来巨大的误差
关闭操作会导致无法梯度检查这些参数,一种更好的解决方案是在计算\(f(x+h)\)和\(f(x-h)\)过程中设置特定的随机数种子
检查少量维度
实际操作中网络存在上百万的参数,这种情况下只能假设大部分参数是正确的,仅检查少数参数,特别注意的是确保在不同参数的某些维度进行梯度检查
python实现
在cs231n
课程的课后作业assignment
中已实现了数值梯度计算 - gradient_check.py,主要操作的是前两个函数:
def eval_numerical_gradient(f, x, verbose=True, h=0.00001): def eval_numerical_gradient_array(f, x, df, h=1e-5):
- 参数\(f\)表示待检查的方法/类/网络
- 参数\(x\)表示输入数据
- 参数\(df\)表示反向传播中回传的梯度
两者均采用中心差分公式计算数值梯度,区别在于前一个函数的\(f\)输出值是单个数值,而后者\(f\)的输出值可以是数组
1 | # -*- coding: utf-8 -*- |
参考cs231n
实现了全连接神经网络:nn_classifier.py,包含了全连接以及ReLU
层的前后向运算,神经网络配置、训练及预测功能
同时实现了全连接神经网络的测试文件:test_nn_classifier.py,里面包含了对全连接操作、ReLU
操作、softmax
损失操作以及2
层和3
层网络的测试