log-sum-expトリックについて多くの場所(例:ここ、ここ)で読みましたが、それがNaive Bayes分類器(例:離散機能と2つのクラス)に具体的に適用される例を見たことがありません。
このトリックを使用して数値のアンダーフローの問題をどの程度正確に回避できますか?
log-sum-expトリックについて多くの場所(例:ここ、ここ)で読みましたが、それがNaive Bayes分類器(例:離散機能と2つのクラス)に具体的に適用される例を見たことがありません。
このトリックを使用して数値のアンダーフローの問題をどの程度正確に回避できますか?
回答:
In
分母と分子の両方が非常に小さくなる可能性があります。これは、通常、が0に近くなる可能性があり、それらの多くを互いに乗算するためです。アンダーフローを防ぐために、分子のログを取得することができますが、分母にはlog-sum-expトリックを使用する必要があります。
より具体的には、アンダーフローを防ぐために:
私たちはどのクラスを知ることを気にしている場合入力が(X = X 1、... 、xはn個)、最も可能性の高い最大事後(MAP)決定ルールとに属し、我々は対数を適用する必要はありませんsum-expトリック。その場合、分母を計算する必要がないため。分子の場合、ログを取得してアンダーフローを防ぐことができます:l o g (p (x | Y = C )p (Y = C ))。すなわち:
これはログを取った後になります:
クラス確率を計算する場合、分母を計算する必要があります。
要素は非常に小さくなる可能性があるためアンダーフローする可能性があります。これは分子と同じ問題ですが、今回は対数内に合計があり、p (x i | C kを変換できません) )(に)0に近いことができるログ( P (X I | CのK))(以降、もはや負なく0に近い0 ≤ P (X I | C K)≤ 1)。この問題を回避するために、という事実を使用して、以下を取得できます。
その時点で、新しい問題が発生し: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トリックを使用する場所です:
と:
変数を導入することでアンダーフローを回避できることがわかります。たとえば、k = 2 、a 1 = − 245 、a 2 = − 255の場合、次のようになります。
log-sum-expトリックを使用して、アンダーフローを回避します: log ∑ k e a k
は3.96143 × 10 − 107または1.798486 × 10 − 111よりも0からはるかに離れているため、アンダーフローを回避し ました。
この回答から、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の機能を見てみましょう。
以下の簡単なNLPタスクでその機能をインスタンス化しましょう。
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)
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
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
そしてここに派生があります:
return jll - np.atleast_2d(log_prob_x).T
お役に立てば幸いです。
参照:
1. ベルヌーイ単純ベイズ分類器
2. 単純ベイズによるスパムフィルタリング–単純ベイズはどれですか?