命名实体识别 (NER):从文本中抽取人名、地名、机构名

📂 所属阶段:第四阶段 — 预训练模型与迁移学习(应用篇)
🔗 相关章节:Hugging Face 实战 · Prompt Engineering 基础


1. NER 概述

1.1 什么是 NER?

NER = Named Entity Recognition

任务:从文本中识别出特定类型的实体

常见实体类型:
├── PER(人名):小明、Alice、张三
├── LOC(地名):北京、上海
├── ORG(机构):清华大学、阿里巴巴
├── TIME(时间):2024年、昨天
└── MONEY(金额):100元、$100

应用场景:
├── 搜索引擎(关键词高亮)
├── 知识图谱(实体抽取)
├── 智能客服(提取用户意图中的实体)
└── 金融风控(识别公司名、金额)
"""

2. BIO 标注法

2.1 BIO 规则

"""
BIO 标注法:

B-xxx:实体的开始(Begin)
I-xxx:实体的中间/结尾(Inside)
O:非实体(Outside)

示例:
小明在北京大学读书。
PER  LOC    ORG
→ B-PER I-PER B-LOC I-ORG O

B-PER + I-PER + I-PER = 一个人名实体:小明
B-LOC + I-ORG = 一个地名+机构:清华大学
"""

2.2 标注数据集

# conll格式示例(NER 标准格式):
小明 B-PER
在 O
北京 B-LOC
大学 I-ORG
读 O
书 O

# 转换为 JSON
ner_data = [
    {"tokens": ["小明", "在", "北京", "大学", "读", "书"],
     "labels": ["B-PER", "O", "B-LOC", "I-ORG", "O", "O"]},
]

3. BERT NER 实现

from transformers import AutoTokenizer, AutoModelForTokenClassification, Trainer
from datasets import load_dataset, load_metric
import torch

# 加载中文 BERT + NER 模型
model_name = "bert-base-chinese"
tokenizer = AutoTokenizer.from_pretrained(model_name)

# 直接用中文 NER 预训练模型
model = AutoModelForTokenClassification.from_pretrained(
    "bert-base-chinese", num_labels=6  # 调整标签数
)

# 使用预训练的中文 NER 模型
from transformers import pipeline
ner_pipeline = pipeline(
    "ner",
    model="bert-base-chinese",  # 或用 fine-tuned 的中文 NER 模型
    aggregation_strategy="simple"
)

result = ner_pipeline("小明在北京大学读书")
print(result)
# [{'entity_group': 'PER', 'word': '小明', 'score': 0.99},
#  {'entity_group': 'ORG', 'word': '北京大学', 'score': 0.97}]

4. 微调 NER 模型

# fine_tune_ner.py
from transformers import AutoTokenizer, AutoModelForTokenClassification, TrainingArguments
from datasets import load_dataset
import numpy as np

def align_labels_with_tokens(labels, word_ids):
    """将词级别标签对齐到 token 级别"""
    aligned_labels = []
    current_word = None
    for word_id in word_ids:
        if word_id is None:
            aligned_labels.append(-100)  # 特殊 token 忽略
        elif word_id != current_word:
            aligned_labels.append(labels[word_id])  # 新词的第一个 token
            current_word = word_id
        else:
            aligned_labels.append(labels[word_id])  # 续 token 用 I-
    return aligned_labels

def tokenize_and_align_labels(examples):
    tokenized = tokenizer(
        examples["tokens"],
        is_split_into_words=True,
        truncation=True,
        max_length=128,
    )
    aligned = []
    for labels, word_ids in zip(examples["labels"], tokenized.word_ids()):
        aligned.append(align_labels_labels_with_tokens(labels, word_ids))
    tokenized["labels"] = aligned
    return tokenized

# 微调代码与分类器类似,只是:
# 1. 模型换成 AutoModelForTokenClassification
# 2. 评估指标换成 seqeval

5. 小结

# NER 速查

# 预训练 NER pipeline
ner = pipeline("ner", aggregation_strategy="simple")
result = ner("文本")

# 标注类型
B-PER  # 人名开始
I-PER  # 人名中间
B-LOC  # 地名开始
I-LOC  # 地名中间
B-ORG  # 机构开始
I-ORG  # 机构中间
O      # 非实体

💡 实践建议:中文 NER 推荐用 bert-base-chinese 微调,或直接用 Hugging Face Hub 上的中文 NER 模型(如 ctext/biaobiao)。


🔗 扩展阅读