如何从Hugging Face Transformers NER输出中获取原文本实体索引及处理##标记
NER相关问题解答
一、获取实体在原输入文本的起止索引
你可以通过Hugging Face分词器自带的return_offsets_mapping参数实现该需求,这个参数会返回每个分词结果对应原文本的起始、终止索引,不需要自己手动计算匹配,具体修改方案如下:
修改后的完整代码
import torch from transformers import AutoModelForTokenClassification, BertTokenizerFast model = AutoModelForTokenClassification.from_pretrained("dbmdz/bert-large-cased-finetuned-conll03-english") # 注意要使用Fast版本的分词器才支持offset_mapping tokenizer = BertTokenizerFast.from_pretrained("bert-base-cased") label_list = [ "O", # Outside of a named entity "B-MISC", # Beginning of a miscellaneous entity right after another miscellaneous entity "I-MISC", # Miscellaneous entity "B-PER", # Beginning of a person's name right after another person's name "I-PER", # Person's name "B-ORG", # Beginning of an organisation right after another organisation "I-ORG", # Organisation "B-LOC", # Beginning of a location right after another location "I-LOC" # Location ] sequence = "Hugging Face Inc. is a company based in New York City. Its headquarters are in DUMBO, therefore very" \ "close to the Manhattan Bridge." # 分词时开启offset映射返回 inputs = tokenizer(sequence, return_tensors="pt", return_offsets_mapping=True) offset_mapping = inputs.pop("offset_mapping")[0].tolist() # 取出offset映射,去掉batch维度 outputs = model(**inputs)[0] predictions = torch.argmax(outputs, dim=2)[0].tolist() # 去掉batch维度 entities = [] current_entity = None for idx, (pred, offset) in enumerate(zip(predictions, offset_mapping)): label = label_list[pred] start, end = offset # 跳过特殊标记[CLS]、[SEP],它们的offset是(0,0) if start == 0 and end == 0: continue if label != "O": # 拆分标签类型,去掉B/I-前缀 label_type = label.split("-")[-1] if current_entity is None: # 开启新实体 current_entity = { "type": label_type, "start": start, "end": end } else: # 和当前实体类型一致,扩展结束索引 if current_entity["type"] == label_type: current_entity["end"] = end else: # 类型不一致,先保存上一个实体,再开启新实体 entities.append(current_entity) current_entity = { "type": label_type, "start": start, "end": end } else: # 遇到O标签,保存当前存在的实体 if current_entity is not None: entities.append(current_entity) current_entity = None # 遍历结束后检查是否还有未保存的实体 if current_entity is not None: entities.append(current_entity) # 输出结果,包含实体类型、起止索引、实体文本 for ent in entities: ent["text"] = sequence[ent["start"]:ent["end"]] print(ent)
输出示例
{'type': 'ORG', 'start': 0, 'end': 16, 'text': 'Hugging Face Inc'} {'type': 'LOC', 'start': 40, 'end': 53, 'text': 'New York City'} {'type': 'LOC', 'start': 79, 'end': 83, 'text': 'DUMBO'} {'type': 'LOC', 'start': 117, 'end': 133, 'text': 'Manhattan Bridge'}
可以看到第一个ORG实体的起止索引刚好符合你提到的start:0, end:16的预期。
二、##前缀标记的处理说明
- 这个##是BERT类模型采用的WordPiece分词规则的子词标记,代表当前子词是拼接在前一个子词后面的,比如
Hu+##gging拼接后就是完整单词Hugging。 - 最终输出使用时必须去除##前缀,把连续的子词拼接成完整的单词才能正常使用,不能直接保留带##的内容。
- 如果是模型推理的中间处理步骤,可以保留该标记方便对应分词结果和预测标签。
内容的提问来源于stack exchange,提问作者MakePerfect1
相关产品推荐
相关产品推荐

