首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >ElasticSearch-如何组合不同查询的结果以提高平均平均精度

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

Stack Overflow用户
提问于 2019-06-07 17:59:01
回答 1查看 748关注 0票数 2

我在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能给我一个关于它是如何工作以及为什么工作的直觉,那就太好了)

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-06-10 00:38:27

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

如果您需要组合不同的每个字段的评分函数(也称为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实现了原始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来完成。

在我很难想象的情况下,你可能想要组合,比如说,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的主干是如何计算相关性分数的。

希望这能有所帮助!

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/56492145

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档