zoukankan      html  css  js  c++  java
  • 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))

    这样就可以求得最大的概率路线

  • 相关阅读:
    Windows XP下 Android开发环境 搭建
    Android程序的入口点
    在eclipse里 新建android项目时 提示找不到proguard.cfg
    64位WIN7系统 下 搭建Android开发环境
    在eclipse里 新建android项目时 提示找不到proguard.cfg
    This Android SDK requires Android Developer Toolkit version 20.0.0 or above
    This Android SDK requires Android Developer Toolkit version 20.0.0 or above
    Android requires compiler compliance level 5.0 or 6.0. Found '1.4' instead
    Windows XP下 Android开发环境 搭建
    Android程序的入口点
  • 原文地址:https://www.cnblogs.com/catnip/p/10190979.html
Copyright © 2011-2022 走看看