我正在对弹性搜索进行查询A,并获得前50个结果。我还制作了一个查询B,其中包含查询A的30%的条件。查询A的每个结果都有一个相似度得分ListFragment
,而查询B的每个结果都有scoreA
。
我想要实现的是结合A和B的结果来提高每个个性化查询的平均平均精度。我发现的一种方法是根据以下公式对结果重新排序:
scoreB
其中SIMnew = λ*scoreA + (1-λ)*scoreB
是我应该调整的超参数。我注意到该公式与在Elastic Search(https://hbase.apache.org/book.html#dm.sort)中实现的Jelineck-Mercer平滑非常相似。
是否有任何默认方法可以通过Elastic Search进行重新排序,或者唯一方法是自定义实现?
(鉴于我在该公式中进行了大量搜索,但没有发现有用的东西,如果有人让我对它的工作原理和原因有所了解,那就太好了
答案 0 :(得分:1)
Elasticsearch中不同查询的结果组合通常是通过bool
查询来实现的。可以使用function_score
查询来更改它们的组合方式。
例如,如果您需要组合不同的按字段计分功能(也称为similarity),例如,对BM25
和DFR
进行相同的查询并将其组合结果,使用fields
多次索引同一字段会有所帮助。
现在让我解释一下这件事是如何工作的。
假设我们有一个包含以下映射和示例文档的索引:
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
实现原始帖子中的公式。
"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的骨干Lucene如何计算相关性得分。
希望有帮助!