中文序列标注任务(二)

简介

记录中文序列标注任务:动宾搭配识别,主要学习代码中的数据处理,评价函数,模型搭建部分

1. 数据长这样:


采用的是 BIOES 标注,利用句子中成对出现的动宾搭配,到原句子中去匹配,获得带有 动宾 标签的 原句子序列.

2. 数据处理:

下面主要记录一下,要输入bert 预训练模型之前,将数据应该处理成什么样子:

  • 原始代码是手动处理的,其实可以直接使用AutoTokenizer,通过调用函数:word_ids() 获得转换为token 之后的词(可能有子词)在原始句子中的位置id,然后根据这个id 序列将 label 转换为对应的 id
  • 这里注意 label 序列是如何分布的,因为经过 tokenizer 之后原始句子会 前后加上一个 [CLS] 和 [SEP],然后后面进行padding;对应到 label 上面需要跟 句子 input 长度保持一致,[CLS]&[SEP]是补上的 标签 'O' 对应的 id,padding 部分是直接填充 数字 0,作为索引.
  • 同时这里还保存了 原始的句子token:ntokens ,为后续的预测句子预留了调用原始token 空间
textlist=['实', '际', '上', ',', '不', '仅', '法', '国', '殖', '民', '主', '义', '者', ',', '而', '且', '美', '、', '英', '殖', '民', '主', '义', '者', '都', '对', '这', '个', '“', '根', '本', '法', '”', '草', '案', '寄', '予', '很', '大', '希', '望', '。']
labellist=['O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'B-V', 'E-V', 'O', 'O', 'B-N', 'E-N', 'O']
label_map={'B-V': 0, 'E-N': 1, 'E-V': 2, 'B-N': 3, 'O': 4}
print("label_map: ",label_map)

from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-chinese")
tokenized_input = tokenizer(textlist, is_split_into_words=True)

print("tokenized_input: ",tokenized_input)
ntokens = tokenizer.convert_ids_to_tokens(tokenized_input["input_ids"])
print("ntokens: ",ntokens)
word_ids=tokenized_input.word_ids()
print("word_ids: ",word_ids)
label_id=[]
for item in word_ids:#词的id 索引
    if item is None:
        label_id.append(label_map["O"])
    else:
        label_id.append(label_map[labellist[item]])
print(label_id)
while len(tokenized_input["input_ids"]) < 50:
    tokenized_input["input_ids"].append(0)
    tokenized_input["attention_mask"].append(0)
    tokenized_input["token_type_ids"].append(0)
    # we don't concerned about it!
    label_id.append(0)#padding直接补零
    ntokens.append("**NULL**")
print("input_ids: ",tokenized_input["input_ids"])
print("input_mask: ",tokenized_input["attention_mask"])
print("segment_ids: ",tokenized_input["token_type_ids"])
print("label_ids: ",label_id)
print("ntokens: ",ntokens)
#这一部分因为transformer 版本的原因没有使用成功,还是使用的手动处理各项,input,attention,labe 等

3. 模型部分

模型采用的是 bert+BiLSTM+crf,其中BiLSTM部分是可选项,可以在传参的过程中选择是否保留,CRF 是调用的 torchcrf 包,封装好的包,使用很方便进行预测解码等操作.

4. 评价函数

  • 这一部分发现,使用crf预测值 长度是不包含 padding 部分的,也就是说,最后模型给出一个序列可能的标签id,长度为原始句子长度加上前后的 [CLS] & [SEP] 开始和结束标识.但是原始 label 序列经过处理是经过padding的,长度往往远大于预测标签序列长度,zip()函数可以对应起来进行预测,下面会说.
  • 使用的是 python 版本的 conll03官方的评测脚本conlleval.py
    原始脚本在:https://github.com/spyysalo/conlleval.py
    python 版本支持 IOBES 标签,个人感觉代码稍微有些麻烦,总结下来评价方式是:
    预测的标签真实标签原始句子token一一对应之后:zip()函数实现,可以只获得以 [SEP] 结尾的长度,全部句子对应成三元组形式,放入一个list,句子之间以 '\n' 分割.
    下面这只是一个句子,最后是'\n'
['实 O E-N\n', '际 O B-V\n', '上 O B-V\n', ', O B-V\n', '不 O B-V\n', '仅 O B-V\n', '法 O B-V\n', '国 O B-V\n', '殖 O B-V\n', '民 O B-V\n', '主 O B-V\n', '义 O B-V\n', '者 O B-V\n', ', O B-V\n', '而 O B-V\n', '且 O B-V\n', '美 O B-V\n', '、 O B-V\n', '英 O B-V\n', '殖 O B-V\n', '民 O B-V\n', '主 O B-V\n', '义 O B-V\n', '者 O B-V\n', '都 O B-V\n', '对 O B-V\n', '这 O B-V\n', '个 O B-V\n', '“ O B-N\n', '根 O B-V\n', '本 O B-V\n', '法 O B-N\n', '” O B-N\n', '草 O B-N\n', '案 O B-N\n', '寄 B-V B-V\n', '予 E-V B-V\n', '很 O B-V\n', '大 O B-V\n', '希 B-N B-V\n', '望 E-N B-N\n', '。 O B-N\n', '\n']

然后整个代码就是遍历这些对应的 三元组,判断模型预测值跟真实的标签值是否一致,同时还要判断两者并行的 序列是否是正确预测的,比如模型预测的是否是 一个 chunk 块的开始,结束,中间,是否都跟真实标签值对应上了,然后一一计数,装在一个数据结构中:

self.correct_chunk = 0    # number of correctly identified chunks
self.correct_tags = 0     # number of correct chunk tags
self.found_correct = 0    # number of chunks in corpus
self.found_guessed = 0    # number of identified chunks
self.token_counter = 0    # token counter (ignores sentence breaks)

# counts by type#对chunk的type进行计数,参与正确率计算
self.t_correct_chunk = defaultdict(int)
self.t_found_correct = defaultdict(int)
self.t_found_guessed = defaultdict(int)

最后计算精确率,召回率,F1值

def calculate_metrics(correct, guessed, total):#计算正确率,召回率和F1
    tp, fp, fn = correct, guessed-correct, total-correct#正确预测的/错误预测的/全部语料块-你正确预测的=还有多少是正确的
    p = 0 if tp + fp == 0 else 1.*tp / (tp + fp)#正确识别的/正确识别的+错误预测的##真正正确的占所有预测为正的比例。
    r = 0 if tp + fn == 0 else 1.*tp / (tp + fn)#正确识别的/正确识别的+还有多少是正确的##真正正确的占所有实际为正的比例。
    f = 0 if p + r == 0 else 2 * p * r / (p + r)
    return Metrics(tp, fp, fn, p, r, f)

同时会计算 每个标签的 type 识别正确率,这是通过 建立的字典数据结构存储的:

# counts by type#对chunk的type进行计数,参与正确率计算
self.t_correct_chunk = defaultdict(int)
self.t_found_correct = defaultdict(int)
self.t_found_guessed = defaultdict(int)

在 test 上面预测新的标签的时候有一个要注意的地方,原始label 没有加了 [CLS]&[SEP]标志,而预测的结果是加了的,写入文件的时候是zip并行对应写入,遇到开始结束标志会 跳过,所以真实序列跟预测的序列位置为相差一个值,但是不影响测评结果,也就是这样的:第二列是真实label,第三列是预测label:


下面是简单是实验结果,没有精细的微调参数,只是上手实践.

image.png
image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,204评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,091评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,548评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,657评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,689评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,554评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,302评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,216评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,661评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,851评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,977评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,697评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,306评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,898评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,019评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,138评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,927评论 2 355