pytorch实现BiLSTM+CRF用于NER(命名实体识别)
在写这篇博客之前,我看了网上关于pytorch,BiLstm+CRF的实现,都是一个版本(对pytorch教程的翻译),

翻译得一点质量都没有,还有一些竟然说做得是词性标注,B,I,O是词性标注的tag吗?真是误人子弟。所以

自己打算写一篇关于pytorch上实现命名实体识别的翻译,加入自己的理解。前面是一些牢骚话

BiLSTM
我上篇博客介绍了pytorch实现LSTM 链接,这里是BiLSTM,网络结构图如下

单向的LSTM,当前序列元素只能看见前面的元素,而无法看见后面的元素,双向LSTM克服了这个缺点,既能

看见前面的元素,也能看见后面的元素。学术一点的就是,单向LSTM无法编码从后往前的信息

注意一点双向LSTM的输出O OO是[Oleft O_{left}O
left

,Oright O_{right}O
right

],即size为(2, 隐藏单元数)

CRF
CRF是判别模型, 判别公式如下y yy是标记序列,x xx是单词序列,即已知单词序列,求最有可能的标记序列
P(y∣x)=exp(Score(x,y))∑y′exp(Score(x,y′)) P(y|x) = \frac{\exp{(\text{Score}(x, y)})}{\sum_{y'} \exp{(\text{Score}(x, y')})}
P(y∣x)=

y


exp(Score(x,y

))
exp(Score(x,y))

Score(x,y) Score(x,y)Score(x,y)即单词序列x xx产生标记序列y yy的得分,得分越高,说明其产生的概率越大。

在pytorch教程中链接,其用于实体识别定义的Score(x,y) Score(x,y)Score(x,y)包含两个特征函数,一个是转移特征函数

一个是状态特征函数
Score(x,y)=∑ilogψEMIT(yi→xi)+logψTRANS(yi−1→yi) {Score}(x,y) = \sum_i \log \psi_\text{EMIT}(y_i \rightarrow x_i) + \log \psi_\text{TRANS}(y_{i-1} \rightarrow y_i)
Score(x,y)=
i


logψ
EMIT

(y
i

→x
i

)+logψ
TRANS

(y
i−1

→y
i

)

代码中用到了前向算法和维特比算法,在代码中我会具体解释

log_sum_exp函数就是计算log∑ni=1exi log\sum^n_{i=1}{e^{x_{i}}}log∑
i=1
n

e
x
i

,前向算法需要用到这个函数

def log_sum_exp(vec):
max_score = vec[0, argmax(vec)]
max_score_broadcast = max_score.view(1, -1).expand(1, vec.size()[1])
return max_score + \
torch.log(torch.sum(torch.exp(vec - max_score_broadcast)))
1
2
3
4
5
前向算法,求出α \alphaα,即Z(x) Z(x)Z(x), 也就是∑y′exp(Score(x,y′)) {\sum_{y'} \exp{(\text{Score}(x, y')})}∑
y


exp(Score(x,y

)),如果不懂可以看一下李航的书关于CRF的前向算法

但是不同于李航书的是,代码中α \alphaα都取了对数,一个是为了运算方便,二个为了后面的最大似然估计。

这个代码里面没有进行优化,作者也指出来了,其实对feats的迭代完全没有必要用两次循环,其实矩阵相乘

就够了,作者是为了方便我们理解,所以细化了步骤

def _forward_alg(self, feats):

init_alphas = torch.full((1, self.tagset_size), -10000.)

#初始时,start位置为0,其他位置为-10000
init_alphas[0][self.tag_to_ix[START_TAG]] = 0.

#赋给变量方便后面反向传播
forward_var = init_alphas

for feat in feats:
alphas_t = []
for next_tag in range(self.tagset_size):
#状态特征函数的得分
emit_score = feat[next_tag].view(1, -1).expand(1, self.tagset_size)

#状态转移函数的得分
trans_score = self.transitions[next_tag].view(1, -1)

#从上一个单词的每个状态转移到next_tag状态的得分
#所以next_tag_var是一个大小为tag_size的数组
next_tag_var = forward_var + trans_score + emit_score

#对next_tag_var进行log_sum_exp操作
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[self.tag_to_ix[STOP_TAG]]
alpha = log_sum_exp(terminal_var)
return alpha
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
维特比算法中规中矩,可以参考李航书上条件随机场的预测算法

def _viterbi_decode(self, feats):
backpointers = []
#初始化
init_vvars = torch.full((1, self.tagset_size), -10000.)
init_vvars[0][self.tag_to_ix[START_TAG]] = 0

forward_var = init_vvars
for feat in feats:
#保持路径节点,用于重构最优路径
bptrs_t = []
#保持路径变量概率
viterbivars_t = []

for next_tag in range(self.tagset_size):

next_tag_var = forward_var + self.transitions[next_tag]
best_tag_id = argmax(next_tag_var)
bptrs_t.append(best_tag_id)
viterbivars_t.append(next_tag_var[0][best_tag_id].view(1))

forward_var = (torch.cat(viterbivars_t) + feat).view(1, -1)
backpointers.append(bptrs_t)

#转移到STOP_TAG
terminal_var = forward_var + self.transitions[self.tag_to_ix[STOP_TAG]]
best_tag_id = argmax(terminal_var)
path_score = terminal_var[0][best_tag_id]

#反向迭代求最优路径
best_path = [best_tag_id]
for bptrs_t in reversed(backpointers):
best_tag_id = bptrs_t[best_tag_id]
best_path.append(best_tag_id)

#把start_tag pop出来,最终的结果不需要
start = best_path.pop()
assert start == self.tag_to_ix[START_TAG]
best_path.reverse()
return path_score, best_path
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
其实我最想讲的是这个函数

def neg_log_likelihood(self, sentence, tags):
feats = self._get_lstm_features(sentence)
forward_score = self._forward_alg(feats)
gold_score = self._score_sentence(feats, tags)
return forward_score - gold_score
1
2
3
4
5
我们知道forward_score是logZ(x) logZ(x)logZ(x),即log∑y′exp(Score(x,y′)) log{\sum_{y'} \exp{(\text{Score}(x, y')})}log∑
y


exp(Score(x,y

)),

gold_score是logexp(Score(x,y′) log\exp{(\text{Score}(x, y')}logexp(Score(x,y

)

我们的目标是极大化
P(y∣x)=exp(Score(x,y))∑y′exp(Score(x,y′)) P(y|x) = \frac{\exp{(\text{Score}(x, y)})}{\sum_{y'} \exp{(\text{Score}(x, y')})}
P(y∣x)=

y


exp(Score(x,y

))
exp(Score(x,y))

两边取对数即
logP(y∣x)=log exp(Score(x,y))−log∑y′exp(Score(x,y′))logP(y∣x)=gold_score−forward_score logP(y|x) = log\ {\exp{(\text{Score}(x, y)})}-log{\sum_{y'} \exp{(\text{Score}(x, y')})} \\logP(y|x)=gold\_score-forward\_score
logP(y∣x)=log exp(Score(x,y))−log
y



exp(Score(x,y

))
logP(y∣x)=gold_score−forward_score

所以我们需要极大化gold_score−forward_score gold\_score - forward\_scoregold_score−forward_score,也就是极小化forward_score−gold_score forward\_score -gold\_scoreforward_score−gold_score

也就是为什么forward_score−gold_score forward\_score - gold\_scoreforward_score−gold_score可以作为loss的根本原因
---------------------
作者:zycxnanwang
来源:CSDN
原文:https://blog.csdn.net/zycxnanwang/article/details/90385259
版权声明:本文为博主原创文章,转载请附上博文链接!

pytorch实现BiLSTM+CRF用于NER(命名实体识别)的更多相关文章

  1. 基于条件随机场(CRF)的命名实体识别

    很久前做过一个命名实体识别的模块,现在有时间,记录一下. 一.要识别的对象 人名.地名.机构名 二.主要方法 1.使用CRF模型进行识别(识别对象都是最基础的序列,所以使用了好评率较高的序列识别算法C ...

  2. 基于 bi-LSTM和CRF的中文命名实体识别

    follow: https://github.com/zjy-ucas/ChineseNER  这里边主要识别的实体如图所示,其实也就主要识别人名PER,机构ORG和地点LOC: B表示开始的字节,I ...

  3. NER命名实体识别,实体级level的评估,精确率、召回率和F1值

    pre = "0 0 B_SONG I_SONG I_SONG 0 B_SONG I_SONG I_SONG 0 0 B_SINGER I_SINGER I_SINGER 0 O O O B ...

  4. NLP入门(八)使用CRF++实现命名实体识别(NER)

    CRF与NER简介   CRF,英文全称为conditional random field, 中文名为条件随机场,是给定一组输入随机变量条件下另一组输出随机变量的条件概率分布模型,其特点是假设输出随机 ...

  5. 零基础入门--中文命名实体识别(BiLSTM+CRF模型,含代码)

    自己也是一个初学者,主要是总结一下最近的学习,大佬见笑. 中文分词说到命名实体抽取,先要了解一下基于字标注的中文分词.比如一句话 "我爱北京天安门”. 分词的结果可以是 “我/爱/北京/天安 ...

  6. pytorch 文本情感分类和命名实体识别NER中LSTM输出的区别

    文本情感分类: 文本情感分类采用LSTM的最后一层输出 比如双层的LSTM,使用正向的最后一层和反向的最后一层进行拼接 def forward(self,input): ''' :param inpu ...

  7. 『深度应用』NLP命名实体识别(NER)开源实战教程

    近几年来,基于神经网络的深度学习方法在计算机视觉.语音识别等领域取得了巨大成功,另外在自然语言处理领域也取得了不少进展.在NLP的关键性基础任务—命名实体识别(Named Entity Recogni ...

  8. 基于bert的命名实体识别,pytorch实现,支持中文/英文【源学计划】

    声明:为了帮助初学者快速入门和上手,开始源学计划,即通过源代码进行学习.该计划收取少量费用,提供有质量保证的源码,以及详细的使用说明. 第一个项目是基于bert的命名实体识别(name entity ...

  9. 命名实体识别(NER)

    一.任务 Named Entity Recognition,简称NER.主要用于提取时间.地点.人物.组织机构名. 二.应用 知识图谱.情感分析.机器翻译.对话问答系统都有应用.比如,需要利用命名实体 ...

随机推荐

  1. 使用DIV+CSS布局网站的优点和缺陷

    随着WEB2.0标准化设计理念的普及,国内很多大型门户网站已经纷纷采用DIV+CSS制作方法,从实际应用情况来看,此种方法绝对好于表格制作页面的方法. 如今大部分网站仍然采用表格嵌套内容的方式来制作网 ...

  2. 来实现一个缩水版Vuex

    对 Vuex 源码进行浓缩,DIY 一个小型 Vuex 功能如下 通过 $store.commit 改变 $store.state 实现 strict model 源码约70行左右比较好理解,下面讲解 ...

  3. 新一代互联网传输协议QUIC浅析

    QUIC(Quick UDP Internet Connection)是谷歌制定的一种互联网传输层协议,它基于UDP传输层协议,同时兼具TCP.TLS.HTTP/2等协议的可靠性与安全性,可以有效减少 ...

  4. Python之collection

    1.计数器(counter) Counter是对字典类型的补充,用于追踪值的出现次数. ps:具备字典的所有功能 + 自己的功能 c = Counter('abcdeabcdabcaba') prin ...

  5. jquery的innerWidth 和 innerHeight的使用

    innerWidth This method returns the width of the element, including left and right padding, in pixels ...

  6. 视觉暂留-Info:这些神奇的“视觉暂留”动画,每一幅都让人拍案叫绝!

    ylbtech-视觉暂留-Info:这些神奇的“视觉暂留”动画,每一幅都让人拍案叫绝! 1.返回顶部 1. 这些神奇的“视觉暂留”动画,每一幅都让人拍案叫绝! 原创|发布:2018-05-28 19: ...

  7. 用Java来获取访问者真实的IP地址

    用Java来获取访问者真实的IP地址 转载 2016年06月07日 14:36:02 标签: 16497 编辑 删除 在JSP里,获取客户端的IP地址的方法是:request.getRemoteAdd ...

  8. iOS9 CASpringAnimation 弹簧动画详解

    http://blog.csdn.net/zhao18933/article/details/47110469 1. CASpringAnimation iOS9才引入的动画类,它继承于CABaseA ...

  9. 如何在Liferay 7中创建一个简单的JSF Portlet

    这个将在Liferay IDE 3.1 M3的发布版中提供创建的选项,但是你也可以通过命令行来创建. 1.这是Liferay JSF团队的官网:http://liferayfaces.org/ 你能在 ...

  10. 设备 VMnet0 上的网络桥接当前未在运行。

    早上,我打开我的虚拟机,却发现一个问题, 桥接网络怎么都连接不上. 报的是如下的错误 ------------------------------ 设备 VMnet0 上的网络桥接当前未在运行.该虚拟 ...