Я считаю, что есть ошибка в том, как вычисляется сглаживание отсрочки в NgramModel.
Я думал, что расчет отсрочки должен делать следующее, скажем, для контекста «b»:
По сути, это заменяет вероятности униграмм на те слова, которые не наблюдались в контексте биграммы, масштабированные соответствующим образом, чтобы заполнить недостающую массу вероятностей.
Я что-то упускаю? Код в NgramModel, кажется, делает что-то совсем другое, и я не мог понять это.
Я считаю, что вы правы, что это действительно ошибка.
Если мы предположим эту настройку:
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 выполняет две важные задачи:
При этом было бы неплохо, если бы 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 все еще существует.
Лето!
Самый полезный комментарий
Вступление в 2016 год и проблема «модели ngram» не имели никаких подвижек.