Релевантность: scoring, TF-IDF и BM25
Разбираем, как Elasticsearch решает, какой документ показать выше — то есть как считается _score.
Релевантность (score) — числовая оценка того, насколько документ соответствует запросу; в ES по умолчанию считается формулой BM25.
Зачем нужна оценка
Полнотекстовый поиск возвращает не «да/нет», а упорядоченный список: самое подходящее — сверху. Чтобы упорядочить, каждому документу присваивается число _score. Вопрос — по какому принципу его считать, чтобы порядок совпадал с интуицией пользователя.
Три интуиции, на которых стоит scoring
| Фактор | Идея |
| Частота терма (TF) | чем чаще искомое слово в документе, тем он релевантнее |
| Обратная частота (IDF) | редкое слово важнее частого: совпадение по «квантовый» ценнее, чем по «и» |
| Длина документа | в длинном тексте слово встречается «случайнее»; короткое попадание весомее |
От TF-IDF к BM25
Классическая формула TF-IDF просто перемножает частоту терма и его обратную частоту. Но у неё есть проблема: если слово встретилось в документе 100 раз, оценка растёт линейно и неограниченно — это перекос. BM25 (Best Match 25) — улучшенная формула, которую ES использует по умолчанию. Её отличия:
- Насыщение по TF. Вклад частоты растёт всё медленнее: с 1 до 2 вхождений score прибавляется заметно, со 100 до 101 — почти нет. Логично: документ со словом 100 раз не в 100 раз релевантнее.
- Нормировка по длине. Учитывается, длиннее или короче документ среднего по индексу.
- IDF сохраняется: редкие термы дают больший вклад.
Как увидеть, почему такой score
ES умеет объяснить оценку — добавьте к запросу explain:
{
"explain": true,
"query": { "match": { "title": "кофемашина" } }
}В ответе появится разбор: вклад IDF, TF и нормировки длины для каждого терма. Это незаменимо при отладке «почему этот документ выше того».
Как работает под капотом
Для каждого документа, совпавшего по терму, ES берёт из инвертированного индекса частоту терма в этом документе (TF) и число документов с этим термом во всём индексе (для IDF), а также длину документа. Подставляет в формулу BM25 с коэффициентами k1 (насыщение) и b (влияние длины) и получает вклад терма. Если термов несколько (или есть should), вклады складываются. Итоговая сумма и есть _score.
Частые ошибки
- Сравнивать score между разными запросами. Оценка не абсолютна:
_score=8в одном запросе и_score=2в другом несопоставимы. Score имеет смысл только для упорядочивания внутри одной выдачи. - Ждать score там, где его нет. В секции
filterи при сортировке по полю релевантность не считается —_scoreбудет нулевым или отсутствует.
Итоги
- Релевантность опирается на TF (частота терма), IDF (редкость слова) и длину документа.
- ES по умолчанию использует BM25 — улучшенный TF-IDF с насыщением частоты и нормировкой длины.
explain: trueпоказывает, из чего сложился_score; сравнивать score между запросами нельзя.