我正在使用自定义 C++ 代码,并编写了一个简单的“均方误差”层。暂时将其用于“分类任务”,而不是简单的回归。...也许这会导致问题?
在这一层之前我没有其他任何东西——甚至没有一个简单的密集层。它本身就是 MSE。它的输入是输入特征行的集合。例如,这里有 8 行输入特征,它们将一次全部传递给 MSE:
{ a0, a1, a2, a3, a4, a5, a6, a7 }
{ b0, b1, b2, b3, b4, b5, b6, b7 }
{ c0, c1, c2, c3, c4, c5, c6, c7 }
{ d0, d1, d2, d3, d4, d5, d6, d7 }
{ e0, e1, e2, e3, e4, e5, e6, e7 }
{ f0, f1, f2, f3, f4, f5, f6, f7 }
{ g0, g1, g2, g3, g4, g5, g6, g7 }
{ h0, h1, h2, g3, h4, h5, h6, h7 } //8x8 matrix (contains 64 different values)
这个矩阵的每一行都被传递到我的“均方误差”层,为这样的一行返回一个标量:“成本”。
然后我计算一个“最终误差”标量,它是这些成本的平均值。
在进行梯度检查时,我正在研究当我扰乱 64 个输入值中的每一个时这个“最终错误”数量如何变化,如上所示。finalError这个想法是,相对于我的 64 个输入值,变化必须对应于公式计算的梯度。如果它们匹配,那么我已经正确编码了反向传播。
这是前向道具:
在哪里是每行的特征数,并且是行数。
这是我的反向传播正在使用的输入值之一的梯度:
问题:
我将每个输入值“向上”,然后“向下”,向前运行道具 64*2 = 128 次。这为我的 64 个输入值提供了梯度的数值估计。
然而,当使用较小的epsilon时,这个数值估计和实际分析梯度变得不太相似。这对我来说是违反直觉的。相反,当我对 epsilon 使用大得离谱的值时,我的向量几乎完全匹配,例如
这是预期的,还是我在 C++ 代码中有错误?
这是伪代码
for every input value i:
i -= EPSILON
finalCost_down = fwdprop( inputMatrix )//very simple - just computes final cost via MSE layer. finalCost_down is a scalar.
i += EPSILON
finalCost_up = fwdprop( inputMatrix )
gradientEstimate[i] = (finalCost_up - finalCost_down) / (2*EPSILON)
//after the loop, some time later, just one invocation of backprop:
trueGradientVec = backprop( vec )
//some time later:
discrepancyScalar = (gradientEstimate - trueGradientVec).magnitude / gradientEstimate.magnitude + trueGradientVec.magnitude)
//somehow discrepancyScalar decreases the larger the EPSILON was used:
// discrepancy is 0.00275, if EPSILON is 0.0001
// discrepancy is 0.00025, if EPSILON is 0.001
// discrepancy is 2.198e-05, if EPSILON is 0.01
// discrepancy is 3.149e-06, if EPSILON is 0.1
// discrepancy is 2.751e-07, if EPSILON is 1
我希望当 epsilon 减少时差异会减少,因为更精细的扰动应该给出更精确的斜率估计......