当前位置:   article > 正文

lstm代码_LSTM+CRF 解析(原理篇)

lstm后面为啥要接crf

最近在搞信息抽取任务,用到了LSTM+CRF模型,之前没有深入了解过,就趁这次好好总结一下。把所有的代码,文章看完一遍后发现,这个LSTM+CRF和一般的CRF还是有点区别的,以及具体的代码实现还是有些细节需要注意的。本文打算对原理,数据的构造,模型的搭建进行详细叙述,不过由于不同人之间的前置知识不同,所以理解起来可能还会有些差别,如果描述的不是很清晰的话,可以在下方评论指出,模型的实现参考李航《统计学习方法第二版》中有关CRF的内容,以及代码具体实现时根据

https://github.com/SkyAndCloud/bilstm_crf_sequence_labeling_pytorch/blob/master/main.py​github.com

进行了check,保证代码准确性,代码使用了Pytorch框架。

解析分为原理篇和代码篇,此篇为原理篇。

原理解析:

首先我们要知道为什么使用LSTM+CRF,序列标注问题本质上是分类问题,因为其具有序列特征,所以LSTM就很合适进行序列标注,确实,我们可以直接利用LSTM进行序列标注。但是这样的做法有一个问题:每个时刻的输出没有考虑上一时刻的输出。我们在利用LSTM进行序列建模的时候只考虑了输入序列的信息,即单词信息,但是没有考虑标签信息,即输出标签信息。这样会导致一个问题,以“我 喜欢 跑步”为例,LSTM输出“喜欢”的标签是“动词”,而“跑步”的标签可能也是“动词”。但是实际上,“名词”标签更为合适,因为“跑步”这里是一项运动。也就是“动词”+“名词”这个规则并没有被LSTM模型捕捉到。也就是说这样使用LSTM无法对标签转移关系进行建模。而标签转移关系对序列标注任务来说是很重要的,所以就在LSTM的基础上引入一个标签转移矩阵对标签转移关系进行建模。这就和CRF很像了。我们知道,CRF有两类特征函数,一类是针对观测序列与状态的对应关系(如“我”一般是“名词”),一类是针对状态间关系(如“动词”后一般跟“名词”)。在LSTM+CRF模型中,前一类特征函数的输出由LSTM的输出替代,后一类特征函数就变成了标签转移矩阵

如下图所示,对于一个输入序列

,经过Embedding后得到输入到LSTM中,经过线性层作用后得到每个词对应到每个label(这里有5个label)上的分数。这里label的集合包括起始标签S,结束标签E,以及一般标签L1,L2,L3。

同样的,根据标签转移矩阵

,我们可以得到上一个时刻的标签 为
,下一个时刻标签为
的得分,即

be613bd1c483b804cbae6ae6159eb2e8.png
LSTM输出每个词在各个标签上的得分

7e011a671185f16e6b57f95a180c4e23.png
标签转移矩阵T,表示标签之间的转移得分

一般来说,对于一个序列

,如果序列
的长度为
,有
个可能的标签,那么共有
个可能的标记结果,即
。我们可以利用LSTM+CRF模型计算出每个可能的标注结果的得分
,然后利用softmax进行归一化求出某个标注结果的概率
,选择概率最大的作为标注结果,这里我们用
表示所有可能路径对应分数的指数和。

这样我们就需要关注几个问题:

1.给定输入

,如何计算某个输出序列
的概率?

2.给定训练数据

,如何对LSTM+CRF模型进行训练?

3.对于训练好的LSTM+CRF模型,给定输入

,如何求得最可能的结果

这三个问题其实也就对应CRF的三个基本问题。

首先是第一个问题

根据前面概率的定义,我们可以知道

,一般我们会对其取对数,然后再求解,即求
。所以首先我们讨论给定输入
,如何计算某个输出序列
的分数。

输入LSTM中得到每个词
对应的标签得分分布
。而
的结果是一个矩阵,我们称之为发射矩阵
。对于词
,其
,是一个m维向量,其标签
的得分为
。(
为int型数值,表示标签索引)

是一条链,我们将所有的
加起来只是得到了节点的分数,我们还需要求边的分数。即
的转移分数,根据前面的定义标签转移矩阵为
,即
的转移分数为

最后,把所有的分数加起来即可得

然后我们需要下一步工作,计算所有可能的路径的分数指数和的对数

。但是我们知道这种情况下共有
个可能的标记结果,如果列举所有的路径再求总分,那么计算量就很大。我们可以考虑采用动态规划的方法,动态规划之所以比列举法高效,是因为它可以复用前面的计算信息,不必在后面重新计算。

我们知道对于

时刻的输出
来说,其有
种可能,如果我们记录下从开始时刻到
时刻时,
取某一个值
时经过的所有路径的分数的指数和的对数
,那么我们就可以利用此信息获得
时刻,
的所有路径的得分的指数和的对数。

我们知道对于

,路径得分变化是在原来的基础上加上
。所以

计算图解如下:

cff0facbd6d846d0f6c9329cd99b9741.png

这样就需要我们维护一个

维向量
表示t时刻,
时的所有路径指数和的对数
。即

如果我们想根据

得到
,则按照前面的公式来说,

当得到最后一个时刻

时,我们只需要用
就可以得到最终所有路径的分数的指数和的对数,即

这样,我们就能最终得到

然后是第二个问题,如何训练模型。

在CRF里面,模型训练方法有点麻烦,但是也还行,不过还是不如LSTM+CRF简单,因为所有的参数都是以神经网络的形式定义,我们只需要表示出损失函数,然后让Pytorch框架自动求导即可。

这里我们使用负对数似然函数作为损失函数,即

对应的真实标签数据,至于为什么,请参考:
visionshao:极大似然角度下的神经网络分类损失函数​zhuanlan.zhihu.com


之后,我们就可以愉快地训练了。

第三个问题,当模型训练好后,如何求得最好的结果。

首先,我们要明白我们要寻找的最优路径即为得分最高的路径,下图是一个demo示例,不同颜色的箭头指向不同的节点,任意时刻,带有颜色的箭头表示的路径是指向该节点的得分最高的路径。

9600c2b8a1a2a77762ea983ceeee95fb.png

在最后一个时刻T=3,我们只需要找出得分最高的路径(某种颜色表示的路径)指向的节点,然后不断回溯即可找到整体的最优路径。

从上面的叙述中,我们可以发现,要想实现这种方法,我们需要保存每个节点对应的得分最高的路径及其分数。

我们扩展到第一个问题定义的情况下,即

,经过LSTM后得到的发射矩阵为
,标签转移矩阵为

首先,t=0时刻,我们获取各个节点对应的最大路径的得分

,即节点的发射得分

然后到t=1时刻我们要求各个节点对应的最大得分路径及其得分。

这时我们对

使用wise加法,这样,我们就得到一个
的矩阵
表示t=0的节点
的最大得分路径到t=1时刻的节点
形成的路径得分。如果我们想求得t=1时刻到节点
的最大得分路径,只需要对
求最大值。同样的,如果我们想得到t=1时刻的各个节点对应的最大路径得分
,我们只需要对
的每一列求最大值。求得最大值的同时将最值路径的来源即箭头的尾部节点记下来,便于遍历查找。这样,我们在得到
的同时,也得到了t=1时刻各个节点对应的最大路径在t=0时刻对应的节点向量

然后,不断重复上面的步骤,直到得到t=n-1时刻的

。这时
对应的是最后时刻的各个节点对应的最大路径得分。我们求得
中最大值对应的索引

这个索引表示最优路径的最后一个节点。然后我们将

带入
,得到
,表示最优路径倒数第二个节点,然后依次类推,知道得到
,这时,最优路径为

至此,上述三个问题全部解决。

后面会针对具体实现过程中的代码进行解析,指出一些需要注意的地方。代码地址为

https://github.com/visionshao/LSTM-CRF​github.com
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/神奇cpp/article/detail/787226
推荐阅读
相关标签
  

闽ICP备14008679号