Оглавление цикла:
Часть третья (вы тут)

Это третья и заключительная статья из цикла, в которой рассмотрим стандартную модель ранжирования документов в Elasticsearch.
Релевантность документов
После того как определено множество документов, которые удовлетворяют параметрам полнотекстового запроса, Elasticsearch рассчитывает метрику релевантности для каждого найденного документа. По значению метрики набор документов сортируется и отдается потребителю.
В Elasticsearch существует несколько моделей ранжирования документов, о которых можно почитать в документации. По умолчанию используется Okapi BM25. Три кита этой модели:

Term frequency (TF) — частотность терма. Чем чаще терм появляется в поле документа, тем он релевантнее;
Inverse document frequency (IDF) — обратная частота употребления терма в документах. Чем больше документов содержат искомый терм в поле, тем меньше значимость этого терма. Данный параметр вводится для снижения веса часто употребляемых слов в выборке;
Normalization by field length — нормализация по длине поля. Если два документа имеют одинаковое количество вхождения искомого терма и документы отличаются по размеру, то наиболее релевантным будет документ, размер которого меньше.
Описание функции ранжирования:
Q (query) — текущий запрос;
𝐷 (document) — текущий документ;
𝑞𝑖 — i-ый терм;
𝑓(𝑞𝑖, 𝐷) — частота появления терма ?? в документе ?;
𝑓𝑖𝑒𝑙𝑑𝐿𝑒𝑛𝑔𝑡ℎ — длина поля в термах;
𝑎𝑣𝑔𝐹𝑖𝑒𝑙𝑑𝐿𝑒𝑛𝑔𝑡ℎ — средняя длина поля в термах;
𝐼𝐷𝐹(𝑞𝑖) — обратная частота употребления терма ?? в документах;
𝑓(𝑞𝑖 ) — кол-во документов, в которых встречается искомый терм;
𝑑𝑜𝑐𝐶𝑜𝑢𝑛𝑡 — количество документов, которые имеют искомое поле;
𝑘1 — коэффициент, который задает порог насыщения по частоте терма (в Elasticsearch равен 1.2);
𝑏 — коэффициент, который усиливает отношение длины документа к средней длине (в Elasticsearch равен 0.75).
Рассмотрим несколько графиков.
Зависимость метрики релевантности от частоты термов при разных значениях k1

Чем больше коэффициент k1, тем выше порог и меньше скорость насыщения. При k1 равным нулю все параметры игнорируются кроме 𝐼𝐷𝐹.
Также на приведенном графике видно, что начиная с некоторого значения частоты, добавление новых термов, которые соответствуют поисковому запросу, не вносит дополнительный вклад в значение метрики релевантности.
Зависимость метрики релевантности от частоты термов при разных значениях отношения длины документы к средней длине

Короткие документы гораздо быстрее достигают порога насыщения, т.к. рассматриваемое отношение находится в знаменателе. Чем больше это отношение, тем меньше значение метрики релевантности.
После достижения "потолка" добавление новых термов, соответствующих поисковому запросу, не вносит дополнительный вклад в значение метрики релевантности.
Задав коэффициент b в ноль, можно полностью убрать влияние данного параметра.
Зависимость IDF от частоты термов при фиксированном количестве документов в выборке

Чем чаще терм встречается в документах, тем меньший вес он будет иметь среди других термов в поисковом запросе.
Пример
Для примера используется индекс с названием kotlin_articles, который имеет стандартные настройки и ровно одну шарду. Если повторить шаги примера для индекса, который имеет больше чем одну шарду, то результат будет отличаться от приведенного. Это связано с тем, что Elasticsearch рассчитывает метрику релевантности в рамках одного шарда.
В рассматриваемый индекс сохраним 5 документов.

Наш поисковый запрос состоит ровно из одного слова — Kotlin. На этом шаге несложно догадаться, в каком порядке будут выданы документы:

Результат ранжирования Elasticsearch:

Значение релевантности находится в после "_score".
А теперь предлагаю проверить, совпадает ли выданное значение метрики с теоретическим. Для проверки возьмем самый релевантный документ, и подставим его параметры в функцию ранжирования.

И мы видим, что значения совпадают!
Для закрепления проверим наши расчеты с помощью Explain API:

{ "_index": "kotlin_articles", "_id": "2", "matched": true, "explanation": { "value": 0.12335789, "description": "weight(name:kotlin in 1) [PerFieldSimilarity], result of:", "details": [ { "value": 0.12335789, "description": "score(freq=2.0), computed as boost * idf * tf from:", "details": [ { "value": 2.2, "description": "boost", "details": [] }, { "value": 0.087011375, "description": "idf, computed as log(1 + (N - n + 0.5) / (n + 0.5)) from:", "details": [ { "value": 5, "description": "n, number of documents containing term", "details": [] }, { "value": 5, "description": "N, total number of documents with field", "details": [] } ] }, { "value": 0.64441884, "description": "tf, computed as freq / (freq + k1 * (1 - b + b * dl / avgdl)) from:", "details": [ { "value": 2, "description": "freq, occurrences of term within document", "details": [] }, { "value": 1.2, "description": "k1, term saturation parameter", "details": [] }, { "value": 0.75, "description": "b, length normalization parameter", "details": [] }, { "value": 5, "description": "dl, length of field", "details": [] }, { "value": 5.6, "description": "avgdl, average length of field", "details": [] } ] } ] } ] } }
В ответе можно узнать функцию ранжирования Okapi BM25, а также какие значения были использованы для расчета.
Это была последняя статья из цикла. Спасибо, что дочитали, если у вас будут какие-то вопросы — добро пожаловать в комментарии.

