在上一章节中,我们学习了如何对整个文本进行分类,这是一种对文本的宏观理解。现在,我们将从宏观走向微观,深入文本内部,学习如何精准识别出句子中的人名、地名等具有特定意义的词语或短语,这就是理论部分多次提到的 命名实体识别 (Named Entity Recognition, NER)。作为信息抽取、知识图谱构建、智能问答等众多高级应用的 关键环节,NER的目标是从非结构化文本中,精准地定位并分类出业务所关心的实体对象。
简单来说,命名实体就是现实世界中事物的名称,是文本中那些指向特定对象、具有可识别性和唯一性的词语或短语。NER 的任务就是将这些“名字”找出来,并贴上预先定义好的标签。
常见的实体类型包括:
- 人名 (PER): 姚明, 雷军
- 地名 (LOC): 北京, 故宫
- 组织机构名 (ORG): 阿里巴巴, 英伟达
- 产品名 (PROD): 黑神话:悟空, Blackwell 芯片
- 作品名 (WORK): 三体, 流浪地球
- 其他/杂项 (MISC): 含义依数据集而异(如民族、语言、事件等)
- 时间 (TIME): 昨天, 2025年
一个词是否被视为实体,以及它属于哪种实体,完全由业务场景的需求来决定。例如,在通用的场景下,“苹果”可能只是一个水果;但在数码产品的讨论中,它很可能需要被识别为一个“组织机构名”或“品牌名”。
不同数据集的实体类型定义差异较大(如 CoNLL 2003 仅含 PER/ORG/LOC/MISC;OntoNotes 5 则包含 PERSON、GPE、ORG、FAC、PRODUCT、EVENT、WORK_OF_ART 等更细类别)。实际项目应先明确标签集合。
如果说 文本分类 是让计算机理解一段话的 主旨大意(比如判断情感是积极还是消极),那么 NER 就是让它更进一步,学会从文本中**“抓住重点”**,精准地找出谁(Who)、在哪(Where)、做了什么(What)等关键信息。它使得机器能够“抓住”文本中的关键信息,是许多更高级NLP任务的基础:
- 知识图谱构建: 从海量文本中抽取出实体及其关系,是构建知识图谱的第一步。
- 信息抽取: 帮助机器从无结构的文本中,整理出结构化的信息。
- 搜索引擎优化: 通过识别查询中的实体,提供更精准、更结构化的搜索结果。
- 智能问答/对话系统: 理解用户意图,从用户的提问中抽取出关键实体,给出准确回答。
以医疗领域为例,NER可以从电子病历、医学文献等海量文本中,像专业医生一样抽取出关键信息,例如:
- 疾病诊断: "II型糖尿病"
- 症状描述: "多饮", "多食"
- 检查方式: "血糖检测"
- 治疗方案: "二甲双胍口服"
通过这些抽取出的实体,可以高效地构建医疗知识图谱,为辅助诊断系统、临床研究等提供强大的数据支持。
每当你在搜索引擎中输入问题并立即得到结构化的“知识卡片”时,背后就有 NER 技术在默默工作。一个典型的流程如下:
- 用户输入 Query:例如,“姚明的身高是多少?”。
- Query 理解:后台对 Query 进行一系列处理,尝试“读懂”用户的真实意图。
- 基础处理:分词、词性标注、纠错等。
- 深度理解:
- 意图识别:首先判断 Query 属于哪种意图,例如“属性查询”、“汇率转换”、“单位换算”等。
- 命名实体识别:根据识别出的意图,有针对性地抽取相应的实体。
- 属性查询 ("姚明的身高是多少?"):抽取“主体”(姚明)和“属性类型”(身高)。
- 汇率转换 ("50欧元等于多少日元"):抽取“源汇率金额”(50)、“源汇率类型”(欧元)、“目标汇率类型”(日元)。
- 单位换算 ("5英尺等于多少厘米"):抽取“源度量值”(5)、“源单位”(英尺)、“目标单位”(厘米)。
- 信息检索:根据理解结果,从庞大的知识图谱或索引库中精准匹配答案。
- 结果排序与呈现:将最相关的结果排序后,以结构化的方式优先呈现给用户。
在金融、医疗等特定领域的对话系统中,NER 同样扮演着重要角色。
- 智能客服:在电商场景下,用户可能会用多种方式咨询同一个问题,例如:
- "我的快递到哪了?"
- "查一下我的订单"
- "我买的东西发货了吗?" 系统无需理解每句话的细微差别,只需通过NER准确识别出用户的核心意图实体“物流信息”,结合用户信息,即可调用订单查询服务,返回最新的物流状态。
- 智能辅助诊断系统:这是一个多技术融合场景。
- 计算机视觉(CV): 负责处理CT、B超等影像图片,进行病灶识别。
- 自然语言处理(NLP): 医生输入病人的文本描述(查体信息),NLP 模块进行处理:
- NER: 从文本中抽取症状、既往病史等实体。
- 关系抽取/查询: 结合知识库进行推理,辅助医生决策:
[症状 A, 症状 B] => 建议检查 [项目 X][项目 X 的结果] => 得出 [结论 Y][症状 A, 症状 B, 结论 Y] => 高概率诊断 [疾病 Z][疾病 Z, 既往病史 P] => 推荐 [疗法 Q]
与大多数深度学习任务一样,NER 模型也需要“吃”大量的数据才能学到知识。
数据质量在很大程度上决定了模型性能的上限。标注的过程,本质上就是人类在手把手地“教”模型:在给定的文本中,哪些词或短语是什么类型的实体。一份高质量的标注数据集是训练出优秀模型的前提。
- 人工标注:
- 优点: 质量高,可靠。
- 缺点: 成本高,耗时长,是典型的人力密集型工作。
- 工具: 可以使用简单的Excel,也可以开发专门的前端标注平台来提高效率。
- 大语言模型辅助标注:
- 流程: 先使用LLM对数据进行预标注,然后由人工进行校对和修正。
- 优点: 可以显著提高标注效率,将人的角色从“从零创造”变为“审核修正”。
- 缺点: LLM的输出质量不稳定,仍需人工审核以保证数据质量。
- 半监督/迭代式标注:
- 流程:
- 先人工标注一小部分数据,训练一个“学生”模型。
- 用这个“学生”模型去预测大量未标注的数据。
- 人工检查和修正模型的预测结果(通常比从零标注快得多)。
- 将修正后的数据加入训练集,训练出更强的“学生”模型。
- 重复以上过程,不断迭代优化。
- 流程:
实现命名实体识别的技术路径多种多样,从简单高效的规则匹配到复杂强大的深度学习模型。选择哪种方法,往往需要在项目初期的效果、成本和开发周期之间做出权衡。下面我们来探讨几种主流的实现方案。
这是最传统和简单的方法。通过维护一个包含各种实体词汇的字典(例如,一个巨大的地名词典),然后在文本中进行字符串匹配。
- 优点: 实现简单、速度快,对于特定、封闭领域的实体,准确率可能很高。
- 缺点: 泛化能力差,无法识别字典外的新词(新出现的人名、公司名等);规则的维护成本极高。
目前常见的 NER 实现方式1。它将 NER 任务转化为了一个 序列标注 问题——即为文本序列中的每一个 token(通常是字或词)打上一个预定义的标签。
这种方法为每个 Token 预测其在实体中扮演的角色,是序列标注最经典的思想。
- 标注体系:
- BMES:
B(Begin),M(Middle),E(End),S(Single) - BIO:
B(Begin),I(Inside),O(Outside)
- BMES:
- 示例:
- 文本 (x):
西 安 的 大 雁 塔 门 票 多 少 钱 - BMES 体系 (y):
B-LOC E-LOC O B-LOC M-LOC E-LOC B-ATTR E-ATTR O O O - BIO 体系 (y):
B-LOC I-LOC O B-LOC I-LOC I-LOC B-ATTR I-ATTR O O O
上例中,
LOC代表“地点”(Location),如“西安”和“大雁塔”;ATTR代表“属性”(Attribute),此处指“门票”。 - 文本 (x):
- 模型结构:
Token Embedding层 -> 序列模型层 -> Token分类层- Token Embedding/Encoder 表示: 可以是静态的
nn.Embedding;更常见的是直接使用预训练 Encoder(如BERT/RoBERTa/DeBERTa等)的上下文表示。 - Token Classify:
- Softmax: 对每个Token独立分类,类别总数为
1 (非实体) + 实体类别数 * 4(以BMES为例)。 - CRF: 在 Softmax 基础上,额外学习标签之间的转移概率(如 B-LOC 后更可能是 M-LOC),对整个序列进行全局最优解码。对 RNN/CNN 等编码器通常有效;但在强大的预训练 Encoder(如 BERT)上收益在不少数据集上已变小,是否采用以实验为准。
- Softmax: 对每个Token独立分类,类别总数为
- Token Embedding/Encoder 表示: 可以是静态的
- 缺点: 无法解决实体嵌套问题。例如,对于文本“他就读于北京大学”,这种方法很难同时识别出“北京”(地名)和“北京大学”(组织机构名)这两个存在包含关系的实体。一个更理想的嵌套实体示例如下:
{ "token": ["他", "就", "读", "于", "北", "京", "大", "学"], "span": [ {"type": "LOC", "start": 4, "end": 5}, {"type": "ORG", "start": 4, "end": 7} ] }
这类方法主要是为了解决实体嵌套问题,是当前处理复杂 NER 场景的主流方案之一。
- 片段网络23:
- 思路: “暴力美学”——枚举出文本中所有可能的连续片段,然后用一个分类器去判断每一个片段属于哪个实体类型(或是“非实体”)。
- 示例: 对于文本 “美式咖啡”,片段网络会进行如下操作:
- 枚举所有候选片段:
- 长度为 1:
美,式,咖,啡 - 长度为 2:
美式,式咖,咖啡 - 长度为 3:
美式咖,式咖啡 - 长度为 4:
美式咖啡
- 长度为 1:
- 对每个片段进行分类:
美式->PROD咖啡->PROD美式咖啡->PROD- (所有其他片段) ->
O(非实体)
- 枚举所有候选片段:
- 优点: 理论上可以解决实体嵌套问题。
- 缺点:
- 计算量大: 候选片段数量随文本长度平方级增长(如上例,4个字的文本就产生了10个候选片段)。
- 样本不均衡: 绝大多数片段都是非实体,导致正负样本严重失衡,训练困难。
- 指针网络4:
- 思路: 与其为每个 token 打一个固定的 BMES 标签,指针网络的思想是为每个 token 训练多个独立的二分类器,分别判断它是否是“某类实体的开头”以及“某类实体的结尾”。这种方式非常适合作为生成候选片段的第一步。
- 示例: 对于句子 “来一杯星巴克的美式咖啡”,如果我们想同时识别出“星巴克”(机构名)以及嵌套的“美式”、“咖啡”、“美式咖啡”(产品名),指针网络的输出会是这样:
Token is_ORG_start is_ORG_end is_PROD_start is_PROD_end ... 来 0 0 0 0 ... 一 0 0 0 0 ... 杯 0 0 0 0 ... 星 1 0 0 0 ... 巴 0 0 0 0 ... 克 0 1 0 0 ... 的 0 0 0 0 ... 美 0 0 1 0 ... 式 0 0 0 1 ... 咖 0 0 1 0 ... 啡 0 0 0 1 ... - 候选生成: 得到预测后,后处理程序会按实体类型分别进行“开头-结尾”配对:
- ORG 类型:
星(start) +克(end) =>星巴克 - PROD 类型:
美(start) +式(end) =>美式咖(start) +啡(end) =>咖啡美(start) +啡(end) =>美式咖啡
- ORG 类型:
通过这种“判断边界,再组合配对”的方式,指针网络巧妙地生成了所有可能的实体片段(包括嵌套的),为后续的分类环节提供了高质量的候选。
- 指针网络 + 片段网络5:
- 思路: 结合两者的长处,形成一个高效的两阶段流程。
- 候选生成 (指针网络): 先使用指针网络高效地预测出所有可能的实体“开头”和“结尾”。
- 候选组合: 将所有合法的“开头-结尾”配对,组合成候选实体片段。这个过程极大地减少了候选片段的数量,过滤掉了绝大多数无意义的组合。
- 候选分类 (片段网络): 再使用片段网络对这些数量大大减少的 候选片段 进行分类。
- 优点: 既能解决嵌套问题,又有效降低了计算量,是解决复杂 NER 问题的有效方案。
- 思路: 将 NER 任务分解为两步:先调用一个现成的分词模型将文本切分成词语,然后对每个 词语 进行分类。
- 示例:
- 原始文本:
西安的大雁塔门票多少钱 - 分词结果:
西安,的,大雁塔,门票,多少,钱 - 分类结果:
LOC,O,LOC,ATTR,O,O
- 原始文本:
- 缺点: 效果高度依赖上游分词模型的质量。如果分词出错(例如,将“大雁塔”错分成“大雁”和“塔”),实体识别基本不可能正确,可谓“一步错,步步错”。
随着大语言模型的发展,也可以将 NER 任务统一到生成框架下,通过精心设计的 Prompt 来“指令”模型完成任务。
- 思路: 将原始文本作为输入的一部分,让模型直接生成包含实体信息的结构化文本。
- 实现方式: 核心思路是通过设计不同的输入输出格式(Prompt 工程),将 NER 任务转化为一个序列到序列(Seq2Seq)的生成任务。
- 方式一:标准 Encoder-Decoder 模式
- 思路: 将原始文本喂给 Encoder,然后训练 Decoder 生成格式化的实体字符串。
- 训练细节:
- Encoder 输入 (x):
西安的大雁塔门票多少钱 - Decoder 输入 (y_in):
[START] 地点实体:西安[SEP]大雁塔[SEP];属性实体:门票 - Decoder 目标 (y_out):
地点实体:西安[SEP]大雁塔[SEP];属性实体:门票 [END]
- Encoder 输入 (x):
- 方式二:带“实体清单”提示的 Encoder-Decoder
- 思路: 在原始文本后附加上下文提示(Context Prompting),明确告知模型需要关注哪些实体类型,以约束模型的输出空间,提高准确性。
- Encoder 输入 (x):
西安的大雁塔门票多少钱 | 可选实体列表为:[地点实体, 属性实体, ...] - Decoder 目标 (y_out):
地点实体:西安[SEP]大雁塔[SEP];属性实体:门票 [END]
- 方式三:续写式生成 (Decoder-Only)
- 思路: 将输入和输出拼接成一个完整的字符串,训练一个 GPT 风格的自回归模型来“续写”出实体部分。
- 统一输入输出:
西安的大雁塔门票多少钱 | 可选实体列表为:[地点实体, 属性实体, ...] | 地点实体:西安[SEP]大雁塔[SEP];属性实体:门票 [END] - 训练: 将整段文本作为模型的输入进行训练,但 只计算 模型在预测“|”符号之后内容时的损失。前面的部分作为已知上下文,不计入损失。
- 推理: 只提供前半部分(
... |之前的内容),让模型续写出后面的实体。
- 方式一:标准 Encoder-Decoder 模式
- 优缺点:
- 优点:
- 统一的生成框架: 将实体抽取任务完全转化为一个“文本到文本”的任务,单个模型就能直接生成包含复杂结构(如实体嵌套)的结果,输出形式非常灵活。
- Few-shot/Zero-shot: 强大的LLMs能极大减少数据标注成本,在很多场景下无需训练就能获得不错的效果。
- 缺点:
- 输出不稳定: 结果格式可能不统一,需要额外的后处理逻辑来解析。
- 幻觉问题: 可能生成文本中不存在的实体。
- 优点:
在第七章中,我们已经学习并实践了文本分类任务,并了解了 NLP 项目的通用流程。命名实体识别作为另一项 NLP 任务,其项目实现思路在宏观上遵循着相同的流程。在深入探讨具体代码实现之前,本章将再次遵循 数据处理 -> 模型构建 -> 训练、评估与持久化 这套标准流程,勾勒出一个标准 NER 项目的实现思路。
作为模型训练的起点,数据质量在很大程度上决定了模型性能的上限。
-
主要流程:
- 分词/分Token: 将原始文本切分为Token序列。
- Token与ID映射: 构建词表,将每个Token映射为一个唯一的数字ID。
- 标签与ID映射: 构建标签表,将
B-LOC,I-LOC等标签也映射为唯一的数字ID。
-
数据增强: 在标注数据有限的情况下,可以通过代码“创造”一些新的、合理的数据,以提升模型的泛化能力和鲁棒性。
- 实体替换: 准备同类型实体的词典(如地名词典、时间词典、天气现象词典),随机替换原始文本中的实体。
- 示例:
“查一下北京明天会下雨吗”->“查一下深圳明天会下雨吗”。 - 说明: 这种方式能教会模型学习 上下文语境,而不是死记硬背具体的实体词。
- 示例:
- 引入噪声: 模拟真实输入数据中的错误,随机地对文本进行微小改动。
- 示例:
“查一下北京明天会下雨吗”->“查一下北京明天汇下雨吗”(同音字替换)。
- 示例:
- 随机遮盖:
- 方法: 在训练时,随机地将文本中的一小部分(非实体)词元替换为
<UNK>(未知)标记。 - 示例:
“查一下北京明天会下雨吗”->“查一下北京<UNK>天会下雨吗”。 - 目的: 这种方式会强制模型在部分信息缺失的情况下,更多地依赖上下文来做出判断,从而有效降低过拟合风险,提升模型的泛化能力。
- 方法: 在训练时,随机地将文本中的一小部分(非实体)词元替换为
- 拼音替换: 将少量中文词替换为拼音,模拟弱规范输入。
- 示例:
“查一下北京明天会下雨吗”->“cha yi xia bei jing ming tian hui xia yu ma”。
- 示例:
- 实体替换: 准备同类型实体的词典(如地名词典、时间词典、天气现象词典),随机替换原始文本中的实体。
在医疗等强约束领域做“实体替换”时,应确保替换后的样本不破坏实体间的真实语义关系(如疾病-症状-药物的搭配约束),否则可能引入反效果。
-
经典组合:
Embedding层 + 动态词向量编码器 (如BERT, Bi-LSTM等) + Token分类层 (如全连接层+Softmax/CRF)。 -
输入与输出:
- 模型输入: 形状通常为
[N, T]的Tensor,其中N是批次大小 (Batch Size),T是序列长度 (Sequence Length),内容是Token ID。 - 模型输出: 形状为
[N, T, num_classes]的Tensor,代表每个Token在所有num_classes个类别上的置信度得分。
- 模型输入: 形状通常为
-
迁移学习与微调: 在实践中,通常使用在通用领域预训练好的模型(如BERT)作为初始化参数,然后进行微调。
- 策略一:冻结参数
- 将迁移过来的预训练模型参数冻结 (
requires_grad = False),只训练自己新增的分类层。速度快,但效果可能受限。
- 将迁移过来的预训练模型参数冻结 (
- 策略二:同等处理
- 将迁移过来的参数和新增的参数视为一体,使用相同的学习率和更新逻辑进行训练。
- 策略三:差分学习率微调
- 为迁移过来的参数设置一个非常小的学习率,进行“微调”,使其在保留通用知识的基础上,向新任务的领域知识“稍稍靠近”;同时为新增的、随机初始化的参数设置一个正常的学习率,使其能快速收敛。
- 策略四:分层冻结 / 部分冻结
- 例如仅冻结 BERT 的前若干层,让后几层与分类头共同更新;在算力有限或数据较少时,常是较好的折中。
- 策略一:冻结参数
这是连接数据和模型,产出最终模型的重要循环。
- 训练循环: 迭代
train_dataloader,在每个批次上执行模型前向传播、计算损失、反向传播和参数更新。 - 评估循环与指标:
- 迭代
eval_dataloader,计算模型在验证集上的性能。 - Token级别指标: 计算Token分类的准确率、F1值等,用于监控训练过程。
- 实体级别指标 (核心): 计算实体片段的精确率 (Precision)、召回率 (Recall) 和 F1值。这是衡量模型最终效果的核心标准。
- 迭代
- 标签序列 -> 实体片段 解码要点(以 BMES/BIO 为例):
- 从左到右扫描标签序列,遇到
B-T开始一个新片段(类型T)。 - 在 BMES 中,
M-T继续片段,E-T结束片段;S-T表示单字实体;O表示非实体。 - 在 BIO 中,
I-T继续片段,遇到类型不一致或O时关闭当前片段。 - 记录每个片段的
(start, end, type)边界与类型,用于与标注集对齐计算 P/R/F1。
- 从左到右扫描标签序列,遇到
- 持久化: 在评估过程中,根据 实体级别 的评估指标结果(如验证集F1值达到新高),决定是否将当前模型的参数保存到磁盘。
Footnotes
-
[Lample, G., Ballesteros, M., Subramanian, S., Kawakami, K., & Dyer, C. (2016). Neural Architectures for Named Entity Recognition.] (https://arxiv.org/abs/1603.01360) ↩
-
[Sohrab, M. G., & Miwa, M. (2018). Deep Exhaustive Model for Nested Named Entity Recognition.] (https://arxiv.org/abs/1810.12148) ↩
-
[Eberts, M., & Ulges, A. (2019). Span-based Joint Entity and Relation Extraction with Transformer Pre-training (SpERT).] (https://arxiv.org/abs/1909.07755) ↩
-
[Vinyals, O., Fortunato, M., & Jaitly, N. (2015). Pointer Networks.] (https://arxiv.org/abs/1506.03134) ↩
-
[Shen, Y., Ma, X., Tan, Z., Zhang, S., Wang, W., & Lu, W. (2021). Locate and Label: A Two-stage Identifier for Nested Named Entity Recognition.] (https://arxiv.org/abs/2105.06804) ↩