Nltk: NgramModelバックオフ平滑化計算にエラーがありますか?

作成日 2013年03月07日  ·  18コメント  ·  ソース: nltk/nltk

NgramModelでのバックオフ平滑化の計算方法にエラーがあると思います。

  • 「単語」のシーケンスを考えてみましょう。aaaababaaccbacbと単語['a'、 'b'、 'c']
  • バイグラムモデルを作成します(n = 2)。 簡単にするために、LidstoneProbDistスムージングを使用します
  • 特に、このシーケンスには、接頭辞「b」または「c」が付いたすべてのバイグラムが含まれているわけではありません。 したがって、バイグラム「bb」、「bc」、および「ca」の確率を取得するには、バックオフが必要です。
  • context = ['a']の場合、model.prob(w、context)はすべての単語に適しているように見え、合計は1になります。
  • context = ['b']の場合、model.prob(w、context)は正しく表示されず、合計は> 1になります。

バックオフの計算では、コンテキスト 'b'の場合、次のようにする必要があると思いました。

  • バイグラムレベルで、「b」コンテキスト(つまり、bbとbc)の見えない値の合計「欠落」確率を計算します。これをBeta2と呼びます。
  • これらの見えない値(つまり、「b」と「c」)の合計ユニグラム確率を計算します。これをBeta1と呼びます。
  • return(Beta2 / Beta1)* backoff.prob()

これは基本的に、バイグラムコンテキストで観察されなかった単語のユニグラム確率を交換し、適切にスケーリングして、欠落している確率質量を埋めます。

私は何かが足りないのですか? NgramModelのコードは、見た目とはかなり異なることをしていて、私には理解できませんでした。

language-model

最も参考になるコメント

2016年に入り、「ngramモデル」の問題は進展していません。

全てのコメント18件

これは確かにバグだとあなたは正しいと思います。

この設定を想定すると、次のようになります。

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つの重要なタスクを実行します。

  1. 現在の高次モデルによってすでに説明されている単語を除外するために、指定されたコンテキストのバックオフモデルを再正規化します。
  2. これは、繰り込まれたバックオフモデルをスケーリングして、現在の_modelの「欠落」/割引確率に「適合」させます。

そうは言っても、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の問題はまだ存在します

夏!

このページは役に立ちましたか?
0 / 5 - 0 評価

関連する問題

alvations picture alvations  ·  3コメント

DavidNemeskey picture DavidNemeskey  ·  4コメント

libingnan54321 picture libingnan54321  ·  3コメント

alvations picture alvations  ·  4コメント

stevenbird picture stevenbird  ·  4コメント