ElasticSearch-Как объединить результаты различных запросов для улучшения средней точности - PullRequest
2 голосов
/ 07 июня 2019

Я делаю запрос А по упругому поиску и получаю первые 50 результатов.Я также делаю запрос B, который содержит 30% условий запроса A. Каждый результат запроса A имеет оценку сходства scoreA, а каждый результат B имеет scoreB.То, что я пытаюсь достичь, - это объединить результаты A и B, чтобы улучшить среднюю точность каждого отдельного запроса.Один из способов, который я нашел, это переупорядочить результаты на основе этой формулы:

SIMnew = λ*scoreA + (1-λ)*scoreB

, где λ - гиперпараметр, который я должен настроить.Я заметил, что эта формула очень похожа на сглаживание Jelineck-Mercer, которое реализовано в Elastic Search (https://www.elastic.co/blog/language-models-in-elasticsearch).

. Есть ли какой-либо способ по умолчанию для такого переупорядочения с Elastic Search, или единственный способ - это пользовательская реализация?

(Учитывая, что я много искал об этой формуле и не нашел что-то полезное, было бы здорово, если бы somenone дал мне интуицию о том, как и почему это работает)

1 Ответ

1 голос
/ 09 июня 2019

Сочетание результатов различных запросов в Elasticsearch обычно достигается с помощью запроса bool. Изменения в способе их объединения можно внести с помощью запроса function_score.

В случае, если вам нужно объединить различные функции оценки для каждого поля (также известные как сходство ), например, выполнить тот же запрос с 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 ..."
}

Практически, у нас есть официальный сайт Дэвида Гилмора, тот, что Дэвид Боуи, и две другие страницы о Дэвиде Боуи.

Давайте попробуем найти официальный сайт Дэвида Гилмора:

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",
...

Почему-то страница Дэвида Гилмора не первая.

Если мы возьмем 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",

Допустим, мы не хотим отбрасывать все остальные результаты, просто хотим изменить порядок, чтобы веб-сайт Дэвида Гилмора был выше в результатах. Что мы можем сделать?

Используйте простые bool query

Цель запроса 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 вычисляет релевантность для каждого поля (которое называется схожесть ), это можно сделать путем определения пользовательской модели оценки .

В случае, который я с трудом представляю, вы можете объединить, скажем, BM25 и DFR подсчет очков. Elasticsearch допускает только одну модель подсчета для каждого поля, но также позволяет анализировать одно и то же поле несколько раз с помощью нескольких полей .

Отображение может выглядеть следующим образом:

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 (пример взят из документации ).

Теперь мы сможем выполнить bool запрос с комбинацией сходств следующим образом:

POST mysim/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "abstract": "david gilmour official"
          }
        },
        {
          "match": {
            "abstract.dfr": "david gilmour official"
          }
        }
      ]
    }
  }
}

Обратите внимание, что мы делаем один и тот же запрос к двум разным полям. Здесь abstract.dfr - это «виртуальное» поле с моделью оценки, установленной в DFR.

Что еще мне следует рассмотреть?

В Elasticsearch вычисляются баллы на шард , что может привести к неожиданным результатам. Например, IDF вычисляется не по всему индексу, а только по подмножеству документов, находящихся в одном и том же сегменте.

Здесь вы можете прочитать, как Lucene, основа Elasticsearch, вычисляет баллы релевантности.


Надеюсь, это поможет!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...