基于规则的分词:正则表达式、领域特定规则,中文分词的特殊挑战
说实话,很多人一上来就扑进深度学习的怀抱,觉得用模型分词才是正路。但我在实际项目中踩过的坑告诉我——规则分词这块硬骨头,你绕不过去。数据清洗、日志解析、特定领域术语提取,这些场景下纯规则往往比模型跑得更快、更可控。
什么叫基于规则?说白了就是你告诉程序:看到什么格式就切,看到哪些字必须连在一起。我们今天拆成三块来讲:正则表达式、领域特定规则、以及绕不开的中文特殊挑战。
核心观点:规则分词是“确定性”的——同样的输入永远返回同样的输出。这在生产环境调试时极其重要。
正则表达式——最灵活的文本扫描工具
正则表达式是规则分词的第一把利器。我习惯把它理解为一种模式描述语言:你描述你要找的东西长什么样,程序帮你找到它。
举个简单的例子,我要从一段文本里提取出所有手机号:
import re
text = "我的电话是13812345678,备用号是010-88886666"
pattern = r'1[3-9]\d{9}'
matches = re.findall(pattern, text)
print(matches) # ['13812345678']
这里的关键在于,\d{9}匹配了9位数字,加上前缀,刚好11位。别小看这个写法,我在处理生产环境日志时经常用这种模式批量切分用户ID和订单号。
我的习惯:写正则前先在Regex101或类似工具上测一遍,尤其是边界情况——比如“号码后面紧跟着汉字”这种。
在实际分词任务中,正则通常扮演三种角色:
- 边界分割——用
\s+、[,。!]这类模式把文本初步切碎。 - 模式提取——把邮箱、URL、金额这类固定格式单独拎出来。
- 替换清理——把冗余字符替换成统一分隔符,方便下一步处理。
嗯,这里要提个醒。正则有个毛病:写得太复杂就会变得又难读又慢。我在一次处理亿级日志的分词任务时就吃过亏——正则里套了好多层交替条件,跑了一天才发现是表达式回溯导致的指数级耗时。
避坑指南:我曾经因为嵌套量词导致正则回溯爆炸。比如 (a+)+b 这种模式,对“aaaaaaaaaac”匹配时,引擎会尝试所有可能的分组方式,时间直接崩到O(2ⁿ)。
领域特定规则——让分词更“懂行”
正则虽然灵活,但它不懂业务。举个例子:在金融领域,“上证综指”必须作为一个整体,你不能把“上证”和“综指”拆开。通用分词器做不到这一点,所以你需要领域词典和业务规则。
我个人习惯把领域规则分为三种:
| 类型 | 说明 | 举例 |
|---|---|---|
| 强制保留词 | 遇到这些词就不切 | "机器学习"、"科创板" |
| 强制切分词 | 遇到这些词必须切 | "但是"、"然而"、"以及" |
| 前后缀规则 | 根据位置决定是否切分 | "总"+"经理"→"总经理" |
实际构建领域规则时,我通常会走这么几步:
- 收集领域术语:让业务方提供一份种子词表,少则几百条,多则几万条。
- 定优先级:长词优先匹配。比如“中华人民共和国”优先级高于“中华”、“人民”、“共和国”。
- 加否定规则:某些写在规则里的词要在特定上下文下取消。比如“小明在人民广场等”中的“人民广场”应该保留,但“他是人民的好儿子”中“人民”应该独立。
# 伪代码展示领域规则优先级
rules = [
("中华人民共和国", MAX_SCORE), # 最长优先
("人民广场", SCORE_HIGH),
("人民", SCORE_BASE), # 短词只有在不被长词覆盖时才生效
]
我在医疗病历分词项目里就这么干过——把“糖尿病肾病”、“高血压性心脏病”这些术语做成保留词,准确率从70%直接提到90%以上。
中文分词的特殊挑战——规则派最难啃的骨头
好,到最难的部分了。中文分词跟英文有个本质区别:英文天然用空格隔开,中文没有。这就产生了几个让规则派头疼的问题。
第一个是歧义问题。经典的例子是:
"南京市长江大桥"
可能切法1: 南京市 / 长江大桥
可能切法2: 南京市长 / 江大桥
为什么会有歧义?因为“南京市”和“南京市长”在词典里同时存在,“长江大桥”和“江大桥”也是。程序需要做决策——这是规则分词的天花板。
我常用的做法是双向最大匹配:从左往右切一次(取最长词),从右往左切一次(也取最长词),然后选成片数量少的那一种。比如“南京市长江大桥”,正向匹配会切成“南京市 / 长江大桥”(2个词),逆向匹配会切成“南 / 京市 / 长江大桥”(3个词),我们选前者。
经验之谈:双向最大匹配只能解决部分交叉歧义,碰到组合歧义(比如“把手弄脏了”的“把手”可以是动词也可以是名词),还是得靠统计或上下文。
第二个是未登录词问题。新出现的网络流行语、罕见的专业术语、人名地名——这些不在词典里,规则根本不知道它是词。我最痛苦的一次是处理用户评论数据,“绝绝子”、“YYDS”这种新词全被切成单字,结果做出来的标签云全是单字符。
第三个是特殊符号的处理。中文全角半角混用、数字和汉字连写(“3D打印”、“5G网络”)、英文缩写的分词边界——这些都让规则变得臃肿不堪。
拿我自己的体会来说,中文分词的规则难写在哪?它不像英文那么“规整”。英文里\b\w+\b基本能搞定大部分场景。中文你得一个字一个字地去猜哪些字该凑在一起。下面这个表格你看看对比就明白了:
| 维度 | 英文分词 | 中文分词 |
|---|---|---|
| 分隔符 | 天然存在(空格) | 无天然分隔符 |
| 词长 | 词长基本固定 | 词长变化大(1~10+字) |
| 歧义率 | 较低 | 高,尤其短文本 |
| 新词出现 | 少,且通常有词缀 | 频繁,无固定规律 |
| 规则复杂度 | 中 | 极高,容易规则冲突 |
面对中文的特殊挑战,纯规则方案很难达到95%以上的准确率。我自己在实际项目中,通常把规则分词当作预切分阶段,先排除掉确定性的模式(手机号、身份证、专有名词),再把模糊区域交给统计模型去解决。说白了就是——规则兜底,模型冲锋。
给你的建议:如果你刚接触规则分词,别想一步到位。先从一种场景出发——比如先写好一个提取手机号/邮箱的正则,再慢慢加入领域词典。我每次迭代都不会超过50条规则,多了就重构。