3. 命名实体识别(NER)入门
好,咱们今天聊聊NER。说白了,就是让机器从一堆文字里,把「人名、地名、机构名、时间」这些关键信息给揪出来。
我刚开始接触这个任务时,觉得挺简单——不就是找关键词吗?后来才发现,这里面的坑多着呢。比如「苹果」到底是水果还是公司?「华盛顿」是地名还是人名?嗯,这就是NER要解决的问题。
3.1 NER任务定义
命名实体识别,全称是Named Entity Recognition。它的目标很明确:给定一段文本,标注出每个词的实体类别。
举个例子:
输入: "马云在杭州创立了阿里巴巴"
输出: [马云]PER [杭州]LOC [阿里巴巴]ORG
常见的实体类型包括:
| 标签 | 含义 | 例子 |
|---|---|---|
| PER | 人名 | 张三、特朗普、李白 |
| LOC | 地名 | 北京、纽约、太平洋 |
| ORG | 机构名 | 腾讯、联合国、哈佛大学 |
| TIME | 时间 | 2024年、昨天、三点 |
核心要点:NER本质上是一个序列标注问题。每个词都要被分配一个标签,而且标签之间是有依赖关系的——「阿里巴巴」是一个整体,不能拆开标。
3.2 基于规则的方法
最早期的NER,全靠人工写规则。我2016年刚入行时,还在用这种方法做金融新闻的实体抽取。
规则法的核心思路:
- 词典匹配:准备一个实体词典,直接查表
- 正则表达式:比如匹配「XX省XX市」这种模式
- 上下文规则:比如「董事长」后面通常跟人名
举个例子:
import re
def rule_based_ner(text):
entities = []
# 匹配公司名:XX有限公司
pattern1 = r'[\u4e00-\u9fa5]+有限公司'
for match in re.finditer(pattern1, text):
entities.append((match.group(), 'ORG'))
# 匹配人名:姓+名(常见姓氏)
surnames = ['张', '李', '王', '赵', '刘']
for surname in surnames:
pattern2 = surname + r'[\u4e00-\u9fa5]{1,2}'
for match in re.finditer(pattern2, text):
entities.append((match.group(), 'PER'))
return entities
text = "张伟在华为有限公司工作"
print(rule_based_ner(text))
# 输出:[('张伟', 'PER'), ('华为有限公司', 'ORG')]
我曾经踩过的坑:规则法最大的问题是「过拟合」。你为金融新闻写的规则,放到医疗文本上就全废了。而且规则之间经常冲突,维护起来像在拆炸弹。
规则法的优缺点很明显:
- 优点:简单、可解释性强、不需要标注数据
- 缺点:泛化能力差、维护成本高、遇到新词就抓瞎
3.3 基于深度学习的方法(BiLSTM-CRF)
后来深度学习火了,大家发现——与其手写规则,不如让模型自己学。这里我重点讲一个经典模型:BiLSTM-CRF。
为什么叫这个名字?说白了就是两个组件拼在一起:
- BiLSTM:双向长短期记忆网络,负责理解上下文
- CRF:条件随机场,负责保证标签的合理性
我的理解:BiLSTM就像个「阅读理解」高手,能看懂每个词在句子里的角色。CRF像个「规则警察」,确保输出的标签序列不犯低级错误——比如不会出现「I-ORG」后面跟着「B-PER」这种荒唐事。
来看一个简化版的实现:
import torch
import torch.nn as nn
class BiLSTM_CRF(nn.Module):
def __init__(self, vocab_size, tag_size, embedding_dim, hidden_dim):
super().__init__()
self.embedding = nn.Embedding(vocab_size, embedding_dim)
self.bilstm = nn.LSTM(
embedding_dim,
hidden_dim // 2,
bidirectional=True,
batch_first=True
)
self.fc = nn.Linear(hidden_dim, tag_size)
# CRF层(简化版)
self.transitions = nn.Parameter(torch.randn(tag_size, tag_size))
def forward(self, x):
emb = self.embedding(x)
lstm_out, _ = self.bilstm(emb)
emissions = self.fc(lstm_out)
# 这里用维特比算法解码
return emissions
训练流程大致是这样的:
- 把句子转成词向量
- BiLSTM读一遍,输出每个词的「得分分布」
- CRF根据转移矩阵,找出最合理的标签序列
- 反向传播,更新参数
我个人的经验是,BiLSTM-CRF在金融事件图谱这个场景下特别好用。为什么?因为金融文本里实体之间的依赖关系很强——「阿里巴巴收购饿了么」这种句子,模型能学会「收购」前后大概率是公司名。
避坑指南:我曾经在训练时忘记做「标签平衡」。金融文本里「ORG」出现的频率远高于「PER」,导致模型学成了「万事皆ORG」。后来加了类别权重,效果才正常。
3.4 两种方法的对比
| 维度 | 规则法 | BiLSTM-CRF |
|---|---|---|
| 数据需求 | 不需要标注数据 | 需要大量标注数据 |
| 泛化能力 | 差,换领域就废 | 好,迁移学习可用 |
| 可解释性 | 高,规则一目了然 | 低,黑盒模型 |
| 维护成本 | 高,规则越加越多 | 低,重新训练即可 |
| 适用场景 | 小规模、固定领域 | 大规模、复杂场景 |
你想想看,如果只是从几十篇新闻里抽公司名,规则法完全够用。但要做金融事件图谱这种大规模工程,还是得上深度学习。
3.5 本章知识体系
下面这张图,是我自己梳理的NER知识脉络:
嗯,这张图把本章的核心逻辑串起来了。从NER任务出发,两条路——规则法和深度学习方法。规则法简单直接,但天花板低;BiLSTM-CRF虽然复杂,但效果更好,也是目前工业界的主流方案。