请选择 进入手机版 | 继续访问电脑版
从零开始 从零开始 查看内容

完全领会 BiLSTM 和 CRF 算法

2020-6-14 22:03| 发布者: 红颜未知己| 查看: 118| 评论: 0

摘要: CRF 是一种常用的序列标注算法,可用于词性标注,分词,命名实体识别等任务。BiLSTM+CRF 是目前比较流行的序列标注算法,其将 BiLSTM 和 CRF 结合在一起,使模型即可以像 CRF 一样考虑序列前后之间的关联性,又可以 ...

CRF 是一种常用的序列标注算法,可用于词性标注,分词,命名实体识别等使命。BiLSTM+CRF 是今朝比力风行的序列标注算法,其将 BiLSTM 和 CRF 连系在一路,使模子即可以像 CRF 一样斟酌序列前后之间的关联性,又可以具有 LSTM 的特征抽取及拟合才能。

1.前言


在之前的文章《CRF 条件随机场》中,先容了条件随机场 CRF,描写了 CRF 和 LSTM 的区分。我们以分词为例,每个字对应的标签可所以 s, b, m, e 四种。

给定一个句子 "什么是地摊经济",其正确的分词方式是 "什么 / 是 / 地摊 / 经济",每个字对应的分词标签是 "be / s / be / be"。从下面的图片可以看出 LSTM 在做序列标注时的题目。

完全领会 BiLSTM 和 CRF 算法__2020-6-14 22:03发布_从零开始_118

BiLSTM 分词

BiLSTM 可以猜测出每一个字属于分歧标签的几率,然后利用 Softmax 获得几率最大的标签,作为该位置的猜测值。这样在猜测的时辰会疏忽了标签之间的关联性,如上图中 BiLSTM 把第一个词猜测成 s,把第二个词猜测成 e。可是现实上在分词时 s 前面是不会出现 e 的,是以 BiLSTM 没有斟酌标签间联系。

是以 BiLSTM+CRF 在 BiLSTM 的输出层加上一个 CRF,使得模子可以斟酌类标之间的相关性,标签之间的相关性就是 CRF 中的转移矩阵,暗示从一个状态转移到另一个状态的几率。假定 CRF 的转移矩阵以下图所示。

完全领会 BiLSTM 和 CRF 算法__2020-6-14 22:03发布_从零开始_118

CRF 状态转移矩阵

则对于前两个字 "什么",其标签为 "se" 的几率 =0.8×0×0.7=0,而标签为 "be" 的几率=0.6×0.5×0.7=0.21。

是以,BiLSTM+CRF 斟酌的是全部类标途径的几率而不但仅是单个类标的几率,在 BiLSTM 输出层加上 CRF 后,以下所示。

完全领会 BiLSTM 和 CRF 算法__2020-6-14 22:03发布_从零开始_118

BiLSTM+CRF 分词

终极算得一切途径中,besbebe 的几率最大,是以猜测成果为 besbebe。

2.BiLSTM+CRF 模子


CRF 包括两种特征函数,不熟悉的童鞋可以看下之前的文章。第一种特征函数是状态特征函数,也称为发射几率,暗示字 x 对应标签 y 的几率。

完全领会 BiLSTM 和 CRF 算法__2020-6-14 22:03发布_从零开始_118

CRF 状态特征函数

在 BiLSTM+CRF 中,这一个特征函数 (发射几率) 间接利用 LSTM 的输出计较获得,如第一小节中的图所示,LSTM 可以计较出每一时辰位置对应分歧标签的几率。

CRF 的第二个特征函数是状态转移特征函数,暗示从一个状态 y1 转移到另一个状态 y2 的几率。

完全领会 BiLSTM 和 CRF 算法__2020-6-14 22:03发布_从零开始_118

CRF 状态转移特征函数

CRF 的状态转移特征函数可以用一个状态转移矩阵暗示,在练习时需要调剂状态转移矩阵的元素值。是以 BiLSTM+CRF 需要在 BiLSTM 的模子内增加一个状态转移矩阵。在代码中以下。
class BiLSTM_CRF(nn.Module):
def __init__(self, vocab_size, tag2idx, embedding_dim, hidden_dim):
self.word_embeds = nn.Embedding(vocab_size, embedding_dim)
       self.lstm = nn.LSTM(embedding_dim, hidden_dim // 2,
                           num_layers=1, bidirectional=True)

       # 对应 CRF 的发射几率,即每一个位置对应分歧类标的几率
       self.hidden2tag = nn.Linear(hidden_dim, self.tagset_size)

       # 转移矩阵,维度即是标签数目,暗示从一个标签转移到另一标签的几率
       self.transitions = nn.Parameter(
           torch.randn(len(tag2idx), len(tag2idx))

给定句子 x,其标签序列为 y 的几率用下面的公式计较。

完全领会 BiLSTM 和 CRF 算法__2020-6-14 22:03发布_从零开始_118

p(y|x)

公式中的 score 用下面的式子计较,其中 Emit 对应发射几率 (即 LSTM 输出的几率),而 Trans 对应了转移几率 (即 CRF 转移矩阵对应的数值)

完全领会 BiLSTM 和 CRF 算法__2020-6-14 22:03发布_从零开始_118

score 的计较公式

BiLSTM+CRF 采用最大似然法练习,对应的损失函数以下:

完全领会 BiLSTM 和 CRF 算法__2020-6-14 22:03发布_从零开始_118

损失函数

其中 score(x,y) 比力轻易计较,而 Z(x) 是一切标签序列 (y) 打分的指数之和,假如序列的长度是 l,标签个数是 k,则序列的数目为 (k^l)。没法间接计较,是以要用前向算法停止计较。

用今朝支流的深度进修框架,对 loss 停止求导和梯度下降,即可优化 BiLSTM+CRF。练习好模子以后可以采用 viterbi 算法 (静态计划) 找出最优的途径。

3.损失函数计较


计较 BiLSTM+CRF 损失函数的难点在于计较 log Z(x),用 F 暗示 log Z(x),以下公式所示。

完全领会 BiLSTM 和 CRF 算法__2020-6-14 22:03发布_从零开始_118

我们将 score 拆分,酿成发射几率 p 和转移几率 T 的和。为了简化题目,我们假定序列的长度为3,则可以别离计较写出长度为 1、2、3 时辰的 log Z 值,以下所示。

完全领会 BiLSTM 和 CRF 算法__2020-6-14 22:03发布_从零开始_118

上式中 p 暗示发射几率,T 暗示转移几率,Start 暗示起头,End 暗示句子竣事。F(3) 即是终极获得的 log Z(x) 值。经过对上式停止变更,可以将 F(3) 转成递归的形式,以下。

完全领会 BiLSTM 和 CRF 算法__2020-6-14 22:03发布_从零开始_118

可以看到上式中每一步的操纵都是一样的,操纵包括 log_sum_exp,例如 F(1):
  • 首先需要计较 exp,对于一切 y1,计较 exp(p(y1)+T(Start,y1))
  • 求和,对上一步获得的 exp 值停止求和
  • 求 log,对求和的成果计较 log

是以可以写出前向算法计较 log Z 的代码,以下所示:
def forward_algorithm(self, probs):
   def forward_algorithm(probs):
   """
  probs: LSTM 输出的几率值,尺寸为 [seq_len, num_tags],num_tags 是标签的个数
  """

   # forward_var (可以了解为文章中的 F) 保存前一时辰的值,是一个向量,维度即是 num_tags
   # 初始时只要 Start 为 0,其他的都取一个很小的值 (-10000.)
   forward_var = torch.full((1, num_tags), -10000.0)  # [1, num_tags]
   forward_var[0][Start] = 0.0

   for p in probs:  # probs [seq_len, num_tags],遍历序列
       alphas_t = []  # alphas_t 保存下一时辰取分歧标签的积累几率值
       for next_tag in range(num_tags): # 遍历标签

           # 下一时辰发射 next_tag 的几率
           emit_score = p[next_tag].view(1, -1).expand(1, num_tags)

           # 从一切标签转移到 next_tag 的几率, transitions 是一个矩阵,长宽都是 num_tags
           trans_score = transitions[next_tag].view(1, -1)

           # next_tag_ver = F(i-1) + p + T
           next_tag_var = forward_var + trans_score + emit_score

           alphas_t.append(log_sum_exp(next_tag_var).view(1))

       forward_var = torch.cat(alphas_t).view(1, -1)

   terminal_var = forward_var + self.transitions[Stop] # 最初转移到 Stop 暗示句子竣事
   alpha = log_sum_exp(terminal_var)
   return alpha

4.viterbi 算法解码


练习好模子后,猜测进程需要用 viterbi 算法对序列停止解码,感爱好的童鞋可以参看《统计进修方式》。下面先容一下 viterbi 的公式,首先是一些标记的意义,以下:

完全领会 BiLSTM 和 CRF 算法__2020-6-14 22:03发布_从零开始_118

然后可以获得 viterbi 算法的递推公式

完全领会 BiLSTM 和 CRF 算法__2020-6-14 22:03发布_从零开始_118

终极可以按照 viterbi 计较获得的值,往前查找最合适的序列

完全领会 BiLSTM 和 CRF 算法__2020-6-14 22:03发布_从零开始_118

最初保举大师阅读 pytorch 官网的 BiLSTM+CRF 代码,经过代码更轻易了解。

5.参考文献


ADVANCED: MAKING DYNAMIC DECISIONS AND THE BI-LSTM CRF

鲜花

握手

雷人

路过

鸡蛋
  • 联系我们
  • 邮箱:admin@c0ks.com(请把#改成@)
  • 电话:18530790808
  • QQ客服 1031180668
  • 工作时间:周一至周五(早上9点至下午5点)
  • 微信二维码

  • 扫描访问手机版

Archiver|手机版|小黑屋|从零开始

GMT+8, 2020-7-4 07:27 , Processed in 0.120590 second(s), 17 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

  • QQ: 1031180668

    客服电话

    18530790808

    电子邮件

    admin@c0ks.com

    在线时间:8:00-16:00