Naive Bayesでlog-sum-expトリックがどのように機能するかの例


14

log-sum-expトリックについて多くの場所(例:ここ、ここ)で読みましたが、それがNaive Bayes分類器(例:離散機能と2つのクラス)に具体的に適用される例を見たことがありません。

このトリックを使用して数値のアンダーフローの問題をどの程度正確に回避できますか?


2
ここではいくつかの使用例がありますが、ナイーブベイズでは必ずしも明示的には使用されていません。ただし、トリックのアイデアは非常に単純で、すぐに適用できるため、問題はほとんどありません。
— Glen_b-モニカを復活させる14

問題はオーバーフローよりもアンダーフローである可能性が高いです。
— ヘンリー

アンダーフローで検索してから、質問を更新して、まだカバーされていないものを具体的に取り上げることをお勧めします。
— Glen_b-モニカを復活させる'07 / 07/03

明確にしてもらえますか-これはベルヌーイモデルの素朴なベイズですか?おそらく何か他に?
— Glen_b-モニカを復活させる14

ここの例をご覧ください。右下( 'See Also'の直前でログを取得します。両側を累乗しますが、RHSを(ログの合計のexpとして)そのままにしておきます)がログの例です。 -sum-expトリック。これにより、Naive Bayesでの使用に関連して、より具体的な質問をするのに十分な情報が得られますか?
— Glen_b -Reinstate Monica

回答:


26

In

p(Y=C|バツ)=p(バツ|Y=C)p(Y=C) Σk=1|C|p(バツ|Y=Ck)p(Y=Ck)

分母と分子の両方が非常に小さくなる可能性があります。これは、通常、が0に近くなる可能性があり、それらの多くを互いに乗算するためです。アンダーフローを防ぐために、分子のログを取得することができますが、分母にはlog-sum-expトリックを使用する必要があります。p(バツ私|Ck)


より具体的には、アンダーフローを防ぐために:

  • 私たちはどのクラスを知ることを気にしている場合入力が(X = X 1、... 、xはn個)、最も可能性の高い最大事後(MAP)決定ルールとに属し、我々は対数を適用する必要はありませんsum-expトリック。その場合、分母を計算する必要がないため。分子の場合、ログを取得してアンダーフローを防ぐことができます:l o g (p (x | Y = C )p (Y = C ))(y^)(バツ=バツ1、…、バツん)log(p(バツ|Y=C)p(Y=C))。すなわち:

    y^=argmaxk∈{1、…、|C|}p(Ck|バツ1、…、バツん)=argmaxk∈{1、…、|C|} p(Ck)Π私=1んp(バツ私|Ck)

    これはログを取った後になります:

y^=argmaxk∈{1、…、|C|}ログ⁡(p(Ck|バツ1、…、バツん))=argmaxk∈{1、…、|C|}ログ⁡( p(Ck)Π私=1んp(バツ私|Ck))=argmaxk∈{1、…、|C|}(ログ⁡(p(Ck))+ Σ私=1んログ⁡(p(バツ私|Ck)))
  • クラス確率を計算する場合、分母を計算する必要があります。p(Y=C|バツ)

    ログ⁡(p(Y=C|バツ))=ログ⁡(p(バツ|Y=C)p(Y=C) Σk=1|C|p(バツ|Y=Ck)p(Y=Ck))=ログ⁡(p(バツ|Y=C)p(Y=C)⏟分子)−ログ⁡( Σk=1|C|p(バツ|Y=Ck)p(Y=Ck)⏟分母)

    要素ログ⁡( Σk=1|C|p(バツ|Y=Ck)p(Y=Ck))は非常に小さくなる可能性があるためアンダーフローする可能性があります。これは分子と同じ問題ですが、今回は対数内に合計があり、p (x i | C kを変換できません) )(に)0に近いことができるログ( P (X I | CのK))(以降、もはや負なく0に近い0 ≤ P (X I | C K)≤ 1p(バツ私|Ck)p(バツ私|Ck)ログ⁡(p(バツ私|Ck))0≤p(バツ私|Ck)≤1)。この問題を回避するために、という事実を使用して、以下を取得できます。p(バツ私|Ck)=exp⁡(ログ⁡(p(バツ私|Ck)))

    ログ⁡( Σk=1|C|p(バツ|Y=Ck)p(Y=Ck))=ログ⁡( Σk=1|C|exp⁡(ログ⁡(p(バツ|Y=Ck)p(Y=Ck))))

    その時点で、新しい問題が発生し:log ( p (x | Y = C k)p (Y = C k))は非常に負の値になる可能性があります。これは、exp ( log ( p (x | Y = C k)p (Y = C k)))は、0に非常に近くなる可能性があります(アンダーフロー)。これがlog-sum-expトリックを使用する場所です:ログ⁡(p(バツ|Y=Ck)p(Y=Ck))exp⁡(ログ⁡(p(バツ|Y=Ck)p(Y=Ck)))

    ログ⁡Σkeak=ログ⁡Σkeakeあ−あ=あ+ログ⁡Σkeak−あ

    と:

    • 、ak=ログ⁡(p(バツ|Y=Ck)p(Y=Ck))
    • あ=最高k∈{1、…、|C|}ak。

    変数を導入することでアンダーフローを回避できることがわかります。たとえば、k = 2 、a 1 = − 245 、a 2 = − 255の場合、次のようになります。あk=2、a1=−245、a2=−255

    • exp⁡(a1)=exp⁡(−245)=3.96143×10−107
    • exp⁡(a2)=exp⁡(−255)=1.798486×10−111

    log-sum-expトリックを使用して、アンダーフローを回避します: log ∑ k e a kあ=最高(−245、−255)=−245ログ⁡Σkeak=ログ⁡Σkeakeあ−あ=あ+ログ⁡Σkeak−あ=−245+ログ⁡Σkeak+245=−245+ログ⁡(e−245+245+e−255+245)=−245+ログ⁡(e0+e−10)

    は3.96143 × 10 − 107または1.798486 × 10 − 111よりも0からはるかに離れているため、アンダーフローを回避し ました。e−103.96143×10−1071.798486×10−111


2

2つのデータベースのどちらがフレーズを生成した可能性が高いかを特定したいとします(たとえば、このフレーズはどの小説から来た可能性が高いですか)。データベースを条件として、単語の独立性を仮定することができます(単純ベイズ仮定)。

次に、投稿した2番目のリンクを調べます。そこデータベース所与の文を観察する同時確率を表すであろうとのE bはT Sは文章中の単語の各々を観測する確率を表すことになります。aebt


1

この回答から、Pythonの最小数(たとえば、それを取り上げる)はIEEE7545e-324によるものであり、ハードウェアの原因は他の言語にも当てはまることがわかります。

In [2]: np.nextafter(0, 1)
Out[2]: 5e-324

そして、それよりも小さいフロートは0になります。

In [3]: np.nextafter(0, 1)/2
Out[3]: 0.0

そして、with discrete features and two classes必要に応じてNaive Bayesの機能を見てみましょう。

p(S=1|w1、。。。wん)=p(S=1)Π私=1んp(w私|S=1) Σs={0、1}p(S=s)Π私=1んp(w私|S=s)

以下の簡単なNLPタスクでその機能をインスタンス化しましょう。

S=1S=0ん=5、000w私p(w私|S=1)1−p(w私|S=1)

In [1]: import numpy as np
In [2]: from sklearn.naive_bayes import BernoulliNB
# let's train our model with 200 samples
In [3]: X = np.random.randint(2, size=(200, 5000))
In [4]: y = np.random.randint(2, size=(200, 1)).ravel()
In [5]: clf = BernoulliNB()
In [6]: model = clf.fit(X, y)

p(S=s)Π私=1んp(w私|S=s)p(w私|S=1)1−p(w私|S=1)Π私50005e−3240/0

In [7]: (np.nextafter(0, 1)*2) / (np.nextafter(0, 1)*2)
Out[7]: 1.0

In [8]: (np.nextafter(0, 1)/2) / (np.nextafter(0, 1)/2)
/home/lerner/anaconda3/bin/ipython3:1: RuntimeWarning: invalid value encountered in double_scalars
  #!/home/lerner/anaconda3/bin/python
Out[8]: nan
In [9]: l_cpt = model.feature_log_prob_
In [10]: x = np.random.randint(2, size=(1, 5000))
In [11]: cls_lp = model.class_log_prior_
In [12]: probs = np.where(x, np.exp(l_cpt[1]), 1-np.exp(l_cpt[1]))
In [13]: np.exp(cls_lp[1]) * np.prod(probs)
Out[14]: 0.0

p(S=1|w1、。。。wん)

sklearnの公式実装を見ることができます:

jll = self._joint_log_likelihood(X)
# normalize by P(x) = P(f_1, ..., f_n)
log_prob_x = logsumexp(jll, axis=1)
return jll - np.atleast_2d(log_prob_x).T

分子の場合、確率の積を対数尤度の合計に変換し、分母の場合、scipyでlogsumexpを使用しました。

out = log(sum(exp(a - a_max), axis=0))
out += a_max

Σs={0、1}ejlls−メートルaバツ_jlllog⁡∑s={0,1}ejlls−max_jllmax_jll+log⁡∑s={0,1}ejlls−max_jllメートルaバツ_jll

そしてここに派生があります:

ログ⁡Σs={0、1}ejlls=ログ⁡Σs={0、1}ejllseメートルaバツ_jll−メートルaバツ_jll=ログ⁡eメートルaバツ_jll+ログ⁡Σs={0、1}ejlls−メートルaバツ_jll=メートルaバツ_jll+ログ⁡Σs={0、1}ejlls−メートルaバツ_jll

メートルaバツ_jlla_メートルaバツ

ログ⁡p(S=1|w1、。。。wん)

return jll - np.atleast_2d(log_prob_x).T

お役に立てば幸いです。

参照:
1. ベルヌーイ単純ベイズ分類器
2. 単純ベイズによるスパムフィルタリング–単純ベイズはどれですか?

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.