crf可谓是NER任务小能手了,所以搞NER就得玩玩crf。
⭐torch官方tutorials部分提供的crf链接:点击进入,
该链接里是结合了bi-lstm和crf的代码教程(适合学习CRF原理),不过我看了下这只支持CPU的。
⭐我使用的是pytorch-crf库,该crf可支持GPU加速处理(即支持批处理的数据)。
pytorch-crf文档链接:点击进入。
不过文档里的讲解较少,有些地方不太清楚。百度谷歌了一波发现该库的使用例子并没有,所以自己搭模型做了尝试,是可以使用的。
这里附上我写的一个完成NER任务的bi-lstm+crf模型(包含BERT,NEZHA, GlobalPointer等详细实现代码,谢谢各位帮忙仓库点个星星⭐⭐,后序我将继续更新一些常用的模型)
github地址:https://github.com/XFR1998/NER-Model
import torch
from torchcrf import CRF
num_tags = 5 # NER数据集中
crf = CRF(num_tags=num_tags,
batch_first=True)
注意,若是用了批处理,需要做mask。因为每个批次你都有做padding的话,通过mask可以将这些padding的部分mask掉,预测(解码)最优序列时将不会对这些mask的部分做预测。
一开始不太确定,就问了下作者应该是
如文本为:“the dog is so cool
那mask向量(mask_tensor)就为:[1, 1, 1, 1, 0, 0]
1.crf层的输出:
因为crf的得分输出是正的,得分越高说明选出的最优标注序列路径有准,那我们可以将其取负,作为我们的损失函数进行反向传播:
【其中的emissions参数指的是发射得分,比如我们可以在lstm层后输出每个token的特征,并将其出入一个全连接层(输出维度为标签数量),这样每个token对应一个关于各标签的得分分布,所有token构成的这个张量就是emissions了,主要也就将lstm和crf衔接了起来。如下图所示的结构。】
# 当然这个crf层可以写在模型中,在forward中计算返回损失即可
loss = crf(emissions=lstm_feats,
tags=label_tensor,
mask=mask_tensor)
loss.backward()
2.crf的解码,解码出最优序列:
这样解码出的out就是批次里每条文本对应的最优标注序列了。
out = self.crf.decode(emissions=lstm_feats,
mask=mask_tensor)