Lucene рассчитать среднюю частоту терминов

В настоящее время я реализую модификацию стандарта Lucene Сходство BM25, основанное на следующем статье. Реализация фактической формулы проста, но я борюсь с вычислением необходимой статистики.

Мне нужны следующие две статистики:

  • Средняя частота терминов в документе: length of document / # unique terms of the document (т. е. показатель повторяемости документа — для документа без повторений это будет 1, если каждый термин встречается дважды, это будет 2 и т. д. )
  • Средняя частота терминов. Это среднее арифметическое приведенного выше показателя по всем документам коллекции. Это можно рассматривать как среднюю повторяемость всего корпуса.

Я обнаружил, что могу рассчитать среднюю частоту терминов для каждого документа во время индексации, переопределив метод computeNorm моей реализации подобия. Я могу хранить значение вместе с нормальным значением с помощью битовых операций (не очень красиво, но пока это работает). Затем во время запроса я могу извлечь среднюю частоту и длину термина в документе.

Однако это не помогает найти среднюю среднюю частоту терминов. Очевидно, что это значение для всей коллекции, и поэтому его следует вычислять в Similarity.computeWeight насколько я понимаю, но не не вижу, как это можно сделать, учитывая аргументы функции.

Какое место было бы идеальным для расчета этой статистики?

Я новичок в Lucene, поэтому, возможно, есть очевидное решение, которого я еще не видел. Я благодарен за любой вклад.


person scriptator    schedule 09.12.2017    source источник


Ответы (2)


Similarity.computeWeight имеет параметр CollectionStatistics, который содержит maxDoc (возвращает общее количество документов, независимо от того, все ли они содержат значения для этого поля), и TermStatistics, который содержит term и totalTermFreq (возвращает общее количество вхождений этого термина) и по разделив его, вы можете получить среднюю частоту термина

person Mysterion    schedule 10.12.2017
comment
почему общая частота терминов, деленная на общее количество документов, не дает вам среднего значения? - person Mysterion; 10.12.2017
comment
Я не получил вашу формулу, не могли бы вы объяснить ее в исходном посте? - person Mysterion; 10.12.2017
comment
Я не думаю, что это соответствует тому, что мне нужно. Мне нужно 1/|D| * sum for d ∈ D (L_d / |T_d| ), т.е. сколько в среднем повторяющихся слов в документе. Вы предложили что-то другое - это можно также назвать средней частотой терминов, но не в смысле статьи, о которой я говорю. (Извините за этот беспорядок - к сожалению, мне пока не разрешено публиковать формулы в виде изображений, иначе формулы выглядели бы лучше) - person scriptator; 10.12.2017
comment
общая частота терминов, деленная на общее количество документов, дает мне среднее значение, но не то, которое я ищу. Я попытался объяснить это лучше сейчас в комментарии выше и в исходном вопросе. - person scriptator; 10.12.2017

Вам нужно будет рассчитать свою собственную «норму», чтобы придерживаться индекса 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 ...
    }
}
person John Foley    schedule 10.12.2017
comment
Это выглядит очень красиво. Я уже видел, что могу получить numericDocValues, но понятия не имел, как их вычислить. Единственное, чего мне все еще не хватает, — это средней средней частоты терминов (т. е. значения для всей коллекции). Это среднее значение всех length / uniqLength для каждого документа. У вас есть предложение, где вычислить это значение в вашем подходе? - person scriptator; 10.12.2017
comment
Я бы возился с методом setTextField, чтобы взять документ, добавить в него поля и вернуть length/uniqLength как double. Затем в своем цикле индексации документов отслеживайте sumLengthRatio и totalNumDocuments, а затем разделяйте их, когда вам нужно среднее значение. В Lucene нет подходящего места для ее хранения, но вы можете добавить волшебный документ для этой статистики, то есть документ со специальным идентификатором и StoredField("lengthRatioMean", sumLengthRatio/totalNumDocuments). - person John Foley; 11.12.2017