ElasticSearch-如何合并不同查询的结果以提高平均平均精度

时间:2019-06-07 09:59:00

标签: elasticsearch

我正在对弹性搜索进行查询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进行重新排序,或者唯一方法是自定义实现?

(鉴于我在该公式中进行了大量搜索,但没有发现有用的东西,如果有人让我对它的工作原理和原因有所了解,那就太好了

1 个答案:

答案 0 :(得分:1)

Elasticsearch中不同查询的结果组合通常是通过bool查询来实现的。可以使用function_score查询来更改它们的组合方式。

例如,如果您需要组合不同的按字段计分功能(也称为similarity),例如,对BM25DFR进行相同的查询并将其组合结果,使用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查询的目的是以ORANDNOT的方式组合几个查询的结果。在我们的情况下,我们可以使用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来完成。

在我难以想象的情况下,您可能想要结合BM25DFR得分。 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如何计算相关性得分。


希望有帮助!