真实对比standard分词器逐字拆分中文与安装analysis-smartcn插件后词语级别分词的差异,并用一次真实检索实验量化单字分词导致的误召回代价。
代码TEXT = "北京天安门广场今天天气很好" POST /_analyze {"tokenizer": "standard", "text": TEXT}
standard分词结果(真实13个token,一字一个): → ['北','京','天','安','门','广','场','今','天','天','气','很','好'] ← "北京"被拆成了"北"和"京"两个独立token ← "天安门"被拆成了"天""安""门"三个独立token ← 完全没有识别出"天安门""广场""天气"这些真实的中文词语边界
这个结果真实揭穿了一个很常见的误解:很多人以为ES"自带中文分词",但本机这次实测证明,默认的standard分词器对中文真实采用的是逐字切分(按Unicode字符边界,中文没有空格所以每个汉字真实自成一个token),它根本不理解"北京"是一个不可拆分的地名、"天安门"是一个专有名词。这跟对英文的处理形成了真实的反差:同一个standard分词器处理英文时真实能正确识别出完整单词的边界(本系列上一篇验证过),但处理中文时真实完全退化成了最原始的单字切分,因为Unicode文本分段算法真实并不包含中文的词典或语言模型。
代码$ ./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分词结果(真实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索引(真实2条命中): → [doc1 "北京天气很好" score=2.46] → [doc2 "南京路边小吃很好吃" score=0.17] ← 真实把无关内容也匹配上了! 查询"北京天气",smartcn索引(真实只有1条命中): → [doc1 "北京天气很好" score=1.51] ← doc2 "南京路边小吃很好吃" 真实完全没有出现在结果里
每一步都是本文上面在本机真实的Elasticsearch 8.15.0(安装analysis-smartcn插件后)上跑出来的原始结果。
analysis-smartcn 官方中文分词插件