这两天对RNN循环神经网络进行了学习,由一无所知到现在对什么是RNN以及它的前向传播和反向传播有了认识,尤其是BPTT算法的推导有些繁琐,但是推过一次后,对RNN反向传播求梯度的过程有了更清晰的认识。
下面是朴素的RNN循环神经网络图。(图1)
我在写博客前,自己先手写了一份推导过程。(图2)
为何BPTT更难?
因为多了状态之间的传递(即隐层单元之间的“交流”),根据前向传播算法,我们知道
s∗t=Wst−1+Uxt,
而
st−1=f(s∗t−1)=f(Wst−2+Uxt−1)
,这说明
st−1
也是关于
W
的式子。
这样层层嵌套下去……就会追溯到
s0
。可以意识到我们对
W、U
的梯度求解是繁琐的,而这正是BPTT的难点所在。对于
V
的梯度求解,并没有受到状态之间传递的影响,因此和我们BP算法求解方式是一样的。
我们用
∗
表示element-wise,
×
表示矩阵乘法。
我们采用交叉熵损失函数,即
Lt=−(ytlog(ot)+(1−yt)log(1−ot))
我们定义隐藏层的激活函数为sigmoid函数
st=f(s∗t)
,输出层的激活函数也为sigmoid函数
ot=g(o∗t)
。
f′=st∗(1−st),g′=ot∗(1−ot)
。具体求导读者自行证明。
由前向传播可知,
ot=g(o∗t)=g(Vst)
那么
∂Lt∂V=∂Lt∂ot∗∂ot∂o∗t⋅∂o∗t∂V=−(ytot+yt−1∂1−ot)∗ot∗(1−ot)⋅∂o∗t∂V=(ot−yt)×sTt
不同时刻的
∂Lt∂V
要相加,得到最后的
∂L∂V
。
由前向传播可知,对于时刻t而言,
st−1
也是关于
W
的式子,因此我们在求
∂Lt∂W
时,不能简单的将
st−1
视为常量,因此
∂Lt∂W=∑tk=0∂Lt∂s∗k×sTk−1
(注意,在我这里是把第一个时刻从0开始)。
∂Lt∂s∗t=∂Lt∂o∗t⋅∂o∗t∂s∗t=VT×(ot−yt)∗st∗(1−st)
∂Lt∂s∗k−1=∂Lt∂s∗k⋅∂s∗k∂sk−1∗∂sk−1∂s∗k−1=sk−1∗(1−sk−1)∗WT×∂Lt∂s∗k(k=1,2,3...t)
同理,
∂Lt∂U=∑tk=0∂Lt∂s∗k×xTk
。