我试图实施ANN,我还为反向传播写了一个数值梯度检查。 当我使用sigmoid函数时,数值梯度检查正常工作 但是,当我使用relu激活时,渐变检查失败。
我得到的渐变如下:
switch opts.act_function
case 'relu'
d_act = a{i} > 0;
case 'sigmoid'
d_act = a{i} * (1 - a{i});
end
我的问题是0处没有渐变。如果我将0的子梯度设置为0,这是正确的吗?
答案 0 :(得分:5)
已知使用ReLU函数进行数值检查会在x = 0
处出现问题。如果您还记得,ReLU函数的定义是f(x) = max(0, x)
。它是一个斜坡函数,其中小于0的值被钳制为0,而严格为正值的值保持相同的值。
数字梯度检查功能(如ReLU)遇到的问题通常被称为 kinks 的问题。扭结指的是目标或激活函数的不可微分部分。对于ReLU函数,从x = 0
左侧和x = 0
右侧接近的导数不相等,因此导数在x = 0
处不存在或者更通俗地说,x = 0
存在扭结。
即使您没有渐变为0,但对于给定的w
和epsilon
,您可能会在执行渐变检查时计算非零渐变。例如,x = -1e-5
非零的情况,并考虑epsilon = 1e-4
时的情况。通过使用评论中所见的居中差异近似,f(x + epsilon) = f(-1e-5 + 1e-4) = f(9e-5) = 9e-5
给出了ReLU的定义。同样,f(x - epsilon) = f(-1e-5 - 1e-4) = f(-1.1e-5) = 0
给出了ReLU的定义。因此,如果您尝试近似导数:
(f(x + epsilon) - f(x - epsilon)) / (2*epsilon) = (9e-5 - 0) / 2e-4 = 0.45
数值梯度在理论上应为0时给出0.45。因此,对于x = 0
的小值,不能依赖数值梯度。你没有遇到sigmoid函数的这个问题,因为它是一个在任何地方都是可微分的函数,所以对于一个足够小的epsilon
,你应该能够获得与函数的实际导数大致相同的值。
当数字不准确时,您可以做的是识别。您可以做的是确定f(x + epsilon)
和f(x - epsilon)
符号何时不同,这表明您正在x = 0
穿越扭结。然后,您可以向用户输出已发生此情况的警告,并且不应依赖数值梯度。否则,当f(x + epsilon)
和f(x - epsilon)
具有相同符号时,渐变应该能够正常传递。