4、分词技术:基于规则的分词(最大匹配法),基于统计的分词(HMM、CRF),Python jieba分词库实战

分词,说白了就是把一句话切成一个个词。你想想看,中文不像英文,词与词之间没有空格。所以分词就成了NLP的第一步,也是最基础的一步。我刚开始做文本挖掘时,觉得分词不就是切句子嘛,有啥难的?后来踩了不少坑,才发现这里面的门道真不少。

4.1 基于规则的分词:最大匹配法

规则分词,就是靠词典来干活。你给机器一本词典,它照着词典去匹配文本。最经典的就是最大匹配法,分正向和反向两种。

正向最大匹配(FMM):从左往右扫描,每次取尽可能长的词。比如句子“我们在野生动物园玩”,词典里有“我们”、“在”、“野生”、“动物”、“动物园”、“野生动物园”。FMM会先取“我们在野”去查词典,没有;缩短成“我们在”,也没有;直到“我们”,匹配成功。然后继续从“在”开始……

反向最大匹配(BMM):从右往左扫,逻辑一样。我个人的经验是,反向匹配对中文的歧义处理往往更好一些。比如“研究生命科学”,正向可能切成“研究生/命/科学”,反向能切成“研究/生命/科学”。

嗯,这里要注意:最大匹配法的核心是词典要大、要全。我在项目中遇到过词典漏词的情况,结果“自然语言处理”被切成了“自然/语言/处理”,虽然也能用,但丢失了“自然语言处理”这个整体概念。

核心要点:最大匹配法简单、速度快,但严重依赖词典质量,且无法处理未登录词。

4.2 基于统计的分词:HMM与CRF

规则分词搞不定的事情,统计分词来补。统计分词不靠词典,靠概率。它把分词看作一个序列标注问题——每个字属于词的开头(B)、中间(M)、结尾(E)还是单独成词(S)。

4.2.1 隐马尔可夫模型(HMM)

HMM是统计分词的老祖宗。它假设每个字的状态(B/M/E/S)只跟前一个字的状态有关。说白了,就是“我猜下一个字是什么角色,只看前一个字是什么角色”。

HMM有五个要素:初始概率、转移概率、发射概率。训练时,我们从标注好的语料里统计出这些概率。预测时,用维特比算法找出最可能的标签序列。

我曾经用HMM做过一个分词器,效果还行,但遇到“结婚的和尚未结婚的”这种句子就翻车了。HMM会切成“结婚/的/和/尚未/结婚/的”,因为“和尚”这个词的概率干扰了判断。

我的建议:HMM适合做快速原型,但精度有限。如果你追求效果,建议直接上CRF。

4.2.2 条件随机场(CRF)

CRF比HMM聪明在哪?它不光看前一个字的状态,还能看前后文。比如“我来到北京清华大学”,CRF能利用“北京”和“清华”的上下文关系,正确切出“清华大学”。

CRF的核心是特征函数。你可以定义各种特征:当前字是什么、前一个字是什么、后一个字是什么、当前字是不是数字、是不是英文……特征越多,模型越强,但训练也越慢。

我记得有一次做金融文本的分词,里面全是“量化宽松”、“逆回购”这种专业词。用HMM切得一塌糊涂,换成CRF后,准确率从82%直接飙到94%。说白了,CRF就是能“看到”更远的上下文。

避坑指南:我曾经在CRF的特征设计上踩过坑——特征太多导致过拟合。建议特征数量控制在50-100个,并且要做特征选择。

4.3 Python jieba分词库实战

讲完了理论,咱们来点实际的。jieba是Python里最流行的分词库,没有之一。它把规则和统计结合在了一起——先用词典做最大匹配,再用HMM处理未登录词。

安装很简单:

pip install jieba

基本用法:

import jieba

text = "我来到北京清华大学"
# 精确模式(推荐)
words = jieba.lcut(text)
print(words)  # ['我', '来到', '北京', '清华大学']

# 全模式(把所有可能的词都切出来)
words = jieba.lcut(text, cut_all=True)
print(words)  # ['我', '来到', '北京', '清华', '清华大学', '华大', '大学']

# 搜索引擎模式(精确模式基础上再切长词)
words = jieba.lcut_for_search(text)
print(words)  # ['我', '来到', '北京', '清华', '华大', '大学', '清华大学']

你想想看,三种模式各有用途。精确模式最常用,全模式适合做关键词提取,搜索引擎模式适合做搜索索引。

自定义词典

我在做医疗文本分词时,发现“新型冠状病毒”总被切成“新型/冠状病毒”。怎么办?加词典:

jieba.load_userdict("my_dict.txt")
# my_dict.txt 内容:
# 新型冠状病毒 5 n
# 核酸检测 3 n

加完之后,再切就对了:“新型/冠状病毒”变成了“新型冠状病毒”。

调整词频

有时候不想加词典,只想临时调一下某个词的权重:

jieba.add_word("自然语言处理", freq=100)
jieba.del_word("自然")  # 如果不想让“自然”单独成词

嗯,这里要注意:freq值越大,这个词越容易被切出来。我一般设成100-200就够用了。

实战经验:jieba默认词典有35万词,覆盖了大部分常见场景。但如果你做垂直领域(法律、医疗、金融),一定要加自定义词典。我做过一个法律文书项目,加了5000个法律术语后,分词准确率从88%提升到了96%。

性能优化

jieba默认是单线程的。如果你要处理海量文本,可以开多进程:

from multiprocessing import Pool
import jieba

def cut_text(text):
    return jieba.lcut(text)

texts = ["句子1", "句子2", ...]  # 大量文本
with Pool(4) as p:
    results = p.map(cut_text, texts)

我试过,4个进程处理100万条文本,时间从30分钟缩短到8分钟。效果很明显。

小技巧:如果你对分词速度要求极高,可以试试jieba的enable_parallel()方法,但要注意内存消耗。

最后总结一下:规则分词(最大匹配法)适合词典全、领域窄的场景;统计分词(HMM、CRF)适合开放域、需要高精度的场景;而jieba则是两者的结合体,开箱即用。我个人建议:先拿jieba跑一遍,如果效果不满意,再考虑上CRF或者深度学习模型。