Kneser-Ney使用Python NLTK平滑三元组

时间:2016-02-06 14:29:29

标签: 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)

输出为“41.51696428571428”。根据语料库大小,此值会无限大。这使得任何prob()都返回除了概率分布之外的任何东西。

看看NLTK代码,我会说实现是有问题的。也许我只是不明白代码应该如何使用。在那种情况下,你能给我一个提示吗?在任何其他情况下:你知道任何有效的Python实现吗?我真的不想自己实现它。

3 个答案:

答案 0 :(得分:6)

我认为你误解了Kneser-Ney的计算方法。

来自Wikipedia:

归一化常数λ 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

  • 您的样本很可能包含训练数据中未出现的单词(也称为 Out-Of-Vocabulary(OOV)单词),如果处理不当,可能会导致你得到的概率。也许这可能会导致大而无效的问题?

答案 2 :(得分:3)

回答您的其他问题:

  

在任何其他情况下:你知道任何有效的Python实现吗?

我刚用Python完成了Kneser-Ney实现。代码是here; README中也有报告。写下我的任何疑问。