使用Python NLTK的Kneser-Ney三元图平滑

2024-09-30 14:26:58 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图使用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.5169642857428”。根据语料库的大小,这个值会无限大。这使得prob()只返回概率分布。

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


Tags: ofthe代码in列表概率howsum
3条回答

Kneser-Ney(也可以查看一下Goodman and Chen,了解不同的平滑技术)是一个相当复杂的平滑,只有少数几个包我知道它是正确的。不知道任何python实现,但如果您只需要概率等,您绝对可以尝试SRILM

  • 很有可能你的样本中有一些在训练数据中没有出现的单词(也就是词汇表外的单词),如果处理不当,这些单词可能会扰乱你得到的概率。也许这会导致异常大而无效的prob?

回答你的另一个问题:

In any other case: do you know any working Python implementation?

我刚刚用Python完成了Kneser-Ney的实现。代码是here;自述文件中也有报告。如果有任何疑问请写信给我。

我认为你误解了奈伊在计算什么。

来自Wikipedia:

The normalizing constant λwi-1 has value chosen carefully to make the sum of conditional probabilities pKN(wi|wi-1) equal to one.

当然,我们在这里讨论的是大论,但对于高阶模型,同样的原理也是正确的。基本上,这句话的意思是,对于一个固定的上下文wi-1(或者更高阶模型的上下文),所有wi的概率必须加在一起。当你把所有样本的概率加起来时,你所做的就是包含多个上下文,这就是为什么你最终得到的“概率”大于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”而第二个单词是“忏悔”的语料库中的三元组。剩余的.28概率保留给在语料库中不跟在“I”和“忏悔”后面的wIs。这就是平滑的全部要点,从出现在语料库中的ngram到那些不出现的ngram重新分配一些概率质量,这样你就不会得到一堆0概率的ngram。

也不是这样



    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!")

计算字符三联图?我认为这需要被标记化来计算单词三元组。

相关问题 更多 >