NLTK包用于估计(一元)困惑度。

11

我正在尝试计算我拥有的数据的困惑度。我使用的代码是:

 import sys
 sys.path.append("/usr/local/anaconda/lib/python2.7/site-packages/nltk")

from nltk.corpus import brown
from nltk.model import NgramModel
from nltk.probability import LidstoneProbDist, WittenBellProbDist
estimator = lambda fdist, bins: LidstoneProbDist(fdist, 0.2)
lm = NgramModel(3, brown.words(categories='news'), True, False, estimator)
print lm

但是我收到了错误信息,

File "/usr/local/anaconda/lib/python2.7/site-packages/nltk/model/ngram.py", line 107, in __init__
cfd[context][token] += 1
TypeError: 'int' object has no attribute '__getitem__'

我已经对我手头的数据执行了潜在狄利克雷分配(Latent Dirichlet Allocation),并生成了单个词和它们各自的概率(它们被归一化为数据总概率之和为1)。

我的单个词和它们的概率如下:

Negroponte 1.22948976891e-05
Andreas 7.11290670484e-07
Rheinberg 7.08255885794e-07
Joji 4.48481435106e-07
Helguson 1.89936727391e-07
CAPTION_spot 2.37395965468e-06
Mortimer 1.48540253778e-07
yellow 1.26582575863e-05
Sugar 1.49563800878e-06
four 0.000207196011781

这只是我拥有的单个字文件的片段。大约有1000行遵循相同的格式。第二列总概率和为1。

我是一名初学者程序员。这个ngram.py属于nltk包,我不知道如何纠正这个问题。我在这里有一个示例代码来自nltk文档,现在我不知道该怎么做。请帮忙指导一下,谢谢!


你最初说你想在文本语料库上计算一元模型的困惑度。但现在你删除了“一元”这个词。 - Omid
nltk的示例代码本身不起作用 :( 在示例代码中,它是一个三元组,如果它能工作,我会将其更改为一个一元组。如何解决这个错误? - Ana_Sam
你必须使用NLTK吗? - Omid
不特别关注NLTK。我只是觉得作为一个编程新手,使用它更容易。除了布朗语料库之外,我还能用什么其他方式或包来估计我拥有的数据的困惑度? - Ana_Sam
当然有。我假设你有一个简单的文本文件,想要构建一个unigram语言模型,然后计算该模型的困惑度。对吧? - Omid
不,我已经对我的数据执行了LDA,并生成了unigrams。这个步骤已经完成了。对于这些unigrams,我需要计算perplexity。我的数据格式如下:Negroponte 1.22948976891e-05 Andreas 7.11290670484e-07 Rheinberg 7.08255885794e-07 Joji 4.48481435106e-07;也就是说,我有unigrams及其相应的归一化分布。 - Ana_Sam
2个回答

23

困惑度是测试集的倒数概率,除以单词数进行标准化。对于单个词而言:

enter image description here

假设您已经构建了单个词模型,也就是说,每个词都有其相关概率。然后您只需要套用公式即可。我假设您拥有一个大字典unigram[word],该字典提供了语料库中每个单词的概率。您还需要一组测试集。如果您的单个词模型不是以字典形式存在,请告诉我您使用了哪种数据结构,以便我能相应地调整我的解决方案。

perplexity = 1
N = 0

for word in testset:
    if word in unigram:
        N += 1
        perplexity = perplexity * (1/unigram[word])
perplexity = pow(perplexity, 1/float(N))

更新:

由于您要求提供一个完整的工作示例,这里有一个非常简单的示例。

假设这是我们的语料库:

corpus ="""
Monty Python (sometimes known as The Pythons) were a British surreal comedy group who created the sketch comedy show Monty Python's Flying Circus,
that first aired on the BBC on October 5, 1969. Forty-five episodes were made over four series. The Python phenomenon developed from the television series
into something larger in scope and impact, spawning touring stage shows, films, numerous albums, several books, and a stage musical.
The group's influence on comedy has been compared to The Beatles' influence on music."""

我们首先构建单个词(unigram)模型:

import collections, nltk
# we first tokenize the text corpus
tokens = nltk.word_tokenize(corpus)

#here you construct the unigram language model 
def unigram(tokens):    
    model = collections.defaultdict(lambda: 0.01)
    for f in tokens:
        try:
            model[f] += 1
        except KeyError:
            model [f] = 1
            continue
    N = float(sum(model.values()))
    for word in model:
        model[word] = model[word]/N
    return model

我们的模型是平滑的。对于超出其知识范围的单词,它会分配一个低概率的值 0.01 。我已经告诉你如何计算困惑度:

#computes perplexity of the unigram model on a testset  
def perplexity(testset, model):
    testset = testset.split()
    perplexity = 1
    N = 0
    for word in testset:
        N += 1
        perplexity = perplexity * (1/model[word])
    perplexity = pow(perplexity, 1/float(N)) 
    return perplexity

现在我们可以在两个不同的测试集上进行测试:

testset1 = "Monty"
testset2 = "abracadabra gobbledygook rubbish"

model = unigram(tokens)
print perplexity(testset1, model)
print perplexity(testset2, model)

您得到以下结果:

>>> 
49.09452736318415
99.99999999999997

请注意,处理困惑度时,我们会尝试降低它。相对于固定测试集而言,具有更低困惑度的语言模型比大困惑度的语言模型更为理想。在第一个测试集中,单字模型中包含了单词Monty,因此其困惑度数值也较小。


请您提供上述代码的一个样本输入,并给出输出。这会更容易让我根据数据来构建我的数据。我已经编辑了问题,添加了我的输入文件中的单个词和它们的概率,需要计算困惑度。 - Ana_Sam
嘿!但是,我还必须包括对数似然,例如困惑度(测试集)= exp {-(对数似然/标记数量)}?http://qpleple.com/perplexity-to-evaluate-topic-models/ - Ana_Sam
非常感谢您的时间和代码。我会尝试一下。我需要计算由LDA模型生成的unigrams的困惑度。我想对于我拥有的数据,我可以使用这段代码并检查它。非常感谢! - Ana_Sam
2
在第model[word] = model[word]/float(len(model))行中,模型的构建有错误吗?难道不应该是model[word] = model[word]/float(sum(model.values()))吗? - mknaf
1
在这行代码model[word]/float(sum(model.values()))中,每次更新归一化模型值后都会计算sum(model.values())。因此,归一化后的值之和不是1,而是3.4。必须在for循环内部计算总和并重复使用。@heiner确实是正确的,我没有看到它被回答过。 - chmodsss

0

感谢您提供的代码片段!这句话不应该是:

for word in model:
        model[word] = model[word]/float(sum(model.values()))

相当于:

v = float(sum(model.values()))
for word in model:
        model[word] = model[word]/v

哦...我看到已经有答案了...


嗨,Heiner,欢迎来到SO,正如你已经注意到的,这个问题几年前就有了一个很好的答案,添加更多答案到已经回答过的问题并没有问题,但是你可能需要确保它们足够有价值,以使它们值得,对于这种情况,你可能想考虑专注于回答这些新问题 - colsw

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接