NgramModelでのバックオフ平滑化の計算方法にエラーがあると思います。
バックオフの計算では、コンテキスト 'b'の場合、次のようにする必要があると思いました。
これは基本的に、バイグラムコンテキストで観察されなかった単語のユニグラム確率を交換し、適切にスケーリングして、欠落している確率質量を埋めます。
私は何かが足りないのですか? NgramModelのコードは、見た目とはかなり異なることをしていて、私には理解できませんでした。
これは確かにバグだとあなたは正しいと思います。
この設定を想定すると、次のようになります。
from nltk.model import NgramModel
from nltk.probability import LidstoneProbDist
word_seq = list('aaaababaaccbacb')
words = ['a', 'b', 'c', '']
est = lambda freqdist, bins: LidstoneProbDist(freqdist, 0.2, bins=bins)
model = NgramModel(2, word_seq, True, True, est, 4)
不一致はすぐにわかります。
sum(model.prob(w, ['b']) for w in words)
Out[150]: 2.4583333333333335
sum(model.prob(w, ['a']) for w in words)
Out[151]: 1.0
[(w, model.prob(w, ['b'])) for w in words]
Out[152]:
[('a', 0.6666666666666667),
('b', 0.875),
('c', 0.6666666666666667),
('', 0.25)]
[(w, model.prob(w, ['a'])) for w in words]
Out[153]:
[('a', 0.47727272727272724),
('b', 0.25),
('c', 0.25),
('', 0.022727272727272728)]
しばらく前にNgramModelで作業していたとき、バックオフの実装方法が少しわかりにくいことも覚えています。 長い間見ていなかったので、それがどのように機能するかについての直感的な理解を失いました。 Katz Back-offを実装していると私は主張しているようですが、計算はWikipediaのものとは少し異なります。
NgramModel._beta
から呼び出されたLidstoneProbDist.discount
関数はすでに合計を考慮しているためだと思いますが、さらに詳しく調べる必要があります。
def _alpha(self, tokens):
return self._beta(tokens) / self._backoff._beta(tokens[1:])
def _beta(self, tokens):
return (self[tokens].discount() if tokens in self else 1)
バイグラムレベルのベータはユニグラムレベルのベータよりもはるかに大きいため、ベータ計算は問題が発生しているように見えます。これにより、比率アルファが正になります。
model._beta(('b',))
Out[154]: 0.16666666666666669
model._backoff._beta(())
Out[155]: 0.05063291139240506
model._alpha(('b',))
Out[155]: 3.291666666666667
また、問題があるのは実際のLidstoneProbDist自体であることも除外しました。
[(w, model._model[('b',)].prob(w)) for w in words]
Out[159]:
[('a', 0.6666666666666667),
('b', 0.04166666666666667),
('c', 0.04166666666666667),
('', 0.25)]
sum([model._model[('b',)].prob(w) for w in words])
Out[161]: 1.0
これらすべてのパーツがどのように相互接続されているかを理解し、これを修正できるかどうかを確認します。 他の誰かが(@desilinguistのように)飛び込みたいのなら、私はこれに別の目を向けていただければ幸いです。
こんにちは、これをチェックしてくれてありがとう。 もう少し考えてみましょう。
まず、紛らわしいのは、「割引」の概念が異なることです。 さまざまな平滑化方法によって達成される割引があります。 たとえば、単純なラプラシアン(1つ追加)スムージングは、観測された単語の確率を割り引いて、その質量を観測されていない単語にシフトします。 _beta関数で呼び出されるdiscount()関数は、ProbDistによって行われる平滑化のためのものであり、バックオフ平滑化とは関係ありません(私は思いません)。 割引のバックオフの概念は、高次モデルのさまざまなコンテキストで「欠落している」(観察されていない)単語のサブセットの確率と関係があると思います。
そこで、私は自分の目的のためにコードを変更して、正しいと思うことを実行しました。以下にいくつかのスニペットを共有しました。 基本的に、特定のコンテキストのモデルで欠落している単語のサブセットを識別し、そのサブセットについて、これらの「欠落している」単語の合計確率と、バックオフモデルの対応する量を計算します。 比率は「アルファ」であり、これはコンテキストの関数であることに注意してください。 この実装は、あなたが提供するウィキペディアのリンクにあるものに対応していると思います。 また、私の場合、_beta関数は使用されなくなりました。
これが議論に役立つことを願っています。 再度、感謝します。
# (Code fragment for calculating backoff)
# Now, for Katz backoff smoothing we need to calculate the alphas
if self._backoff is not None:
self._backoff_alphas = dict()
# For each condition (or context)
for ctxt in self._cfd.conditions():
pd = self._model[ctxt] # prob dist for this context
backoff_ctxt = ctxt[1:]
backoff_total_pr = 0
total_observed_pr = 0
for word in self._cfd[ctxt].keys(): # this is the subset of words that we OBSERVED
backoff_total_pr += self._backoff.prob(word,backoff_ctxt)
total_observed_pr += pd.prob(word)
assert total_observed_pr <= 1 and total_observed_pr > 0
assert backoff_total_pr <= 1 and backoff_total_pr > 0
alpha_ctxt = (1.0-total_observed_pr) / (1.0-backoff_total_pr)
self._backoff_alphas[ctxt] = alpha_ctxt
# Updated _alpha function, discarded the _beta function
def _alpha(self, tokens):
"""Get the backoff alpha value for the given context
"""
if tokens in self._backoff_alphas:
return self._backoff_alphas[tokens]
else:
return 1
みなさん、こんにちは。私はこの議論に参加したいと思いました。問題は、確率の合計が1.0にならないよりもはるかに悪いことを指摘しました。
次のトリグラムの例を考えてみましょう。
#!/usr/bin/python
from nltk.model import NgramModel
from nltk.probability import LidstoneProbDist
word_seq = ['foo', 'foo', 'foo', 'foo', 'bar', 'baz']
# Set up a trigram model, nothing special
est = lambda freqdist, bins: LidstoneProbDist(freqdist, 0.2, bins)
model = NgramModel(3, word_seq, True, True, est, 3)
# Consider the ngram ['bar', 'baz', 'foo']
# We've never seen this before, so the trigram model will fall back
context = ('bar', 'baz',)
word = 'foo'
print "P(foo | bar, baz) = " + str(model.prob(word,context))
# Result:
# P(foo | bar, baz) = 2.625
うん-この条件付き確率は> 1.0です
厄介な部分は、モデルがフォールバックするほど、確率が高くなることです。
トレーニング例を追加すると、問題も悪化します。
word_seq = ['foo' for i in range(0,10000)]
word_seq.append('bar')
word_seq.append('baz')
est = lambda freqdist, bins: LidstoneProbDist(freqdist, 0.2, bins)
model = NgramModel(3, word_seq, True, True, est, 3)
# Consider the ngram ['bar', 'baz', 'foo']
# We've never seen this before, so the trigram model will fall back
context = ('bar', 'baz',)
word = 'foo'
print "P(foo | bar, baz) = " + str(model.prob(word,context))
# Result:
P(foo | bar, baz) = 6250.125
現状では、NgramModelは信頼できません。少なくとも、追加のLidstoneスムージングでは信頼できません。
@afourney :これは意図されたものだと思います(LidstoneProbDistにはSUM_TO_ONE = False
属性があります)
@afourneyこれが修正されるまで、NgramModelを実際に使用できないことに同意します。 残念ながら、私は最近これを突き刺す時間がありませんでした。
@kmike SUM_TO_ONEは、LidstoneProbDistではFalseです。これは、初期分布にないイベントが発生し、bins値を可能なイベントの数に設定しなかった場合、合計が1にならないためです。 しかし、適切に使用すれば、実際に1つになります。 ここでの問題は、LidstoneProbDist自体ではなく、NgramModelのベータ計算です。
@kmike :ええ、SUM_TO_ONEがfalseであることに気づきました。 私の懸念は、モデルが、合計に組み込む前に、すでに1より大きい個々の条件付き確率(単一イベントの場合)を返していることでした。
@bcroyあなたの解決策は正しいアプローチだと思います。 簡単に言うと、_alphaは2つの重要なタスクを実行します。
そうは言っても、NgramModelがバックオフ戦略の代わりに補間戦略も提供してくれれば素晴らしいと思います。 これにより、Jelinek-MercerまたはWitten-Bellの平滑化のサポートが可能になります。後者は単純であり、非常にうまく機能することがわかりました。 参照: http :
誰かがこれがまだ未解決のバグであることを確認できますか?
はい、まだP(foo | bar、baz)= 2.625を取得しています
みなさん、こんにちは。
この問題に進展はありますか? それはまだ未解決のバグですか? P(foo | bar、baz)= 2.625を取得しているので、問題は継続します。
これは重要な問題であり、言語モデルはNLPのほとんどすべてのアプリケーションで使用されているため、修正する必要があると思います。
残念ながら、私はNgramModel
に関する多くの問題を調べる時間がなく、すぐにそれができるとは思っていません。 誰かがこれらのバグに取り組むまで、 NgramModel
はnltkから削除されました。
ダン、答えてくれてありがとう。
更新をチェックインするだけです。 いくつかの問題が解決されたことがわかりますが、それがまだ使用可能にはほど遠いことを確認したいだけですか?
@ZeerakW残念ながら、ngramモデルの進歩はほとんどなく、まだ誰もこれに取り組むことを約束していません。
2016年に入り、「ngramモデル」の問題は進展していません。
私たちがついにこれを閉じることができる人々:)
2018年の更新。すでに卒業して作業を開始しましたが、Ngramの問題はまだ存在します
夏!
最も参考になるコメント
2016年に入り、「ngramモデル」の問題は進展していません。