LSTM + linear-CRF序列标注笔记
CRF
许多随机变量组成一个无向图G = {V, E},V代表顶点,E代表顶点间相连的边,
每个顶点代表一个随机变量,边代表两个随机变量间存在相互影响关系(变量非独立),
如果随机变量根据图的结构而具有对应的条件独立性,
具体来说,两个没有边连接随机变量V1、V2,在其它随机变量O都确定的情况下,是独立的。
即 P(V1, V2 | O) = P(V1 | O) * P(V2 | O)
那么这被称为【成对马尔科夫性】,另有不同定义的【局部马尔科夫性】、【全局马尔科夫性】,它们互为充要条件(此处无证明)
对于满足成对马尔科夫性无向图,可以对最大团作定义
一个最大团是一组随机变量(顶点)的集合,且要满足两个条件
(1).这一组顶点之间,两两都有边相连
(2).任意一个不在这组内的顶点,不能和该组顶点的每一个都有边相连
那么一个无向图G,可以唯一地表示为一个最大团的集合C = {C1, C2, ...}
我们可能会对这组随机变量的联合分布感兴趣,比如计算P(V1=a,V2=b,V3=c...)
可以证明,无向图G的联合分布,可以被最大团表示为 phi1(C1)phi1(C2)....phin(Cn) / Z
其中,phi1~phin称为最大团C1~Cn上的势函数,Z是所有可能的随机变量取值组合下,phi1(C1)phi1(C2)....phin(Cn)的和
可以看出来Z实际上就是一个归一项
因为势函数一般要求是严格正的,所以会用一种指数函数的形式来表示
即phi1(C1)phi1(C2)....phin(Cn) = exp(E1(C1))exp(E2(C2))...exp(En(Cn)) = exp(En(Cn) + E1(Cn) + ... + En(Cn))
其中这个E1~En,可以看做是对某个最大团的随机变量的当前取值的打分
总结起来,如果要求P(Y1Y2...Yn)的值,则应该计算当前每个最大团的分数,求和,并执行softmax,softtmax的底是所有可能的变量值组合。
linear-CRF
线性的CRF,每个Y都只和前一个或后一个随机变量相连,
如 Y1——Y2——Y3——...Yn
每个最大团都是邻近的两个随机变量,如(Y1, Y2)、(Y2、Y3)等
线性CRF通常用于序列预测中,
比如,假设输入为X,输出为序列Y,每一个随机变量的取值都在T = {'B', 'E', 'M', 'O'}之中,
那么计算某个特定序列的条件概率。
更具体地,比如计算
P('BMEOBMEO' | X) / P(Y | X) = exp(score('BMEOBMEO', X)) / ∑Y exp(score(Y), X)
∑Y exp(Y | X)这项作为归一项,实际是求同样长度的所有序列组合的exp分数之和,
对于目标序列'BMEOBMEO',它长度为8,那么同样长度的序列包括'BBBBBBBB'、'BBBBBBBM'等等....
linear-CRF的前向计算
这个同样长度的所有序列,数量是非常巨大的,
确切地说,应该是O(|T|^n)的量级,|T|是状态集的大小,n是序列长度。
如果每一个序列都要计算一次分数,那稍长一点的序列计算时间都会长到无法接受。
此时,根据linear-CRF图结构的特性,可以采用动态规划的方式,减少重复计算量,降低时间复杂度。
考虑Score(Y1:n-1, X)和Score(Y1:n, X)的区别,在两个无向图中,后者比前者增加了两条边,
(1).边Yn-1——Yn (2).边X——Yn
所以Score(Y1:n, X) = Score(Y1:n-1, X) + E-tran(Yn-1, Yn) + E-emmi(X, Yn)
其中E(Yn-1, Yn)又可称为转移分数,E(X, Yn)又可称为发射分数。
发现了该递推关系以后,再思考一个非常重要的递推中间变量:
记 g(k, tag) = exp(Score(Y1:k-1Yk = tag, X))
也就是,在时间点k上,Yk等于tag,但是前面的Y1:k-1状态随意的分数,
因为Y1:k-1状态随意,它实质上是Y1:k-1字序列所有可能组合,且Yk以tag为结尾的分数和
具体地,g(3, 'B') = ∑(Y1:k-1) exp(Score(Y1:Y2Y3='B', X)) = exp(Score('BBB', X)) + exp(Score('BEB', X)) + exp(Score('BMB', X)) + ... + exp(Score('OOB', X))
最后一个等式,很容易计算到,有 4 * 4 = 16个加和项
定义了g(k, tag),很重要一个点就是计算递推关系,
即,假如知道了g(k, tag),那对于计算g(k+1, Yk+1)又有什么帮助呢?首先展开g(k+1, Yk+1)
g(k+1, Yk+1) = ∑(Y1:k) exp(Score(Y1:kYk+1, X))
= ∑(Y1:k) exp[Score(Y1:k, X) + E-tran(Yk, Yk+1) + E-emis(X, Yk+1)]
= ∑(Y1:k) exp[Score(Y1:k, X)]exp[E-tran(Yk, Yk+1)]exp[E-emis(X, Yk+1)]
= ∑(Yk)∑(Y1:k-1) exp[Score(Y1:k-1Yk, X)]exp[E-tran(Yk, Yk+1)]exp[E-emis(X, Yk+1)]
= ∑(Yk) g(k, Yk)exp[E-tran(Yk, Yk+1)]exp[E-emis(X, Yk+1)]
这样,就产生了g(k, tag)的递推公式,也举一个简单的例子
g(3, 'B') = g(2, 'B')exp(E-tran('B', 'B'))exp(E-emis(X, 'B')) + g(2, 'E')exp(E-tran('E', 'B'))exp(E-emis(X, 'B')) + g(2, 'M')exp(E-tran('M', 'B'))exp(E-emis(X, 'B')) + g(2, 'O')exp(E-tran('O', 'B'))exp(E-emis(X, 'B'))
在这种情况下,要计算全序列的exp分数和,就要先计算每个时间点上,以某个状态结束的全序列exp分数和,并向后递推
每次递推都要用前一个时间的全状态,组合后一个时间的每个状态,故递推一次时间复杂度为O(|T|^2)
总时间复杂度为O(n|T|^2),比起强行计算的O(|T|^n)大大减少
linear-CRF的数值优化
我们得到了重要的递推公式
g(k+1, Yk+1) = ∑(Yk) g(k, Yk)exp[E-tran(Yk, Yk+1)]exp[E-emis(X, Yk+1)]
但是,大量exp的相乘,可能导致浮点运算的数值不稳定,故在代码中,会采取一些方法优化数值稳定性
首先求和可以写成对向量的求和
sum(g(k, Yk+1)exp(E-tran('B', Yk+1))exp(E-emis(X, Yk+1)) + g(k, 'E')exp(E-tran('E', Yk+1))exp(E-emis(X, Yk+1)) + g(k, 'M')exp(E-tran('M', Yk+1))exp(E-emis(X, Yk+1)) + g(k, 'O')exp(E-tran('O', Yk+1))exp(E-emis(X, Yk+1)))
进一步,调研向量中的每个分量,以第二个为例
g(k, Yk+1)exp(E-tran('B', Yk+1))exp(E-emis(X, Yk+1))
= exp(log(g(k, 'E')exp(E-tran('E', Yk+1))exp(E-emis(X, Yk+1)))) #先log后exp,数值还是一样的
= exp(log(g(k, 'E')) + E-tran('E', Yk+1) + E-emis(X, Yk+1)) #log掉后两项自带的exp,直接可以用分数相加
这个时候,可以发现原来乘积的第一项,已经由g函数变成了log g函数,但等式左边还是g函数
如果想要保持两边一致,不妨左侧也加上log,就会变成
log g(k+1, Yk+1) = log(sum(exp(vector))),其中vector = (log g(k, Yk+1)+E-tran('E', Yk+1) + E-emis(X, Yk+1))
右侧这个log(sum(exp(xxx)))的计算,在pytorch里有API,直接可以看torch.logsumexp的文档
实际上,这里都还没有讲到真正数值稳定计算的部分,只是做了恒等变换,变成方便观察的形式,
对于logsumexp的数值稳定计算,假设其内部的vector为(v1, v2, ...vn),且max(v1, v2, ...vn) = vmax,可进行如下变形
logsumexp([v1, v2, ..., vn])
= log(sum(exp([v1-vmax+vmax, v2-vmax+vmax, ..., vn-vmax+vmax])))
= log(sum([exp(vmax)exp(v1-vmax), exp(vmax)exp(v2-vmax), ... , exp(vmax)exp(vn-vmax)]))
= log(exp(vmax) * sum([exp(v1-vmax), exp(v2-vmax), ..., exp(vn-vmax)]))
= vmax + log(sum(exp[v1-vmax, v2-vmax, ..., vn-vmax]))
这样子,(v1, v2, ..., vn)中的最大分量vmax将会被直接提出来,不进行一遍exp后再log的计算,
其余的较小值进行exp的计算,使得数值偏差尽可能小,达到数值稳定的效果
linear-CRF的学习
在此前linear-CRF的计算过程中,我们可以使用递推公式(状态转移方程),使得计算指定序列P(Y | X)的时间复杂度降为O(nT^2)
但是,使用递推公式要求E-tran和E-emis函数是已知的
如果仅有观测数据集(X, Y)对,却不值得E-tran和E-emis,应如何计算E-tran和E-emis呢
实际上,当知道确切E-tran、E-emis时,计算出来的exp(score('BMEOBMEO', X)) / ∑Y exp(score(Y), X)可以被认为是条件概率
而不知道E-tran、E-emis时,根据初始化参数计算得的exp(score('BMEOBMEO', X)) / ∑Y exp(score(Y), X)则可以被认为是似然
求解E-tran、E-emis的过程,即对具体的数据集(X, Y)求使得exp(score(Y, X)) / ∑Y' exp(score(Y'), X)最大的E-train、E-emis参数
其实就是最大似然,可以使用梯度下降等优化方法求解。实际操作中,会去最小化负对数似然,即,
minimize log(∑Y' exp(score(Y'), X)) - score(Y, X)
那具体的参数应该如何设置呢,按照流行的LSTM + linear-CRF的方法,
E-tran被直接设置为一个 T * T 的参数矩阵,矩阵代表了序列状态间的转移分数
E-emis则不直接设置为参数,而是由LSTM + dense_layer把X映射成一个feature,
feature的维度是 n * T,第一个维度代表时间(序列长度),第二维度代表每个时间状态上都有T个发射分数,
这T个发射分数则对应每个时间段的E-emis
即E-emis(Yk, X) = E-emis(Yk, feature) = feature[k, tags.index(Yk)]
这样,E-emis的参数便被暗含在LSTM + dense_layer的参数中
LSTM + linear-CRF的动机
既然如此,那么可能出现疑问,为什么E-tran可以单独设置,E-emis却不单独设置呢?
换句话说,既然单纯的linear-CRF就能完整解决一个序列预测的问题,为什么还要在前面加一个LSTM呢?
这个问题其实又可以反过来问,既然单纯的LSTM已经可以完整解决一个序列预测的问题,为什么还要在后面加一个CRF呢?
实际上,这两种方法的能力各有侧重,有刚刚好可以互补,所以在序列预测中被放在一起,成为了非常流行的结构。
LSTM的优点是具有非常强大的特征提取能力,因为它可以使用许多非线性函数、上下文关系等,构建出高维又稠密的非线性特征,
如果不使用LSTM的特征提取,像E-trans一样,设置一个纯粹参数化的E-emis矩阵,
那这种矩阵相乘的特征提取是线性的,且同时还是稀疏的,也无法结合X序列的上下文关系,效果差距非常非常大。
LSTM虽然具有以上特点,但是如果仅仅使用它进行序列预测,它则缺少一个显式地对转移关系的建模。
很明显地,LSTM只能模拟发射函数,却不能模拟转移函数,它可以提取复杂的特征,但是有时会对不合理的局部过于宽容。
例如,BMEO分词标注中(可以是分词标注、实体标注,以分词为例),
B代表词语开始,E代表词语结束,M代表词语中部,O代表单字词,
逻辑上而言,一组距离最近的B和E的中间只能存在M,而像BB、MB这种局部结构,是绝对错误的,
但LSTM更关注总体的loss最小化,不显式地对邻近转移关系进行建模,所以总是免不了出现这样的局部小错
linear-CRF则带来了一个转移矩阵参数,能够很有效地解决这个问题。
这就是LSTM与linear-CRF配合使用的原因,一句话总结,LSTM负责特征提取,linear-CRF负责邻近转移关系管理。
但是写到这里还有一个有趣的思考:虽然linear-CRF要注重转移关系,
但是E-trans是否可以像E-emis一样,使用一个复杂网络来模拟,而不直接使用一个线性矩阵呢?
这个似乎是一个很有趣的话题,到时要去算算间复杂度是否能允许这种情况下的前向计算,说不定是一个很有趣的点?
linear-CRF的预测
在linear-CRF的计算问题中,刚刚已经讨论了两种,一个是概率计算问题,一个是参数学习问题
概率计算问题知道E-tran和E-emis,要计算P(Y | X),
参数学习问题知道一批(X, Y),希望参数估计E-tran,E-emis,
最后剩下一个预测问题,也称解码问题,模型训练好以后,得到了估计的E-tran,E-emis,此时来一个新的X,最可能的Y是什么?
即,预测问题,知道E-tran,E-emis,X,要计算argmaxY P(Y | X)
这个式子可以作一些简化,
argmaxY P(Y | X)
= argmaxY exp(score(Y, X)) / ∑Y exp(score(Y), X)
= argmaxY exp(score(Y, X)) #normalize项对Y的取值不影响
= argmaxY score(Y, X) #exp是单调函数
这三个问题,实际上跟HMM的三个问题也是完全对应的。
linear-CRF的预测问题,实际上和概率计算问题是极其类似的,
只不过前者是求最大的概率路线,后者是前全路线的概率和,
同样地,如果不使用动态规划方法,枚举的时间复杂度是O(T^n),使用后时间复杂度是O(nT^2)
然而最大概率路线的算法还有个独有的名字,也就是平常非常常见的viterbi算法233333
在前向概率计算时,我们定义的递推公式单元是
g(k, tag) = exp(Score(Y1:k-1Yk = tag, X))
思想是,只要知道了k时间点Yk为特定状态的全路线概率,就可以知道k+1时间点Yk+1位特定状态的全路线概率
同理,定义一个 h(k, tag) = argmax Y1:k-1 score(Y1:k-1Yk=tag, X)
思想是,只要知道了k时间点Yk为特定状态的最大概率路线,希望能够递推出k+1时间点Yk+1位特定状态的最大概率路线
假设,后者这个路线,在k时间点Yk要经过状态t,且k时间点Yk经过状态t的最大概率路线是Yt1:k-1,
那么后者的路线必定是Yt1:k-1,Yk=t,Yk+1,否则,不存在更大概率的路线。
h(k+1, Yk+1)
= argmax Y:k score(Y1:kYk+1, X)
= argmax Y:k score(Y1:kYkYk+1, X)
= argmax Y:k-1Yk score(Y1:k-1Yk, X) + E-tran(Yk, Yk+1) + E-emis(Yk, X)
= Y1:k-1argmaxYk (argmax Y:k-1 score(Y1:k-1Yk, X) + E-tran(Yk, Yk+1) + E-emis(Yk, X))
= Y1:k-1argmaxYk (h(k, Yk) + E-tran(Yk, Yk+1) + E-emis(Yk, X))
这样就可以求得最大的概率路线
LSTM + linear-CRF序列标注笔记的更多相关文章
- 基于CRF序列标注的中文依存句法分析器的Java实现
这是一个基于CRF的中文依存句法分析器,内部CRF模型的特征函数采用 双数组Trie树(DoubleArrayTrie)储存,解码采用特化的维特比后向算法.相较于<最大熵依存句法分析器的实现&g ...
- BI-LSTM-CRF在序列标注中的应用
1. 前言 在NLP中有几个经典的序列标注问题,词性标注(POS),chunking和命名实体识别(NER).序列标注器的输出可用于另外的应用程序.例如,可以利用在用户搜索查询上训练的命名实体识别器来 ...
- End to End Sequence Labeling via Bi-directional LSTM CNNs CRF
来看看今日头条首席科学家的论文: End-to-end Sequence Labeling via Bi-directional LSTM-CNNs-CRF 使用LSTM方法进行序列标注,完成大规模标 ...
- TensorFlow (RNN)深度学习 双向LSTM(BiLSTM)+CRF 实现 sequence labeling 序列标注问题 源码下载
http://blog.csdn.net/scotfield_msn/article/details/60339415 在TensorFlow (RNN)深度学习下 双向LSTM(BiLSTM)+CR ...
- LSTM+CRF进行序列标注
为什么使用LSTM+CRF进行序列标注 直接使用LSTM进行序列标注时只考虑了输入序列的信息,即单词信息,没有考虑输出信息,即标签信息,这样无法对标签信息进行建模,所以在LSTM的基础上引入一个标签转 ...
- HanLP《自然语言处理入门》笔记--6.条件随机场与序列标注
笔记转载于GitHub项目:https://github.com/NLP-LOVE/Introduction-NLP 6. 条件随机场与序列标注 本章介绍一种新的序列标注模型条件随机场.这种模型与感知 ...
- Bi-LSTM+CRF在文本序列标注中的应用
传统 CRF 中的输入 X 向量一般是 word 的 one-hot 形式,前面提到这种形式的输入损失了很多词语的语义信息.有了词嵌入方法之后,词向量形式的词表征一般效果比 one-hot 表示的特征 ...
- TensorFlow教程——Bi-LSTM+CRF进行序列标注(代码浅析)
https://blog.csdn.net/guolindonggld/article/details/79044574 Bi-LSTM 使用TensorFlow构建Bi-LSTM时经常是下面的代码: ...
- 序列标注(BiLSTM-CRF/Lattice LSTM)
前言 在三大特征提取器中,我们已经接触了LSTM/CNN/Transormer三种特征提取器,这一节我们将介绍如何使用BiLSTM实现序列标注中的命名实体识别任务,以及Lattice-LSTM的模型原 ...
随机推荐
- Android目录结构(详解)
Android目录结构(详解) 下面是HelloAndroid项目在eclipse中的目录层次结构: 由上图可以看出项目的根目录下共有九个文件(夹),下面就这九个文件(夹)进行详解: 1.1src文件 ...
- HDU-1040-As Easy As A+B(各种排序)
希尔排序 Accepted 1040 0MS 1224K 564 B G++ #include "cstdio" using namespace std; ]; int main( ...
- js页面--年份自动增加
) { document.write("-" + new Date().getFullYear()); }</script> // 大于2017年自动加上 - 年份
- Java IO: 字节和字符数组
原文链接 作者: Jakob Jenkov 译者:homesick 内容列表 从InputStream或者Reader中读入数组 从OutputStream或者Writer中写数组 在java中 ...
- 一下午简单写个搭建Flutter开发环境,dome跑起来!
1.下载flutter包由于需要翻墙,国内下载会出现问题,所有需要先配置一下用户环境变量. export PUB_HOSTED_URL=https://pub.flutter-io.cn export ...
- Django ORM必会13条之外的查询方法
基于双下划线的查询 # 价格 大于 小于 大于等于 小于等于 filter(price__gt=') # 筛选出大于90 filter(price__lt=') # 筛选出小于90 filter(pr ...
- 从ArrayList的优化中想到的
在JDK7中ArrayList有一个小的改动,使用延迟加载的思想,默认构造函数不再初始化生成一个大小为10的数组,而是将elementData先赋值为一个共享的空数组. package java.ut ...
- GNS3(1)——OSPF多区域配置
GNS3(1)——OSPF多区域配置 RIP适用于中小网络,比较简单.没有系统内外.系统分区,边界等概念,用到不是分类的路由. OSPF适用于较大规模网络.它把自治系统分成若干个区域,通过系列内外路由 ...
- js中的函数应用
js中的函数应用 什么是函数,函数的概念 函数就像一个黑匣子,里面的东西你都不知道,但是你提供一些材料放进去,他可以制造出你需要的东西; 可以让多个一样的功能封装组合起来,然后想执行几次就执行几次 函 ...
- Python使用input方法输入字母显示NameError
如图,每次用input方法,输入数字正常,但是输入字母就会报错. 到网上查找资料之后,明白了原来在python2.7中应该用raw_input. 修改之后,代码就正常了.