在我上一个关于使用 Flux 训练循环神经网络的困惑之后,我更深入地研究了 Flux 训练过程,现在我更加困惑了。我认为我的麻烦在于在loss
函数中使用总和,因此损失会考虑到序列中的许多点。请参见此处,其中损失定义为:
loss(x, y) = sum((Flux.stack(m.(x),1) .- y) .^ 2)
如果x
是具有多个点的序列,并且y
是每个点的对应输出,则此loss
函数评估整个序列的损失。我想了解的是 Flux 如何采用这样的函数的梯度。想象一下将其简化为:
L(x, y) = sum((Flux.stack(m.(x), 1) .- y))
我们还可以创建一个非常简单的循环神经“网络”作为单个 1 -> 1 节点,没有激活函数:
m = Flux.RNN(1, 1, x -> x)
这(有点)相当于:
h = [0.0]
function m(x)
y = Wx .* x + Wh .* h .+ b
global h = y
return y
end
loss
相对于的梯度是Wx
多少?取一个包含两个点的序列,x = [x1, x2] 和 y* = [y1*, y2*]。将 x1 通过 RNN 得到:
y1 = h2 = Wx*x1 + Wh*h1 + b
然后把 x2 通过,你得到:
y2 = h3 = Wx*x2 + Wh*h2 + b = Wx*x2 + Wh*(Wx*x1 + Wh*h1 + b) + b。
现在计算损失:
L = y1 - y1* + y2 - y2* = Wx*x1 + Wh*h1 + b - y1* + Wx*x2 + Wh*(Wx*x1 + Wh*h1 + b) + b - y2*
很明显,dL/dWx 应该是 x1 + x2 + Wh*x1。所以让我们说x
并且y
是:
x = [[0.3], [2.5]]
y = [0.5, 1.0]
并且参数被初始化为:
Wxs = [0.5]
Whs = [0.001]
bs = [0.85]
如果计算 dL/DWx = x1 + x2 + Wh*x1,则为 2.8003。您也可以尝试有限差分:
h = [0.0]
q = loss(x, y)
Wx .+= 0.01
h = [0.0]
r = loss(x, y)
abs(q - r)/0.01 # = 2.8003
并得到 2.8003。但是如果你使用 Flux 的gradient
功能:
Wx = [0.5]
h = [0.0]
gs = gradient(() -> loss(x, y), params(Wx, Wh, b))
gs[Wxs] # = 2.8025
你得到 2.8025,这似乎是 x1 + x2 + Wh*x2。我不明白为什么结果会有所不同,尤其是考虑到在评估两个不同的损失函数本身时一切都是一致的。有什么我忽略的吗?里面有什么奇怪的事情gradient
吗?