命名实体识别 NER:提取人名、地名与组织

FreeGuideOnline 最新 2026-06-16

命名实体识别 (NER) 完全指南:从原理到实战,精准提取人名、地名与组织

什么是命名实体识别?

命名实体识别(Named Entity Recognition,简称 NER)是自然语言处理中的一项基础任务,目标是从非结构化文本中定位并分类专有名词。你将在本节中理解它的核心定义、应用场景以及与关键词提取的本质区别。

命名实体的定义

在 NER 语境下,命名实体 通常指代现实世界中具有特定指代对象的词语或短语。最常见的类别包括:

  • 人名 (PER):如“乔布斯”、“张三”
  • 地名 (LOC):如“北京”、“珠穆朗玛峰”
  • 组织名 (ORG):如“联合国”、“清华大学”
  • 其他扩展类别:时间、日期、货币、百分比、产品名等

NER 与关键词提取的区别

很多初学者容易混淆这两个概念。关键词提取仅找出重要词汇,不区分类型;而 NER 不仅需要识别边界(实体从哪里开始,到哪里结束),还要赋予一个明确的类别标签。例如:“苹果发布了新手机”中,关键词可能包含“苹果”、“发布”、“手机”,但 NER 会输出 [ORG: 苹果](如果指公司)。

为什么 NER 如此重要?

NER 是信息抽取的基石,支撑着众多上层应用:

  • 知识图谱构建:自动抽取实体和关系
  • 搜索引擎优化:理解查询意图中的实体
  • 推荐系统:基于内容中的实体进行关联
  • 舆情监控:识别提及的公司、品牌或公众人物
  • 自动化摘要与问答系统:定位答案片段中的关键实体

NER 面临的挑战

看似简单的识别任务,在实际文本中充满变数。理解这些难点将帮助你更好地设计解决方案。

歧义性问题

同一个词可能对应不同实体类别或非实体。

  • 词义歧义:“华盛顿”可以指人物(乔治·华盛顿)、城市(华盛顿特区)或州(华盛顿州)。
  • 类别歧义:“苹果”可能属于组织(公司)或水果。上下文决定了最终标签。

边界模糊

实体的边界往往不清晰。例如:“联合国教科文组织总部”是一个完整的组织名还是包含地点?正确的标注可能是 [ORG: 联合国教科文组织] 加上 [LOC: 总部],或者直接作为一个整体。嵌套实体和重叠实体进一步增加了复杂度。

非标准表达与演化

  • 缩写与昵称:“马化腾”可能被称为“小马哥”,“北京大学”缩写为“北大”。
  • 新实体不断涌现:新公司、新产品、新名词每天诞生,固定词表的方法注定过时。
  • 多语言与跨领域:不同语言的命名习惯差异巨大,且医学、法律等专业领域拥有独特的实体类型。

主流 NER 方法技术演进

NER 的技术发展经历了从规则到深度学习的三次飞跃。掌握这些方法的优缺点,有助于你在实际项目中进行选择。

基于规则与词典的方法

最早期的系统依赖人工编写规则和构建地名词典。

  • 工作原理:利用正则表达式、词性标注序列、专有名词词典进行模式匹配。例如:“Mr. [A-Z][a-z]+” 提取英文人名前缀。
  • 优点:对于规则覆盖的场景速度快、可解释性强,无需训练数据。
  • 缺点:极其脆弱,覆盖率低,无法应对语义歧义和新的表达,维护成本高。

传统机器学习方法

将 NER 建模为序列标注问题,典型代表包括隐马尔可夫模型 (HMM)、最大熵马尔可夫模型 (MEMM) 和条件随机场 (CRF)。

  • 特征工程:严重依赖手工设计的特征,如单词本身、词性(POS)、字词上下文窗口、前缀/后缀、是否大写、词典匹配特征等。
  • CRF 的优势:CRF 能够对整个标签序列进行建模,避免标记偏置问题,长久以来都是 NER 的强基线模型。它通过特征模板捕捉相邻标签的转移概率。
  • 局限性:特征构建需要大量专业经验,且难以捕捉长距离依赖。

深度学习时代

深度神经网络自动学习文本的分布式表示,彻底革新了 NER。

  • BiLSTM-CRF:经典架构。双向长短期记忆网络 (BiLSTM) 编码上下文信息,产生每个 token 的发射概率,再由 CRF 层优化标签序列的全局一致性。无需繁重特征工程,效果显著提升。
  • CNN + CRF:利用卷积神经网络捕捉字符级特征,尤其适合处理词形变化丰富或带噪声的文本。
  • 预训练语言模型 + 微调:以 BERT 为代表。在大规模语料上预训练的 Transformer 模型已蕴含丰富的语义知识,只需在顶层添加一个简单的线性分类器(通常再加 CRF)并对特定 NER 数据集微调,即可达到当时的最优性能。微调能迅速适应垂直领域。
  • 大型语言模型 (LLM) 的零样本/少样本抽取:ChatGPT 等模型可通过提示词直接抽取实体,无需训练。适用于标注数据极度稀缺的场景,但成本高、可控性弱、可能存在幻觉。

从零实践:用 Python 构建自己的 NER 系统

理论知识准备就绪,现在带你通过两个路径快速落地 NER:调用成熟库和微调预训练模型。所有代码均简洁、可运行。

环境准备

假设你已安装 Python 3.8+。在终端执行以下命令安装必需库:

pip install spacy transformers datasets seqeval accelerate
python -m spacy download zh_core_web_sm   # 中文小模型
python -m spacy download en_core_web_sm   # 英文小模型

方案一:使用 spaCy 立即提取实体

spaCy 提供了开箱即用的 NER 流水线,适合快速验证和原型开发。

import spacy

# 加载中文模型
nlp = spacy.load("zh_core_web_sm")
text = "李华于2024年5月在北京出席了由清华大学主办的AI峰会。"
doc = nlp(text)

print("实体输出:")
for ent in doc.ents:
    print(f"实体文本: {ent.text:<10} 起始位置: {ent.start_char:<4} 标签: {ent.label_}")

# 解释标签含义 (spaCy中文模型标签)
# PERSON: 人名, ORG: 组织, GPE: 地理政治实体(城市/国家), DATE: 日期, EVENT: 事件等

输出会清晰显示实体边界和类型。对于更高精度需求,spaCy 支持使用自定义训练管线或转换其他模型框架的权重。

方案二:微调 BERT 模型应对自定义实体类别

当通用模型的类别(如人名、地名)无法覆盖你的业务需求(例如需要识别医药领域的“药品名”、“适应症”),微调预训练模型是最佳路径。我们以开源的 bert-base-chinese 为例,使用 transformersdatasets 库。

第一步:准备数据格式
NER 数据通常采用 BIO 或 BIOES 标注方案。以 BIO 为例,每个 token 标注为 B-实体类型、I-实体类型 或 O (非实体)。例如句子“我爱北京”对应的标签可能是:

我  O
爱  O
北  B-LOC
京  I-LOC

你需要将数据集组织成 JSON 或 CSV,包含 tokensner_tags 字段。此处假设你已有 train.jsonvalid.json 符合此格式。

第二步:完整微调脚本
以下脚本基于 trainer API 完成训练,并包含必要的预处理和评估。

from transformers import (AutoTokenizer, AutoModelForTokenClassification, 
                          TrainingArguments, Trainer, DataCollatorForTokenClassification)
from datasets import load_dataset, load_metric
import numpy as np

# 1. 加载数据集
dataset = load_dataset("json", data_files={"train": "train.json", "validation": "valid.json"})

# 2. 定义标签映射(请根据你的实体类别调整)
label_list = ["O", "B-PER", "I-PER", "B-LOC", "I-LOC", "B-ORG", "I-ORG"]
id2label = {i: label for i, label in enumerate(label_list)}
label2id = {label: i for i, label in enumerate(label_list)}

# 3. 加载分词器和模型
model_checkpoint = "bert-base-chinese"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
model = AutoModelForTokenClassification.from_pretrained(
    model_checkpoint, num_labels=len(label_list), id2label=id2label, label2id=label2id
)

# 4. 数据预处理:分词并对齐标签
def tokenize_and_align_labels(examples):
    tokenized_inputs = tokenizer(examples["tokens"], truncation=True, is_split_into_words=True)
    labels = []
    for i, label in enumerate(examples["ner_tags"]):
        word_ids = tokenized_inputs.word_ids(batch_index=i)
        previous_word_idx = None
        label_ids = []
        for word_idx in word_ids:
            if word_idx is None:
                label_ids.append(-100)  # 特殊token忽略
            elif word_idx != previous_word_idx:
                # 每个词的第一个子词保持原始标签
                label_ids.append(label[word_idx])
            else:
                # 该词的其余子词忽略或设为 I- 标签(此处设为-100更简便)
                label_ids.append(-100)
            previous_word_idx = word_idx
        labels.append(label_ids)
    tokenized_inputs["labels"] = labels
    return tokenized_inputs

tokenized_dataset = dataset.map(tokenize_and_align_labels, batched=True)

# 5. 设置训练参数
args = TrainingArguments(
    output_dir="./ner_model",
    evaluation_strategy="epoch",
    learning_rate=2e-5,
    per_device_train_batch_size=16,
    per_device_eval_batch_size=16,
    num_train_epochs=3,
    weight_decay=0.01,
    save_strategy="epoch",
    load_best_model_at_end=True,
)

# 6. 数据收集器(动态填充)
data_collator = DataCollatorForTokenClassification(tokenizer)

# 7. 评估指标:使用seqeval库计算实体级别F1
metric = load_metric("seqeval")

def compute_metrics(p):
    predictions, labels = p
    predictions = np.argmax(predictions, axis=2)

    true_predictions = [
        [label_list[p] for (p, l) in zip(prediction, label) if l != -100]
        for prediction, label in zip(predictions, labels)
    ]
    true_labels = [
        [label_list[l] for (p, l) in zip(prediction, label) if l != -100]
        for prediction, label in zip(predictions, labels)
    ]

    results = metric.compute(predictions=true_predictions, references=true_labels)
    return {
        "precision": results["overall_precision"],
        "recall": results["overall_recall"],
        "f1": results["overall_f1"],
        "accuracy": results["overall_accuracy"],
    }

# 8. 训练
trainer = Trainer(
    model=model,
    args=args,
    train_dataset=tokenized_dataset["train"],
    eval_dataset=tokenized_dataset["validation"],
    tokenizer=tokenizer,
    data_collator=data_collator,
    compute_metrics=compute_metrics,
)

trainer.train()

# 9. 推理示例
from transformers import pipeline
classifier = pipeline("token-classification", model="./ner_model", tokenizer=tokenizer, aggregation_strategy="simple")
result = classifier("张伟在华为工作,负责国际业务拓展。")
print(result)

运行后,你将得到每个实体及其类别和置信度。通过调整训练轮数、学习率,添加领域数据,可以持续提升模型质量。

方案三:使用大模型 API 进行抽取
如果标注数据稀缺,可直接调用 OpenAI 等 API,设计提示词让模型输出结构化 JSON。

import openai
openai.api_key = "your-api-key"

prompt = """
从以下文本中抽取所有人名(PER)、地名(LOC)和组织(ORG)。以JSON格式返回。
文本: 马云在杭州创立了阿里巴巴,改变了电子商务格局。
"""
response = openai.ChatCompletion.create(
    model="gpt-3.5-turbo",
    messages=[{"role": "user", "content": prompt}],
)
print(response.choices[0].message.content)

提升 NER 效果的实战技巧

掌握以下技巧可使你的系统在真实生产环境中表现更稳健。

数据增强

  • 同义实体替换:用同类别实体随机替换句子中的实体,如用“上海”替换“北京”,并同步修改标签,扩充数据规模。
  • 回译:将句子翻译为其他语言再翻译回原语言,生成多样化表达。
  • 领域词典注入:对于自定义实体,生成大量包含这些实体的合成句子。

处理类别不平衡与嵌套实体

  • 加权损失函数:对于长尾实体(如某些罕见组织名),增加其损失权重。
  • 跨度式标注(Span-based):直接预测实体片段的起始和结束位置,而不是逐 token 分类,天然解决嵌套和边界模糊问题。例如用 SPAN-BERT 或基于读取理解的 NER 框架。

后处理与规则结合

深度学习模型可能对模式化信息不敏感。在输出层融合词典和正则规则进行修正:

  • 匹配所有日期、货币等具有明显格式的实体。
  • 用高可信度词典修正明显的漏召(如已知所有国家名)。
  • 校验组织机构后缀(“有限公司”、“Corp.”)辅助边界修正。

主动学习

当人工标注成本高时,初期用少量数据训练模型,然后选择模型最不确定的样本进行人工标注,迭代循环。这能最大化每一份标注的效用。

行业应用案例精选

金融信息抽取

从新闻、研报中抽取公司名、高管人名、金融指标(营收、股价)、事件(并购、上市)等,辅助构建金融知识图谱和量化因子。

医疗临床文档处理

从电子病历中抽取症状、诊断、药物、手术、时间等实体,辅助构建患者画像、临床决策支持和药物不良反应监测。

法律文书分析

识别裁判文书中的当事人、案由、法院、法律条款引用,用于类案推送、案件要素提取和合同审查。

电商评论细粒度挖掘

从用户评论中提取产品特征词(如“电池续航”、“屏幕”)、情感倾向和竞品名称,形成更精准的商品优化建议。

总结与学习路径

命名实体识别是让机器“看懂”文本中具体事物的重要一步。从理解概念到动手实践,再到优化技巧,你已经获得了建立高效NER系统的完整视角。建议你按以下路径继续深入:

  1. 用 spaCy 或 BERT 微调完成一个你感兴趣领域的 NER 迷你项目。
  2. 阅读关于“平铺式NER”(Flat NER)、嵌套NER、少样本NER的最新论文。
  3. 探索将 NER 与关系抽取、实体链接相结合,搭建完整的信息抽取管道。
  4. 关注评估方法:除了实体级 F1,还要评估边界检测的精确率,这直接影响下游应用。

现在就开始敲下第一行代码吧,把你的文本数据变成可用的结构化知识。