网站建设常州,好听的网站名称,可以做网站,南京网站制作公司有哪些提高准确率的方法有很多#xff0c;但是要在提高准确率的同时保证召回率往往比较困难#xff0c;本文只介绍一种比较常见的情况。
问题场景
我们经常搜索内容#xff0c;往往不止针对某个字段进行搜索#xff0c;比如#xff1a;标题、内容#xff0c;往往都是一起搜索…提高准确率的方法有很多但是要在提高准确率的同时保证召回率往往比较困难本文只介绍一种比较常见的情况。
问题场景
我们经常搜索内容往往不止针对某个字段进行搜索比如标题、内容往往都是一起搜索的。 index结构如下
{settings: {number_of_shards: 1,number_of_replicas: 0},mappings: {properties: {title: {type: text,analyzer: ik_smart},content: {type: text,analyzer: ik_smart}}}
}样例数据如下
{index:{_id:1}}
{title:我喜欢的一种水果,content:我喜欢的苹果是红色的含有铜、碘、锰、锌、钾等元素}
{index:{_id:2}}
{title:红色的番茄,content:番茄是一种红色的水果含有各种维生素以及糖分}
{index:{_id:3}}
{title:樱桃的介绍,content:樱桃是红色的含有丰富的糖分、铁、维生素C、蛋白质、维生素E、维生素B族和胡萝卜素}
{index:{_id:4}}
{title:不知名介绍,content:我爱吃红色红色红色红色红色红色红色红色红色红色红色红色红色红色红色红色的水果}现在我要搜索【红色的苹果】我们人眼看下来id1的文档肯定是最佳匹配的。但我们真实搜索结果会怎么样呢 搜索语句假设如下
{query: {bool: {should: [{match: {title: 红色的苹果}},{match: {content: 红色的苹果}}]}}
}上面搜索语句dsl语句看着略微复杂我们换个写法效果一样
{query: {multi_match: {query: 红色的苹果,type: most_fields,fields: [title,content]}}
}结果
[{_index:dong_analyzer_test2,_type:_doc,_id:2,_score:1.9675379,_source:{title:红色的番茄,content:番茄是一种红色的水果含有各种维生素以及糖分}},{_index:dong_analyzer_test2,_type:_doc,_id:1,_score:1.9362588,_source:{title:我喜欢的一种水果,content:我喜欢的苹果是红色的含有铜、碘、锰、锌、钾等元素}},{_index:dong_analyzer_test2,_type:_doc,_id:3,_score:0.63812846,_source:{title:樱桃的介绍,content:樱桃是红色的含有丰富的糖分、铁、维生素C、蛋白质、维生素E、维生素B族和胡萝卜素}},{_index:dong_analyzer_test2,_type:_doc,_id:4,_score:0.2719918,_source:{title:不知名介绍,content:我爱吃红色红色红色红色红色红色红色红色红色红色红色红色红色红色红色红色的水果}}
]很明显和我们人眼评分肯定是不一样的
思考
问题1为什么id2的番茄评分最高
我们先看下切词
{tokens: [{token: 红色,start_offset: 0,end_offset: 2,type: CN_WORD,position: 0},{token: 的,start_offset: 2,end_offset: 3,type: CN_CHAR,position: 1},{token: 苹果,start_offset: 3,end_offset: 5,type: CN_WORD,position: 2}]
}因为番茄中title中有【红色】【的】content中有【红色】【的】title和content同时都命中了所以匹配到了它。
问题2id1的content中不仅有【苹果】还有【红色】【的】为什么评分比id2的番茄低
因为id1的title种没有【红色】【的】尽管id1的content的匹配度 大于 id2的content但是title匹配度不及id2
问题3凭什么title分低一点content分高一点不能把整体评分拉齐
一般来说title分低只要content分高照样总分可以超过其他文档。那这个样例的问题出在哪了呢
我们再看下样例
{title:我喜欢的一种水果,content:我喜欢的苹果是红色的含有铜、碘、锰、锌、钾等元素}
{title:红色的番茄,content:番茄是一种红色的水果含有各种维生素以及糖分}
{title:樱桃的介绍,content:樱桃是红色的含有丰富的糖分、铁、维生素C、蛋白质、维生素E、维生素B族和胡萝卜素}
{title:不知名介绍,content:我爱吃红色红色红色红色红色红色红色红色红色红色红色红色红色红色红色红色的水果}我们发现content中【红色】这个词出现频率非常高。 我们可以想到es的搜索算法中有一个逆向文档频率它描述的是某个词在所有文档中出现的频率越高它的权重越低。 回到问题2content分高一点不能把整体评分拉齐答案是可以的但是问题出在了content的分虽然高但是高的不多比起title差的远上面样例中title出现【红色】只有一个而已。
解决方案
方案1 - 调整权重不建议
能不能给title权重降低一点这样就能弥补【红色】权重低的问题了。
针对这个样例来说这样做是可以的。但这仅仅是个样例现实中我们不能这样去解决问题因为上面的样例完全可以逆转让title和content字段互换。难道又要去调整content的权重么
方案2 - 精确匹配略微不建议
上面有个问题就是id2的文档中根本没有【苹果】也被匹配出来了那么我精确匹配是不是就可以了
{query: {multi_match: {query: 红色的苹果,type: most_fields,operator: and,fields: [title,content]}}
}and代表所有词都必须匹配当然也可以使用minimum_should_match但本文的样例必须使用100%
查询结果
[{_index:dong_analyzer_test2,_type:_doc,_id:1,_score:1.499949,_source:{title:我喜欢的一种水果,content:我喜欢的苹果是红色的含有铜、碘、锰、锌、钾等元素}}
]确实我们最希望匹配出来的结果被匹配出来了并且排在了第一但是其他不是很相关的文档却没有匹配出来这降低了召回率。所以这种方案不是特别推荐 备注这种解决问题的思路是没有问题的往往这种精确匹配要搭配其他查询条件一起使用但和本文想讨论的问题不相关放到其他文章中去介绍。
方案3 - 新建字段还行
上面的问题关键在哪呢 仔细分析可以发现我们的需求是希望搜索一个query进行多个字段title、content的搜索。换句话说我们其实是希望title和content是一个字段他们共享TF/IDF我们并不希望因为某些词在content中出现很频繁但在title中出现不频繁导致最终评分不符合预期。
根据上面思路我们是不是可以建一个新字段把title和content拼接在一起就行了
{settings:{number_of_shards:1,number_of_replicas:0},mappings:{properties:{title:{type:text,analyzer:ik_smart},content:{type:text,analyzer:ik_smart},title_content:{type:text,analyzer:ik_smart}}}
}这样做是可以的但是有两个弊端
业务系统在插入的时候需要手动把title和content拼接在一起然后整体写入title_content。es存储空间变大title和content的内容相当于存了双份
方案4 - 新建字段索引不错
怎么解决方案3的这个问题呢 可以利用copy_to
{settings:{number_of_shards:1,number_of_replicas:0},mappings:{person:{properties:{title:{type:text,analyzer:ik_smart,copy_to:title_content},content:{type:text,analyzer:ik_smart,copy_to:title_content},title_content:{type:string,analyzer:ik_smart}}}}
}这样es帮我们在插入数据的时候自动把映射的索引copy到了title_content中去。 注意这里所有的分词器要保持一致 但它同样还有弊端
在创建索引的时候就必须考虑到这种情况不然还要刷重刷数据
方案5 - term-centric推荐
其实解决办法除了重新刷一遍数据以外还有别的更加优雅的方式可以不用在建索引的时候把所有情况考虑到位。 利用cross_fields词中心式的方式来解决
{query: {multi_match: {query: 红色的苹果,type: cross_fields,fields: [title,content]}}
}搜索结果
[{_index:dong_analyzer_test2,_type:_doc,_id:1,_score:1.499949,_source:{title:我喜欢的一种水果,content:我喜欢的苹果是红色的含有铜、碘、锰、锌、钾等元素}},{_index:dong_analyzer_test2,_type:_doc,_id:4,_score:0.30932084,_source:{title:不知名介绍,content:我爱吃红色红色红色红色红色红色红色红色红色红色红色红色红色红色红色红色的水果}},{_index:dong_analyzer_test2,_type:_doc,_id:2,_score:0.2428131,_source:{title:红色的番茄,content:番茄是一种红色的水果含有各种维生素以及糖分}},{_index:dong_analyzer_test2,_type:_doc,_id:3,_score:0.23682731,_source:{title:樱桃的介绍,content:樱桃是红色的含有丰富的糖分、铁、维生素C、蛋白质、维生素E、维生素B族和胡萝卜素}}
]他的原理就是把所有字段当成一个大字段并在每个字段中查找每个词。 看下es的对cross_fields的分析过程
blended(terms:[title:红色, content:红色])
blended(terms:[title:的, content:的])
blended(terms:[title:苹果, content:苹果])可以发现es进行三次大搜索每次大搜索下面有两次小搜索每次大搜索都是把切词的结果词进行匹配每次小搜索都是把当前的切词对title和content进行terms匹配最后把里层和外层搜索评分相加得到最终结果。
总结
本文探讨了多字段搜索的时候每个字段的词频和逆向文档频率不同带来的搜索准确率问题。 问题的根本原因在于搜索的时候大多数都是针对字段进行搜索但本文中的情况是希望对词进行搜索。 解决思路也是很简单就是把多个字段的词频和逆向文档频率整合到一起当然可以在建立索引的时候整合也可以搜索的时候进行整合查询。