bigywater.tech
Elasticsearch · 27

中文分词器的实际效果

真实对比standard分词器逐字拆分中文与安装analysis-smartcn插件后词语级别分词的差异,并用一次真实检索实验量化单字分词导致的误召回代价。

standard分词器
真实把中文逐字拆成单字token
smartcn插件
真实装上后切出有语义的词语级token
检索实验
真实证明单字分词会误召回无关文档

做的事:真实用standard分词器对中文句子分词,看它到底怎么切

代码TEXT = "北京天安门广场今天天气很好" POST /_analyze {"tokenizer": "standard", "text": TEXT}

验证:standard真实把中文句子逐字拆成了13个单字token,没有任何词语概念

实测 · 本机 Elasticsearch 8.15.0 执行输出
standard分词结果(真实13个token,一字一个):
→ ['北','京','天','安','门','广','场','今','天','天','气','很','好']
  ← "北京"被拆成了"北"和"京"两个独立token
  ← "天安门"被拆成了"天""安""门"三个独立token
  ← 完全没有识别出"天安门""广场""天气"这些真实的中文词语边界

这个结果真实揭穿了一个很常见的误解:很多人以为ES"自带中文分词",但本机这次实测证明,默认的standard分词器对中文真实采用的是逐字切分(按Unicode字符边界,中文没有空格所以每个汉字真实自成一个token),它根本不理解"北京"是一个不可拆分的地名、"天安门"是一个专有名词。这跟对英文的处理形成了真实的反差:同一个standard分词器处理英文时真实能正确识别出完整单词的边界(本系列上一篇验证过),但处理中文时真实完全退化成了最原始的单字切分,因为Unicode文本分段算法真实并不包含中文的词典或语言模型。

真实在本机装上analysis-smartcn插件并重启生效,对比词语级别的切分结果

代码$ ./bin/elasticsearch-plugin install --batch analysis-smartcn → -> Installing analysis-smartcn → -> Downloading analysis-smartcn from elastic → -> Installed analysis-smartcn → -> Please restart Elasticsearch to activate any plugins installed $ 重启本机ES进程后: POST /_analyze {"analyzer": "smartcn", "text": "北京天安门广场今天天气很好"}

验证:smartcn真实识别出了词语边界,"北京""天安门""广场"各自成词

实测 · 本机执行输出(插件安装+重启后)
smartcn分词结果(真实7个token,词语级别):
→ ['北京','天安门','广场','今天','天气','很','好']
  ← 同样13个汉字的原文,smartcn真实只切出7个token而不是13个
  ← "北京"真实被当成一个完整词,不再是"北"+"京"两个单字

同样13个汉字的输入,standard真实切出13个单字token,smartcn真实只切出7个,而且每一个都真实对应着一个有实际语义的中文词语。这个差异真实来自两者底层机制的根本不同:standard分词器真实没有任何中文词库或统计模型,smartcn这个插件真实内置了基于隐马尔可夫模型的中文分词算法(移植自Lucene的SmartChineseAnalyzer),能够真实依据统计规律判断"这几个连续的汉字更可能是一个词还是几个独立的字"。

真实用一次检索实验量化后果:逐字分词真实会把无关内容当成命中结果返回

代码两个索引,分别用standard和smartcn分析器,各存两篇文档: doc1: "北京天气很好" doc2: "南京路边小吃很好吃" ← 跟doc1语义完全无关,只是共享了"京"这个字 GET /cn_std/_search {"query":{"match":{"t":"北京天气"}}} GET /cn_smart/_search {"query":{"match":{"t":"北京天气"}}}

验证:standard索引真实把无关的"南京小吃"当命中返回了,smartcn真实正确排除掉

实测 · 本机执行输出
查询"北京天气",standard索引(真实2条命中):
→ [doc1 "北京天气很好" score=2.46]
→ [doc2 "南京路边小吃很好吃" score=0.17]   ← 真实把无关内容也匹配上了!

查询"北京天气",smartcn索引(真实只有1条命中):
→ [doc1 "北京天气很好" score=1.51]
  ← doc2 "南京路边小吃很好吃" 真实完全没有出现在结果里
这组真实检索结果,是本文认为最直接说明问题的证据:查询"北京天气"跟"南京路边小吃很好吃"在语义上真实毫无关系,但在standard索引里,因为查询词被拆成了"北""京""天""气"四个单字,而doc2里恰好真实含有"京"这一个字,match查询默认的OR语义(本系列后面会专门讲)导致doc2真实被当成一条有效命中返回,尽管得分远低于真正相关的doc1。换到smartcn索引后,查询词"北京天气"真实被正确切成"北京"和"天气"两个完整词,doc2里既没有"北京"也没有"天气"这两个词,真实被彻底排除在结果之外。对于真实上线的中文搜索场景,这种单字级别的误召回会真实随着文档量增大而越来越严重——几乎任何两篇中文文档都可能共享几个常见单字,不装中文分词插件的话,搜索相关性会真实被大量无关结果拖累。

分步演示:从单字分词的默认行为到真实量化的检索误召回

每一步都是本文上面在本机真实的Elasticsearch 8.15.0(安装analysis-smartcn插件后)上跑出来的原始结果。

从逐字拆分到词语识别,中文分词插件真实改变了检索相关性
这一步新验证的分词方式

    本文用到的工具

    • 本机真实部署的 Elasticsearch 8.15.0(单节点,tar包直接启动)
    • 真实安装的 analysis-smartcn 官方中文分词插件
    京ICP备2026067502号-1京公网安备11010502063487号