转载来源:https://mp.weixin.qq.com/s/7s9dTJuT0yWVJOT9tOkjuQ
导读:今天是第三部分,介绍如何推理新的句子。
为新的句子推理标签
在前面的章节中,我们学习了BiLSTM-CRF模型的结构和CRF损失函数的细节。你可以通过各种开源框架(Keras、Chainer、TensorFlow等)实现自己的BiLSTM-CRF模型。最重要的事情之一是模型的反向传播是在这些框架上自动计算的,因此你不需要自己实现反向传播来训练你的模型(即计算梯度和更新参数)。此外,一些框架已经实现了CRF层,因此将CRF层与你自己的模型结合起来非常容易,只需添加一行代码即可。
在本节中,我们将探索如何在模型准备好时在测试期间推断句子的标签。
步骤1:BiLSTM-CRF模型的Emission和transition得分
假设,我们有一个包含三个单词的句子: x = [ w 0 , w 1 , w 2 ] x=\left[w_{0}, w_{1}, w_{2}\right] x=[w0,w1,w2]
此外,我们已经从BiLSTM模型得到了Emission分数,从下面的CRF层得到了transition分数:
x
i
j
x_{i j}
xij表示
w
i
w_{i}
wi被标记为
l
j
l_{j}
lj的得分
t
i
j
t_{i j}
tij是从标签i转换成标签j的得分。
步骤2:开始推理
如果你熟悉Viterbi算法,那么这一部分对你来说很容易。但如果你不熟悉,请不要担心。与前一节类似,我将逐步解释该算法。我们将从句子的左到右进行推理算法,如下图所示:
- w 0 w_{0} w0
- w 0 − > w 1 w_{0}->w_{1} w0−>w1
- w 0 − > w 1 − > w 2 w_{0}->w_{1}->w_{2} w0−>w1−>w2
你会看到两个变量:obs和previous。previous存储前面步骤的最终结果。obs表示当前单词的信息。
a
l
p
h
a
0
alpha_{0}
alpha0是历史最好得分,
a
l
p
h
a
1
alpha_{1}
alpha1是历史对应的索引。这两个变量的细节将在它们出现时进行解释。请看下面的图片:你可以把这两个变量当作狗在探索森林时沿路留下的“记号”,这些“记号”可以帮助狗找到回家的路。
狗需要找到最好的路径来得到他最喜欢的骨头玩具,然后沿着他来的路回家
w
0
w_{0}
w0:
o
b
s
=
[
x
01
,
x
02
]
p
r
e
v
i
o
u
s
=
N
o
n
e
o b s=\left[x_{01}, x_{02}\right] previous = None
obs=[x01,x02]previous=None
现在,我们观察到第一个单词,现在,对于是很明显的。
比如,如果 o b s = [ x 01 = 0.2 , x 02 = 0.8 ] o b s=\left[x_{01}=0.2, x_{02}=0.8\right] obs=[x01=0.2,x02=0.8],很显然 w 0 w_{0} w0的最佳的标签是 l 2 l_{2} l2。
因为只有一个单词,而且没有标签直接的转换,transition的得分没有用到。
W
0
−
>
W
1
W_{0}->W_{1}
W0−>W1:
o
b
s
=
[
x
11
,
x
12
]
p
r
e
v
i
o
u
s
=
[
x
01
,
x
02
]
o b s=\left[x_{11}, x_{12}\right] previous =\left[x_{01}, x_{02}\right]
obs=[x11,x12]previous=[x01,x02]
- 把previous扩展成:
previous = ( previous [ 0 ] previous [ 0 ] previous [ 1 ] previous [ 1 ] ) = ( x 01 x 01 x 02 x 02 ) \text {previous}=\left(\begin{array}{ll} \text {previous}[0] & \text {previous}[0] \\ \text {previous}[1] & \text {previous}[1] \end{array}\right)=\left(\begin{array}{ll} x_{01} & x_{01} \\ x_{02} & x_{02} \end{array}\right) previous=(previous[0]previous[1]previous[0]previous[1])=(x01x02x01x02) - 把obs扩展成:
o b s = ( o b s [ 0 ] o b s [ 1 ] o b s [ 0 ] o b s [ 1 ] ) = ( x 11 x 12 x 11 x 12 ) o b s=\left(\begin{array}{ll} o b s[0] & o b s[1] \\ o b s[0] & o b s[1] \end{array}\right)=\left(\begin{array}{ll} x_{11} & x_{12} \\ x_{11} & x_{12} \end{array}\right) obs=(obs[0]obs[0]obs[1]obs[1])=(x11x11x12x12) - 把previous, obs和transition分数都加起来:
scores = ( x 01 x 01 x 02 x 02 ) + ( x 11 x 12 x 11 x 12 ) + ( t 11 t 12 t 21 t 22 ) \text {scores}=\left(\begin{array}{cc} x_{01} & x_{01} \\ x_{02} & x_{02} \end{array}\right)+\left(\begin{array}{cc} x_{11} & x_{12} \\ x_{11} & x_{12} \end{array}\right)+\left(\begin{array}{cc} t_{11} & t_{12} \\ t_{21} & t_{22} \end{array}\right) scores=(x01x02x01x02)+(x11x11x12x12)+(t11t21t12t22)
然后:
scores
=
(
x
01
+
x
11
+
t
11
x
01
+
x
12
+
t
12
x
02
+
x
11
+
t
21
x
02
+
x
12
+
t
22
)
=
(
0.2
0.3
0.5
0.4
)
\text {scores}=\left(\begin{array}{cc} x_{01}+x_{11}+t_{11} & x_{01}+x_{12}+t_{12} \\ x_{02}+x_{11}+t_{21} & x_{02}+x_{12}+t_{22} \end{array}\right)=\left(\begin{array}{cc} 0.2 & 0.3 \\ 0.5 & 0.4 \end{array}\right)
scores=(x01+x11+t11x02+x11+t21x01+x12+t12x02+x12+t22)=(0.20.50.30.4)
你可能想知道,当我们计算所有路径的总分时,与上一节没有什么不同。请耐心和细心,你很快就会看到区别。
为下一次迭代更改previous的值:
previous
=
[
max
(
scores
[
00
]
,
scores
[
10
]
)
,
max
(
scores
[
01
]
,
scores
[
11
]
)
]
\text {previous}=[\max (\text {scores}[00], \text {scores}[10]), \max (\text {scores}[01], \text {scores}[11])]
previous=[max(scores[00],scores[10]),max(scores[01],scores[11])]
比如,如果我们的得分是:
scores
=
(
x
01
+
x
11
+
t
11
x
01
+
x
12
+
t
12
x
02
+
x
11
+
t
21
x
02
+
x
12
+
t
22
)
=
(
0.2
0.3
0.5
0.4
)
\text {scores}=\left(\begin{array}{cc} x_{01}+x_{11}+t_{11} & x_{01}+x_{12}+t_{12} \\ x_{02}+x_{11}+t_{21} & x_{02}+x_{12}+t_{22} \end{array}\right)=\left(\begin{array}{cc} 0.2 & 0.3 \\ 0.5 & 0.4 \end{array}\right)
scores=(x01+x11+t11x02+x11+t21x01+x12+t12x02+x12+t22)=(0.20.50.30.4)
我们的下个迭代的previous是:
previous
=
[
max
(
scores
[
00
]
,
scores
[
10
]
)
,
max
(
scores
[
01
]
,
scores
[
11
]
)
]
=
[
0.5
,
0.4
]
\text {previous}=[\max (\text {scores}[00], \text {scores}[10]), \max (\text {scores}[01], \text {scores}[11])]=[0.5,0.4]
previous=[max(scores[00],scores[10]),max(scores[01],scores[11])]=[0.5,0.4]
previous有什么含义吗? previous列表存储了每个当前的单词的标签的最大的得分。
举个例子:
我们知道在我们的语料中,我们总共只有2个标签, l a b e l 1 ( l 1 ) label1(l_{1}) label1(l1)和 l a b e l 1 ( l 2 ) label1(l_{2}) label1(l2)。这两个标签的索引是0和1。
previous[0]是以第0个标签
l
1
l_{1}
l1为结尾的路径的最大得分,类似的previous[1]是以第1个标签
l
2
l_{2}
l2为结尾的路径的最大得分。在每个迭代中,变量previous存储了以每个标签为结尾的路径的最大得分。换句话说,在每个迭代中,我们只保留了每个标签的最佳路径的信息($
\text {previous}=\max (\text {scores}[00], \text {scores}[10]), \max (\text {scores}[01], \text {scores}[11])
$)。具有小得分的路径信息会被丢掉。
回到我们的主任务:
同时,我们还有两个变量用来存储历史信息(得分和索引) a l p h a 0 alpha_{0} alpha0和 a l p h a 1 alpha_{1} alpha1。
在这个迭代中,我们把最佳得分加上
a
l
p
h
a
0
alpha_{0}
alpha0
scores
=
(
x
01
+
x
11
+
t
11
x
01
+
x
12
+
t
12
x
02
+
x
11
+
t
21
x
02
+
x
12
+
t
22
)
=
(
0.2
0.3
0.5
0.4
)
\text {scores}=\left(\begin{array}{cc} x_{01}+x_{11}+t_{11} & x_{01}+x_{12}+t_{12} \\ x_{02}+x_{11}+t_{21} & x_{02}+x_{12}+t_{22} \end{array}\right)=\left(\begin{array}{cc} 0.2 & 0.3 \\ 0.5 & 0.4 \end{array}\right)
scores=(x01+x11+t11x02+x11+t21x01+x12+t12x02+x12+t22)=(0.20.50.30.4)
a
l
p
h
a
0
=
[
(
scores
[
10
]
,
scores
[
11
]
)
]
=
[
(
0.5
,
0.4
)
]
a l p h a_{0}=[(\text {scores}[10], \text {scores}[11])]=[(0.5,0.4)]
alpha0=[(scores[10],scores[11])]=[(0.5,0.4)]
另外,对应的列的索引存在
a
l
p
h
a
1
alpha_{1}
alpha1里。
alpha
1
=
[
(
ColumnIndex
(
scores
[
10
]
)
,
ColumnIndex
(
scores
[
11
]
)
)
]
=
[
(
1
,
1
)
]
\text {alpha}_{1}=[(\text {ColumnIndex}(\text {scores}[10]), \text { ColumnIndex }(\text {scores}[11]))]=[(1,1)]
alpha1=[(ColumnIndex(scores[10]), ColumnIndex (scores[11]))]=[(1,1)]
说明一下,
l
1
l_{1}
l1的索引是0,
l
2
l_{2}
l2的索引是1,所以
(
1
,
1
)
=
(
l
2
,
l
3
)
(1,1)=(l_{2},l_{3})
(1,1)=(l2,l3)表示对于当前的单词
w
i
w_{i}
wi和标签
l
(
i
)
l^{(i)}
l(i),当路径
l
i
−
1
=
l
2
−
>
l
i
=
l
1
l^{i-1}=l_{2}->l^{i}=l_{1}
li−1=l2−>li=l1是的时候,我们可以得到最大的得分是0.5,当路径是
l
i
−
1
=
l
2
−
>
l
i
=
l
2
l^{i-1}=l_{2}->l^{i}=l_{2}
li−1=l2−>li=l2的时候,我们可以得到最大的得分是0.4。是过去的单词
w
i
−
1
w_{i-1}
wi−1的标签。
w 0 − > w 1 − > w 2 w_{0}->w_{1}->w_{2} w0−>w1−>w2: o b s = [ x 21 , x 22 ] p r e v i o u s = [ 0.5 , 0.4 ] o b s=\left[x_{21}, x_{22}\right] previous =[0.5,0.4] obs=[x21,x22]previous=[0.5,0.4]
- 把previous扩展成:
previous = ( previous [ 0 ] previous [ 0 ] previous [ 1 ] previous [ 1 ] ) = ( 0.5 0.5 0.4 0.4 ) \text {previous}=\left(\begin{array}{ll} \text {previous}[0] & \text {previous}[0] \\ \text {previous}[1] & \text {previous}[1] \end{array}\right)=\left(\begin{array}{cc} 0.5 & 0.5 \\ 0.4 & 0.4 \end{array}\right) previous=(previous[0]previous[1]previous[0]previous[1])=(0.50.40.50.4) - 把obs扩展成:
o b s = ( o b s [ 0 ] o b s [ 1 ] o b s [ 0 ] o b s [ 1 ] ) = ( x 21 x 22 x 21 x 22 ) o b s=\left(\begin{array}{ll} o b s[0] & o b s[1] \\ o b s[0] & o b s[1] \end{array}\right)=\left(\begin{array}{ll} x_{21} & x_{22} \\ x_{21} & x_{22} \end{array}\right) obs=(obs[0]obs[0]obs[1]obs[1])=(x21x21x22x22) - 把previous, obs和transition 分数都加起来:
scores = ( 0.5 0.5 0.4 0.4 ) + ( x 21 x 22 x 21 x 22 ) + ( t 11 t 12 t 21 t 22 ) \text {scores}=\left(\begin{array}{cc} 0.5 & 0.5 \\ 0.4 & 0.4 \end{array}\right)+\left(\begin{array}{cc} x_{21} & x_{22} \\ x_{21} & x_{22} \end{array}\right)+\left(\begin{array}{cc} t_{11} & t_{12} \\ t_{21} & t_{22} \end{array}\right) scores=(0.50.40.50.4)+(x21x21x22x22)+(t11t21t12t22)
然后:
scores
=
(
0.5
+
x
11
+
t
11
0.5
+
x
12
+
t
12
0.4
+
x
11
+
t
21
0.4
+
x
12
+
t
22
)
\text {scores}=\left(\begin{array}{cc} 0.5+x_{11}+t_{11} & 0.5+x_{12}+t_{12} \\ 0.4+x_{11}+t_{21} & 0.4+x_{12}+t_{22} \end{array}\right)
scores=(0.5+x11+t110.4+x11+t210.5+x12+t120.4+x12+t22)
为下一次迭代更改previous的值:
previous
=
[
max
(
scores
[
00
]
,
scores
[
10
]
)
,
max
(
scores
[
01
]
,
scores
[
11
]
)
]
\text {previous}=[\max (\text {scores}[00], \text {scores}[10]), \max (\text {scores}[01], \text {scores}[11])]
previous=[max(scores[00],scores[10]),max(scores[01],scores[11])]
这次迭代我们得到的分数是:
scores
=
(
0.6
0.9
0.8
0.7
)
\text {scores}=\left(\begin{array}{cc} 0.6 & 0.9 \\ 0.8 & 0.7 \end{array}\right)
scores=(0.60.80.90.7)
我们得到最新的previous:
p
r
e
v
i
o
u
s
=
[
0.8
,
0.9
]
previous = [0.8,0.9]
previous=[0.8,0.9]
实际上,previous[0]和previous[1]中最大的那个就是预测的最佳路径。
同时,每个标签和索引的最大得分会加到
a
l
p
h
a
0
alpha_{0}
alpha0上和
a
l
p
h
a
1
alpha_{1}
alpha1上。
a
l
p
h
a
0
=
[
(
0.5
,
0.4
)
,
(
scores
[
10
]
,
scores
[
01
]
)
]
=
[
(
0.5
,
0.4
)
,
(
0.8
,
0.9
)
]
a
l
p
h
a
1
=
[
(
1
,
1
)
,
(
1
,
0
)
]
\begin{array}{c} a l p h a_{0}=[(0.5,0.4),(\operatorname{scores}[10], \operatorname{scores}[01])] \\ =[(0.5,0.4),(0.8,0.9)] \\ a l p h a_{1}=[(1,1),(1,0)] \end{array}
alpha0=[(0.5,0.4),(scores[10],scores[01])]=[(0.5,0.4),(0.8,0.9)]alpha1=[(1,1),(1,0)]
步骤3:找到具有最高得分的最佳路径
这是最后一步!你就快完成了!在此步骤中,将使用 a l p h a 0 alpha_{0} alpha0上和 a l p h a 1 alpha_{1} alpha1来查找得分最高的路径。我们将从最后一个到第一个的元素检查这两个列表中。
w
1
−
>
w
2
w_{1}->w_{2}
w1−>w2:
首先,检查
a
l
p
h
a
0
alpha_{0}
alpha0上和
a
l
p
h
a
1
alpha_{1}
alpha1的最后一个元素:(0.8,0.9)和(1,0)。0.9表示当label为时,我们可以得到最高的路径分数0.9。我们还知道
l
2
l_{2}
l2的索引是1,因此检查(1,0)[1]=0的值。索引“0”表示前一个标签为
l
1
l_{1}
l1(
l
1
l_{1}
l1的索引为0),因此我们可以得到是
w
1
−
>
w
2
w_{1}->w_{2}
w1−>w2的最佳路径是
l
1
−
>
l
2
l_{1}->l_{2}
l1−>l2。
w
0
−
>
w
1
w_{0}->w_{1}
w0−>w1:
其次,我们继续向后移动并得到
a
l
p
h
a
1
alpha_{1}
alpha1:(1,1)的元素。从上一段我们知道w1的label是
l
1
l_{1}
l1(index是0),因此我们可以检查(1,1)[0]=1。因此,我们可以得到这部分的最佳路径
w
0
−
>
w
1
w_{0}->w_{1}
w0−>w1是
l
2
−
>
l
1
l_{2}->l_{1}
l2−>l1。
恭喜!我们这个例子中的最佳路径是 l 2 − > l 1 − > l 2 l_{2}->l_{1}->l_{2} l2−>l1−>l2。