命名实体识别 (NER) 是自然语言理解的基础构件之一。当人类阅读文本时,我们会根据上下文和世界知识自然地识别和分类命名实体。例如,在句子“微软首席执行官萨提亚·纳德拉在西雅图的一次会议上发表了讲话”中,我们毫不费力地识别出组织、个人和地理位置的指代。然而,要教会机器复制这种看似直观的人类能力,会带来一些挑战。幸运的是,这个问题可以使用预训练的机器学习模型来有效解决。
在这篇文章中,您将学习如何使用 BERT 模型,仅通过几行 Python 代码来解决 NER 问题。
通过我的书籍《Hugging Face Transformers中的NLP》,快速启动您的项目。它提供了带有工作代码的自学教程。
让我们开始吧。

如何使用 BERT 模型进行命名实体识别(NER)
图片来自 Jon Tyson。部分权利保留。
概述
本文分为六个部分;它们是
- NER 系统的复杂性
- NER 技术的发展历程
- BERT 的 NER 革命性方法
- 将 DistilBERT 与 Hugging Face 的 Pipeline 结合使用
- 将 DistilBERT 与 AutoModelForTokenClassification 显式结合使用
- NER 实现的最佳实践
NER 系统的复杂性
命名实体识别的挑战远远超出了简单的模式匹配或字典查找。有几个关键因素导致了它的复杂性。
最大的挑战之一是**上下文依赖**——理解单词如何根据周围的文本改变含义。同一个词在不同的上下文中可以代表不同的实体类型。考虑以下示例:
- “苹果公司宣布了新产品。”(苹果公司是一个组织。)
- “午餐我吃了一个苹果。”(苹果是一种普通名词,不是命名实体。)
- “苹果街已关闭。”(苹果是一种地点。)
命名实体通常由多个单词组成,这使得**边界检测**成为另一项挑战。实体名称可能很复杂,例如:
- 企业实体:“美国银行公司”
- 产品名称:“iPhone 14 Pro Max”
- 人名:“马丁·路德·金”
此外,语言是动态的并且在不断发展。模型必须根据上下文推断出什么构成实体,而不是记住它。**语言演变**引入了新实体,例如新兴公司、新产品和新造词。
现在,让我们探讨一下最先进的 NER 模型如何解决这些挑战。
NER 技术的发展历程
NER 技术的发展反映了自然语言处理的广泛进步。早期的方法依赖于基于规则的系统和模式匹配——定义语法模式、识别首字母大写以及使用上下文标记(例如,在专有名词前加上“the”)。然而,这些规则通常数量众多、不一致且难以扩展。
为了提高准确性,研究人员引入了统计方法,利用概率模型,如隐马尔可夫模型 (HMM) 和条件随机场 (CRF) 来识别命名实体。
随着深度学习的兴起,神经网络成为 NER 的首选方法。最初,双向 LSTM 网络显示出潜力。然而,注意力机制和基于 Transformer 的模型的引入被证明更有效。
BERT 的 NER 革命性方法
BERT(Bidirectional Encoder Representations from Transformers)通过几项关键创新彻底改变了 NER:
上下文理解
与处理文本单向的传统模型不同,BERT 的双向性使其能够同时考虑前面的文本和后面的文本。这使得它能够捕捉长距离依赖关系,理解细微的上下文细微差别,并更有效地处理歧义情况。
分词和子词单元
虽然不是 BERT 独有的,但其子词分词策略使其能够处理未知单词,同时保留形态信息。这减小了词汇量,并使模型能够适应不同的语言和领域。
IOB 标记机制
NER 结果可以用多种方式表示,但 BERT 使用“Inside-Outside-Beginning”(IOB)标记方案:
- B 标记实体的开头。
- I 表示实体的延续。
- O 表示非实体。
这种方法使 BERT 能够有效地处理多词实体、嵌套实体和重叠实体。
将 DistilBERT 与 Hugging Face 的 Pipeline 结合使用
执行 NER 最简单的方法是使用 Hugging Face 的 `pipeline` API,它可以抽象掉许多复杂性,同时仍然提供强大的结果。示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
from transformers importpipeline # 初始化 NER pipeline ner_pipeline = pipeline("ner", model="dbmdz/bert-large-cased-finetuned-conll03-english", aggregation_strategy="simple") # 文本示例 text = "Apple CEO Tim Cook announced new iPhone models in California yesterday." # 执行 NER entities = ner_pipeline(text) # 打印结果 for entity in entities: print(f"Entity: {entity['word']}") print(f"Type: {entity['entity_group']}") print(f"Confidence: {entity['score']:.4f}") print("-" * 30) |
现在,让我们详细解析这段代码。首先,您初始化 pipeline:
1 2 3 |
ner_pipeline = pipeline("ner", model="dbmdz/bert-large-cased-finetuned-conll03-english", aggregation_strategy="simple") |
`pipeline()` 函数创建了一个即用型 NER pipeline。这一点至关重要,因为虽然 BERT 是一个机器学习模型,但在文本被模型处理之前,必须对其进行预处理。此外,还需要将模型的输出转换为可用格式。pipeline 会自动处理这些步骤。
参数 `"ner"` 指定您想要进行命名实体识别,而 `model="dbmdz/bert-large-cased-finetuned-conll03-english"` 加载了一个经过预训练且专门针对 NER 进行微调的模型。最后一个参数 `aggregation_strategy="simple"` 确保将子词合并成完整的单词,使输出更易于阅读。
上述 pipeline 返回一个字典列表,其中每个字典包含:
- `word`:检测到的实体文本
- `entity_group`:实体类型(例如,`PER` 表示人,`ORG` 表示组织)
- `score`:置信度得分,介于 0 和 1 之间
- `start` 和 `end`:原始文本中的字符位置
此代码将输出以下内容:
1 2 3 4 5 6 7 8 9 10 11 12 |
Entity: Apple Type: ORG Confidence: 0.9987 ------------------------------ Entity: Tim Cook Type: PER Confidence: 0.9956 ------------------------------ Entity: California Type: LOC Confidence: 0.9934 ------------------------------ |
将 DistilBERT 与 AutoModelForTokenClassification 显式结合使用
为了更精细地控制 NER 过程,您可以绕过 pipeline,直接使用模型和 tokenizer 进行操作。这种方法提供了更大的灵活性和对过程的深入了解。示例如下:
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 40 41 42 43 44 45 46 47 |
from transformers importAutoTokenizer, AutoModelForTokenClassification import torch # 加载模型和 tokenizer model_name = "dbmdz/bert-large-cased-finetuned-conll03-english" tokenizer = AutoTokenizer.from_pretrained(model_name) model = AutoModelForTokenClassification.from_pretrained(model_name) # 文本示例 text = "Google and Microsoft are competing in the AI space while Elon Musk founded SpaceX." # 对文本进行分词 inputs = tokenizer(text, return_tensors="pt", add_special_tokens=True) # 获取预测结果 with torch.no_grad(): outputs = model(**inputs) predictions = torch.argmax(outputs.logits, dim=2) # 将预测结果转换为标签 label_list = model.config.id2label tokens = tokenizer.convert_ids_to_tokens(inputs["input_ids"][0]) predictions = predictions[0].tolist() # 处理结果 current_entity = [] current_entity_type = None for token, prediction in zip(tokens, predictions): if token.startswith("##"): if current_entity: current_entity.append(token[2:]) else: if current_entity: print(f"Entity: {''.join(current_entity)}") print(f"Type: {current_entity_type}") print("-" * 30) current_entity = [] if label_list[prediction] != "O": current_entity = [token] current_entity_type = label_list[prediction] # 打印最终实体(如果存在) if current_entity: print(f"Entity: {''.join(current_entity)}") print(f"Type: {current_entity_type}") |
这个实现更详细。让我们一步步来看。首先,您加载模型和 tokenizer:
1 2 3 |
model_name = "dbmdz/bert-large-cased-finetuned-conll03-english" tokenizer = AutoTokenizer.from_pretrained(model_name) model = AutoModelForTokenClassification.from_pretrained(model_name) |
`AutoTokenizer` 类会根据 **model card** 自动选择合适的 tokenizer,确保兼容性。Tokenizer 负责将输入文本转换为 tokens。`AutoModelForTokenClassification` 加载了一个为 token 分类任务微调的模型,包括模型架构和预训练的权重。
接下来,您预处理输入文本:
1 |
inputs = tokenizer(text, return_tensors="pt", add_special_tokens=True) |
此步骤将文本转换为模型可以处理的 token ID。token 通常是一个单词,但也可能是一个子词。例如,“sub-”和“-word”即使出现在同一个单词中,也可能被单独识别。`return_tensors="pt"` 参数将序列作为 PyTorch tensors 返回,而 `add_special_tokens=True` 确保在输出的开头和结尾包含 `[CLS]` 和 `[SEP]` tokens,这是 BERT 所必需的。
然后,您在输入 tensor 上运行模型:
1 2 3 |
with torch.no_grad(): outputs = model(**inputs) predictions = torch.argmax(outputs.logits, dim=2) |
使用 `torch.no_grad()` 在推理期间禁用梯度计算,节省了时间和内存。`torch.argmax(outputs.logits, dim=2)` 函数为每个 token 选择最可能的标签。`predictions` tensor 是一个整数 tensor。
为了将模型的输出转换为人类可读的文本,我们准备了一个预测索引与实际实体标签之间的映射:
1 2 3 |
label_list = model.config.id2label tokens = tokenizer.convert_ids_to_tokens(inputs["input_ids"][0]) predictions = predictions[0].tolist() |
字典 `model.config.id2label` 是从预测索引到实际实体标签的映射。`convert_ids_to_tokens` 函数将整数 token ID 转换回可读文本。由于您使用单行输入文本运行模型,因此只期望得到一个输出序列。我们将其转换为 Python 列表以方便处理。
最后,您使用循环重构实体预测。由于 BERT 的 tokenizer 有时会将单词拆分为子词(用 `"##"` 表示),因此您需要将它们重新合并为完整的单词。实体类型是使用 `label_list` 字典确定的。
NER 实现的最佳实践
执行命名实体识别 (NER) 就像上面展示的那样简单。但是,您不需要使用提供的确切代码。具体来说,您可以在不同的模型(以及相应的 tokenizer)之间切换。如果您需要更快的处理速度,可以考虑使用 DistilBERT 模型。如果准确性是首要任务,请选择更大的 BERT 或 RoBERTa 模型。此外,如果您的输入需要特定领域的知识,您可以使用领域适应模型。
如果您需要处理大量文本进行 NER,可以通过批量处理输入来提高效率。使用 GPU 进行加速或缓存经常访问的文本结果等其他技术可以进一步提高性能。
在生产系统中,还应实现适当的错误处理逻辑。这包括验证输入、处理空字符串和特殊字符等边缘情况,以及解决其他潜在问题。
以下是一个结合了这些最佳实践的完整示例:
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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
from transformers import pipeline import torch import logging from typing importList, Dict class NERProcessor: def __init__(self, model_name: str = "dbmdz/bert-large-cased-finetuned-conll03-english", confidence_threshold: float = 0.8): self.confidence_threshold = confidence_threshold try: self.device = "cuda" if torch.cuda.is_available() else "cpu" self.ner_pipeline = pipeline("ner", model=model_name, aggregation_strategy="simple", device=self.device) except Exception as e: logging.error(f"Failed to initialize NER pipeline: {str(e)}") raise def process_text(self, text: str) -> List[Dict]: if not text or not isinstance(text, str): logging.warning("Invalid input text") return [] try: # Get predictions entities = self.ner_pipeline(text) # Post-process results filtered_entities = [ entity for entity in entities if entity['score'] >= self.confidence_threshold ] return filtered_entities except Exception as e: logging.error(f"Error processing text: {str(e)}") return [] if __name__ == "__main__": # Initialize processor processor = NERProcessor() # Text example text = """ Apple Inc. CEO Tim Cook announced new partnerships with Microsoft and Google during a conference in New York City. The event was also attended by Sundar Pichai and Satya Nadella. """ # Process text results = processor.process_text(text) # Print results for entity in results: print(f"Entity: {entity['word']}") print(f"类型: {entity['entity_group']}") print(f"置信度: {entity['score']:.4f}") print("-" * 30) |
总结
使用BERT模型进行命名实体识别(NER)是一种从文本中提取结构化信息的强大方法。Hugging Face Transformers库可以轻松地使用最先进的模型来实现NER,无论您需要简单的流水线方法还是对过程进行更详细的控制。
在本教程中,您学习了如何使用BERT进行NER。具体来说,您学习了如何
- 使用pipeline API进行快速原型设计和简单应用程序
- 使用显式模型处理以获得更多控制和自定义处理
- 考虑生产应用程序的性能优化
- 始终处理边缘情况并实施适当的错误处理
有了这些工具和技术,您可以构建健壮的NER系统,用于各种应用,从信息提取到文档处理等等。
暂无评论。