Оценка релевантности в Manticore: часть I

Эта статья первая в серии, в которой мы говорим о том, как работает оценка релевантности в 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, соответственно наименьшее 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' должны обеспечивать лучшие результаты в общих случаях.

Что дальше?

На этом этапе мы можем создать оценку на основе частоты и специфичности слов, но это только говорит нам о том, что слова находятся в документе, а не о том, сгруппированы ли слова рядом друг с другом или просто разбросаны по тексту. Это приводит к еще одной вещи, которую нам нужно добавить в коллекцию: позиции слов в тексте.
Наличие позиций позволяет нам знать, совпадают ли входные слова с точным полем документа, найдены ли они дословно или насколько близки/дальние они друг от друга, или насколько близки они к началу текста (так как мы можем считать, что быть ближе или даже начинать текст более релевантно, чем находиться в конце документа).
Поскольку документ может иметь более одного поля, важность полей также может быть разной. Например, если у нас есть столбцы 'title' и 'body', совпадения в 'title' рассматриваются как более важные, и они должны получать более высокий балл. Но об этом подробнее в следующей статье.

Установить Manticore Search

Установить Manticore Search