如果你对下面的内容有疑惑,可能需要看一下我前一篇写的对BiLSTM-CRF的讲解
CRF+BiLSTM代码分步骤解读
之前也讲过forward_algorithm的是用来求解所有路径得分之和的函数,下面将用一个具体的例子来讲解一下这个函数实现的流程。
先随机初始化一个发射矩阵e_score (batch_size, seq_len, tags_size)

再随机初始化一个发射矩阵t_score (tags_size, tags_size)

创建一个init_matrix,然后再复制一份给pre_matrix,这里为了方便理解将模型竖起来 (batch_size, 1, tags_size)

这里仅展示当时刻为0,状态为’B’时的计算过程

下面都是在log_sum_exp中的临时变量


def forward_algorithm(self, e_matrix):
# matrix 是在当前状态下总路径之和
init_matrix = torch.full((BATCH_SIZE, 1, tags_size), -10000.0)
init_matrix[:, 0, self.s2i[START_TAG]] = 0.
# 前一步的最优值
pre_matrix = init_matrix
# 循环时间
for i in range(SEQ_LEN):
# 保存当前时间步的的路径值
matrix_value = []
# 循环状态
for s in range(tags_size):
# 计算发射分数, (BATCH_SIZE, 1, tags_size)
e_score = e_matrix[:, i, s].view(BATCH_SIZE, 1, -1).expand(BATCH_SIZE, 1, tags_size)
# 计算转移分数 (1,tags_size)
t_score = self.t_score[s, :].view(1, -1)
# 下一步的得分 (BATCH_SIZE, 1, tags_size)
next_matrix = pre_matrix + e_score + t_score
# self.log_sum_exp(next_matrix) (BATCH_SIZE, 1)
matrix_value.append(self.log_sum_exp(next_matrix))
# 在把其记录到pre_matrix变量中
pre_matrix = torch.cat(matrix_value, dim=-1).view(BATCH_SIZE, 1, -1)
# 最终的变量:之前的得分+转移到终点的得分 (BATCH_SIZE, 1, tags_size)
terminal_var = pre_matrix + self.t_score[self.s2i[STOP_TAG], :]
alpha = self.log_sum_exp(terminal_var)
# (BATCH_SIZE,1)
return alpha
可看到这里也不太明白为什么这样做可以得到所有路径之和,其实,这样做无非是为了简化运算,但这样计算的不足在于使用了很多遍logsumexp,这就和原先的值其实有一些差距。
理想值
s
c
o
r
e
i
d
e
a
l
=
l
o
g
(
e
S
1
+
e
S
2
+
.
.
.
+
e
S
N
)
(1)
score_{ideal} = log(e^{S_1}+e^{S_2}+...+e^{S_N})\tag1
scoreideal=log(eS1+eS2+...+eSN)(1)
现实值
s
c
o
r
e
r
e
a
l
i
t
y
=
l
o
g
(
∑
e
p
r
e
+
t
)
=
l
o
g
(
∑
e
l
o
g
(
∑
e
p
r
e
+
t
+
e
s
)
+
t
)
=
.
.
.
(2)
t->t_score
es->e_score
pre->pre_matrix

如上图所示,🟣球处已经计算了从<START>到"我",前一步所有状态到B的全部路径得分S1,求logsumexp(S1)记录到🟣球处,同理🔵球处则是前两步所有路径到达"爱",并且所有状态转移至B的全部路径得分S2,求logsumexp(S2)记录到🔵球处。
至此,你学废了吗?