Оглавление цикла:
Часть третья (вы тут)
![](https://webcf.waybackmachine.org/web/20231010210351/https://habrastorage.org/getpro/habr/upload_files/eb1/a0a/00a/eb1a0a00af0f9c3b3b20fbea38d77d53.jpg)
Это третья и заключительная статья из цикла, в которой рассмотрим стандартную модель ранжирования документов в Elasticsearch.
Релевантность документов
После того как определено множество документов, которые удовлетворяют параметрам полнотекстового запроса, Elasticsearch рассчитывает метрику релевантности для каждого найденного документа. По значению метрики набор документов сортируется и отдается потребителю.
В Elasticsearch существует несколько моделей ранжирования документов, о которых можно почитать в документации. По умолчанию используется Okapi BM25. Три кита этой модели:
![Концепция Okapi BM25 Концепция Okapi BM25](https://webcf.waybackmachine.org/web/20231010210351/https://habrastorage.org/getpro/habr/upload_files/67b/a76/7a3/67ba767a3e88188b307092a3d2b8ed83.png)
Term frequency (TF) — частотность терма. Чем чаще терм появляется в поле документа, тем он релевантнее;
Inverse document frequency (IDF) — обратная частота употребления терма в документах. Чем больше документов содержат искомый терм в поле, тем меньше значимость этого терма. Данный параметр вводится для снижения веса часто употребляемых слов в выборке;
Normalization by field length — нормализация по длине поля. Если два документа имеют одинаковое количество вхождения искомого терма и документы отличаются по размеру, то наиболее релевантным будет документ, размер которого меньше.
Описание функции ранжирования:
Q (query) — текущий запрос;
𝐷 (document) — текущий документ;
𝑞𝑖 — 𝑖-ый терм;
𝑓(𝑞𝑖, 𝐷) — частота появления терма 𝑞𝑖 в документе 𝐷;
𝑓𝑖𝑒𝑙𝑑𝐿𝑒𝑛𝑔𝑡ℎ — длина поля в термах;
𝑎𝑣𝑔𝐹𝑖𝑒𝑙𝑑𝐿𝑒𝑛𝑔𝑡ℎ — средняя длина поля в термах;
𝐼𝐷𝐹(𝑞𝑖) — обратная частота употребления терма 𝑞𝑖 в документах;
𝑓(𝑞𝑖 ) — кол-во документов, в которых встречается искомый терм;
𝑑𝑜𝑐𝐶𝑜𝑢𝑛𝑡 — количество документов, которые имеют искомое поле;
𝑘1 — коэффициент, который задает порог насыщения по частоте терма (в Elasticsearch равен 1.2);
𝑏 — коэффициент, который усиливает отношение длины документа к средней длине (в Elasticsearch равен 0.75).
Рассмотрим несколько графиков.
Зависимость метрики релевантности от частоты термов при разных значениях k1
![Зависимость метрики релевантности от частоты термов при разных значениях k1 Зависимость метрики релевантности от частоты термов при разных значениях k1](https://webcf.waybackmachine.org/web/20231010210351/https://habrastorage.org/getpro/habr/upload_files/8bd/db6/991/8bddb69917cddff2164f02027f3e6ab6.png)
Чем больше коэффициент k1, тем выше порог и меньше скорость насыщения. При k1 равным нулю все параметры игнорируются кроме 𝐼𝐷𝐹.
Также на приведенном графике видно, что начиная с некоторого значения частоты, добавление новых термов, которые соответствуют поисковому запросу, не вносит дополнительный вклад в значение метрики релевантности.
Зависимость метрики релевантности от частоты термов при разных значениях отношения длины документы к средней длине
![Зависимость метрики релевантности от частоты термов при разных значениях отношения длины документы к средней длине Зависимость метрики релевантности от частоты термов при разных значениях отношения длины документы к средней длине](https://webcf.waybackmachine.org/web/20231010210351/https://habrastorage.org/getpro/habr/upload_files/127/894/025/12789402519eb0492cd9d88077120f19.png)
Короткие документы гораздо быстрее достигают порога насыщения, т.к. рассматриваемое отношение находится в знаменателе. Чем больше это отношение, тем меньше значение метрики релевантности.
После достижения "потолка" добавление новых термов, соответствующих поисковому запросу, не вносит дополнительный вклад в значение метрики релевантности.
Задав коэффициент b в ноль, можно полностью убрать влияние данного параметра.
Зависимость IDF от частоты термов при фиксированном количестве документов в выборке
![Зависимость IDF от частоты термов при фиксированном количестве документов в выборке Зависимость IDF от частоты термов при фиксированном количестве документов в выборке](https://webcf.waybackmachine.org/web/20231010210351/https://habrastorage.org/getpro/habr/upload_files/a28/119/8fd/a281198fd9c68ae83e01925054a4f82d.png)
Чем чаще терм встречается в документах, тем меньший вес он будет иметь среди других термов в поисковом запросе.
Пример
Для примера используется индекс с названием kotlin_articles, который имеет стандартные настройки и ровно одну шарду. Если повторить шаги примера для индекса, который имеет больше чем одну шарду, то результат будет отличаться от приведенного. Это связано с тем, что Elasticsearch рассчитывает метрику релевантности в рамках одного шарда.
В рассматриваемый индекс сохраним 5 документов.
![Сохраняемые документы Сохраняемые документы](https://webcf.waybackmachine.org/web/20231010210351/https://habrastorage.org/getpro/habr/upload_files/e76/94a/3ba/e7694a3ba4c56c70189e0456c9e25f81.png)
Наш поисковый запрос состоит ровно из одного слова — Kotlin. На этом шаге несложно догадаться, в каком порядке будут выданы документы:
![Результат ранжирования Результат ранжирования](https://webcf.waybackmachine.org/web/20231010210351/https://habrastorage.org/getpro/habr/upload_files/dd3/805/23a/dd380523aa072cac80a8975437b71d9a.png)
Результат ранжирования Elasticsearch:
![Результат ранжирования Elasticsearch Результат ранжирования Elasticsearch](https://webcf.waybackmachine.org/web/20231010210351/https://habrastorage.org/getpro/habr/upload_files/16e/05c/75f/16e05c75f965303b8fd6be191444adc3.png)
Значение релевантности находится в после "_score".
А теперь предлагаю проверить, совпадает ли выданное значение метрики с теоретическим. Для проверки возьмем самый релевантный документ, и подставим его параметры в функцию ранжирования.
![Самый релевантный документ в выборке Самый релевантный документ в выборке](https://webcf.waybackmachine.org/web/20231010210351/https://habrastorage.org/getpro/habr/upload_files/c49/5ac/a38/c495aca3846c63556bd73822b7dc46f2.png)
И мы видим, что значения совпадают!
Для закрепления проверим наши расчеты с помощью Explain API:
![Использование explain API для документа с id = 2 Использование explain API для документа с id = 2](https://webcf.waybackmachine.org/web/20231010210351/https://habrastorage.org/getpro/habr/upload_files/6e3/1c6/55b/6e31c655b05c6b31f8810a234370152e.png)
{
"_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, а также какие значения были использованы для расчета.
Это была последняя статья из цикла. Спасибо, что дочитали, если у вас будут какие-то вопросы — добро пожаловать в комментарии.