Jan*_*rff 7 python nlp nltk smoothing
我正在尝试使用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)
Run Code Online (Sandbox Code Playgroud)
输出为"41.51696428571428".根据语料库大小,此值会无限大.这使得任何prob()返回除了概率分布之外的任何东西.
看看NLTK代码我会说实现是有问题的.也许我只是不明白代码应该如何使用.在那种情况下,你能给我一个提示吗?在任何其他情况下:你知道任何有效的Python实现吗?我真的不想自己实现它.
use*_*629 10
我认为你误解了Kneser-Ney的计算方式.
来自维基百科:
归一化常数λ 瓦特I-1 已经选择值小心使条件概率p的总和KN(瓦特我 | W I-1 )等于一.
当然,我们在这里讨论的是bigrams,但对于高阶模型,同样的原则也是如此.基本上这个引用意味着,对于固定的上下文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
Run Code Online (Sandbox Code Playgroud)
该和(.72)小于1的原因在于,仅在出现在语料库中的三元组中计算概率,其中第一个单词是"I"而第二个单词是"confess".其余的0.28概率是留给W¯¯ 我 S的不按"I"和文集"坦白".这是平滑的全部要点,从语料库中出现的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!")
Run Code Online (Sandbox Code Playgroud)
计算字符三元组?我认为这需要被标记化来计算单词三元组.
Kneser -Ney(也可以看看Goodman 和 Chen,对不同的平滑技术进行了很好的调查)是一个相当复杂的平滑,据我所知,只有少数几个包能做到这一点。不知道任何 python 实现,但如果你只需要概率等,你绝对可以尝试SRILM 。