3、基于规则的抽取方法:正则表达式编写技巧、词典匹配(AC自动机)、规则模板设计、优缺点分析
说到实体关系抽取,很多人一上来就想着上深度学习模型。但我得说一句大实话——规则方法,永远是性价比最高的起点。我在工业项目里见过太多场景,几条精心设计的正则,就能干掉80%的抽取需求。今天咱们就聊聊这个“老派但好用”的方法。
3.1 正则表达式编写技巧
正则表达式,说白了就是“文本模式的匹配游戏”。我刚开始写正则时,也踩过不少坑。比如匹配一个电话号码,写出来的表达式要么漏掉号码,要么把身份证号也抓进来了。
核心技巧一:从具体到抽象
别一上来就写复杂的模式。我的习惯是:先写死样本,再逐步抽象。举个例子:
# 先匹配具体文本
"张三毕业于清华大学" → 匹配"毕业于"
# 再抽象成模式
(.*?)毕业于(.*?) → 匹配"人物"和"学校"
# 最后加上边界约束
(?<=^|。|;)(.*?)毕业于(.*?)(?=。|$|;)
嗯,这里要注意:边界条件比匹配本身更重要。我曾经因为没加边界,把“张三毕业于清华大学,李四毕业于北京大学”抽成了“张三毕业于清华大学,李四”——这显然不对。
核心技巧二:善用非贪婪匹配
你想想看,默认的贪婪匹配会吞掉尽可能多的字符。比如:
文本:"张三毕业于清华大学,李四毕业于北京大学"
模式:.*毕业于.*
结果:匹配了整个句子(贪婪)
模式:.*?毕业于.*?
结果:匹配了"张三毕业于清华大学"(非贪婪)
我个人习惯在抽取任务中,默认使用非贪婪匹配,除非有特殊需求。
核心技巧三:分组与命名捕获
正则的分组功能,是抽取的利器。我建议用命名捕获组,代码可读性会好很多:
import re
text = "张三毕业于清华大学"
pattern = r"(?P<person>.*?)毕业于(?P<school>.*?)$"
match = re.search(pattern, text)
if match:
print(match.group("person")) # 张三
print(match.group("school")) # 清华大学
3.2 词典匹配:AC自动机
正则虽然强大,但遇到大规模词典匹配就力不从心了。比如你要从文本中找出10000个公司名,用正则写10000个模式?那性能会惨不忍睹。
这时候,AC自动机(Aho-Corasick)就派上用场了。它本质上是一个多模式匹配算法,能在O(n)时间内完成所有词典词的匹配。
我记得在做一个金融舆情项目时,需要从新闻中抽取2000多个金融实体。用正则跑了半小时没跑完,换成AC自动机后,3秒搞定。差距就是这么大。
AC自动机的核心逻辑:
- 构建Trie树:把所有词典词插入到一棵树中
- 构建失败指针:当匹配失败时,快速跳转到下一个可能匹配的位置
- 扫描文本:沿着Trie树走,遇到失败指针就跳转
Python里用pyahocorasick库,几行代码就能搞定:
import ahocorasick
# 构建AC自动机
automaton = ahocorasick.Automaton()
# 添加词典词
words = ["清华大学", "北京大学", "浙江大学"]
for idx, word in enumerate(words):
automaton.add_word(word, (idx, word))
# 构建失败指针
automaton.make_automaton()
# 匹配文本
text = "张三毕业于清华大学,李四毕业于北京大学"
for end_index, (idx, word) in automaton.iter(text):
start_index = end_index - len(word) + 1
print(f"找到实体: {word}, 位置: ({start_index}, {end_index})")
3.3 规则模板设计
有了正则和词典,接下来就是设计规则模板了。我个人习惯把规则模板分为三层:
| 层级 | 名称 | 示例 |
|---|---|---|
| 第一层 | 原子规则 | 匹配人名、地名、机构名等基础实体 |
| 第二层 | 组合规则 | 匹配"人物 + 毕业于 + 学校"这样的关系 |
| 第三层 | 上下文规则 | 考虑前后文约束,如否定词、修饰词 |
设计原则一:规则要可配置
别把规则写死在代码里。我习惯用JSON或YAML来管理规则模板:
{
"rules": [
{
"name": "毕业关系",
"pattern": "{person}毕业于{school}",
"entities": {
"person": "人名词典",
"school": "学校词典"
},
"relation": "毕业"
},
{
"name": "任职关系",
"pattern": "{person}担任{company}的{position}",
"entities": {
"person": "人名词典",
"company": "公司词典",
"position": "职位词典"
},
"relation": "任职"
}
]
}
设计原则二:规则要有优先级
你想想看,如果多条规则都能匹配同一段文本,该用哪条?我的做法是:精确度高的规则优先。比如:
- 精确匹配规则(如"毕业于")优先级最高
- 模糊匹配规则(如"就读于"、"在...学习")次之
- 兜底规则(如"人物+学校"共现)优先级最低
3.4 优缺点分析
聊了这么多,咱们客观地分析一下规则方法的优缺点。
优点:
- 可解释性强:每条规则都能说清楚为什么匹配。这在金融、医疗等强监管领域特别重要。
- 零样本可用:不需要标注数据,直接写规则就能跑。我经常在项目初期用规则快速验证可行性。
- 性能极高:AC自动机+正则,处理百万级文本也就几秒钟。
- 易于调试:哪里抽错了,改规则就行,不用重新训练模型。
缺点:
- 维护成本高:规则多了以后,互相之间可能冲突。我见过一个项目维护了3000多条规则,改一条可能影响一片。
- 泛化能力弱:换个领域,规则基本要重写。比如从新闻领域换到医疗领域,实体类型和关系模式完全不同。
- 无法处理歧义:"苹果"是水果还是公司?规则很难判断。
- 长尾问题:总有规则覆盖不到的文本模式。我曾经为了覆盖一个特殊句式,写了5条规则,结果只多覆盖了0.1%的数据。
好了,关于规则抽取的方法就聊到这儿。说白了,规则方法就像一把瑞士军刀——小巧、实用、关键时刻能救命。但别指望它解决所有问题。该上模型的时候,也别犹豫。
公众号:蓝海资料掘金营,微信deep3321