我正在尝试使用Python NLTK使用Kneser-Ney平滑来平滑一组n-gram概率。 不幸的是,整个文档相当稀少。
我要做的是:我将文本解析为三元组元组列表。从这个列表中我创建了一个FreqDist然后使用该FreqDist来计算KN平滑分布。
我很确定,结果是完全错误的。当我总结个体概率时,我得到的东西超越1.采用这个代码示例:
import nltk
ngrams = nltk.trigrams("What a piece of work is man! how noble in reason! how infinite in faculty! in \
form and moving how express and admirable! in action how like an angel! in apprehension how like a god! \
the beauty of the world, the paragon of animals!")
freq_dist = nltk.FreqDist(ngrams)
kneser_ney = nltk.KneserNeyProbDist(freq_dist)
prob_sum = 0
for i in kneser_ney.samples():
prob_sum += kneser_ney.prob(i)
print(prob_sum)
输出为“41.51696428571428”。根据语料库大小,此值会无限大。这使得任何prob()都返回除了概率分布之外的任何东西。
看看NLTK代码,我会说实现是有问题的。也许我只是不明白代码应该如何使用。在那种情况下,你能给我一个提示吗?在任何其他情况下:你知道任何有效的Python实现吗?我真的不想自己实现它。
答案 0 :(得分:6)
我认为你误解了Kneser-Ney的计算方法。
归一化常数λ w i-1 具有小心选择的值,使条件概率p KN (w i | w i-1 )的总和等于1。
当然我们在这里讨论的是双胞胎,但对于高阶模型来说,同样的原则也是如此。基本上这个引用意味着,对于固定的上下文w i-1 (或更高阶模型的更多上下文),所有w i 的概率必须加起来为1 。当你将所有样本的概率相加时,你正在做的是包括多个上下文,这就是为什么你最终得到大于1的“概率”。如果你保持上下文固定,如下面的代码示例,你最终数字<= 1。
from nltk.util import ngrams
from nltk.corpus import gutenberg
gut_ngrams = ( ngram for sent in gutenberg.sents() for ngram in ngrams(sent, 3, pad_left = True, pad_right = True, right_pad_symbol='EOS', left_pad_symbol="BOS"))
freq_dist = nltk.FreqDist(gut_ngrams)
kneser_ney = nltk.KneserNeyProbDist(freq_dist)
prob_sum = 0
for i in kneser_ney.samples():
if i[0] == "I" and i[1] == "confess":
prob_sum += kneser_ney.prob(i)
print "{0}:{1}".format(i, kneser_ney.prob(i))
print prob_sum
基于NLTK Gutenberg语料库子集的输出如下:
(u'I', u'confess', u'.--'):0.00657894736842
(u'I', u'confess', u'what'):0.00657894736842
(u'I', u'confess', u'myself'):0.00657894736842
(u'I', u'confess', u'also'):0.00657894736842
(u'I', u'confess', u'there'):0.00657894736842
(u'I', u'confess', u',"'):0.0328947368421
(u'I', u'confess', u'that'):0.164473684211
(u'I', u'confess', u'"--'):0.00657894736842
(u'I', u'confess', u'it'):0.0328947368421
(u'I', u'confess', u';'):0.00657894736842
(u'I', u'confess', u','):0.269736842105
(u'I', u'confess', u'I'):0.164473684211
(u'I', u'confess', u'unto'):0.00657894736842
(u'I', u'confess', u'is'):0.00657894736842
0.723684210526
该和(.72)小于1的原因是,仅在出现在语料库中的三元组计算概率,其中第一个单词是“I”而第二个单词是“confess”。剩余的.28概率保留给w i s,其不遵循语料库中的“I”和“confess”。这是平滑的全部要点,要从语料库中出现的ngrams中重新分配一些概率质量,而不是那样你不会得到一堆0概率ngrams。
也不行
ngrams = nltk.trigrams("What a piece of work is man! how noble in reason! how infinite in faculty! in \
form and moving how express and admirable! in action how like an angel! in apprehension how like a god! \
the beauty of the world, the paragon of animals!")
计算字符三元组?我认为这需要被标记化以计算单词三元组。
答案 1 :(得分:3)
Kneser-Ney(也可以查看Goodman and Chen进行关于不同平滑技术的精彩调查)是一个非常复杂的平滑,只有少数我认识到的包正确。不知道任何python实现,但如果你只需要概率等,你绝对可以尝试SRILM。
答案 2 :(得分:3)