参考:
http://ir.hit.edu.cn/~jguo/docs/notes/bptt.pdf
http://www.cnblogs.com/wacc/p/5341670.html
https://zybuluo.com/hanbingtao/note/541458
一段时间学习了ML的基础算法,但是对与DL没怎么接触,这次算首次触电吧。写的不足之处,请大家指正。
一、下图是对RNN的整体解析,大家应该比较熟悉了:
若我们以E表示交叉熵误差,V、W、U为各传递权重,则权重的更新算法为:
二、我们所用于分析权重梯度变化的无非用到里面的一个具体环节,现在把它形式化展现:
此处的表示t-1时刻的隐含层输出作为t时刻隐含层的输入,
表示t时刻的输入。
,代表前一时刻和本时刻输入的融合。设激活函数
,则有
;设输出层函数为
,则输出
是输出标签。
若我们以表示第t时刻的损失函数,并将其定义为交叉熵误差
。那么整个序列的误差,就是所有时刻误差的总和。
三、下面,我们来展现权值更新的计算细节:
self.calc_delta(sensitivity_array, activator)
self.calc_gradient()
def calc_delta(self, sensitivity_array, activator):
self.delta_list = [] # 用来保存各个时刻的误差项
for i in range(self.times):
self.delta_list.append(np.zeros(
(self.state_width, 1)))
#对每一时刻的误差项进行初始化
self.delta_list.append(sensitivity_array)
#将sensitivity_array作为最后的误差项;就是t时刻的误差项delta
for k in range(self.times - 1, 0, -1):
self.calc_delta_k(k, activator)
# 迭代计算每个k时刻的误差项
def calc_delta_k(self, k, activator):
'''
根据k+1时刻的delta计算k时刻的delta
'''
state = self.state_list[k+1].copy()
element_wise_op(self.state_list[k+1],
activator.backward)
#对于k+1时刻的s状态,每个隐层中的s执行反向backward()操作,实际上得到上一时刻k对应的s状态state_list[k];
self.delta_list[k] = np.dot(
np.dot(self.delta_list[k+1].T, self.W),
np.diag(state[:,0])).T
# .T代表矩阵的转置
计算每个时刻t权重的梯度
'''
gradient = np.dot(self.delta_list[t],
self.state_list[t-1].T)
self.gradient_list[t] = gradient
def calc_gradient(self):
self.gradient_list = [] # 保存各个时刻的权重梯度
for t in range(self.times + 1):
self.gradient_list.append(np.zeros(
(self.state_width, self.state_width)))
for t in range(self.times, 0, -1):
self.calc_gradient_t(t)
# 实际的梯度是各个时刻梯度之和
self.gradient = reduce(
lambda a, b: a + b, self.gradient_list,
self.gradient_list[0]) # [0]被初始化为0且没有被修改过