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

训练流程大致是这样的:

  1. 把句子转成词向量
  2. BiLSTM读一遍,输出每个词的「得分分布」
  3. CRF根据转移矩阵,找出最合理的标签序列
  4. 反向传播,更新参数

我个人的经验是,BiLSTM-CRF在金融事件图谱这个场景下特别好用。为什么?因为金融文本里实体之间的依赖关系很强——「阿里巴巴收购饿了么」这种句子,模型能学会「收购」前后大概率是公司名。

避坑指南:我曾经在训练时忘记做「标签平衡」。金融文本里「ORG」出现的频率远高于「PER」,导致模型学成了「万事皆ORG」。后来加了类别权重,效果才正常。

3.4 两种方法的对比

维度 规则法 BiLSTM-CRF
数据需求 不需要标注数据 需要大量标注数据
泛化能力 差,换领域就废 好,迁移学习可用
可解释性 高,规则一目了然 低,黑盒模型
维护成本 高,规则越加越多 低,重新训练即可
适用场景 小规模、固定领域 大规模、复杂场景

你想想看,如果只是从几十篇新闻里抽公司名,规则法完全够用。但要做金融事件图谱这种大规模工程,还是得上深度学习。

3.5 本章知识体系

下面这张图,是我自己梳理的NER知识脉络:

NER知识体系 命名实体识别 基于规则的方法 词典匹配 | 正则表达式 | 上下文规则 深度学习方法 BiLSTM + CRF BiLSTM CRF 序列标注 → 实体抽取 → 事件图谱构建

嗯,这张图把本章的核心逻辑串起来了。从NER任务出发,两条路——规则法和深度学习方法。规则法简单直接,但天花板低;BiLSTM-CRF虽然复杂,但效果更好,也是目前工业界的主流方案。


专注资料整理