В настоящее время я реализую модификацию стандарта Lucene Сходство BM25, основанное на следующем статье. Реализация фактической формулы проста, но я борюсь с вычислением необходимой статистики.
Мне нужны следующие две статистики:
- Средняя частота терминов в документе:
length of document / # unique terms of the document (т. е. показатель повторяемости документа - для документа без повторений это будет 1, если каждый термин встречается дважды, это будет 2 и т. д. )
- Средняя частота терминов. Это среднее арифметическое приведенного выше показателя по всем документам коллекции. Это можно рассматривать как среднюю повторяемость всего корпуса.
Я обнаружил, что могу рассчитать среднюю частоту терминов для каждого документа во время индексации, переопределив метод computeNorm моей реализации подобия. Я могу хранить значение вместе с нормальным значением с помощью битовых операций (не очень красиво, но пока это работает). Затем во время запроса я могу извлечь среднюю частоту и длину термина в документе.
Однако это не помогает найти среднюю среднюю частоту терминов. Очевидно, что это значение для всей коллекции, и поэтому его следует вычислять в length of document / # unique terms of the document0/core/org/apache/lucene/search/similarities/Similarity.html#computeWeight. -float-org.apache.lucene.search.CollectionStatistics-org.apache.lucene.search.TermStatistics...-" rel="nofollow noreferrer">Similarity.computeWeight насколько я понимаю, но не не вижу, как это можно сделать, учитывая аргументы функции.
Какое место было бы идеальным для расчета этой статистики?
Я новичок в Lucene, поэтому, возможно, есть очевидное решение, которого я еще не видел. Я благодарен за любой вклад.
Ответ 1 · Mysterion
Similarity.computeWeight имеет параметр CollectionStatistics, который содержит maxDoc (возвращает общее количество документов, независимо от того, все ли они содержат значения для этого поля), и TermStatistics, который содержит term и totalTermFreq (возвращает общее количество вхождений этого термина) и по разделив его, вы можете получить среднюю частоту термина
Ответ 2 · John Foley
Вам нужно будет рассчитать свою собственную "норму", чтобы придерживаться индекса Lucene. По сути, вы можете сохранить дополнительные функции для использования в вашей оценке с помощью NumericDocValuesField.
Это означает, что во время индексации вы сами захотите токенизировать свой текст. У меня есть пример кода (на Kotlin, но с удовольствием отвечу на дополнительные вопросы, если вы предпочитаете Java)
Токенизация на основе любого анализатора Lucene: (выраженная как функция расширения Kotlin, просто представьте, что this является первым аргументом этого статического метода в качестве анализатора, если вам удобнее работать с Java.
fun Analyzer.tokenize(field: String, input: String): List<String> {
val tokens = arrayListOf<String>()
this.tokenStream(field, input).use { body ->
val charTermAttr = body.addAttribute(CharTermAttribute::class.java)
// iterate over tokenized field:
body.reset()
while(body.incrementToken()) {
tokens.add(charTermAttr.toString())
}
}
return tokens
}
Затем вы берете токенизированный текст и вычисляете на его основе необходимую вам информацию. Код, который я использую, требует их разделения, но что-то вроде этого должно помочь вам.
fun setTextField(field: String, text: String, terms: List<String>): List<IndexableField> {
val length = terms.size
val uniqLength = terms.toSet().size
val keep = ArrayList<IndexableField>()
keep.add(TextField(field, text, Field.Store.YES))
keep.add(NumericDocValuesField("lengths:$field", length.toLong()))
keep.add(NumericDocValuesField("unique:$field", uniqLength.toLong()))
return keep
}
Это статистика для каждого документа, поэтому вы можете отслеживать среднее значение при индексировании и хранить его отдельно от Lucene, т. е. я обычно создаю "meta.json" рядом с индексом для таких вещей.
Я не знаком с SOLR как таковой, но когда вы приступите к реализации подкласса Weight в Lucene, у вас будет доступ к этим числовым значениям документа следующим образом:
class SpecialBM25(...) : Weight(...) {
...
override fun scorer(context: LeafReaderContext): Scorer {
val uniq = context.reader().getNumericDocValues("unique:$field")
val lengths = context.reader().getNumericDocValues("lengths:$field")
... generate Scorer and give it your additional features ...
}
}