З цієї відповіді ми бачимо, що найменше число в 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
як потрібно:
р ( S= 1 | ш1, . . . шн) = p ( S= 1 ) ∏нi = 1р ( шi| S= 1 ) ∑s = { 0 , 1 }р ( S= s ) ∏нi = 1р ( шi| S= с )
Дозвольте мені описати цю функцію простим завданням NLP внизу.
Ми вирішуємо виявити, чи приходить електронний лист спам ( ) чи не спам ( ), і у нас є словниковий запас розміром 5000 ( ), і єдине питання полягає в тому, якщо слово ( ) трапляється ( ) в електронній пошті чи ні ( ) для простоти ( Bernoulli naive Bayes ).S= 1S= 0п = 5 , 000шiр ( шi| S= 1 )1 - р ( ш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) , отже ми впевнені, що добуток буде меншим за і ми просто отримаємо .р ( S= s ) ∏нi = 1р ( шi| S= с )р ( шi| S= 1 )1 - р ( шi| S= 1 )∏5000i5 е- 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
Тоді виникає проблема: Як можна обчислити ймовірність того, що електронна пошта є спамом ? Або як можна обчислити чисельник і знаменник?р ( 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
Оскільки ми не можемо додати дві спільні ймовірності, додавши ймовірність його спільного журналу, і нам слід вийти з простору журналу в простір ймовірностей. Але ми не можемо додати дві істинні ймовірності, оскільки вони занадто малі, і ми повинні їх масштабувати і робити додавання: і повернути результат назад у простір журналу а потім змінити його масштаб назад: у просторі журналу, додавши .∑s = { 0 , 1 }еj l lс- m a x _ j l lжурнал∑s = { 0 , 1 }еj l lс- m a x _ j l lm a x _ j l l + лог∑s = { 0 , 1 }еj l lс- m a x _ j l lm a x _ j l l
І ось деривація:
журнал∑s = { 0 , 1 }еj l lс= журнал∑с= { 0 , 1 }еj l lсеm a x _ j l l - m a x _ j l l= журналем а х_ j l l+ журнал∑s = { 0 , 1}еjl lс- m a x _ j l l= m a x _ j l l + лог∑s = { 0 , 1 }еj llс- m a x _ j l l
де - в коді.m a x _ j l la _ m a x
Як тільки ми отримуємо і чисельник, і знаменник у просторі журналу, ми можемо отримати умовну ймовірність журналу ( ), віднявши знаменник від чисельника : журналр ( S= 1 | ш1, . . . шн)
return jll - np.atleast_2d(log_prob_x).T
Сподіваюся, що це допомагає.
Довідка:
1. Класифікатор Naive Bayes Бернуллі
2. Фільтрація спаму за допомогою Naive Bayes - Який Naive Bayes?