这两天对RNN循环神经网络进行了学习,由一无所知到现在对什么是RNN以及它的前向传播和反向传播有了认识,尤其是BPTT算法的推导有些繁琐,但是推过一次后,对RNN反向传播求梯度的过程有了更清晰的认识。
下面是朴素的RNN循环神经网络图。(图1)
我在写博客前,自己先手写了一份推导过程。(图2)
为何BPTT更难?
因为多了状态之间的传递(即隐层单元之间的“交流”),根据前向传播算法,我们知道 s t ∗ = W s t − 1 + U x t , s_t^* = Ws_{t-1} + Ux_t , st∗=Wst−1+Uxt, 而 s t − 1 = f ( s t − 1 ∗ ) = f ( W s t − 2 + U x t − 1 ) s_{t-1} = f(s_{t-1}^*) = f(Ws_{t-2}+Ux_{t-1}) st−1=f(st−1∗)=f(Wst−2+Uxt−1),这说明 s t − 1 s_{t-1} st−1也是关于 W W W的式子。
这样层层嵌套下去…就会追溯到 s 0 s_0 s0。可以意识到我们对 W 、 U W、U W、U的梯度求解是繁琐的,而这正是BPTT的难点所在。对于 V V V的梯度求解,并没有受到状态之间传递的影响,因此和我们BP算法求解方式是一样的。
我们用 ∗ * ∗表示element-wise, × × ×表示矩阵乘法。
我们采用交叉熵损失函数,即 L t = − ( y t l o g ( o t ) + ( 1 − y t ) l o g ( 1 − o t ) ) L_t = - (y_tlog(o_t)+(1-y_t)log(1-o_t)) Lt=−(ytlog(ot)+(1−yt)log(1−ot))
我们定义隐藏层的激活函数为sigmoid函数 s t = f ( s t ∗ ) s_t = f(s_t^*) st=f(st∗),输出层的激活函数也为sigmoid函数 o t = g ( o t ∗ ) o_t = g(o_t^*) ot=g(ot∗)。 f ′ = s t ∗ ( 1 − s t ) , g ′ = o t ∗ ( 1 − o t ) f' = s_t*(1-s_t), g' = o_t*(1-o_t) f′=st