Nltk: Ошибка в вычислении сглаживания отсрочки NgramModel?

Созданный на 7 мар. 2013  ·  18Комментарии  ·  Источник: nltk/nltk

Я считаю, что есть ошибка в том, как вычисляется сглаживание отсрочки в NgramModel.

  • Рассмотрим последовательность «слов»: aaaababaaccbacb со словами ['a','b','c']
  • Построить модель биграммы (n=2). Для простоты используйте сглаживание LidstoneProbDist.
  • Примечательно, что эта последовательность не содержит всех биграмм с префиксом «b» или «c». Таким образом, требуется отсрочка, чтобы получить вероятность биграммы «bb», «bc» и «ca».
  • Для context = ['a'] model.prob(w,context) выглядит хорошо для всех слов и суммируется до 1
  • Для context = ['b'] model.prob(w,context) выглядит неправильно, сумма > 1

Я думал, что расчет отсрочки должен делать следующее, скажем, для контекста «b»:

  • Рассчитайте общую вероятность «отсутствия» для невидимых значений в контексте «b» (т.е. bb и bc) на уровне биграммы, назовите это Beta2
  • Рассчитайте общую вероятность униграммы для этих невидимых значений (т. е. «b» и «c»), назовите это Beta1.
  • возврат (Beta2 / Beta1) * backoff.prob()

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

Я что-то упускаю? Код в NgramModel, кажется, делает что-то совсем другое, и я не мог понять это.

language-model

Самый полезный комментарий

Вступление в 2016 год и проблема «модели ngram» не имели никаких подвижек.

Все 18 Комментарий

Я считаю, что вы правы, что это действительно ошибка.

Если мы предположим эту настройку:

from nltk.model import NgramModel
from nltk.probability import LidstoneProbDist

word_seq = list('aaaababaaccbacb')
words = ['a', 'b', 'c', '']

est = lambda freqdist, bins: LidstoneProbDist(freqdist, 0.2, bins=bins)
model = NgramModel(2, word_seq, True, True, est, 4)

Мы можем довольно быстро увидеть расхождения:

sum(model.prob(w, ['b']) for w in words)
Out[150]: 2.4583333333333335
sum(model.prob(w, ['a']) for w in words)
Out[151]: 1.0

[(w, model.prob(w, ['b'])) for w in words]
Out[152]: 
[('a', 0.6666666666666667),
 ('b', 0.875),
 ('c', 0.6666666666666667),
 ('', 0.25)]

[(w, model.prob(w, ['a'])) for w in words]
Out[153]: 
[('a', 0.47727272727272724),
 ('b', 0.25),
 ('c', 0.25),
 ('', 0.022727272727272728)]

Некоторое время назад, когда я работал над NgramModel, я также помню, что нашел способ реализации отсрочки немного запутанным. Теперь, когда я не смотрел на него долгое время, я потерял то интуитивное понимание того, как это работает. Мне кажется, мы утверждаем, что реализуем Katz Back-off, но расчеты немного отличаются от тех, что в Википедии .

Я полагаю, это потому, что функция LidstoneProbDist.discount вызванная из NgramModel._beta уже учитывает суммирование, но мне придется изучить это подробнее.

def _alpha(self, tokens):
    return self._beta(tokens) / self._backoff._beta(tokens[1:])

def _beta(self, tokens):
    return (self[tokens].discount() if tokens in self else 1)

Мне кажется, что в бета-расчетах все идет не так, потому что бета на уровне биграмм намного больше, чем бета на уровне униграмм, что делает отношение альфа положительным.

model._beta(('b',))
Out[154]: 0.16666666666666669
model._backoff._beta(())
Out[155]: 0.05063291139240506
model._alpha(('b',))
Out[155]: 3.291666666666667

Я также исключил, что проблема связана с самим LidstoneProbDist:

[(w, model._model[('b',)].prob(w)) for w in words]
Out[159]: 
[('a', 0.6666666666666667),
 ('b', 0.04166666666666667),
 ('c', 0.04166666666666667),
 ('', 0.25)]

sum([model._model[('b',)].prob(w) for w in words])
Out[161]: 1.0

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

Привет, спасибо, что проверил это. Еще несколько мыслей:

Во-первых, сбивает с толку разные понятия «дисконтирования». Есть дисконтирование, достигаемое различными методами сглаживания. Например, простое лапласианское (добавить одно) сглаживание снижает вероятность наблюдаемых слов и переносит эту массу на ненаблюдаемые слова. Функция Discount (), вызываемая в функции _beta, предназначена для сглаживания, выполняемого ProbDist, а не (я не думаю), имеющего отношение к сглаживанию отката. Я думаю, что понятие отсрочки дисконтирования связано с вероятностью подмножества слов, которые «отсутствуют» (не наблюдаются) для различных контекстов в модели более высокого порядка.

Итак, я изменил код для своих целей, чтобы делать то, что считаю правильным, и поделился некоторыми фрагментами ниже. По сути, я определяю подмножество слов, которые отсутствуют в модели для данного контекста, и для этого подмножества вычисляю общую вероятность этих «пропущенных» слов и соответствующее количество в модели отсрочки. Отношение равно «альфа», и обратите внимание, что это функция контекста. Я думаю, что эта реализация соответствует тому, что указано в предоставленной вами ссылке на Википедию. Кроме того, в моем случае функция _beta больше не используется.

Надеюсь, это будет полезно для обсуждения. Еще раз спасибо.

    # (Code fragment for calculating backoff)

    # Now, for Katz backoff smoothing we need to calculate the alphas
    if self._backoff is not None:
        self._backoff_alphas = dict()

        # For each condition (or context)
        for ctxt in self._cfd.conditions():
            pd = self._model[ctxt] # prob dist for this context

            backoff_ctxt = ctxt[1:]
            backoff_total_pr = 0
            total_observed_pr = 0
            for word in self._cfd[ctxt].keys(): # this is the subset of words that we OBSERVED
                backoff_total_pr += self._backoff.prob(word,backoff_ctxt) 
                total_observed_pr += pd.prob(word)

            assert total_observed_pr <= 1 and total_observed_pr > 0
            assert backoff_total_pr <= 1 and backoff_total_pr > 0

            alpha_ctxt = (1.0-total_observed_pr) / (1.0-backoff_total_pr)

            self._backoff_alphas[ctxt] = alpha_ctxt

# Updated _alpha function, discarded the _beta function
def _alpha(self, tokens):
    """Get the backoff alpha value for the given context
    """
    if tokens in self._backoff_alphas:
        return self._backoff_alphas[tokens]
    else:
        return 1

Привет всем, я просто хотел вмешаться в это обсуждение и указать, что проблема намного хуже, чем просто невозможность суммирования вероятностей до 1,0.

Рассмотрим следующий пример триграммы:

#!/usr/bin/python
from nltk.model import NgramModel
from nltk.probability import LidstoneProbDist

word_seq = ['foo', 'foo', 'foo', 'foo', 'bar', 'baz']

# Set up a trigram model, nothing special  
est = lambda freqdist, bins: LidstoneProbDist(freqdist, 0.2, bins)
model = NgramModel(3, word_seq, True, True, est, 3)

# Consider the ngram ['bar', 'baz', 'foo']
# We've never seen this before, so the trigram model will fall back
context = ('bar', 'baz',)
word = 'foo'
print "P(foo | bar, baz) = " + str(model.prob(word,context))

# Result:
# P(foo | bar, baz) = 2.625

Ага - эта условная вероятность> 1.0

Самое неприятное в том, что чем больше откатываются модели, тем больше увеличивается вероятность.

Проблема также усугубляется, когда мы добавляем больше обучающих примеров!

word_seq = ['foo' for i in range(0,10000)]
word_seq.append('bar')
word_seq.append('baz')

est = lambda freqdist, bins: LidstoneProbDist(freqdist, 0.2, bins)
model = NgramModel(3, word_seq, True, True, est, 3)

# Consider the ngram ['bar', 'baz', 'foo']
# We've never seen this before, so the trigram model will fall back
context = ('bar', 'baz',)
word = 'foo'
print "P(foo | bar, baz) = " + str(model.prob(word,context))

# Result:
P(foo | bar, baz) = 6250.125

В его нынешнем виде на NgramModel нельзя положиться - по крайней мере, с аддитивным сглаживанием Лидстоуна.

@afourney : я считаю, что это предназначено (LidstoneProbDist имеет атрибут SUM_TO_ONE = False )

@afourney Я согласен с тем, что NgramModel нельзя использовать, пока это не будет исправлено. К сожалению, в последнее время у меня просто не было времени этим заняться.

@kmike SUM_TO_ONE имеет значение False для LidstoneProbDist, потому что если вы столкнулись с событием, которого не было в исходном распределении, и вы не установили значение бинов как количество возможных событий, тогда оно не будет суммироваться с единицей. Но при правильном использовании в сумме он действительно будет равен единице. Проблема здесь заключается в расчете бета-версии NgramModel, а не в самом LidstoneProbDist.

@kmike : Да, я заметил, что SUM_TO_ONE было ложным. Меня беспокоило то, что модель возвращала индивидуальные условные вероятности (для отдельных событий), которые уже были больше 1, прежде чем включать их в суммирование.

@bcroy Я думаю, что ваше решение - правильный подход. Проще говоря, _alpha выполняет две важные задачи:

  1. Он перенормирует модель отсрочки для данного контекста, чтобы исключить слова, которые уже учитываются текущей моделью более высокого порядка.
  2. Он масштабирует перенормированную модель отсрочки, чтобы «соответствовать» «отсутствующей»/дисконтированной вероятности текущей _model.

При этом было бы неплохо, если бы NgramModel также предлагал стратегию интерполяции в качестве альтернативы стратегии отсрочки. Это позволило бы поддерживать сглаживание Елинека-Мерсера или Виттена-Белла — последнее из которых я нашел простым и довольно хорошо работающим. См.: http://nlp.stanford.edu/~wcmac/papers/20050421-smoothing-tutorial.pdf

Может ли кто-нибудь подтвердить, что это все еще открытая ошибка?

Да, я все еще получаю P(foo | bar, baz) = 2,625

Всем привет,

Есть ли прогресс в этом вопросе? Это все еще открытая ошибка? Я получаю P(foo | bar, baz) = 2,625, поэтому проблема сохраняется.

Я думаю, что это важная проблема, и ее следовало решить, поскольку языковые модели используются почти во всех приложениях НЛП.

К сожалению, у меня не было времени рассмотреть многочисленные проблемы с NgramModel , и я не предвижу, что смогу это сделать в ближайшее время. Пока кто-то не исправит эти ошибки, NgramModel удален из nltk.

Дэн, спасибо за ответ.

Просто проверяю обновления. Я вижу, что некоторые проблемы были закрыты, но просто хочу убедиться, что они все еще далеки от использования?

@ZeerakW К сожалению, в моделях ngram наблюдается небольшой прогресс, и никто еще не решил заняться этим.

Вступление в 2016 год и проблема «модели ngram» не имели никаких подвижек.

Ребята, мы наконец-то можем закрыть это :)

Обновление 2018. Уже выпустился и начал работать, но проблема с Ngram все еще существует.

Лето!

Была ли эта страница полезной?
0 / 5 - 0 рейтинги