命名实体识别(NER):从文本中精准提取关键信息的完整指南

目录


NER概述与应用

命名实体识别(Named Entity Recognition, NER)是**自然语言处理的核心基础任务**。它的目标是,从一段自由文本中: 1. 精确找出哪些词属于“实体” 2. 将这些实体划分到**预定义的类别**中

换句话说,NER 就是让机器读懂文本里“谁、哪里、什么组织、什么时候、多少钱”这类关键信息。

常见预定义实体类型

标签简写英文全称中文说明典型示例
PERPerson人名实体小明、雷军、屠呦呦
ORGOrganization组织机构清华大学、字节跳动、联合国
LOCLocation地理位置北京、珠穆朗玛峰、太平洋
TIMETime时间表达式2024年、昨天、上午10点
MONEYMoney货币金额100元、$999、五百万
PRODProduct产品名称iPhone 16、华为Mate XT、特斯拉Model Y

这些类别可以根据业务需求灵活扩展,比如医疗场景下会有“症状”“药品”等实体,金融场景下会有“公司”“金额”“日期”等。

核心应用场景

NER 通常是信息抽取的第一步,为更复杂的应用铺路:

领域具体用途落地示例
搜索引擎关键词高亮、实体链接搜索“马化腾”时,同时高亮“腾讯”等关联公司
智能客服意图理解、槽位填充“查询订单AB123456的退款进度”中,识别订单号、操作类型
金融风控风险实体识别从交易文本中抽取出涉黑公司、可疑金额
医疗健康医学实体抽取从病历中抽取症状、药品、诊断结果

理解了它能做什么,我们再来看 NER 在技术上到底怎么实现。


BIO标注法详解

NER 通常被建模为一个序列标注问题:给定一串字符(或词),为每个位置打上一个标签。BIO 标注法是这个领域 最通用、最易实现 的标注方式。

核心标注规则

标签前缀含义说明
B-实体的开始实体的第一个词/字符
I-实体的内部实体的后续词/字符
O非实体不属于任何预定义实体

例如,“小明在北京大学读书”这句话,如果按字符级别标注,结果如下:

原文本:  小  明  在  北  京  大  学  读  书
字符索引: 0  1  2  3  4  5  6  7  8
BIO标签:  B-PER I-PER O B-ORG I-ORG I-ORG I-ORG O O
恢复实体: 小明(PER)、北京大学(ORG)

为什么中文通常按字符而不是词来做标注?
因为分词本身就可能出错,如果“北京大学”被错误切分成“北京”和“大学”,实体的边界就很难对齐。基于字符的标注可以避免这种错误传播,让模型直接学习字符之间的实体边界。

如果需要更精细的边界控制,可以使用 **BIOES 标注法**。它在 BIO 的基础上增加了两种标签: - **S-xxx**:单独实体,例如“深圳”只用一个标签 S-LOC 表示 - **E-xxx**:实体结尾,例如“北京大学”的“学”会标为 E-ORG

在实际项目里,BIO 已经能覆盖绝大多数场景。当你发现模型经常把实体的边界弄错时,再考虑升级到 BIOES。


中文NER数据集与预处理

常用公开中文数据集

想要训练自己的 NER 模型,你需要有标注好的数据。以下是几个高质量、免费的中文 NER 数据集:

数据集名称数据规模实体类型来源领域下载渠道
MSRA~5万句PER/ORG/LOC新闻CLUE基准
OntoNotes 4~1.6万句PER/ORG/LOC/MISC多领域CoNLL 2012
Weibo~1.3万句PER/ORG/LOC/TIME社交媒体LTP工具包
Resume~1.7万句NAME/ORG/RACE 等 8 类简历哈工大

选择数据集时要注意:如果你做的是社交媒体分析,用 Weibo 数据集更贴近真实场景;如果做简历解析,Resume 数据集就非常合适。

核心预处理:文本与实体互转

拿到数据后,最常见的预处理操作就是在原始文本BIO 标签序列之间互相转换。下面是一个通用的处理工具类:

class NERDataProcessor:
    """
    中文NER预处理器:字符级BIO标注与实体恢复
    """
    def __init__(self, entity_types: list = None):
        if entity_types is None:
            entity_types = ["PER", "ORG", "LOC"]
        self.entity_types = entity_types

    def text_to_bios(self, text: str, entities: list) -> tuple:
        """
        文本转字符级BIO标签
        entities格式:[(start_char, end_char, label), ...]
        """
        char_labels = ["O"] * len(text)
        # 按起始位置排序,防止实体覆盖
        for s, e, l in sorted(entities, key=lambda x: x[0]):
            if 0 <= s < e <= len(text) and l in self.entity_types:
                char_labels[s] = f"B-{l}"
                for i in range(s + 1, e):
                    char_labels[i] = f"I-{l}"
        return list(text), char_labels

    def bios_to_entities(self, chars: list, labels: list) -> list:
        """
        从字符级BIO标签恢复完整实体
        返回格式:[(实体文本, 标签, 起始位置, 结束位置), ...]
        """
        entities, curr = [], None
        for i, (c, l) in enumerate(zip(chars, labels)):
            if l.startswith("B-"):
                if curr:
                    entities.append(("".join(curr[0]), curr[1], curr[2], i))
                curr = ([c], l[2:], i)
            elif l.startswith("I-") and curr and curr[1] == l[2:]:
                curr[0].append(c)
            else:
                if curr:
                    entities.append(("".join(curr[0]), curr[1], curr[2], i))
                curr = None
        if curr:
            entities.append(("".join(curr[0]), curr[1], curr[2], len(chars)))
        return entities

这个类做了两件事:

  1. text_to_bios:给定文本和实体区间(例如 [(0,2,"PER")]),生成每个字符的标签。
  2. bios_to_entities:从字符序列和标签序列中把实体重新“拼接”出来,这在模型预测后还原结果时非常有用。

你可以借助它快速查看训练样本的标注效果,或者在模型推理后把标签转换成人类可读的实体。


基于BERT的NER实现

今天最主流的做法是基于预训练语言模型(如 BERT)来完成 NER。好消息是,社区已经有很多预训练好并且微调到 NER 任务上的中文模型,你甚至不用自己训练就能直接使用。

使用现成模型快速推理

Hugging Face 的 transformers 库提供了一个非常方便的 pipeline 接口:

from transformers import pipeline

# 推荐的中文开源模型:ckiplab/bert-base-chinese-ner
ner_pipeline = pipeline(
    task="token-classification",
    model="ckiplab/bert-base-chinese-ner",
    aggregation_strategy="simple"  # 自动聚合相邻的同类型实体
)

# 推理示例
text = "雷军在北京小米科技园发布了小米15"
results = ner_pipeline(text)

# 格式化输出
print("识别结果:")
for res in results:
    print(f"[{res['entity_group']}] {res['word']} (置信度:{res['score']:.2f})")

输出可能类似:

识别结果:
[PER] 雷军 (置信度:0.99)
[LOC] 北京 (置信度:0.98)
[ORG] 小米科技园 (置信度:0.96)
[PROD] 小米15 (置信度:0.94)
模型库中还有专门针对不同领域的 NER 模型,比如新闻、社交媒体、医疗等。如果开源模型在你的数据上效果不够好,再考虑用自有数据做微调。

模型微调与评估

虽然直接用现成模型很方便,但如果你的业务中实体类型很特殊(比如内部产品代号、特定行业的术语),微调就是必不可少的了。

微调核心流程(精简版)

微调 BERT 做 NER 通常包含几个关键步骤:

  1. 数据对齐:把你的字符级 BIO 标签对应到 BERT 的 token 级别。BERT 可能会把一个字符拆成多个子词(token),这时需要合理处理标签(例如把子词的标签都设成同一个实体的 I- 标签)。
  2. 模型适配:在预训练 BERT 的基础上加一个分类头,将隐藏层的输出映射到 num_labels 个类别上。
  3. 训练与评估:使用交叉熵损失进行训练,并用实体级别的 F1 分数进行评测。

实体级评估指标

评估 NER 模型时,不能只看 token 级别的准确率。因为大部分 token 都是 'O'(非实体),模型就算全预测成 'O' 也能拿到很高的准确率,却一个实体都没识别出来。所以我们要用实体级 F1 分数,它要求实体的边界和类型都完全正确才算预测成功。

使用 seqeval 库可以很方便地进行实体级评估:

from seqeval.metrics import f1_score, classification_report

# 示例:真实标签与预测标签(token级序列)
y_true = [["B-PER", "I-PER", "O", "B-ORG", "I-ORG", "I-ORG"]]
y_pred = [["B-PER", "O",     "O", "B-ORG", "I-ORG", "I-ORG"]]

# 实体级评估
print(f"实体级F1分数:{f1_score(y_true, y_pred):.2f}")
print("\n分类报告:")
print(classification_report(y_true, y_pred))

在这个例子中,预测把“小明”的“明”错标成了 O,导致“小明”这个实体没能被完整召回,F1 分数就会下降。classification_report 还会详细给出每个实体类别的准确率、召回率和 F1,帮你定位问题。


实际应用案例

电商客服意图槽位提取

NER 在对话系统里最常见的落地形式就是槽位填充。比如一个电商客服的查询:“帮我查下订单号AB123456的退款,昨天付的999元”,我们需要从中提取出产品、订单号、时间、金额等信息。

下面是一个简化的实现框架:

class EcomNER:
    def __init__(self, model_name="your-finetuned-ecom-ner"):
        # 加载你自己微调过的电商NER模型
        self.ner = pipeline(
            "token-classification",
            model=model_name,
            aggregation_strategy="simple"
        )

    def extract_slots(self, query: str) -> dict:
        """
        从用户查询中提取电商核心槽位
        """
        slots = {"product": [], "order_id": [], "time": [], "amount": []}
        results = self.ner(query)
        for res in results:
            eg = res["entity_group"].lower()
            if eg in slots:
                slots[eg].append(res["word"])
        return slots

# 演示
ecom_ner = EcomNER()
query = "帮我查下订单号AB123456的退款,昨天付的999元"
print("用户查询:", query)
print("提取槽位:", ecom_ner.extract_slots(query))

如果模型训练得当,输出会类似于:

用户查询: 帮我查下订单号AB123456的退款,昨天付的999元
提取槽位: {'product': [], 'order_id': ['AB123456'], 'time': ['昨天'], 'amount': ['999元']}

拿到这些结构化的槽位后,客服系统就可以直接去后台查询订单、匹配金额和时间,大大提升自动处理率。


相关教程

1. 先搞清楚 **BIO 标注法** 和字符级预处理,这是所有工作的基础。 2. 用 Hugging Face Hub 上的开源模型直接跑通推理,获得直观感受。 3. 在你的场景下用少量标注数据做一次微调,对比微调前后的效果。 4. 实际项目中,优先保证 **数据标注质量**,这比调整模型结构更重要。

🔗 扩展阅读

📂 所属阶段:第四阶段 — 预训练模型与迁移学习(应用篇)