主要内容来处:https://createmomo.github.io:
- CRF Layer on the Top of BiLSTM - 1 Outline and Introduction
- CRF Layer on the Top of BiLSTM - 2 CRF Layer (Emission and Transition Score)
- CRF Layer on the Top of BiLSTM - 3 CRF Loss Function
- CRF Layer on the Top of BiLSTM - 4 Real Path Score
- CRF Layer on the Top of BiLSTM - 5 The Total Score of All the Paths
- CRF Layer on the Top of BiLSTM - 6 Infer the Labels for a New Sentence
- CRF Layer on the Top of BiLSTM - 7 Chainer Implementation Warm Up
- CRF Layer on the Top of BiLSTM - 8 Demo Code
通常在序列标注模型的最后一层layer会添加CRF计算,因为序列标注任务中lable之前有较强的约束性,例如,B-Person与I-Person之前有强关联,B-Person和I-Locations之间有强“非关联”,而CRF模型中的转移矩阵则可以很好体现这些特性。
以下面为例,对去北京
进行预测得到结果如下:
B-Location | I-Location | O | |
---|---|---|---|
去 | 0.1 | 0.1 | 0.8 |
北 | 0.8 | 0.1 | 0.1 |
京 | 0.1 | 0.4 | 0.5 |
通过会取个字对应标签的最大值做为最终标签,结果就是:O B-Location O
,显然B-Location 后面应该是I-Location 才对,而模型却只从单个标签的概率值来决定,即只考虑了局部最优没考虑到全局最优。
既然问题是由于没考虑B-Location到I-Location的可能性引起的,所以把这个特征加到模型中不就能解决我们的问题了吗,就也是CRF的方式。
PS: 如果实际经验中使用LSTM+CRF模型后发现,发现学习到的转移矩阵很弱,这可能是由于学习率和初始化发射矩阵的问题;
CRF计算方法
如上面所述,加入crf层的主要目的是要对最后的输出标签进行约束,因此我们想告诉模型的时,B-Location后面大概率是I-Location,而不是O;所以在决定最终标签时不再只考虑局部标签的概率,也要考虑全局标签和标签之间的关联性。因此,引入CRF中的转移概率矩阵:
B-Location | I-Location | O | |
---|---|---|---|
B-Location | 0.1 | 0.8 | 0.1 |
I-Location | 0.1 | 0.45 | 0.45 |
O | 0.2 | 0.01 | 0.79 |
原来计算路径的方式是:去(O=0.8) + 北(B-Location=0.8)+ 京(O=0.5)
CRF计算路径的方式是:去(O=0.8) + 北(B-Location=0.8)+ 京(O=0.5)+ (O->B-Location=0.2) + (B-Location->O=0.1)
可以看到,原来计算方式去(O)北(B-Location)京(O)
路径上的得分是最高的,而改成CRF的计算方式的话
去(O)北(B-Location)京(O)
比去(O)北(B-Location)京(I-Location)
的路径得分更高,以此也就达到了我们想要的结果。
去(O)北(B-Location)京(O) = 0.8 + 0.8 + 0.5 + 0.2 + 0.1 = 2.4
去(O)北(B-Location)京(I-Location) = 0.8 + 0.8 + 0.4 + 0.2 + 0.8 = 3
到这里,已经了解到如何计算一条路径上的得分,那么目标是让true path上的得分在total path的占比最大,即:
true path 已经知道如何计算了,但 total path的计算相对比较难办,我们当然可以通过遍历所有的路径得到total path,但时间复杂度太高,这里的技巧是使用动态规划的方式来计算。
(p_i) 是每i条路径,则所有路径的得分是:
那么crf的损失函数如下:
那么:
因此要最小化:
即真实路径在所有路径中的概率最大。因此我们只有计算出(P_{RealPath})和(P_{total})这两部分,就可以进行训练了。
单路径分数
单路径得分上面已经讲过如何计算了,现在重新整理一下:
(S_i)由两部分组成:
举例,计算 t0,t1,t0 这条路径的值:
所有路径总分数
计算所有路径总分数需要先定义2个变量:
obs:obj表示当前的信息
previous:previous表示上一步各个tag经过这个tag的所有路径的得分总各。
另外用transition表示转移矩阵
(w_0)->(w_1)
当前变量值 :
注意这里的(x_{01})指的是,第0个字的第1个标签的预测值。
解码
解码这里和上面比较像,最主要的区别是,这里取的是max,上面取的是sum。