命名实体识别基础
好,咱们正式开始聊 NER(Named Entity Recognition)。
什么是 NER?
说白了,就是让机器从文本里找出“谁、哪、什么时候、什么组织”。举个例子:
“2024年3月,辉瑞公司宣布其新冠疫苗有效性达95%。”
NER 要抽出来的实体是:2024年3月(时间)、辉瑞公司(组织)、新冠疫苗(药物/产品)。
我在项目里遇到过最坑的事——中文 NER 里的“药品名”和“疾病名”经常混在一起。比如“阿司匹林”是药,但“阿司匹林哮喘”是个病。分词一旦错了,实体边界就全乱套。所以你想想看,NER 不只是打个标签,它要解决边界识别 + 类别分类两个问题。
评价指标:精确率、召回率、F1 值
这仨指标是 NER 模型好坏的核心。我每次跑完实验第一件事就是看 F1,因为它最能体现综合能力。
| 指标 | 含义 | 公式 |
|---|---|---|
| 精确率 (Precision) | 模型预测出的实体中,有多少是真正正确的 | TP / (TP + FP) |
| 召回率 (Recall) | 所有真实实体中,模型找出了多少 | TP / (TP + FN) |
| F1 值 | 精确率和召回率的调和平均 | 2 × P × R / (P + R) |
避坑提醒:我曾经在一个药物实体抽取项目里,模型 Precision 冲到 98%,但 Recall 只有 20%——它只挑了最容易的实体,根本不能用。实际场景里宁可 Recall 高一点,漏掉几个好过全吞掉。
计算时通常用 严格匹配(边界和类型都对才算 TP)或 宽松匹配(部分重叠也算)。我建议先跑严格匹配,再看细粒度错误。
一个简单的评估代码片段:
def compute_metrics(y_true, y_pred):
tp = sum(1 for t, p in zip(y_true, y_pred) if t == p and t != 'O')
fp = sum(1 for t, p in zip(y_true, y_pred) if t == 'O' and p != 'O')
fn = sum(1 for t, p in zip(y_true, y_pred) if t != 'O' and p == 'O')
p = tp / (tp + fp + 1e-8)
r = tp / (tp + fn + 1e-8)
f1 = 2 * p * r / (p + r + 1e-8)
return p, r, f1
经典方法概述
NER 的方法论经历了三代变化。我个人习惯按“规则 → 统计 → 深度学习”的时间线来理解:
| 类别 | 代表方法 | 核心思路 | 我踩过的坑 |
|---|---|---|---|
| 规则方法 | 正则表达式、词典、上下文模板 | 人工编写模式,匹配固定格式 | 曾花两周写中药名词典,结果数据里出现“复方丹参滴丸(薄膜衣)”这种变体,词典根本不够用。 |
| 统计方法 | HMM、CRF、MEMM | 用标注数据训练概率模型,CRF 尤其擅长处理标签间的依赖 | CRF 特征工程非常痛苦,一个特征忘写就直接掉 5 个点。 |
| 深度学习方法 | BiLSTM-CRF、BERT + CRF、Transformer | 自动学习上下文表示,端到端训练 | 现在主流方案,但推理速度比规则慢一个数量级。 |
千万别上来就搞 BERT: 如果数据量连 500 条带标注句子都没有,先试试 CRF + 人工特征,效果往往更好、更可控。
下面这张图展示了从文本到实体的基本流程,也是我经常在项目文档里贴的那张:
从图上能看出,规则方法速度快但扩展性差,深度学习精度高但门槛高。我现在的建议是:
- 冷启动阶段:先用规则产出第一批标注数据,再交给 CRF 迭代。
- 数据量上万:上 BiLSTM-CRF 或 BERT + CRF,注意平衡训练时间。
- 生产部署:通常规则 + 模型串联,规则做前置过滤,模型做精排。