BiLSTM, CRF,BiLSTM+CRF原理讲解以及viterbi算法python实现

1 训练数据

中文分词,基于模型识别方法,主要是通过识别每个字的label,然后进行切词,其中字的label一般有如下4种标识:

BMES
词的开头词的中间词的结尾单字的词

有了这些label,就可以进行切词了,从B开始直到E的片段就是一个词的切分。分词任务是一个有监督的学习任务,需要提供有标签的训练数据,所以训练数据需要对每个字打上一个label,例如 “宽松连衣裙红”对应的分词为“宽松,连衣裙,红",则对应的label如下:

BEBMES

有了这样的带标签的数据,我们就可以训练模型了。

2 模型结构

2.1 BiLSTM

BiLSTM是RNN序列模型中较常见的一种结构,由于能够学习序列关系,在序列任务中比如标注等经常使用。接下来我们来看下BiLSTM模型如何训练一个分词模型。

2.1.1 结构

基于BiLSTM模型做分词任务,本质上就是对每个字进行分类,label为"B",“M”,“E”,“S”共4种label。如下图所示:
在这里插入图片描述
输入每个字的embedding,经过一个双向的LSTM,然后将前向和后向提取的字特征向量concat到一起作为最终的字向量特征,最后输入到包含4个类别的分类层,softmax后得到每个label的分值,其中分值最大的就是该字的label,用交叉熵作为loss,梯度下降方法更新整个模型参数。

2.1.2 预测

在预测阶段,模型对输入的每个字在4个label上预测一个概率分值,其中最大的一个概率分值对应的label就是当前字的label’,比如“姚明进入总决赛”预测每个字对应的label为“BEBEBME”,一个词是从"B"开头,"E"结尾,也就是B的位置是一个词的开始索引,E的位置时一个词的结束索引,根据这样的规则,我们得到分词结果:“姚明,进入,总决赛”。

2.1.3 缺点

BiLSTM模型做分词有什么问题呢?没有利用label与label之间的关系信息,在分词任务中,一个序列的label之间是有约束关系的,比如给出这样一组预测结果“BEBBME”,我们马上知道连续的两个B是不合理的,因为B后面只能够接M或者E。而BiLSTM模型在预测每个字的label时候并没有考虑label与label之间的约束关系(预测当前字的label只用到了上一个字和下一个字的文本特征,并没有用到字的label信息,若把上一个字的label也作为预测当前字的一个特征输入,有可能有缓解),这样会导致最终预测的序列label结果有可能是不合法的。

2.2 CRF

2.2.1 基础概念

基于BiLSTM做分词任务,任务模式如下:
在这里插入图片描述
对每个输入预测各自的label, p ( y i ∣ x i ) p(y_i|x_i) p(yixi),但是我们需要预测的是给定一个序列输入预测一个序列输出, p ( y 1 , y 2 , . . . , y t ∣ x 1 , x 2 , . . . x t ) p(y_1,y_2,...,y_t|x_1,x_2,...x_t) p(y1,y2,...,ytx1,x2,...xt),模式如下图所示:
在这里插入图片描述
给定输入 X = x 1 , x 2 , . . . , x t X={x_1,x_2,...,x_t} X=x1,x2,...,xt,我们需要预测 p ( Y ∣ X ) p(Y|X) p(YX),普通分类器概率分值计算结果如下:
p ( Y ∣ X ) = ∏ k p ( y k ∣ x k ) = ∏ k e x p ( U ( x k , y k ) ) / Z ( x k ) p(Y|X) = \prod_k p(y_k|x_k) = \prod_k exp(U(x_k, y_k))/Z(x_k) p(YX)=kp(ykxk)=kexp(U(xk,yk))/Z(xk)
= e x p ( ∑ k U ( x k , y k ) ) / ( ∏ k Z ( x k ) ) = exp(\sum_kU(x_k,y_k))/(\prod_kZ(x_k)) =exp(kU(xk,yk))/(kZ(xk))
其中 U ( x , y ) U(x,y) U(x,y)表示的unary分值,给定 x x x,对应的label y y y的分值。其中 Z ( x ) Z(x) Z(x)就是一个partition function,可以看做一个归一化因子,目的是得到一个概率分布,总和为1, Z ( x ) Z(x) Z(x)也可以看做是softmax函数的分母。
而通过建模label y k y_k yk y k + 1 y_{k+1} yk+1之间的关系,我们就创建了连续label之间的依赖关系,这就是 linear-chain CRF。在 U ( x , y ) U(x,y) U(x,y)上加上一项转移分值(transition score) T ( x , y ) T(x,y) T(x,y),表达公式如下:
p ( Y ∣ X ) = e x p ( ∑ k U ( x k , y k ) + ∑ k T ( y k − 1 , y k , x k ) ) / Z ( X ) p(Y|X) = exp(\sum_kU(x_k,y_k)+\sum_{k}T(y_{k-1},y_{k},x_k))/Z(X) p(YX)=exp(kU(xk,yk)+kT(yk1,yk,xk))/Z(X)
其中 T ( . ) T(.) T(.)表示的是第 i i i个label到第 j j j个label的转移概率。所以在学习CRF之前,我们先来熟悉如下几个基本概念:

  • 发射概率 (emissions or unary scores):对于输入 x k x_k xk,对应label 为 y k y_k yk的多大概率可能性。
  • 转移概率 (transition scores):对于label y k − 1 y_{k-1} yk1其后为label y k y_{k} yk多大概率可能性。
  • 归一化因子 (partition function):归一化因子,为了得到最后的概率分布。

若label数量有 ∣ y ∣ |y| y,输入序列长度为 L L L,则输出的序列情况有 ∣ y ∣ L |y|^L yL种可能,也就是计算归一化函数 Z ( X ) Z(X) Z(X)复杂度为 O ( ∣ y ∣ L ) O(|y|^L) O(yL),若 L L L较大的情况下,将会十分耗时。所以,接下来我们就围绕以下几个问题一步步分析CRF训练和预测过程:
第一个问题: U ( x i , y i ) U(x_i,y_i) U(xi,yi) T ( y i , y i + 1 ) T(y_i, y_{i+1}) T(yi,yi+1)特征函数如何计算
第二个问题:如何计算 Z ( X ) Z(X) Z(X)
第三个问题:如何定义CRF模型损失函数
第四个问题:CRF如何进行预测

2.2.2 如何计算特征函数

我们先来了解下CRF中的特征函数,特征函数定义如下:
p ( Y ∣ X ) = 1 Z ( x ) e x p ( ∑ i , k λ k t k ( y i , y i + 1 , x , i ) + ∑ i , l u l s l ( y i , x , i ) ) p(Y|X) = \frac{1}{Z(x)}exp(\sum_{i,k}\lambda_kt_k(y_i, y_{i+1}, x, i)+\sum_{i,l}u_ls_l(y_i,x,i)) p(YX)=Z(x)1exp(i,kλktk(yi,yi+1,x,i)+i,lulsl(yi,x,i))
其中, t k ( . ) , s l ( . ) t_k(.), s_l(.) tk(.),sl(.)分别表示转移特征函数和状态特征函数,是二值函数,当条件满足时函数值为1否则为0
t k ( y i − 1 , y i , x , i ) = { 1 , 条 件 0 , 其 它 t_k(y_{i-1}, y_i, x, i) = \begin{cases} 1,条件\\\\ 0 , 其它 \end{cases} tk(yi1,yi,x,i)=10

s l ( y i , x , i ) = { 1 , 条 件 0 , 其 它 s_l( y_i, x, i) = \begin{cases} 1,条件\\\\ 0 , 其它 \end{cases} sl(yi,x,i)=10

那这种特征函数又是怎么定义的呢?我们用crf++里定义特征模板进行一个简单说明。假设我们输入数据如下:
在这里插入图片描述
第一列是原始token,第二列是词性特征,第三例是label标签。在crf++中,模板设置如下:
在这里插入图片描述
其中Unigram定义的模板就是公式对应的状态特征函数,Bigram定义的就是转移特征函数,其中每一行代表一个模板特征,“U00:%x[0,0]”表示的是当前位置,假设是“the”,则“U12:%x[0,1]”表示的相对[0,0]的位移,对应的是“DT”。
给定了这些特征模板,那么征函数怎么生成呢?假设数据的所有token数量有 N N N个,label标签有 L L L个,在Unigram模板中,例如“U12:%x[0,1]”就会生成 N × L N \times L N×L个特征,对于每个token在每个label上是否满足条件,函数如下形式:

函数权重参数
func1 = if (output=B-NP and feature=“U12:PRP”) return 1 else return 0 λ 1 \lambda_1 λ1
func2 = if (output=B-VP and feature=“U12:PRP”) return 1 else return 0 λ 2 \lambda_2 λ2
func3 = if (output=I-NP and feature=“U12:PRP”) return 1 else return 0 λ 3 \lambda_3 λ3
funcxx = if (output=B-NP and feature=“U12:NN”) return 1 else return 0 λ i \lambda_i λi
funcxx = if (output=B-VP and feature=“U12:NN”) return 1 else return 0 λ i + 1 \lambda_{i+1} λi+1

每个模板都有对应生成的特征函数,每个函数都有对应的参数权重,每个参数 λ i \lambda_i λi权重就是crf模型需要学习的权重
在Bigram里,只有一个模板B,表示的是前一个输出label和当前输出label的组合关系,生成的特征函数 L × L × N L \times L \times N L×L×N,特征函数如下:

函数权重参数
func1 = if (pre_output=B-NP and output=B-NP and feature=“B:He”) return 1 else return 0 u 1 u_1 u1
func2 = if (pre_output=B-VP and output=B-NP and feature=“U12:He”) return 1 else return 0 u 2 u_2 u2
func3 = if (pre_output=I-NP and output=B-NP and feature=“U12:He”) return 1 else return 0 u 3 u_3 u3

同样在状态转移函数里,每个函数有对应的参数权重 u i u_i ui,这些参数权重是crf模型需要学习的。为简单起见,我们把转移特征函数和状态特征函数用一个公式表示,crf的公式如下:
p ( y ∣ x ) = 1 Z ( x ) e x p ∑ j = 1 n ∑ i = 1 m λ i f i ( y j − 1 , y j , x , j ) p(y|x) = \frac{1}{Z(x)}exp\sum_{j=1}^n\sum_{i=1}^m\lambda_if_i(y_{j-1},y_j,x,j) p(yx)=Z(x)1expj=1ni=1mλifi(yj1,yj,x,j)
其中 Z ( x ) = ∑ y e x p ( ∑ j = 1 n ∑ i = 1 m λ i f i ( y j − 1 , y j , x , j ) ) Z(x) = \sum_y exp(\sum_{j=1}^n\sum_{i=1}^m\lambda_if_i(y_{j-1},y_j,x,j)) Z(x)=yexp(j=1ni=1mλifi(yj1,yj,x,j))是归一化因子,是所有输出序列标签结果的分值相加。其中 ∑ j = 1 n \sum_{j=1}^n j=1n是对序列长度遍历 ∑ i = 1 m \sum_{i=1}^m i=1m是对特征总数量遍历,特征函数 f i ( y j − 1 , y j , x , j ) f_i(y_{j-1},y_j,x,j) fi(yj1,yj,x,j)我们可以自己定义,其值为0或者1,每个特征函数对应一个参数权重 λ i \lambda_i λi,这个参数权重就是crf模型基于训练语料需要学习的。
在语料中经常出现的模式对应的特征函数,会越多频率去更新参数,进而学习的参数权重也会越大。crf模型的整个表现主要依赖两点:第一,训练语料的质量和数量;第二:特征的设计,越能够提取对于任务有区别特征,模型的准确率就会越高。

2.2.3 如何求解归一化因子 Z ( x ) Z(x) Z(x)

从上面公式我们知道,归一化因子 Z ( x ) Z(x) Z(x)是对所有可能的路径打分,并求指数和,假设label数量为 k k k,序列长度为 n n n,则有 k n k^n kn条路径数,因此直接算是不可能的。如下图所示:
在这里插入图片描述
假设输入序列是“兔子喜欢吃菜”,对应的label序列为“b->e->b->e->s->s“:
在这里插入图片描述
从crf定义的公式可知,目标序列的概率分值计算为:
p ( y ∣ x ) = 1 Z ( x ) e s ( 兔 , B ) + t ( B , E ) + s ( 子 , E ) + t ( E , B ) + s ( 喜 , B ) + t ( B , E ) + s ( 欢 , E ) + t ( E , S ) + s ( 吃 , s ) + t ( S , S ) + s ( 菜 , s ) p(y|x) = \frac{1}{Z(x)}e^{s(兔,B)+t(B,E)+s(子,E)+t(E,B)+s(喜,B)+t(B,E)+s(欢,E)+t(E,S)+s(吃,s)+t(S,S)+s(菜,s)} p(yx)=Z(x)1es(,B)+t(B,E)+s(,E)+t(E,B)+s(,B)+t(B,E)+s(,E)+t(E,S)+s(,s)+t(S,S)+s(,s)
分子的指数项主要是发射概率分值加上转移概率分值,而在crf++中,根据定义的特征函数(函数值为0或者1),每个特征函数有对应的权重参数(模型需要学习的参数)进行加权相加。分子是很容易求得,而分母 Z ( x ) Z(x) Z(x)归一化因子路径数量是指数级别的,所以不能直接计算。那该怎么计算?
通过递归计算归一化因子
首先,我们将计算到t时刻的归一化因子记为 Z t Z_t Zt,并将他分为k个部分:
Z t = Z t ( 1 ) + Z t ( 2 ) + . . . + Z t ( k ) Z_t = Z_t^{(1)}+Z_t^{(2)}+...+Z_t^{(k)} Zt=Zt(1)+Zt(2)+...+Zt(k)
其中 Z t ( 1 ) , . . . , Z t ( k ) Z_t^{(1)}, ..., Z_t^{(k)} Zt(1),...,Zt(k)分别表示到当前时刻t中,以标签1,…,k为终点的所有路径的得分指数和。那么我们可以递归的计算:

Z t + 1 ( 1 ) = ( Z t ( 1 ) T 11 + Z t ( 2 ) T 21 + . . . + Z t ( k ) T k 1 ) s t + 1 ( 1 ∣ x ) Z_{t+1}^{(1)} = (Z_t^{(1)}T_{11}+Z_t^{(2)}T_{21}+...+Z_t^{(k)}T_{k1})s_{t+1}(1|x) Zt+1(1)=(Zt(1)T11+Zt(2)T21+...+Zt(k)Tk1)st+1(1x)
Z t + 1 ( 2 ) = ( Z t ( 1 ) T 12 + Z t ( 2 ) T 22 + . . . + Z t ( k ) T k 2 ) s t + 1 ( 2 ∣ x ) Z_{t+1}^{(2)} = (Z_t^{(1)}T_{12}+Z_t^{(2)}T_{22}+...+Z_t^{(k)}T_{k2})s_{t+1}(2|x) Zt+1(2)=(Zt(1)T12+Zt(2)T22+...+Zt(k)Tk2)st+1(2x)

Z t + 1 ( 1 ) = ( Z t ( k ) T 1 k + Z t ( 2 ) T 2 k + . . . + Z t ( k ) T k k ) s t + 1 ( k ∣ x ) Z_{t+1}^{(1)} = (Z_t^{(k)}T_{1k}+Z_t^{(2)}T_{2k}+...+Z_t^{(k)}T_{kk})s_{t+1}(k|x) Zt+1(1)=(Zt(k)T1k+Zt(2)T2k+...+Zt(k)Tkk)st+1(kx)

其中 T ( i , j ) T(i, j) T(i,j)元素表示的是label y i y_i yi到label y j y_j yj的转移取指数后的分值,而 s t + 1 ( i ∣ x ) s_{t+1}(i|x) st+1(ix)表示的是对label标签为 i i i的发射分值。上述可以简写为矩阵乘法形式如下:
Z t + 1 = Z t T ⊗ S ( y t + 1 ∣ x ) Z_{t+1} = Z_tT \otimes S(y_{t+1}|x) Zt+1=ZtTS(yt+1x)
其中 S ( y t + 1 ∣ x ) S(y_{t+1}|x) S(yt+1x)表示的是在位置t+1时刻,对每个label标签打分的指数值。

2.2.4 如何定义crf损失函数

训练crf模型,我们用负对数似然函数为损失函数,如:
L ( θ ) = − l o g P ( y ∣ x ) L(\theta)=-logP(y|x) L(θ)=logP(yx)
= − l o g ( e x p ( ∑ j = 1 n ∑ i = 1 m λ i f i ( y j − 1 , y j , x , j ) ) ∑ y e x p ( ∑ j = 1 n ∑ i = 1 m λ i f i ( y j − 1 , y j , x , j ) ) ) =-log(\frac{exp(\sum_{j=1}^n\sum_{i=1}^m \lambda_i f_i(y_{j-1},y_j,x,j))}{\sum_yexp(\sum_{j=1}^n\sum_{i=1}^m \lambda_i f_i(y_{j-1},y_j,x,j))}) =log(yexp(j=1ni=1mλifi(yj1,yj,x,j))exp(j=1ni=1mλifi(yj1,yj,x,j)))
= − ∑ j = 1 n ∑ i = 1 m λ i f i ( y j − 1 , y j , x , j ) + l o g ( ∑ y e x p ( ∑ j = 1 n ∑ i = 1 m λ i f i ( y j − 1 , y j , x , j ) ) ) =-\sum_{j=1}^n\sum_{i=1}^m\lambda_if_i(y_{j-1},y_j,x,j) + log(\sum_y exp(\sum_{j=1}^n\sum_{i=1}^m \lambda_i f_i(y_{j-1},y_j,x,j))) =j=1ni=1mλifi(yj1,yj,x,j)+log(yexp(j=1ni=1mλifi(yj1,yj,x,j)))
我们的优化目标是使得正确的label序列 P ( y 1 , . . . , y n ∣ x ) P(y_1,...,y_n|x) P(y1,...,ynx)概率值尽可能的大,则 − l o g P ( y 1 , . . . , y n ) -logP(y_1,...,y_n) logP(y1,...,yn)损失函数loss就会尽可能的小。
那如何求导参数 λ k \lambda_k λk梯度?
∂ L ( θ ) ∂ λ k = − ∑ j = 1 n f k ( y j − 1 , y j , x , j ) + 1 Z ( x ) ∂ Z ( x ) ∂ λ k \frac{ \partial L(\theta)}{\partial \lambda_k} = -\sum_{j=1}^nf_k(y_{j-1},y_j,x,j)+\frac{1}{Z(x)}\frac{\partial Z(x)}{\partial \lambda_k} λkL(θ)=j=1nfk(yj1,yj,x,j)+Z(x)1λkZ(x)
= − ∑ j = 1 n f k ( y j − 1 , y j , x , j ) + ∑ y p ( y ∣ x ) ∑ j = 1 n f k ( y j − 1 , y j , x , j ) =-\sum_{j=1}^nf_k(y_{j-1},y_j,x,j)+ \sum_y p(y|x)\sum_{j=1}^nf_k(y_{j-1},y_j,x,j) =j=1nfk(yj1,yj,x,j)+yp(yx)j=1nfk(yj1,yj,x,j)
其中,
1 Z ( x ) ∂ Z ( x ) ∂ λ k \frac{1}{Z(x)}\frac{\partial Z(x)}{\partial \lambda_k} Z(x)1λkZ(x)
= 1 Z ( x ) ∑ y e x p ( ∑ j = 1 n ∑ i = 1 m λ i f i ( y j − 1 , y j , x , j ) ) ⋅ ∑ j = 1 n f k ( y j − 1 , y j , x , j ) =\frac{1}{Z(x)}\sum_yexp(\sum_{j=1}^n\sum_{i=1}^m\lambda_if_i(y_{j-1},y_j,x,j))\cdot \sum_{j=1}^nf_k(y_{j-1},y_j,x,j) =Z(x)1yexp(j=1ni=1mλifi(yj1,yj,x,j))j=1nfk(yj1,yj,x,j)
= ∑ y 1 Z ( x ) e x p ( ∑ j = 1 n ∑ i = 1 m λ i f i ( y j − 1 , y j , x , j ) ) ⋅ ∑ j = 1 n f k ( y j − 1 , y j , x , j ) = \sum_y \frac{1}{Z(x)} exp(\sum_{j=1}^n\sum_{i=1}^m\lambda_if_i(y_{j-1},y_j,x,j))\cdot \sum_{j=1}^nf_k(y_{j-1},y_j,x,j) =yZ(x)1exp(j=1ni=1mλifi(yj1,yj,x,j))j=1nfk(yj1,yj,x,j)
= ∑ y p ( y ∣ x ) ∑ j = 1 n f k ( y j − 1 , y j , x , j ) = \sum_y p(y|x)\sum_{j=1}^nf_k(y_{j-1},y_j,x,j) =yp(yx)j=1nfk(yj1,yj,x,j)

最终,我们可以得到对参数 λ k \lambda_k λk的导数公式为:
∂ L ( θ ) ∂ λ k = ∑ y p ( y ∣ x ) ∑ j = 1 n f k ( y j − 1 , y j , x , j ) − ∑ j = 1 n f k ( y j − 1 , y j , x , j ) \frac{ \partial L(\theta)}{\partial \lambda_k} =\sum_y p(y|x)\sum_{j=1}^nf_k(y_{j-1},y_j,x,j)-\sum_{j=1}^nf_k(y_{j-1},y_j,x,j) λkL(θ)=yp(yx)j=1nfk(yj1,yj,x,j)j=1nfk(yj1,yj,x,j)
= E λ [ f i ] − E D [ f i ] =E_{\lambda}[f_i]-E_D[f_i] =Eλ[fi]ED[fi]
其中 E λ [ f i ] E_{\lambda}[f_i] Eλ[fi]表示的是模型对特征的期望,更直观的解释是,模型对每条路径预测的概率分值 p ( y ∣ x ) p(y|x) p(yx)与该条路径对应的在参数为 λ k \lambda_k λk对应的特征函数分值的加权之和 E D [ f i ] E_D[f_i] ED[fi]表示的是数据对特征的期望,也就是说在参数为 λ k \lambda_k λk对应的特征函数分值。从公式可以看出,当 E λ [ f i ] E_{\lambda}[f_i] Eλ[fi] E D [ f i ] E_D[f_i] ED[fi]越接近的时候,对应的导数越接近0,则更新参数不需调整,而当 E λ [ f i ] E_{\lambda}[f_i] Eλ[fi]> E D [ f i ] E_D[f_i] ED[fi]时,则梯度>0,模型的参数需要调小,相反,需要调大。整体就是希望模型对特征的期望与数据对特征的期望尽可能的一致,这样我们学出来的模型才能够尽可能的拟合我们训练的数据。

2.2.4 预测 (Viterbi)

crf训练完后,我们可以得到每个特征函数对应的参数权重,在分词任务中,我们对应的label是4种,对于一个6个字的输入,eg: “兔子喜欢吃菜”,穷举计算的话,我们需要计算 4 6 = 256 4^6=256 46=256条路径,而对应最大分值的路径就是我们的全局最优解。但如果输入序列长100,则需要计算 4 100 4^{100} 4100,复杂度太高。
有没有更高效的算法?答案肯定是有,这就是维特比 (viterbi) 算法,viterbi算法的思想就是基于每一步结果的局部最优去寻找全局最优,如下图所示:

在这里插入图片描述

我们要求从S (开始)到E (结束) 的分值最大路径,假设全局最优解是红色的路径“S->m1->b2->b3->E”,若穷举法,我们需要计算 4 3 = 64 4^3=64 43=64条路径,也就是复杂度为 O ( K N ) O(K^N) O(KN),其中 K K K表示label总数量,在这里等于4,而 N N N表示的输入序列长度,那基于viterbi算法,则时间复杂度降为 O ( N K 2 ) O(NK^2) O(NK2),当N很大,维特比算法大大降低了时间复杂度。
首先我们从图可知,全局分值最大路径“S->m1->b2->b3->E”,经过中间节点"m1",“b2”, "b3"也必是局部分值最大路径,也就是说路径"S->m1->b2"是S到b2的分值最大路径,这样我们就可以把问题分成一个更小的问题,通过局部的最优去寻找全局最优。每一层的节点都只记录到达该节点的分值最大路径,其余到达该节点的路径可以丢弃,减少计算量。计算步骤如下:

  • S到第一层分值最大路径计算:由于第一层的4个节点s1,b1,m1,e1,从S出发,只有一条路径可到达,所以每个节点保留的最优路径为"S->s1", “S->b1”, “S->m1”, “S->e1”
  • 第一层到第二层分值最大路径计算:第二层有4个节点s2,b2,m2,e2,对于s2节点,可以从s1,b1,m1,e1这4个节点经过,我们只需要保留到达s2节点分值最大的路径,计算量为 4 × 4 4\times4 4×4,同理计算其它节点
  • 第二层到第三层分值最大路径计算:第三层4个节点s3,b3,m3,e3,同上,到达每个节点路径有4条,只需保留分值最大路径,计算量为 4 × 4 4\times4 4×4
  • 第三层到E分值最大路径计算:计算到这来,我们已经得到到达s3,b3,m3,e3的最大分值路径(共4条),到达E, 只需要计算这4条路径分值分别加上s3,b3,m3,e3节点到E的分值,然后选取最大的分值路径就是我们的全局最优路径。

所以整个计算复杂度由 O ( K N ) O(K^N) O(KN)变为 O ( N K 2 ) O(NK^2) O(NK2),减少的计算量核心是两点:第一,全局分值最大解路径必定经过这些局部分值最大路径第二,通过将问题的规模缩小,把不是局部分值最大路径都可以丢弃

代码实现:

import numpy as np
import sys


def viterbi(score, transition):
	"""
		输入
		score: [labelnum, seqlen], 发射分值
		trasition: [labelnum, labelnum], 转移概率, 这里假设每一层的label i 到label j 的转移的分值一致
		输出
		decode: [seqlen], path索引
	"""
	labelnum,seqlen = score.shape
	# 记录每一层到达该节点的最大累积分值
	trellis = score.copy()
	# 记录到达当前节点的最大分值路径的上一个节点索引位置 
	backpointers = np.zeros((labelnum,seqlen),dtype=int)
	# 全局分值最大路径path对应的节点索引
	decode = [0]*seqlen
	for i in range(1,seqlen):
		#转移分值加上发射分值
		tran_score = transition+trellis[:,i-1:i]
		#当前层的节点最大分值更新
		trellis[:,i] += np.max(tran_score, axis=0)
		#记录到达当前节点最大路径分值的上一层节点位置
		backpointers[:,i] = np.argmax(tran_score,axis=0)
	#记录最后一层分值最大的节点索引位置
	idxpath = np.argmax(tran_score[:,seqlen-1])
	#存储
	decode[seqlen-1] = idxpath
	print(trellis)
	print(backpointers)
	#读取最后一层最大值位置索引内容(上一层的节点位置),依次回溯)
	for t in range(seqlen-1,0,-1):
		idxpath = backpointers[idxpath,t]
		decode[t-1] = idxpath
	
	return decode
	

if __name__ == '__main__':
	score = np.array([[0.9, 0.1, 0.3],
         	[0.1, 0.8, 0.4],
         	[0.0, 0.1, 0.3]])
	transition = np.array([[0.1, 0.4, 0.5], [0.2, 0.7, 0.1], [0.9, 0.0, 0.1]])
	print(viterbi(score,transition))

2.3 BiLSTM+CRF

上述介绍了BiLSTM和CRF分别是怎么做分词任务的,对于BiLSTM+CRF我们就容易理解多了,BiLSTM+CRF结合了BiLSTM模型较强的语义向量表征能力和CRF对label与label的转移概率学习的优点,进而提升整个模型的效果。

2.3.1 BiLSTM作用

我们知道,在crf中,目标序列概率分值简约计算公式如下:
p ( y ∣ x ) = e s ( x , y ) + t ( y , y ) Z ( x ) p(y|x) = \frac{e^{s(x,y)+t(y,y)}}{Z(x)} p(yx)=Z(x)es(x,y)+t(y,y)
其中 s ( x , y ) s(x,y) s(x,y)表示的是发射分值, t ( y , y ) t(y,y) t(y,y)表示的是转移分值。在BiLSTM+CRF中,BiLSTM作用是对每个输入,预测对应的label分值,作为crf中的发射概率分值,也就是 s ( x , y ) s(x,y) s(x,y)的分值由BiLSTM计算提供

2.3.2 CRF作用

而crf层只需要学习转移概率分值 t ( y , y ) t(y,y) t(y,y),也就是学习一个 n u m s _ l a b e l × n u m s _ l a b e l nums\_label \times nums\_label nums_label×nums_label大小的转移概率矩阵。有了发射概率分值和转移概率分值,我们就可以得到序列的概率 p ( y ∣ x ) p(y|x) p(yx)分值,loss用负对数似然函数, − l o g p ( y ∣ x ) -logp(y|x) logp(yx),这样就可以进行end-to-end的训练了。

2.3.2· 预测 (Viterbi)

BiLSTM模型对每个输入打分(图上的节点分值),crf提供label与label的转移分值(图的边分值),然后用viterbi算法,求解全局分值最大的路径。具体预测过程可参考上述的viterbi算法代码,其中score分值由BiLSTM对序列输入计算得到,transition转移矩阵分值是CRF训练得到的参数矩阵

  • 6
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值