Python 使用Witten-Bell平滑法在nltk中使用NgramModel训练和评估二元/三元分布

Python 使用Witten-Bell平滑法在nltk中使用NgramModel训练和评估二元/三元分布,python,nltk,n-gram,Python,Nltk,N Gram,我想在一组句子上训练一个NGramodel,使用Witten Bell平滑来估计看不见的NGram,然后使用它来获得由该分布生成的测试集的对数可能性。我想做与这里的文档示例几乎相同的事情:,但是使用Witten Bell平滑。下面是一些我想做的玩具代码: from nltk.probability import WittenBellProbDist from nltk import NgramModel est = lambda fdist, bins: WittenBellProbDist(

我想在一组句子上训练一个NGramodel,使用Witten Bell平滑来估计看不见的NGram,然后使用它来获得由该分布生成的测试集的对数可能性。我想做与这里的文档示例几乎相同的事情:,但是使用Witten Bell平滑。下面是一些我想做的玩具代码:

from nltk.probability import WittenBellProbDist
from nltk import NgramModel

est = lambda fdist, bins: WittenBellProbDist(fdist)
fake_train = [str(t) for t in range(3000)]
fake_test = [str(t) for t in range(2900, 3010)]

lm = NgramModel(2, fake_train, estimator = est)

print lm.entropy(fake_test)
不幸的是,当我尝试运行它时,我得到以下错误:

Traceback (most recent call last):
  File "ngram.py", line 8, in <module>
    lm = NgramModel(2, fake_train, estimator = est)
  File "/usr/lib/python2.7/dist-packages/nltk/model/ngram.py", line 63, in __init__
    self._model = ConditionalProbDist(cfd, estimator, len(cfd))
  File "/usr/lib/python2.7/dist-packages/nltk/probability.py", line 2016, in __init__
    **factory_kw_args)
  File "ngram.py", line 4, in <lambda>
    est = lambda fdist, bins: WittenBellProbDist(fdist)
  File "/usr/lib/python2.7/dist-packages/nltk/probability.py", line 1210, in __init__
    self._P0 = self._T / float(self._Z * (self._N + self._T))
ZeroDivisionError: float division by zero
回溯(最近一次呼叫最后一次):
文件“ngram.py”,第8行,在
lm=NgramModel(2,伪序列,估计值=est)
文件“/usr/lib/python2.7/dist packages/nltk/model/ngram.py”,第63行,在__
self._模型=条件概率分布(cfd,估计器,len(cfd))
文件“/usr/lib/python2.7/dist packages/nltk/probability.py”,第2016行,在__
**工厂功率(千瓦)
文件“ngram.py”,第4行,在
est=lambda fdist,箱子:WittenBellProbDist(fdist)
文件“/usr/lib/python2.7/dist packages/nltk/probability.py”,第1210行,在__
self.\u P0=self.\u T/float(self.\u Z*(self.\u N+self.\u T))
ZeroDivisionError:浮点除以零
是什么导致了这个错误?据我所知,根据文档,我正确地使用了所有东西,当我使用Lidstone而不是Witten Bell时,效果很好


作为第二个问题,我有一组不相交句子的数据。我怎样才能像使用字符串列表一样使用这些句子,或者做一些等效的事情来产生相同的分布?(也就是说,我当然可以使用一个包含所有句子的列表,其中包含一个虚拟标记,用于分隔后续句子,但这不会产生相同的分布。)一个地方的文档说明允许使用字符串列表,但是后来我发现了一个bug报告,其中文档被认为是经过编辑的,以反映这是不允许的(当我只是尝试字符串列表时,我得到了一个错误)。

这显然是一个已知的问题。
ZeroDivisionError
的原因是由于
\uuuu init\uuuu
中的以下行

if bins == None: 
    bins = freqdist.B() 
self._freqdist = freqdist 
self._T = self._freqdist.B() 
self._Z = bins - self._freqdist.B() 
每当未指定
bins
参数时,它默认为
None
因此
self.\u Z
实际上只是
freqdist.B()-freqdist.B()

self._P0 = self._T / float(self._Z * (self._N + self._T))
减少到,

self._P0 = freqdist.B() / 0.0
此外,如果在执行这一行代码时,将
bin
指定为大于
freqdist.B()
的任何值

print lm.entropy(fake_test)
您将收到
NotImplementedError
,因为在
WittenBellProbDist
类中

def discount(self): 
    raise NotImplementedError()
NgramModel
类的
prob
logprob
中显然也使用了
discount
方法,因此您也无法调用它们


在不更改NLTK的情况下,解决这些问题的一种方法是继承
WittenBellProbDist
,并覆盖相关方法。

我暂时不使用NLTK的NgramModel。当前存在一个平滑错误,该错误导致模型在n>1时大大高估了可能性。这适用于包括WittenBellProbDist和LidstoneProbDist在内的所有估算值。我认为这个错误已经存在了几年,这表明NLTK的这一部分没有经过很好的测试

见:

2018年12月更新
NLTK 3.4包含可导入为
NLTK.lm

的经过改造的ngram建模模块,这很有意义——我注意到,给定令牌的条件概率在所有可能的令牌上相加时,加起来不等于1。我有一段时间有点担心,后来才意识到,由于基线突然扼杀了我更复杂的方法,呵呵。谢谢大家的回答。我最终选择了SRILM,因为该代码实际上是完整的,而且看起来是正确的。。。