BM25和Lucene Default Similarity比较 (原文标题:BM25 vs Lucene Default Similarity)
原文链接: https://www.elastic.co/blog/found-bm-vs-lucene-default-similarity
原文 By Konrad Beiske
翻译 By 高家宝
这篇文章是之前讨论相似度模型(vsm和bm25)的文章的后续,在这篇文章中我们将使用维基百科的文章数据比较这两个模型的准确率和召回率。
概述
在前一篇文章中我从定义上比较了BM25和tf-idf的不同。然而Lucene/Elasticsearch中的默认相似度并非是纯粹的tf-idf实现,事实上里面加入文档长度的归一化参数。因此学术论文中关于tf-idf与BM25比较的结论(BM25比tf-idf更好)并不能完全适用于Lucene中,这使得比较Lucene中的默认相似度和BM25变得更加困难。为了解决这个疑惑我将使用维基百科的文章作为数据来测试比较两者的准确率和召回率。
索引数据
使用Wikipedia river来索引文章是一个很直接的方法,但是索引整个数据集的话欠缺一些灵活性。基于数据集的大小,仅仅是下载数据就会花费数个小时,你可能要为此等上一晚。我起初尝试使用默认的镜像,但是在我下载的中途它更新成了一个新的镜像导致下载失败了。解决办法很简单,选择一个指定的镜像。这也带来了附加的好处:使得使用不同的相似度模型重新索引完全相同的文档集变得更容易。你可能想要自己尝试一下,我推荐在river使得你的集群过载前马上开始查询,而且最好修改mapping和index设置以防因为配置不当将所有的时间都浪费在等待上。
river 配置:
{
"type" : "wikipedia",
"index" : {
"index": "wikipedia",
"type": "page",
"url" : "http://dumps.wikimedia.org/enwiki/20131202/enwiki-20131202-pages-articles.xml.bz2",
"bulk_size" : 1000,
"flush_interval" : "1s",
"max_concurrent_bulk" : 2
}
}
default similarity的index配置:
{
"settings":{
"number_of_shards":2,
"number_of_replicas":0
},
"mappings":{
"page":{
"properties":{
"category":{
"type":"string"
},
"disambiguation":{
"type":"boolean"
},
"link":{
"type":"string"
},
"redirect":{
"type":"boolean"
},
"redirect_page":{
"type":"string"
},
"special":{
"type":"boolean"
},
"stub":{
"type":"boolean"
},
"text":{
"type":"string"
},
"title":{
"type":"string"
}
}
}
}
}
BM25 similarity的index配置:
{
"settings":{
"number_of_shards":2,
"number_of_replicas":0
},
"mappings":{
"page":{
"properties":{
"category":{
"type":"string",
"similarity":"BM25"
},
"disambiguation":{
"type":"boolean",
"similarity":"BM25"
},
"link":{
"type":"string",
"similarity":"BM25"
},
"redirect":{
"type":"boolean"
},
"redirect_page":{
"type":"string",
"similarity":"BM25"
},
"special":{
"type":"boolean"
},
"stub":{
"type":"boolean"
},
"text":{
"type":"string",
"similarity":"BM25"
},
"title":{
"type":"string",
"similarity":"BM25"
}
}
}
}
}
可以看到,改变一个特定字段的相似度算法只要简单的修改mapping就可以了。
版本
在本次测试中我使用的是Elasticsearch 0.90.7 和 elasticsearch-river-wikipedia 1.3.0。
使用的查询
我用一个基本的匹配查询开始了我的实验,并把我得到的搜索结果和维基搜索的结果相比较。有两件事情很清楚:维基百科搜索的时候主要使用你输入的关键词匹配标题;返回的文档集合里也有很多文档只是一个标题别称,重定向到另一个文档。基于维基百科的词典属性,使用标题进行搜索匹配是文档的标题有强相关性的正常推论。重定向是一个指向一篇文档的替代标题或拼写,但是事实上在索引到Elasticsearch的时候重定向是作为只有重定向信息的文档存在于文档集中,更好的实现应该是包含文档具体内容的。
Wikipedia river事实上检测到了重定向文档并且用布尔字段redirect标记了他们,因此可以很容易地过滤掉。我决定将这些重定向文档过滤掉,因为对于其他文档来说,重定向文档往往在包含搜索词的同时更短从而获取更高的分数。为了更好的获得匹配文档我还尝试了一些只匹配标题的查询,这种查询更容易找到我想要的文档。如果我想要用Elasticsearch重新实现一个真实的维基百科的搜索,我当然会给标题/重定向标题加上boost。
现在我们回到最初的问题。我需要一种方法来创建一个query集合并且对于每个query应该有一个已知的正确返回结果。基于文章标题能够代表文章的特点,我只是随机找出了1000篇文档,使用它们的标题作为查询词,这些标题对应的文档就是查询对应的正确返回结果。
然后我写了一个脚本来循环查询所有测试集中的查询,下面代码中的<title>替换为对应的查询。
{
"query": {
"filtered": {
"query": {
"match": {
"text": "<title>"
}
},
"filter": {
"bool": {
"must_not": [
{
"term": { "redirect": true }
},
{
"regexp": { "text": "redirect[^\\s]+" }
}
]
}
}
},
"explain": true
}
对这个测试来说理想情况下不应该索引重定向文档,但是事实上是索引后使用上面的查询来将这些文档过滤掉。过滤器通过river创建的redirect字段进行过滤,对于一些river没有检测到的重定向文档,使用正则表达式来过滤掉。
这个查询只返回前10条结果(Elasticsearch的默认设定)。通常情况下,比较相似度模型使用准确率(在查询结果中相关文档的比率)和召回率(所有相关文档中被返回的比率),但是只返回前10条结果的限制以及每个查询只有一个相关文档让这两个度量值基本失去了意义,所有的查询的召回率只会是0或100%,准确率只会是0或10%。对于我这次的非学术测试,我使用自己的度量来替代准确率和召回率:使用没有找到目标文档的查询的比率反映召回率,使用找到了目标文档的所有查询的目标文档排名平均值反映准确率。
这个脚本的执行结果是对于两种相似度模型分别计算准确率和召回率,匹配方法是用文档的标题去匹配文档的内容(除去标题)。
结果和结论
| 相似度模型 | 相关文档平均排名 | 未返回相关文档的查询比率 |
| BM25 | 2.07 | 16.0% |
| Default | 2.44 | 57.7% |
很显然在这次测试中BM25要比默认的相似度模型表现得更好,但是在解读这个结果的时候要注意到查询的时候有一个只返回前10条结果的限制。如果返回更多的结果,两个相似度模型的未返回相关文档的查询比率都会下降(召回率提升),但是相关文档平均排行也会上升(准确率下降)。
除非在每次查询都调整返回数量保证相关结果返回,否则没有办法计算确切的准确率和召回率,但是这个证明并不重要,因为这个结果只是关于用英文版本的维基百科的标题匹配内容而已。这次实验更倾向于搜索实践而不是标准证明,我选择使用前10个返回结果的另一个原因是因为当用户在前10个结果中找不到相关结果时,往往更倾向于重新定义更精炼的查询词重新进行搜索。
换句话说本次实验的结论并不是一个BM25总是比Default similarity要好的标准证明,但是实验结论揭示了比起Default similarity,BM25确实有很大的潜力,至少在一部分场景下。我强烈建议你使用自己真实场景下的数据花些时间来测试一下这两种相关性模型的差异。
BM25和Lucene Default Similarity比较 (原文标题:BM25 vs Lucene Default Similarity)的更多相关文章
- Lucene底层原理和优化经验分享(1)-Lucene简介和索引原理
Lucene底层原理和优化经验分享(1)-Lucene简介和索引原理 2017年01月04日 08:52:12 阅读数:18366 基于Lucene检索引擎我们开发了自己的全文检索系统,承担起后台PB ...
- Visual C++ 异常(Exception)常见问题 (原文标题:A Visual C++ Exception FAQ)
Visual C++ 异常(Exception)常见问题 版权:Doug Harrison 2001 – 2007 翻译:magictong(童磊) 2011年3月 原文地址:http://membe ...
- Lucene创建索引和索引的基本检索(Lucene 之 Hello World)
Author: 百知教育 gaozhy 注:演示代码所使用jar包版本为 lucene-xxx-5.2.0.jar 一.lucene索引操作 1.创建索引代码 try { // 1. 指定索引文件存 ...
- 【Java】JavaWeb权限管理
权限管理分析 每个网站都涉及到访问权限的控制.每个站点资源都需要被管理起来,用户只有具有访问某个资源的特定权限,才能够访问,否则拒绝访问.网站的访问权限控制,一种方法从 URI 入手,站点的每个资源都 ...
- mysql优化之表建设
就拿常见的用户表.文章类的表.日志表来分析如下 CREATE TABLE `user` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMEN ...
- 仿9GAG制作过程(二)
有话要说: 这次准备讲述用python爬虫以及将爬来的数据存到MySQL数据库的过程,爬的是煎蛋网的无聊图. 成果: 准备: 下载了python3.7并配置好了环境变量 下载了PyCharm作为开发p ...
- 五、Java多人博客系统-2.0版本-数据库设计
数据库设计表如下:文章类别表.文章表.评论表.留言表. 文章列表表:存放文章类别,首页菜单生成也是从这个表取的. 文章表:存放文章标题.发表时间.内容等信息. 评论表:文章评论内容. 留言表:用户发表 ...
- Spring+Mybatis 复杂的分组查询
1.需要的结果数据格式为 { "responseCode": "0000", "responseMsg": null, "data ...
- JAVA实现用户的权限管理
一:写在前面 前两天有个同学问我,那个系统不同的用户登陆不同的页面不同,要写很多个页面啊!而每个用户的在系统中拥有不同的权限,可以访问不同的页面是怎么实现的??那低权限的在浏览器输入高权限的人的url ...
随机推荐
- memcached参数解释及常用命令
一.执行 memcached -h 会显示所有的参数项,对应的中文解释如下: -p <num> 监听的TCP端口(默认: 11211) -U <num> 监 ...
- 爱回收jd图标
http://jd.aihuishou.com/images/icons.png http://misc.360buyimg.com/201007/skin/df/i/bg_hotsale.gif 来 ...
- Nginx的配置文件详解
主配置文件: 查看nginx的进程可以看到nginx所使用的配置文件: 主配置一般会被用来设置一些全局的参数: 参数详解: user nobody nobody; //设置nginx ...
- dotweb——go语言的一个微型web框架(一)
dotweb是16年正式托管到github的一个开源项目,go语言的web框架目前也有很多,出名的有bee和echo.它们都是很优秀的框架,但是我们喜欢更轻.更小的东西,经历一些之后我们更青睐微服务这 ...
- Codeforces 765E. Tree Folding [dfs][树形dp]
题解:先从节点1开始dfs.对于每一个节点,用一个set记录:以该点为根的子树的深度. a) 如果此节点的某个子节点打出了GG,则此节点直接打出GG. b) 若set的元素个数<=1,那么,以该 ...
- webix .datatable 表格分页
grid表格返回参数大都是 以下这种格式(参数名可能不一样) { data:[{...},{...} ...], count:39 } webix的参数格式为 { data:[{...},{...}, ...
- 测试开发Python培训:实现屌丝的黄色图片收藏愿望(小插曲)
男学员在学习python的自动化过程中对于爬虫很感兴趣,有些学员就想能收藏一些情色图片,供自己欣赏.作为讲师只能是满足愿望,帮助大家实现对美的追求,http://wanimal.lofter.com/ ...
- JavaScript ES5面向对象实现一个todolist
todo-list 前言 遵守 开始 布局 设计对象 对象的属性 事件绑定 业务逻辑单元的操作 实例化对象 参考 todo-list 前言 最近阅读了JavaScript设计模式的面向对象篇,但是又苦 ...
- linux环境下 mysql数据库忘记密码 处理办法UPDATE user SET Password = password ( 'new-password' ) WHERE User = 'root' ;
整个修改过程大概3-10分钟(看个人操作),这个时间内mysql出于不需要密码就能登陆的状态,请设法保证系统安全 不罗嗦直接上步骤 1.vi /etc/my.cnf 在[mysqld]下,添加一句:s ...
- 优化Servlet:(利用反射的思想)
1.创建BaseServlet (重写父类的service方法) package com.learning.web.servlet; import java.io.IOException; impor ...