Эта статья первая в серии, в которой мы говорим о том, как работает оценка релевантности в Manticore Search. Мы начинаем с основ релевантности: частоты терминов и специфичности.
Простое введение в оценку
Поисковые системы, такие как Sphinx/Manticore Search, принимают тексты из документов, разбивают их на токены и позволяют выполнять поиск по множеству токенов/слов в этой коллекции.
В самом простом сценарии эта коллекция содержит токены/слова (в том, что мы называем индексным словарем) и список документов, в которых они были найдены, и полей документов, в которых они находятся.
Поиск в первую очередь определяет, есть ли одно из входных слов в коллекции, и извлекает список документов, который позже объединяется с другими списками слов для составления списка документов, содержащих все слова из входных данных.
Если у нас есть более одного документа, соответствующего этому критерию, следующим шагом будет различение между ними. Какой из этих документов более релевантен для данного входа?
Первый и простой дифференциатор — это подсчет вхождений каждого входного слова в документе. Большее количество вхождений означает более релевантное слово? Это зависит.
Некоторые слова могут встречаться много раз в документе - что мы можем считать, что это слово важно для этого документа, но это слово также может появляться во множестве других документов из коллекции - что ставит вопрос о том, является ли это слово просто общим словом, и его важность должна быть уменьшена.
Если мы подсчитаем вхождения (частоту) слова в документе, мы хотим подавить возможный эффект (на наш счет) этих общих слов. Для этого мы добавляем счетчик документов, содержащих слово - это “частота термина” - и другой счетчик, который говорит нам, в скольких документах (по отношению к общему количеству) слово найдено - обратное известно как частота обратного документа (IDF) и является мерой специфичности термина.
Вместе эти два формируют TF-IDF, который говорит нам, насколько важно слово относительно документа в коллекции. Основываясь на IDF, мы также можем вычислить другую широко используемую функцию ранжирования - BM25.
BM25 и TF-IDF
Как эти факторы реализованы и могут быть использованы в Manticore Search?
Для начала, есть несколько встроенных ранжиров (формул для оценки), которые можно использовать сразу. Но самым важным является выражение ранжирования, которое позволяет получить доступ к этим факторам и создавать пользовательские формулы.
Мы упоминали ранее функцию ранжирования BM25, которая в Manticore включена в стандартный ранжир (proximity_bm25) и также может использоваться как фактор на уровне документа в выражениях ранжирования.
Мы не собираемся углубляться в детали BM25, поскольку о нем написано множество научных работ, но мы сосредоточимся на реализации в Manticore.
Что следует отметить о факторе bm25, так это то, что название немного неприемлемо, так как это не традиционная реализация BM25, а скорее оценка, эквивалентная BM25(K1=1.2,B=0)
- что в некоторых статьях называется BM15.
Вы все еще можете использовать классический BM25 и даже настраивать параметры k1 и b с помощью функции ранжирования bm25a(k1,b)
.
Еще более продвинутая версия - bm25f(k1, b, {field=weight, …})
, которая позволяет также устанавливать веса для полей. Обратите внимание, что это отличает от обычных field_weights (подробнее об этом позже) и требует опции index_field_lenghts.
На уровне полей TF-IDF можно получить несколькими способами: **tf\_idf**
- это сумма значений tf_idf для каждого вхождения совпадающего ключевого слова в поле. Кроме того, **sum\_idf**
- это сумма каждого значения tf_idf для каждого совпадающего ключевого слова (то есть не умноженного на количество вхождений), а **max\_idf**
и **min\_idf**
предоставляют максимальное и минимальное значение tf_idf соответственно.
Расчет IDF можно контролировать с помощью **idf**
из
OPTION
клаузулы. В начальной реализации (до Sphinx 2.1) значения IDF были нормализованы - это означает, что общие слова (встретившиеся более чем в 50% документов) подвергались штрафу (idf=‘normalized’). Значение IDF варьируется от [-log(N),log(N)]
. В этом режиме совпадение “общего слова + не очень общего слова” получит сумму IDF ниже, чем совпадение, содержащее только не очень общее слово. Чтобы избежать этого (если это было желаемо), был добавлен новый режим (idf=‘plain’), при котором нет штрафа, и значение IDF варьируется от [0,log(N)]
. Кроме того, значение IDF делится на количество ключевых слов запроса, чтобы сумма tf_idf вписалась в диапазон [0,1]
.
Это привело к проблеме в булевых запросах, когда поиск “found-word” против “found-word OR not-found-word” дает разные веса в одном и том же наборе результатов из-за деления.
Чтобы исправить это, idf
получил еще одну пару настроек, которые позволяют контролировать, применяется ли деление или нет. Настройки по умолчанию для idf
остаются normalized,tfidf_normalized
по соображениям совместимости, но idf='plain,tfidf_unnormalized'
должны дать более хорошие результаты в общих случаях.