十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

TongSearch集成analysis-hanlp:中文分词、拼音搜索与自定义词典实战

TongSearch集成analysis-hanlp:中文分词、拼音搜索与自定义词典实战 1. 为什么需要 analysis-hanlp 插件1.1 中文分词与英文检索的差距做搜索的同学应该都有体会英文检索天生就比中文好做。英文单词之间有空格天然分词做大小写归一化和词干还原后倒排索引的匹配逻辑就非常清晰。而中文句子长这样“东方之珠新品发布会下周在上海举行”没有空格没有明确的词边界怎么切、切得对不对直接决定搜索引擎的召回率和排序效果。TongSearch本身提供了标准分词器standard analyzer它把中文按单个汉字切分。这种切分方式有个问题搜索“新品发布会”时它会被切成“新”“品”“发”“布”“会”五个字匹配出来的结果往往是一堆包含这些字的无关文档。还有更麻烦的场景比如人名“周杰伦”被切成“周”“杰”“伦”用户搜“杰伦哥”时系统根本不知道“杰伦”和“周杰伦”是同一个人的不同叫法。再比如“研究生”和“研究生命科学”标准分词器会切出让人哭笑不得的结果。TongSearch集成analysis-hanlp插件就是来解决这个问题的。这个插件把HanLP的分词、词性标注、命名实体识别、拼音转换能力带进了搜索引擎让TongSearch处理中文文本时不再是“见字拆字”而是真正理解了词、短语和实体边界。1.2 HanLP在分词之外还带来了什么很多人对analysis-hanlp的理解停留在“中文分词”这一个功能上实际用过之后会发现它带来的是一整套中文NLP能力分词模式多标准分词、索引分词、NLP分词、最短路径分词等多种模式可选不同场景可以配不同的切分策略。词性标注每个词自动带上词性标签比如名词、动词、形容词、机构名、人名这对搜索引擎做字段权重调整、搜索结果摘要抽取非常有用。命名实体识别人名、地名、机构名、时间、数值等实体能被自动识别。比如搜索“昨天马云在杭州谈了什么”分词后能识别出“马云”是人名、“杭州”是地名、“昨天”是时间词。拼音搜索支持内置了拼音转换功能。用户输入“hangzhou”或者“hz”能匹配到“杭州”的中文文档这对很多业务系统来说是个救命功能。自定义词典与热更新可以随时添加行业词汇、人名、品牌名线上词典热更新不需要重启节点。这些能力叠加起来搜索系统就从“字面匹配”升级到了“语义匹配”的层次。对用户来说搜“计算机”能带出“电脑”相关的内容搜“高血压药”能带出“降压药”的结果这在病历检索、商品搜索、文档检索这类场景里价值极高。1.3 哪些项目真正需要这个插件我做过的项目里有三类场景特别适合用analysis-hanlp第一类是站内全文检索比如企业知识库、电商商品库、文档管理系统。这类系统搜索词偏口语化、长短语多标准分词器召回一堆垃圾结果排序效果差用户根本不想用第二次。第二类是日志与舆情分析。比如需要从大量新闻、微博、工单文本里提取人名、机构名、地名然后做统计或关联分析。这个场景不仅需要分词还需要实体识别能力和词性标注analysis-hanlp都能覆盖。第三类是需要模糊匹配和拼音搜索的行业系统。比如医疗系统的病历检索、公安系统的户籍查询、电商系统的品牌搜索用户可能输入谐音、简拼、全拼很多情况下拼音搜索是刚需。当然如果只是做ID精确匹配、方案编号查询这类简单检索那就没必要上这个插件。每多一层分析器索引构建和查询都会多一层开销选型前要想清楚自己的检索场景到底需要什么级别的文本理解能力。个人建议拿到项目后先拿真实语料做一轮分词效果评估看标准分词器和HanLP分词的差距有多大再决定要不要引入。别拍脑袋直接上也别因为几个新词切分不对就完全否定插件。2. 插件安装与基础配置2.1 版本匹配是第一步安装analysis-hanlp插件最容易踩的坑就是版本不匹配。TongSearch底层对应某个版本的Elasticsearch而analysis-hanlp插件是针对特定ES版本编译的。装错版本会导致插件加载失败、节点无法启动严重时整个集群起不来。以我实际部署的经验版本匹配这件事没有捷径只能老老实实看官方兼容性说明。一般步骤是先查TongSearch对应的ES内核版本。到analysis-hanlp插件的发布页面找对应ES版本的jar包。不要只看“latest”latest可能只适用于最新的ES版本。确认HanLP本身的版本要求部分新功能需要特定版本的HanLP库支持。举个例子如果TongSearch内核是ES 7.10.2那需要下载elasticsearch-analysis-hanlp-7.10.2这个版本的插件包。把7.17的包直接放到7.10.2环境里启动时大概率报Unsupported major.minor version或者Incompatible plugin错误。我记得有一次帮客户排查问题对方装的是7.10.2的TongSearch插件包却下载成了7.16版本节点启动后一直报ClassNotFoundException。查了半天才发现是插件版本和内核版本对不上。这个排查过程很难受所以强烈建议安装前就把版本对应关系写进交付文档方便后续维护。2.2 安装步骤与目录规范analysis-hanlp插件的安装方式其实和普通ES插件一样核心就是三步放jar包、放配置、改配置项。下面以离线安装为例说明我常用的操作路径。首先是准备插件目录。在TongSearch的plugins目录下新建一个analysis-hanlp目录然后把下载好的jar包放进去。这里要注意jar包不要和别的插件混在一个目录里每个插件一个独立目录这是ES插件管理的基本规范。接着是准备词典文件。分析插件带了HanLP的核心词典但业务上通常需要自定义词典比如行业术语、品牌名、人名列表。需要在config目录下创建词典目录比如analysis-hanlp/把自定义词典放进去。然后是修改节点配置。打开conf/jvm.options根据词典大小调整堆内存。HanLP加载词典和模型是吃内存的默认的1G堆内存在生产环境基本不够用。我一般建议至少2G如果要用NLP分词和实体识别4G起步比较稳。配置完成后重启TongSearch节点。启动日志里如果看到类似HanLP loaded successfully的提示说明插件加载正常。如果看到Failed to load plugin就要重点看依赖冲突和目录权限的问题。2.3 两个必须改的配置项安装好插件后有两个配置项直接影响使用效果不配的话插件基本等于白装。第一个是hanlp.root它指定HanLP词典和配置文件的根目录。可以配成绝对路径比如/data/tongsearch/config/analysis-hanlp也可以配成相对路径。我的实践是配绝对路径这样多节点部署时每个节点都指向同一个位置避免相对路径在不同启动目录下解析错误。第二个是hanlp.remote它是远程词典更新的关键开关。生产环境里业务词是不断增加的如果每次加词都要重启集群运维成本太高。开启远程词典后可以定期从远程服务器拉取最新词典实现热更新。我通常会配一个内网HTTP服务词典文件放在上面更新时只需要更新远程文件节点在设定的时间间隔内自动拉取完全不用重启。这个配置对运维人员特别友好算是我用这个插件最满意的功能之一。注意远程词典更新有几分钟的延迟而且拉取的是整个词典文件。如果词典特别大建议拆分远程和本地词典高频更新的词放远程低频大词库放本地这样远程拉取的开销会小很多。我实际处理过一个词典文件超过200MB的案例远程更新一次要占满一整块磁盘IO后来拆分本地远程之后问题才缓解。3. 核心配置项与索引映射实战3.1 不同分词模式怎么选analysis-hanlp插件提供了多种分词器很多人第一次看到时会懵不知道选哪个。我把常用的几个整理成了对照表方便参考分词器名称切分特点适用场景注意事项hanlp_standard标准分词基于隐马尔可夫模型切分粒度适中搜索关键词分析search analyzer新词识别能力一般需要自定义词典补充hanlp_index索引分词倾向于做细粒度切分索引字段分析index analyzer召回率高但倒排索引体积会增大hanlp_nlp带词性标注和命名实体识别需要实体抽取、文本分析的场景速度比standard慢资源消耗更大hanlp_shortest最短路径分词追求最少的词数对精度要求不高、速度优先的场景切分结果较为粗犷适合短文本hanlp_pinyin输出拼音和拼音首字母拼音搜索场景常配合multi-fields实现“中文拼音”双索引我的经验是索引端和搜索端的分析器最好分开配。索引端建议用hanlp_index宁可多切一些词保证召回率搜索端建议用hanlp_standard保证用户输入的关键词切分相对准确。比如“中华人民共和国”这个词组索引端切成“中华人民共和国”“中华”“人民”“共和国”搜索端切成“中华人民共和国”。这样用户在搜索框输入“中华”时也能召回上述文档搜索准确率不下降召回率还上去了。这里有一个很多人容易忽略的细节如果索引端和搜索端的分词结果不一致有些词在索引时被切开了但搜索时被当成了整体会导致某些查询永远匹配不到。解决方法是测试阶段就对比索引分词和搜索分词的输出尽量让搜索端切出来的词是索引端词集的子集避免交叉匹配丢结果。3.2 索引映射配置实操我以一个商品搜索为例演示完整的索引映射配置。假设我们要建一个商品索引包含商品名称、商品描述、品牌字段且品牌支持拼音搜索。映射配置大致如下PUT /products { settings: { analysis: { analyzer: { product_name_analyzer: { type: custom, tokenizer: hanlp_index }, product_search_analyzer: { type: custom, tokenizer: hanlp_standard }, pinyin_analyzer: { type: custom, tokenizer: hanlp_pinyin } } } }, mappings: { properties: { name: { type: text, analyzer: product_name_analyzer, search_analyzer: product_search_analyzer }, description: { type: text, analyzer: product_name_analyzer, search_analyzer: product_search_analyzer }, brand: { type: text, analyzer: product_name_analyzer, search_analyzer: product_search_analyzer, fields: { pinyin: { type: text, analyzer: pinyin_analyzer } } } } } }这个配置里有个典型的操作品牌字段除了普通分词还加了一个brand.pinyin子字段专门用来做拼音搜索。用户在搜索框输入“apple”时如果只对brand字段做中文分词apple根本匹配不到“苹果”这个品牌但有了brand.pinyin查询就可以同时命中拼音子字段。值得说的是上面的hanlp_pinyin拼音分词器输出的是拼音和首字母的token具体的输出格式取决于HanLP的拼音模式配置。生产环境里我建议单独建立拼音子字段而不是直接替代原字段因为拼音分词会把“重庆”转成“chongqing”和“cq”如果直接在原始字段上做拼音索引检索时纯中文文本也会有很多额外的匹配噪音。3.3 自定义词典与词性标注的联动配置完分词器和映射后通常会发现有些业务词汇切分不对。比如“德芙巧克力”可能被切成“德/芙/巧克力”或者“星巴克”被切成“星/巴克”。这时候就要上自定义词典。自定义词典文件格式很简单每行一个词可以带上词性和频次信息。示例德芙巧克力 nz 100 星巴克 nz 50 研华科技 nt 80第一列是词本身第二列是词性标签第三列是频次。词性标签建议用HanLP的标准标签比如nz表示专有名词nt表示机构名。频次越高分词时越倾向于识别为该词。词典配置在hanlp.properties或者远程配置中通过CustomDictionaryPath指定路径多个词典文件用分号分隔CustomDictionaryPathdata/dictionary/custom/CustomDictionary.txt;data/dictionary/custom/business.txt配置完成后我通常会立刻做一次分词验证POST /_analyze { analyzer: product_name_analyzer, text: 德芙巧克力在星巴克门店热销 }正常情况下返回的tokens里应当出现“德芙巧克力”“星巴克”两个词。如果还是被拆开就要检查词典文件编码、路径配置、节点是否已加载新词典。这里有个容易踩的坑自定义词典只有在词性标注开启时标注信息才有效。如果只是用hanlp_standard分词而不开词性标注词性标签不会对切分产生直接影响但自定义词本身依然会被识别。所以在设计字段时如果需要在搜索结果里用实体类型做过滤要确保用的是hanlp_nlp分词器。3.4 拼音搜索与同义词的细节处理拼音搜索配置好后还有一个常见需求是同义词搜索。比如用户搜“电脑”希望带出“计算机”搜“老公”希望带出“丈夫”。analysis-hanlp插件本身不处理同义词但TongSearch的synonym过滤器和HanLP分词可以结合使用。我常用的做法是自定义分析器里在分词器后加一个同义词过滤器。{ settings: { analysis: { filter: { synonym_filter: { type: synonym, synonyms: [ 电脑, 计算机, 台式机, 老公, 丈夫, 先生 ] } }, analyzer: { product_synonym_analyzer: { type: custom, tokenizer: hanlp_standard, filter: [synonym_filter] } } } } }这样做的好处是同义词扩展发生在分词之后HanLP先把“台式计算机”切成“台式”“计算机”然后同义词过滤器把“计算机”扩展为“电脑”最终搜索时“电脑”和“计算机”都能互相召回。如果顺序反了先把“电脑”扩展为“计算机”再让HanLP去分词会出现“台式计算机”被切得更碎的问题。拼音搜索和同义词搜索结合时还有一个细节拼音子字段不要加同义词过滤器。拼音是字符级别的转换本身没有同义词概念加了反而会让“apple”同时扩展出“苹果”“iphone”“乔布斯”之类的无关词查询噪音剧增。这一点我在早期项目里踩过坑后来统一规范为原字段负责语义扩展拼音子字段只负责字符匹配。4. 常见问题与排查技巧实录4.1 插件装好后不生效这个是最常见的问题。插件装好后创建索引时指定hanlp_standard分析器TongSearch直接报错或者提示Custom analyzer [hanlp_standard] failed to find tokenizer。排查顺序一般是先确认插件目录结构正确。plugins/analysis-hanlp/下必须有jar包且jar包版本与ES内核版本一致。看节点日志。启动时是否有HanLP loaded相关日志。如果没有检查配置的hanlp.root路径是否存在路径写错会导致加载失败。检查插件目录权限。用非root用户启动TongSearch时插件目录和数据目录必须对该用户可读可写否则插件能被扫描到但实际加载时权限不足。另外有个容易忽略的细节修改了插件配置后需要重启整个TongSearch节点而不是只reload索引。插件的配置加载发生在节点启动阶段这个阶段做的初始化工作不是索引级别的reload能替代的。4.2 词典文件加载失败或乱码自定义词典放进目录后分词结果完全没变化或者日志里报Unrecognized line之类的错。排查下来十有八九是编码问题。HanLP的词典文件要求UTF-8编码而且是不带BOM的UTF-8。Windows下用记事本另存的UTF-8默认带BOMBOM会被当成特殊字符混入词典词条导致词典加载异常或匹配不上。我习惯用VS Code或Notepad统一转成UTF-8 without BOM再上传到服务器。还有一种情况是词典文件格式错误。比如行末有空格词条中间有逗号或者空行太多。HanLP的词典解析对格式比较敏感建议每一行就用一个词不要带额外符号德芙巧克力 nz 100如果是从Excel导出的词典粘贴到文本文件后一定要检查是否有多余的制表符和全角空格。我之前处理过一个客户文件看起来没问题实际全角空格把整个词条都搅乱了排查了很久才定位到。4.3 分词结果和预期不一致这个问题的根源通常有三个第一个是自定义词典没生效检查词典路径是否正确、节点是否重启、远程词典是否拉取成功。注意远程词典有缓存机制即使远程文件更新了节点也可能使用旧的缓存需要确认缓存更新时间。第二个是索引端和搜索端分析器不一致索引时用hanlp_index搜索时用hanlp_standard切分粒度不同导致部分查询匹配不到。这种情况要通过_analyze接口分别对索引端和搜索端做分词验证再根据验证结果调整策略。第三个是停用词过滤导致的缺失有些业务词像“新”“老”“大”“小”会被标准停用词表过滤导致搜索“大麦”时“大”被删掉只剩“麦”。如果业务里有大量这类词建议禁用停用词过滤器或者自己维护一份业务停用词表不能一拍脑袋沿用默认配置。遇到分词结果不对时我通常的第一步操作是用POST /_analyze接口分别验证原始字段和查询词的分词结果把两个结果放到一起对比再定位是索引端还是查询端的问题。这个方法看起来简单但能省下大量肉眼查日志的时间。4.4 索引速度变慢与内存水位过高引入HanLP后索引构建速度下降是正常的毕竟分词过程从简单的字符切分变成了模型推理。但如果慢到业务不可接受就要考虑资源分配和配置优化。排查顺序确认堆内存是否足够。HanLP加载模型和词典后堆外内存占用也偏高。如果节点总内存小于4G跑满负荷基本内存报警。建议用jstat -gc pid看老年代使用率频繁Full GC就是内存不够的信号。确认是否每个字段都走了NLP分析。如果索引里十几个text字段都配置了hanlp_nlp性能一定差。我给客户的建议是只有真正需要实体识别的字段才用hanlp_nlp其他字段用hanlp_index或hanlp_standard。确认词典文件是否过大。有些客户把所有行业词一股脑塞进自定义词典词典文件几百MB每次加载都要好几分钟。建议分拆词典把基础词汇放本地业务热词放远程低频大词库放在单独的索引节点上加载。我处理过一个极端案例客户把所有产品描述字段都配置成hanlp_nlp几百GB的数据量索引构建了三天没建完。后来把描述字段改为hanlp_index只对标题和关键字字段用hanlp_nlp索引构建时间缩短到八小时实体识别功能也还在。分词能力和性能之间一定要做权衡不要为了一个边缘功能拖垮整个集群。4.5 集群部署时词典不一致单节点环境下词典更新很简单。但TongSearch集群通常有多个节点每个节点都维护一份本地词典如果只更新了其中一个节点的词典就会造成不同分片上的分词结果不一致搜索质量忽好忽坏。我的建议是集群环境下优先使用远程词典配置。所有节点从同一个远程地址拉取词典文件从根上保证各节点词典一致。远程地址可以是自建的NGINX静态服务、对象存储、或者配置中心下发的文件。如果没有远程词典条件只能在本地维护词典时每次更新都要走一套发布流程先更新一个节点验证无误后再滚动更新其余节点。切忌同时更新所有节点否则词典格式有问题时整个集群一起挂掉。最后把几个高频问题整理成速查表方便大家遇到问题直接对照现象可能原因快速解法插件加载失败插件版本与ES内核不兼容核对版本并重新下载匹配的jar包词典不生效编码问题或路径配置错误转为UTF-8无BOM检查hanlp.root分词结果乱索引和搜索分析器不一致分别用_analyze验证两个分析器的切分结果索引速度慢NLP字段过多或堆内存不足精简NLP字段调大JVM堆内存集群各节点分词不一致本地词典更新不同步切换为远程词典统一分发最后再分享一个实操中的小技巧在正式上线前把典型的用户查询整理成一个测试集比如100条高频搜索词然后对比加插件前后的Top 10召回结果人工给每个结果打分。这个测试集跑一遍不仅能验证插件配置是否合理还能顺带发现一批需要加入自定义词典的业务词。我每次做搜索项目都会保留这个测试集后续词典更新、配置调优都用它做回归验证效果非常稳定。我个人的体会是analysis-hanlp插件不是装完就能一劳永逸的词典需要持续维护配置需要结合业务反复调优但一旦跑顺了中文搜索体验的提升是非常直观的。如果你的业务正好有中文检索、实体识别或者拼音搜索的需求这个插件值得投入时间去打磨。
返回列表