Приклад того, як працює трюк log-sum-exp у Naive Bayes


14

Я читав про трюк log-sum-exp у багатьох місцях (наприклад, тут і тут ), але ніколи не бачив прикладу того, як він застосовується спеціально до класифікатора Naive Bayes (наприклад, з дискретними особливостями та двома класами)

Як саме можна уникнути проблеми числового підтоку, використовуючи цей трюк?


2
Тут є кілька прикладів його використання, хоча не обов'язково явно для наївних Байєсів. Однак це навряд чи має значення, оскільки ідея про трюк досить відверта і легко адаптована.
Glen_b -Встановіть Моніку

Проблема, швидше за все, переповнення, ніж перелив.
Генрі

Я б запропонував вам спробувати здійснити пошук під потоком , а потім оновіть своє запитання, щоб конкретніше вирішити все, що вже не охоплено.
Glen_b -Встановити Моніку

Чи можете ви також уточнити - це наївна модель Беерлі Байєса? щось інше, можливо?
Glen_b -Встановіть Моніку

Дивіться приклад тут , внизу (безпосередньо перед "Дивіться також", де вони беруть журнали; експоненцію обох сторін, але залишення RHS "як є" (як exp суми журналів) буде прикладом журналу - це дає вам достатню інформацію, що стосується його використання в Naive Bayes, щоб задати більш конкретний питання?
Glen_b -Встановити Моніку

Відповіді:


26

У

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

і знаменник, і чисельник можуть стати дуже маленькими, як правило, тому, що може бути близьким до 0, і ми множимо багато з них один з одним. Щоб запобігти підтокам, можна просто взяти журнал чисельника, але для використання в знаменнику потрібно використовувати трюк log-sum-exp.p(xi|Ck)


Більш конкретно, для запобігання надтокам:

  • Якщо нас цікавить лише те, до якого класу вхід швидше за все, належить з максимальним правилом післяорі (MAP), ми не будемо повинні застосувати трюк log-sum-exp, оскільки в цьому випадку нам не потрібно обчислювати знаменник . Для чисельника можна просто взяти журнал, щоб запобігти підтокам: . Більш конкретно:(y^)л про г ( р ( х | Y = С ) р ( 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)

    що стає після взяття журналу:

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)))
  • Якщо ми хочемо обчислити ймовірність класу , нам знадобиться обчислити знаменник: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)

    Елемент може перетікати через може бути дуже малим: це те саме питання, що і в чисельнику, але на цей раз у нас є підсумок всередині логарифму, що заважає нам перетворити (може бути близьким до 0) в (негативний і більше не близький до 0, оскільки ). Щоб обійти це питання, ми можемо використати той факт, що для отримання:log( k=1|C|p(x|Y=Ck)p(Y=Ck))p ( x i | C k ) log ( p ( x i | C k ) ) 0 p ( x i | C k ) 1 p ( x i | C k ) = exp ( журнал ( p ( x i | C k ) )p(xi|Ck)p(xi|Ck)log(p(xi|Ck))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))))

    У цей момент виникає нова проблема: може бути досить негативним, що означає, що може стати дуже близьким до 0, тобто підводним. Тут ми використовуємо фокус 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

    з:

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

    Ми можемо бачити, що введення змінної дозволяє уникнути підтоків. Наприклад, , маємо:k = 2 , a 1 = - 245 , a 2 = - 255Ak=2,a1=245,a2=255

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

    Використовуючи трюк log-sum-exp, ми уникаємо , з : log k e a kA=max(245,255)=245журналкеак=журналкеакеА-А=А+журналкеак-А=-245+журналкеак+245=-245+журнал(е-245+245+е-255+245)=-245+журнал(е0+е-10)

    Ми уникали підтоку, оскільки набагато далі від 0, ніж або . 3.96143 × 10 - 107 1.798486 × 10 - 111е-103,96143×10-1071.798486×10-111


2

Припустимо, ми хочемо визначити, яка з двох баз даних, скоріше за все, створила фразу (наприклад, з якого роману ця фраза частіше походить). Ми можемо вважати незалежність слів, що обумовлюються базою даних (припущення Naive Bayes).

Тепер подивіться друге посилання, яке ви опублікували. Там буде представляти спільну ймовірність спостереження за реченням, що задається базою даних, а s буде представляти ймовірність спостереження за кожним із слів у реченні.e b tаебт


1

З цієї відповіді ми бачимо, що найменше число в Python (просто візьмемо це для прикладу) 5e-324пов’язане з IEEE754 , а технічна причина стосується і інших мов.

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

І будь-який поплавок, менший за це, призведе до 0.

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

І давайте розглянемо функцію Naive Bayes with discrete features and two classesяк потрібно:

p(S=1|ш1,...шн)=p(S=1)i=1нp(шi|S=1) с={0,1}p(S=с)i=1нp(шi|S=с)

Дозвольте мені описати цю функцію простим завданням NLP внизу.

Ми вирішуємо виявити, чи приходить електронний лист спам ( ) чи не спам ( ), і у нас є словниковий запас розміром 5000 ( ), і єдине питання полягає в тому, якщо слово ( ) трапляється ( ) в електронній пошті чи ні ( ) для простоти ( Bernoulli naive Bayes ).S=1S=0н=5,000шip(шi|S=1)1-p(шi|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)

Ми можемо бачити, що було б дуже мало через ймовірності (обидва та в буде від 0 до 1) , отже ми впевнені, що добуток буде меншим за і ми просто отримаємо .p(S=с)i=1нp(шi|S=с)p(шi|S=1)1-p(шi|S=1)i50005е-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|ш1,...шн)

Ми можемо бачити офіційну реалізацію в 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

Для чисельника він перетворив добуток ймовірностей на суму вірогідності журналу, а для знаменника використав logsumexp в scipy, який є:

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

Оскільки ми не можемо додати дві спільні ймовірності, додавши ймовірність його спільного журналу, і нам слід вийти з простору журналу в простір ймовірностей. Але ми не можемо додати дві істинні ймовірності, оскільки вони занадто малі, і ми повинні їх масштабувати і робити додавання: і повернути результат назад у простір журналу а потім змінити його масштаб назад: у просторі журналу, додавши .с={0,1}еjллс-мах_jллжурналс={0,1}еjллс-мах_jллмах_jлл+журналс={0,1}еjллс-мах_jллмах_jлл

І ось деривація:

журналс={0,1}еjллс=журналс={0,1}еjллсемах_jлл-мах_jлл=журналемах_jлл+журналс={0,1}еjллс-мах_jлл=мах_jлл+журналс={0,1}еjллс-мах_jлл

де - в коді.мах_jлла_мах

Як тільки ми отримуємо і чисельник, і знаменник у просторі журналу, ми можемо отримати умовну ймовірність журналу ( ), віднявши знаменник від чисельника : журналp(S=1|ш1,...шн)

return jll - np.atleast_2d(log_prob_x).T

Сподіваюся, що це допомагає.

Довідка:
1. Класифікатор Naive Bayes Бернуллі
2. Фільтрація спаму за допомогою Naive Bayes - Який Naive Bayes?

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.