我在elastic search上做了一个查询A,得到前50个结果。我还创建了一个查询B,其中包含查询A的30%的术语。查询A的每个结果都有一个相似度分数scoreA
,并且B的每个结果都有scoreB
。我试图实现的是将A和B的结果结合起来,以提高每个单独查询的平均精度。我发现的一种方法是根据这个公式对结果进行重新排序:
SIMnew = λ*scoreA + (1-λ)*scoreB
其中λ
是我应该调优的超参数。我注意到这个公式非常类似于在弹性搜索(https://www.elastic.co/blog/language-models-in-elasticsearch)中实现的Jelineck-Mercer平滑。
有没有使用Elastic Search进行这种重新排序的默认方法,或者唯一的方法是自定义实现?
(考虑到我搜索了很多关于这个公式的信息,但没有找到有用的东西,如果somenone能给我一个关于它是如何工作以及为什么工作的直觉,那就太好了)
发布于 2019-06-10 00:38:27
Elasticsearch中不同查询结果的组合通常是通过bool
查询来实现的。可以使用function_score
query更改它们的组合方式。
如果您需要组合不同的每个字段的评分函数(也称为similarity),例如,使用BM25
和DFR
执行相同的查询并组合它们的结果,使用fields
多次为同一字段建立索引可能会有所帮助。
现在让我来解释一下这个东西是如何工作的。
查找David Gilmour的官方网站
假设我们有一个包含以下映射和示例文档的索引:
PUT mysim
{
"mappings": {
"_doc": {
"properties": {
"url": {
"type": "keyword"
},
"title": {
"type": "text"
},
"abstract": {
"type": "text"
}
}
}
}
}
PUT mysim/_doc/1
{
"url": "https://en.wikipedia.org/wiki/David_Bowie",
"title": "David Bowie - Wikipedia",
"abstract": "David Robert Jones (8 January 1947 – 10 January 2016), known professionally as David Bowie was an English singer-songwriter and actor. He was a leading ..."
}
PUT mysim/_doc/2
{
"url": "https://www.davidbowie.com/",
"title": "David Bowie | The official website of David Bowie | Out Now ...",
"abstract": "David Bowie | The official website of David Bowie | Out Now Glastonbury 2000."
}
PUT mysim/_doc/3
{
"url": "https://www.youtube.com/channel/UC8YgWcDKi1rLbQ1OtrOHeDw",
"title": "David Bowie - YouTube",
"abstract": "This is the official David Bowie channel. Features official music videos and live videos from throughout David's career, including Space Oddity, Changes, Ash..."
}
PUT mysim/_doc/4
{
"url": "www.davidgilmour.com/",
"title": "David Gilmour | The Voice and Guitar of Pink Floyd | Official Website",
"abstract": "David Gilmour is a guitarist and vocalist with British rock band Pink Floyd, and was voted No. 1 in Fender's Greatest Players poll in the February 2006 Guitarist ..."
}
实际上,我们有一个David Gilmour的官方网站,一个David Bowie的官方网站,以及另外两个关于David Bowie的页面。
让我们试着搜索一下David Gilmour的官方网站:
POST mysim/_search
{
"query": {
"match": {
"abstract": "david gilmour official"
}
}
}
在我的机器上,这将返回以下结果:
"hits": [
...
"_score": 1.111233,
"_source": {
"title": "David Bowie | The official website of David Bowie | Out Now ...",
...
"_score": 0.752356,
"_source": {
"title": "David Gilmour | The Voice and Guitar of Pink Floyd | Official Website",
...
"_score": 0.68324494,
"_source": {
"title": "David Bowie - YouTube",
...
出于某种原因,David Gilmour的页面并不是第一个。
如果我们从第一个查询中提取30%的术语,就像最初的帖子所问的那样(让我们巧妙地选择gilmour
使我们的示例更出色),我们应该会看到改进:
POST mysim/_search
{
"query": {
"match": {
"abstract": "gilmour"
}
}
}
现在Elasticsearch只返回一个命中:
"hits": [
...
"_score": 0.5956734,
"_source": {
"title": "David Gilmour | The Voice and Guitar of Pink Floyd | Official Website",
比方说,我们不想放弃所有其他结果,只想重新排序,以便David Gilmour的网站在结果中排名更高。我们能做什么?
使用简单的bool
查询
bool
查询的目的是以OR
、AND
或NOT
的方式组合多个查询的结果。在我们的例子中,我们可以使用OR
POST mysim/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"abstract": "david gilmour official"
}
},
{
"match": {
"abstract": "gilmour"
}
}
]
}
}
}
这似乎完成了工作(在我的机器上):
"hits": [
...
"_score": 1.3480294,
"_source": {
"title": "David Gilmour | The Voice and Guitar of Pink Floyd | Official Website",
...
"_score": 1.111233,
"_source": {
"title": "David Bowie | The official website of David Bowie | Out Now ...",
...
"_score": 0.68324494,
"_source": {
"title": "David Bowie - YouTube",
...
bool
查询在幕后所做的就是简单地对每个子查询的分数求和。在本例中,最高命中率的得分1.3480294
是文档对上面两个独立查询的得分的总和:
>>> 0.752356 + 0.5956734
1.3480294000000002
但这可能还不够好。如果我们想将这些分数与不同的系数组合在一起呢?
组合具有不同系数的查询
为了实现这一点,我们可以使用function_score
查询。
POST mysim/_search
{
"query": {
"bool": {
"should": [
{
"function_score": {
"query": {
"match": {
"abstract": "david gilmour official"
}
},
"boost": 0.8
}
},
{
"function_score": {
"query": {
"match": {
"abstract": "gilmour"
}
},
"boost": 0.2
}
}
]
}
}
}
在这里,我们用λ = 0.8
实现了原始post中的公式。
"hits": [
...
"_score": 0.8889864,
"_source": {
"title": "David Bowie | The official website of David Bowie | Out Now ...",
...
"_score": 0.7210195,
"_source": {
"title": "David Gilmour | The Voice and Guitar of Pink Floyd | Official Website",
...
在我的机器上,这仍然会产生“错误”的排序。
但是将λ
更改为0.4似乎可以完成此任务!万岁!
如果我想结合不同的相似性呢?
如果您需要更深入,并能够修改Elasticsearch计算每个字段的相关性(称为similarity)的方式,可以通过定义custom scoring model来完成。
在我很难想象的情况下,你可能想要组合,比如说,BM25
和DFR
评分。Elasticsearch允许每个字段只有一个评分模型,但它也允许通过multi fields对同一字段进行多次分析。
映射可能如下所示:
PUT mysim
{
"mappings": {
"_doc": {
"properties": {
"url": {
"type": "keyword"
},
"title": {
"type": "text"
},
"abstract": {
"type": "text",
"similarity": "BM25",
"fields": {
"dfr": {
"type": "text",
"similarity": "my_similarity"
}
}
}
}
}
},
"settings": {
"index": {
"similarity": {
"my_similarity": {
"type": "DFR",
"basic_model": "g",
"after_effect": "l",
"normalization": "h2",
"normalization.h2.c": "3.0"
}
}
}
}
}
请注意,在这里我们定义了一个名为my_similarity
的新相似度,它可以有效地计算DFR (取自documentation)。
现在,我们将能够通过以下方式进行具有相似性组合的bool
查询:
POST mysim/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"abstract": "david gilmour official"
}
},
{
"match": {
"abstract.dfr": "david gilmour official"
}
}
]
}
}
}
请注意,我们对两个不同的字段执行相同的查询。这里abstract.dfr
是一个“虚拟”字段,评分模型设置为DFR。
我还应该考虑什么?
在Elasticsearch中,分数是per-shard计算的,这可能会导致意外的结果。例如,IDF不是在整个索引上计算的,而是只在同一分片中的文档子集上计算的。
你可以阅读Here,Elasticsearch的主干是如何计算相关性分数的。
希望这能有所帮助!
https://stackoverflow.com/questions/56492145
复制相似问题