该篇博客参考
深度实践OCR 刘树春
接下来谈论反向传播
在讨论反向传播之前,计算一下后向概率(指从时间t到达第s个字符的所有可能子路径概率)
可以理解成从终点开始计算,每个位置能够到达终点的概率,类似的可以得到状态转移方程
与正向计算相反,这次只能朝着左上走
betas[-1,-1]=params[blank,-1]
betas[-2,-1]=params[seq[-1],-1]
c=np.sum(betas[:,-1])
betas[:,-1]=betas[:,-1]/c
llBackward=np.log(c)
for t in range(T-2,-1,-1):
start=max(0,L-2*(T-t))
end=min(2*t+2,L)
for s in range(end-1,-1,-1):
l=(s-l)/2
# 为blank的情况,偶数位的当前状态都是空格
if s % 2 == 0:
if s == 0:
# 如果是当前列第一个,那么只能从前一列的同一行过来了
alphas[s, t] = alphas[s, t + 1] * params[blank, t]
else:
# 可以前一列的同行和上一行过来,不能从上一个空字过来,因为这违法了非空不能跳过
alphas[s, t] = (alphas[s, t + 1] + alphas[s + 1, t + 1]) * params[blank, t]
# 同样的label两次
elif s == l or seq[l] == seq[l + 1]:
# 相同的字符间需要间隔一个空字,所以不能从上上行过来
alphas[s, t] = (alphas[s, t + 1] + alphas[s + 1, t + 1]) * params[seq[l], t]
else:
# 三条路都能走,再向上的话会违背非空不能跳过原则
alphas[s, t] = (alphas[s, t + 1] + alphas[s - 1, t + 1] + alphas[s + 2, t + 1]) * params[seq[l], t]
c=np.sum(betas[start:end,t])
betas[start:end,t]=betas[start:end,t]/c
llBackward+=np.log(c)
这样便完成了后向传播
grad=np.zeros(params.shape)
ab=alphas*betas
for s in range(L):
# blank
if s%2==0:
grad[blank:]+=ab[s,:]
ab[s,:]=ab[s,:]/params[blank,:]
else:
grad[seq[(s-1)/2],:]+=ab[s,:]
ab[s, :] = ab[s, :] / params[seq[(s-1)/2], :]
absum=np.sum(ab,axis=0)
至此梯度回传全部完成