3、分词技术详解:中文分词难点、基于词典的分词、基于统计的分词、常用分词工具对比(IK、jieba、HanLP)

各位同学,咱们今天聊聊分词。这是搜索引擎里最基础,也最磨人的一个环节。

英文分词简单,空格一拆就完事。中文呢?字和字之间没有天然分隔。你想想看,“南京市长江大桥”,到底是“南京/市长/江大桥”,还是“南京市/长江大桥”?

我当年刚入行时,就被这种问题坑过。一个搜索系统上线后,用户搜“长江大桥”,结果把“南京市长江大桥”排到了前面。老板问我怎么回事,我一看分词结果,脸都绿了。

好,咱们今天就把这块硬骨头啃下来。

中文分词到底难在哪

说白了,难点就三个:

  • 歧义切分:同一个句子,不同切法意思完全不同。比如“乒乓球拍卖完了”,是“乒乓球/拍卖/完了”,还是“乒乓球拍/卖完了”?
  • 未登录词:词典里没有的词,比如新出的“元宇宙”、“区块链”,还有人名“特朗普”。词典跟不上时代。
  • 切分粒度:有些词该不该拆?比如“北京大学”,拆成“北京/大学”还是保留“北京大学”?这取决于你的业务场景。

核心观点:没有完美的分词器,只有最适合你业务场景的分词器。

基于词典的分词:简单粗暴但有效

这是最古老的方法。我习惯叫它“查字典法”。

思路很简单:准备一个大词典,然后拿着文本去匹配。匹配方式有三种:

  • 正向最大匹配:从左往右,尽量匹配最长的词。比如“中华人民共和国”,先看“中华人民共和国”在不在词典,不在就缩短。
  • 逆向最大匹配:从右往左,原理一样。中文里逆向匹配往往比正向准,因为中文的修饰语通常在前。
  • 双向最大匹配:正向逆向都做一遍,选结果更合理的那个。

我在项目中遇到过一个问题:用正向最大匹配处理“研究生命科学”,结果分成了“研究生/命/科学”。用户搜“生命科学”根本搜不到。后来改成双向匹配,才解决了。

避坑指南:我曾经以为词典越大越好,结果发现词典太大反而导致匹配效率下降,还容易把不该合并的词合并了。词典要精,不要杂。

基于统计的分词:让数据说话

词典法有个硬伤——遇到新词就抓瞎。这时候就得靠统计了。

核心思想是:如果两个字经常一起出现,那它们很可能是一个词。比如“机器”和“学习”经常挨着,那“机器学习”大概率是个词。

常用的统计模型有:

  • N-gram 模型:计算相邻字出现的概率。比如“我爱北京天安门”,计算“我爱”、“爱北”、“北京”等组合的概率。
  • 隐马尔可夫模型(HMM):把分词看作一个序列标注问题。每个字有四个状态:词首(B)、词中(M)、词尾(E)、单字成词(S)。然后通过训练数据学习状态转移概率。
  • 条件随机场(CRF):比 HMM 更强大,能考虑上下文特征。比如“他说的确实在理”和“他确实在理”,CRF 能根据上下文判断“确实”是不是一个词。

我记得有一次做新闻搜索,用户搜“新冠”,词典法死活分不出来,因为“新冠”是个新词。后来用统计模型,发现“新冠”和“肺炎”的共现频率极高,自动识别出来了。嗯,这就是统计法的优势。

注意:统计法需要大量训练数据。数据量不够,模型效果会很差。我见过有人拿几百条数据去训练 CRF,结果分词效果还不如简单的词典法。

常用分词工具对比:IK、jieba、HanLP

理论讲完了,咱们看看实际工具。这三个是我用得最多的。

工具 语言 算法 速度 词典扩展 适用场景
IK Analyzer Java 词典+正向迭代 支持自定义词典 Lucene/Elasticsearch 集成
jieba Python 词典+HMM 中等 支持自定义词典 Python 项目、快速原型
HanLP Java/Python 词典+CRF+深度学习 较慢 支持自定义词典+模型 高精度场景、学术研究

IK Analyzer:我最早用的就是它。集成到 Elasticsearch 里特别方便,配置一个分词器就完事。速度很快,适合对实时性要求高的搜索系统。但精度一般,遇到复杂歧义容易翻车。

jieba:Python 生态里的王者。我写原型、做数据分析时首选它。支持三种模式:精确模式(最常用)、全模式(把所有可能的词都列出来)、搜索引擎模式(在精确模式基础上再切分长词)。

举个例子:

import jieba

text = "我来到北京清华大学"
# 精确模式
print("/".join(jieba.cut(text, cut_all=False)))
# 输出:我/来到/北京/清华大学

# 全模式
print("/".join(jieba.cut(text, cut_all=True)))
# 输出:我/来到/北京/清华/清华大学/华大/大学

# 搜索引擎模式
print("/".join(jieba.cut_for_search(text)))
# 输出:我/来到/北京/清华/华大/大学/清华大学

你看,精确模式把“清华大学”作为一个整体,全模式把它拆得稀碎。搜索引擎模式则折中,既保留了“清华大学”,又拆出了“清华”和“大学”,方便用户搜“清华”时也能匹配到。

HanLP:这是目前精度最高的开源分词工具。我建议你在对分词质量要求极高的场景下使用它,比如法律文书搜索、医学文献检索。它支持 CRF 和深度学习模型,能处理很多复杂情况。

但代价是速度慢。我曾在线上系统里试过 HanLP,结果 QPS 直接掉了 30%。后来只在离线处理时用它。

我的建议

  • 如果你用 Java 做搜索,首选 IK,简单稳定。
  • 如果你用 Python 做分析,首选 jieba,灵活好用。
  • 如果你追求极致精度,选 HanLP,但要做好性能优化。

如何选择分词器

没有银弹。我一般按这个思路选:

  1. 先看业务场景:是实时搜索还是离线分析?实时搜索对速度要求高,选 IK 或 jieba 的精确模式。
  2. 再看词典需求:你的领域有没有大量专业词汇?比如医疗、法律,一定要支持自定义词典。IK 和 jieba 都支持。
  3. 最后看精度要求:如果分词错了会导致严重后果(比如法律搜索),那就上 HanLP。

我曾经在一个电商搜索项目里,先用 jieba 做原型,效果还行。但上线后发现用户搜“苹果手机”时,分词把“苹果”和“手机”分开了,结果搜出了苹果和手机两个品类。后来我自定义了一个词典,把“苹果手机”作为一个整体词加入,问题才解决。

嗯,这就是分词。看起来简单,但每个细节都可能影响最终效果。下一章咱们聊聊索引结构,看看分词结果怎么存起来才能搜得快。