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会切成“结婚/的/和/尚未/结婚/的”,因为“和尚”这个词的概率干扰了判断。
4.2.2 条件随机场(CRF)
CRF比HMM聪明在哪?它不光看前一个字的状态,还能看前后文。比如“我来到北京清华大学”,CRF能利用“北京”和“清华”的上下文关系,正确切出“清华大学”。
CRF的核心是特征函数。你可以定义各种特征:当前字是什么、前一个字是什么、后一个字是什么、当前字是不是数字、是不是英文……特征越多,模型越强,但训练也越慢。
我记得有一次做金融文本的分词,里面全是“量化宽松”、“逆回购”这种专业词。用HMM切得一塌糊涂,换成CRF后,准确率从82%直接飙到94%。说白了,CRF就是能“看到”更远的上下文。
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默认是单线程的。如果你要处理海量文本,可以开多进程:
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分钟。效果很明显。
enable_parallel()方法,但要注意内存消耗。
最后总结一下:规则分词(最大匹配法)适合词典全、领域窄的场景;统计分词(HMM、CRF)适合开放域、需要高精度的场景;而jieba则是两者的结合体,开箱即用。我个人建议:先拿jieba跑一遍,如果效果不满意,再考虑上CRF或者深度学习模型。