BERT NER如何获取原词级实体而非wordpiece子词标注结果
低复杂度实现方案
方案1:直接用pipeline内置能力(零额外代码,推荐)
不用自己写子词对齐、实体合并逻辑,transformers的NER pipeline原生支持偏移量计算和子词聚合,初始化时加2个参数即可:
nlp_ner = pipeline( "ner", model=mode, tokenizer=tokenize, offset_mapping=True, # 开启原文本字符偏移计算 aggregation_strategy="simple" # 自动合并wordpiece拆分的同实体子词 )
配置后调用接口返回的结果直接携带原文本字符级的起止索引,拆分的子词会被自动拼接成完整实体,你示例里的conf+##using会直接合并为confusing,返回结构示例:
{ "entity_group": "LABEL_9", "score": 0.82, "word": "confusing", "start": 121, # 原文本中实体首字符的索引 "end": 130 # 原文本中实体尾字符后一位的索引,text[start:end]可直接切出完整实体 }
拿到结果后直接过滤你需要的标签(比如LABEL_9),用对应的start/end字段就能完成定位,不管是生成Doccanno标注格式,还是插入HTML高亮标签都可以直接用。
插HTML高亮的注意点:拿到所有待高亮实体后,按start从大到小排序,从原文本末尾往前插入标签,避免插入标签后前面的字符索引错位,示例代码:
target_ents = [e for e in nlp_ner(text) if e["entity_group"] == "LABEL_9"] target_ents.sort(key=lambda x:x["start"], reverse=True) render_text = text for ent in target_ents: s, e = ent["start"], ent["end"] render_text = render_text[:s] + f'<span class="ner-highlight">{render_text[s:e]}</span>' + render_text[e:]
方案2:手动对齐(需要自定义聚合规则时用)
如果内置聚合策略不符合你的标签规则,自己实现对齐逻辑也只有3步,复杂度极低:
- 调用tokenizer分词时传入
return_offsets_mapping=True,分词器会直接返回每个token(包括子词)对应原文本的起止位置,不需要自己做字符串模糊匹配,不会错位 - 遍历预测结果,按你的规则合并连续子词:遇到以
##开头的子词,就和前一个token拼接,实体start继承前一个token的start,end取当前子词的end,直到遇到不带##的新token为止 - 合并完成后过滤目标标签,拿到的start/end和方案1的结果完全一致,可以直接使用
内容的提问来源于stack exchange,提问作者Milos Cuculovic
相关产品推荐
相关产品推荐

