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

目录


NER概述与应用

命名实体识别(Named Entity Recognition, NER)是**自然语言处理的核心基础任务**,目标是从非结构化文本中: 1. 识别出实体的**精确边界** 2. 对实体进行**预定义分类**

常见预定义实体类型

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

核心应用场景

NER是信息抽取的第一步,为知识图谱、问答系统等高级应用提供支撑:

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

BIO标注法详解

BIO是序列标注任务中最通用、易实现的标注方法,通过标签前缀区分实体边界与类型。

核心标注规则

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

中文文本标注示例

中文通常按字符分词(避免分词错误影响实体边界):

原文本:小 明 在 北 京 大 学 读 书
BIO标签:B-PER I-PER O B-ORG I-ORG I-ORG O O
恢复实体:小明(PER)、北京大学(ORG)
如果需要更精细的边界控制,可以使用**BIOES标注法**: - S-xxx:单独实体(如“深圳”) - E-xxx:实体结尾(如“北京大学”的“学”)

中文NER数据集与预处理

常用公开中文数据集

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

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

class NERDataProcessor:
    """
    中文NER预处理器:字符级BIO标注与实体恢复
    """
    def __init__(self, entity_types: list = ["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

基于BERT的NER实现

使用现成预训练模型快速推理

Hugging Face Hub上有大量预微调好的中文NER模型,可直接用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})")

模型微调与评估

微调核心流程(精简版)

  1. 数据对齐:将字符级标签对齐到BERT的token级(处理子词、特殊token)
  2. 模型适配:把预训练BERT的输出层替换为num_labels类的分类层
  3. 训练评估:使用seqeval库计算实体级F1(核心指标)

实体级评估指标

NER不能只用token级准确率,实体级F1分数才是关键:

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))

实际应用案例

电商客服意图槽位提取

class EcomNER:
    def __init__(self):
        # 加载预微调的电商NER模型(可替换为自己训练的)
        self.ner = pipeline("token-classification", model="your-finetuned-ecom-ner", 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))

相关教程

1. 先掌握**BIO标注法**和字符级预处理 2. 直接用Hub上的开源模型跑通推理 3. 用小批量数据微调后对比效果 4. 实际项目中优先保证**数据标注质量**

🔗 扩展阅读

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