Kita dapat melihat dari jawaban ini bahwa angka terkecil dalam Python (anggap saja) adalah 5e-324
karena IEEE754 , dan penyebab perangkat keras juga berlaku untuk bahasa lain.
In [2]: np.nextafter(0, 1)
Out[2]: 5e-324
Dan setiap float yang lebih kecil dari itu akan menghasilkan 0.
In [3]: np.nextafter(0, 1)/2
Out[3]: 0.0
Dan mari kita lihat fungsi Naif Bayes with discrete features and two classes
seperti yang Anda inginkan:
p(S=1|w1,...wn)=p(S=1)∏ni=1p(wi|S=1) ∑s={0,1}p(S=s)∏ni=1p(wi|S=s)
Biarkan saya instantiate fungsi itu dengan tugas NLP sederhana di bawah.
Kami memutuskan untuk mendeteksi apakah email yang datang adalah spam ( ) atau bukan spam ( ) dan kami memiliki kosakata kata berukuran 5.000 ( ) dan satu-satunya masalah adalah jika sebuah kata ( ) muncul ( ) di email atau tidak ( ) untuk kesederhanaan ( Bernoulli naif Bayes ).S=1S=0n=5,000wip(wi|S=1)1−p(wi|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)
Kita dapat melihat bahwa akan sangat kecil karena probabilitasnya (baik dan akan berada antara 0 dan 1) di , dan karenanya kami yakin bahwa produk akan lebih kecil dari dan kami hanya mendapatkan .p(S=s)∏ni=1p(wi|S=s)p(wi|S=1)1−p(wi|S=1)∏5000i5e−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
Kemudian muncul masalah: Bagaimana kita bisa menghitung probabilitas email adalah spam ? Atau bagaimana kita bisa menghitung pembilang dan penyebutnya?p(S=1|w1,...wn)
Kita bisa melihat implementasi resmi di 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
Untuk pembilang itu dikonversi produk probabilitas ke jumlah kemungkinan log dan untuk penyebutnya menggunakan logsumexp dalam scipy yaitu:
out = log(sum(exp(a - a_max), axis=0))
out += a_max
Karena kita tidak dapat menambahkan dua probabilitas gabungan dengan menambahkan log kemungkinan gabungannya, dan kita harus keluar dari ruang log ke ruang probabilitas. Tetapi kami tidak dapat menambahkan dua probabilitas benar karena terlalu kecil dan kami harus menskalakannya dan melakukan penambahan: dan mengembalikan hasilnya ke dalam ruang log lalu kembali: di ruang log dengan menambahkan .∑s={0,1}ejlls−max_jlllog∑s={0,1}ejlls−max_jllmax_jll+log∑s={0,1}ejlls−max_jllmax_jll
Dan di sini adalah derivasi:
log∑s={0,1}ejlls=log∑s={0,1}ejllsemax_jll−max_jll=logemax_jll+log∑s={0,1}ejlls−max_jll=max_jll+log∑s={0,1}ejlls−max_jll
di mana adalah dalam kode.max_jlla_max
Setelah kita mendapatkan pembilang dan penyebut dalam ruang log kita bisa mendapatkan probabilitas bersyarat log ( ) dengan mengurangi penyebut dari pembilang : logp(S=1|w1,...wn)
return jll - np.atleast_2d(log_prob_x).T
Semoga itu bisa membantu.
Referensi:
1. Bernoulli Naive Bayes Classifier
2. Penyaringan Spam dengan Naive Bayes - Naive Bayes yang mana?