第三章:基于规则的方法:手工规则构建、正则表达式在NER中的应用、词典匹配方法、优缺点分析

说实话,现在很多人一上来就搞深度学习做NER。但我得说一句——规则方法,永远不过时。

我在工业界做过好几个项目,初期数据标注还没到位的时候,全靠规则顶着。你想想看,业务方催着上线,标注团队还在吵架讨论实体边界,这时候谁先出活儿?

嗯,就是规则。

3.1 手工规则构建:老工程师的看家本领

手工规则,说白了就是人肉写if-else。但别小看它。我见过一个金融项目,用规则做公司名识别,准确率干到了92%。

怎么构建?我一般分三步走:

  1. 观察数据——先看100条样本,找出实体出现的模式
  2. 抽象规则——把模式转成逻辑条件
  3. 迭代验证——跑一批数据,看漏了什么,补上

举个例子,识别中文日期:

# 伪代码示例
if token in ["今天", "明天", "昨天"]:
    return "DATE"
if token matches r"\d{4}年\d{1,2}月\d{1,2}日":
    return "DATE"
if token matches r"周[一二三四五六日]":
    return "DATE"

我在项目中遇到过最头疼的事——规则冲突。比如“北京”既是地名,又是公司名的一部分。我的解决办法是:优先级排序。长实体优先,专有名词优先。

我的小技巧:规则不要写太死。留一些模糊匹配的口子,比如“大约”、“左右”这些词可以忽略。

3.2 正则表达式在NER中的应用:文本挖掘的瑞士军刀

正则表达式,每个NLP工程师都得会。我面试人的时候,正则写不利索的,基本直接pass。

为什么?因为正则太实用了。尤其是在NER里,它负责干三件事:

  • 模式匹配——找电话号码、邮箱、身份证号
  • 边界界定——确定实体从哪里开始、到哪里结束
  • 上下文约束——比如“在...中”这种结构

来看几个我常用的正则模式:

# 手机号(中国大陆)
r"1[3-9]\d{9}"

# 邮箱
r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}"

# 身份证号(18位)
r"\d{17}[\dXx]"

# 金额
r"\d+(\.\d{2})?元"

你可能会问:这些模式网上都有,为什么还要自己写?

因为业务数据脏。我在电商项目里遇到过“电话:138xxxx1234”这种脱敏数据,标准正则根本匹配不到。怎么办?加规则:

r"1[3-9]\d{2}x{4}\d{4}"
我曾经踩过的坑:正则写得太贪婪。比如匹配“从北京到上海”,如果写成.*到.*,会把整句话都吞掉。一定要用非贪婪模式.*?

3.3 词典匹配方法:最朴素但最稳定

词典匹配,就是准备一个实体列表,然后去文本里找。听起来简单吧?但细节都在工程里。

我常用的词典匹配策略有三种:

策略 原理 适用场景
正向最大匹配 从前往后,优先匹配最长词 中文分词后的实体识别
逆向最大匹配 从后往前,优先匹配最长词 处理后缀明显的实体
双向匹配 正反都做,取结果交集 高精度场景

举个例子,词典里有“北京大学”和“北京”。用正向最大匹配,遇到“北京大学”会优先匹配成整体。用逆向,可能先匹配到“大学”再匹配“北京”。

我个人习惯用双向匹配+消歧。先正向跑一遍,再逆向跑一遍,不一致的地方人工定规则。

# 简单实现示例
def max_match_forward(text, dictionary):
    result = []
    i = 0
    while i < len(text):
        longest = text[i]
        for j in range(i+1, len(text)+1):
            if text[i:j] in dictionary:
                longest = text[i:j]
        result.append(longest)
        i += len(longest)
    return result
避坑指南:词典要定期更新。我见过一个项目,词典三年没更新,结果“拼多多”这种新词全没识别出来。建议每个月跑一次新词发现。

3.4 优缺点分析:规则方法到底行不行?

做了这么多年NLP,我总结一下规则方法的优缺点。不吹不黑,客观说。

优点

  • 可解释性强——匹配上了就是匹配上了,没有黑盒
  • 冷启动快——有词典就能跑,不需要标注数据
  • 精度高——规则写得好,准确率可以到95%以上
  • 资源消耗低——一个正则引擎就够了,GPU?不需要

缺点

  • 召回率低——规则覆盖不到的就漏了
  • 维护成本高——规则多了以后,改一条可能影响十条
  • 泛化能力差——换个领域,规则基本要重写
  • 对歧义处理弱——比如“苹果”是水果还是公司?规则很难判断

我记得有个医疗项目,用规则做症状识别,一开始召回率只有40%。后来加了300多条规则,召回率提到70%,但再往上就提不动了。最后还是上了CRF模型。

我的建议:规则方法适合做冷启动高精度场景。比如金融领域的金额、日期、电话号码,用规则就够了。但如果是开放域、实体类型多、表达方式灵活的场景,还是得靠模型。

说白了,规则和模型不是对立的。我现在的做法是:规则兜底,模型主力。规则负责处理那些确定性强的实体,模型负责处理模糊的、变化的。两者互补,效果最好。

嗯,这一章就到这里。下一章我们聊聊基于统计的方法——HMM和CRF,那才是真正的硬核内容。