Contoh bagaimana trik log-sum-exp bekerja di Naive Bayes

14

Saya telah membaca tentang trik log-sum-exp di banyak tempat (misalnya di sini , dan di sini ) tetapi belum pernah melihat contoh bagaimana itu diterapkan secara khusus untuk classifier Naive Bayes (misalnya dengan fitur diskrit dan dua kelas)

Bagaimana tepatnya seseorang menghindari masalah underflow numerik menggunakan trik ini?

Josh
sumber
2
Ada beberapa contoh penggunaannya di sini, meskipun tidak harus secara eksplisit untuk Bayes naif . Namun, itu hampir tidak penting, karena ide triknya cukup mudah dan mudah beradaptasi.
Glen_b -Reinstate Monica
Masalahnya lebih cenderung menjadi underflow daripada overflow.
Henry
Saya sarankan Anda mencoba pencarian di bagian bawah , dan kemudian perbarui pertanyaan Anda untuk lebih spesifik membahas apa pun yang belum tercakup.
Glen_b -Reinstate Monica
Bisakah Anda juga mengklarifikasi - ini adalah Bernoulli-model naif Bayes? sesuatu yang lain mungkin?
Glen_b -Reinstate Monica
Lihat contoh di sini , tepat di bagian bawah (tepat sebelum 'Lihat Juga' di mana mereka mengambil log; eksponensial kedua belah pihak tetapi meninggalkan RHS "apa adanya" (seperti exp dari jumlah log) akan menjadi contoh log -sum-exp trick. Apakah itu memberi Anda informasi yang cukup terkait dengan penggunaannya di Naif Bayes untuk mengajukan pertanyaan yang lebih spesifik?
Glen_b -Reinstate Monica

Jawaban:

26

Dalam

p(Y=C|x)=p(x|Y=C)p(Y=C) k=1|C|p(x|Y=Ck)p(Y=Ck)

baik penyebut dan pembilangnya bisa menjadi sangat kecil, biasanya karena bisa mendekati 0 dan kami mengalikan banyak dari mereka satu sama lain. Untuk mencegah underflow, seseorang dapat dengan mudah mengambil log dari pembilang, tetapi kita perlu menggunakan trik log-sum-exp untuk penyebut.p(xi|Ck)


Lebih khusus lagi, untuk mencegah underflow:

  • Jika kita hanya peduli mengetahui kelas mana input kemungkinan besar milik dengan aturan keputusan maksimum a posteriori (MAP), kita tidak harus menerapkan trik log-sum-exp, karena kita tidak harus menghitung penyebut dalam kasus itu. Untuk pembilang, cukup ambil log untuk mencegah arus bawah: . Lebih spesifik:( x = x 1 , ... , x n ) l o g ( p ( x | Y = C ) p ( Y = C ) )(y^)(x=x1,,xn)log(p(x|Y=C)p(Y=C))

    y^=argmaxk{1,,|C|}p(Ck|x1,,xn)=argmaxk{1,,|C|} p(Ck)i=1np(xi|Ck)

    yang menjadi setelah mengambil log:

y^=argmaxk{1,,|C|}log(p(Ck|x1,,xn))=argmaxk{1,,|C|}log( p(Ck)i=1np(xi|Ck))=argmaxk{1,,|C|}(log(p(Ck))+ i=1nlog(p(xi|Ck)))
  • Jika kita ingin menghitung probabilitas kelas , kita perlu menghitung penyebutnya:p(Y=C|x)

    log(p(Y=C|x))=log(p(x|Y=C)p(Y=C) k=1|C|p(x|Y=Ck)p(Y=Ck))=log(p(x|Y=C)p(Y=C)numerator)log( k=1|C|p(x|Y=Ck)p(Y=Ck)denominator)

    Elemen dapat di bawah karena bisa sangat kecil: ini adalah masalah yang sama seperti pada pembilang, tetapi kali ini kami memiliki penjumlahan di dalam logaritma, yang mencegah kami mengubah (bisa dekat dengan 0) ke dalam (negatif dan tidak dekat dengan 0 lagi, karena ). Untuk menghindari masalah ini, kita dapat menggunakan fakta bahwa untuk mendapatkan:log( k=1|C|p(x|Y=Ck)p(Y=Ck))log i | C k ) p ( x i | C k ) ( p ( x i | C k ) ) 0p(xi|Ck)p(xi|Ck)log(p(xi|Ck))p ( x i | C k ) = exp ( log ( p ( x i | C k ) )0p(xi|Ck)1p(xi|Ck)=exp(log(p(xi|Ck)))

    log( k=1|C|p(x|Y=Ck)p(Y=Ck))=log( k=1|C|exp(log(p(x|Y=Ck)p(Y=Ck))))

    Pada titik itu, muncul masalah baru: mungkin cukup negatif, yang menyiratkan bahwa dapat menjadi sangat dekat dengan 0, yaitu underflow. Di sinilah kami menggunakan trik log-sum-exp : exp ( log ( p ( x | Y = C k ) p ( Y = C k ) ) )log(p(x|Y=Ck)p(Y=Ck))exp(log(p(x|Y=Ck)p(Y=Ck)))

    logkeak=logkeakeAA=A+logkeakA

    dengan:

    • ak=log(p(x|Y=Ck)p(Y=Ck)) ,
    • A=maxk{1,,|C|}ak.

    Kita dapat melihat bahwa memperkenalkan variabel menghindari arus bawah. Misalnya dengan , kita memiliki:Ak=2,a1=245,a2=255

    • exp(a1)=exp(245)=3.96143×10107
    • exp(a2)=exp(255)=1.798486×10111

    Dengan menggunakan trik log-sum-exp kita menghindari underflow, dengan : A=max(245,255)=245logkeak=logkeakeAA=A+logkeakA=245+logkeak+245=245+log(e245+245+e255+245)=245+log(e0+e10)

    Kami menghindari aliran bawah karena jauh lebih jauh dari 0 daripada atau . 3.96143e10 1.798486 × 10 - 1113.96143×101071.798486×10111

Franck Dernoncourt
sumber
2

Misalkan kita ingin mengidentifikasi dari dua basis data mana yang lebih mungkin menghasilkan frasa (misalnya, novel mana yang frasa ini lebih cenderung berasal). Kita dapat mengasumsikan independensi kata-kata yang tergantung pada database (asumsi Naif Bayes).

Sekarang lihat tautan kedua yang telah Anda poskan. Di sana akan mewakili probabilitas gabungan dari mengamati kalimat yang diberikan database dan akan mewakili probabilitas mengamati setiap kata dalam kalimat.aebt

Sid
sumber
1

Kita dapat melihat dari jawaban ini bahwa angka terkecil dalam Python (anggap saja) adalah 5e-324karena 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 classesseperti yang Anda inginkan:

p(S=1|w1,...wn)=p(S=1)i=1np(wi|S=1) s={0,1}p(S=s)i=1np(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)1p(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)i=1np(wi|S=s)p(wi|S=1)1p(wi|S=1)i50005e3240/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}ejllsmax_jlllogs={0,1}ejllsmax_jllmax_jll+logs={0,1}ejllsmax_jllmax_jll

Dan di sini adalah derivasi:

logs={0,1}ejlls=logs={0,1}ejllsemax_jllmax_jll=logemax_jll+logs={0,1}ejllsmax_jll=max_jll+logs={0,1}ejllsmax_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?

Lerner Zhang
sumber