나는 많은 곳에서 log-sum-exp 트릭에 대해 읽었 지만 (예를 들어 here 와 here ) 이것이 Naive Bayes 분류기에 어떻게 적용되는지에 대한 예를 보지 못했습니다 (예 : 개별 기능과 두 클래스)
이 트릭을 사용하여 수치 언더 플로 문제를 어떻게 정확하게 피할 수 있습니까?
나는 많은 곳에서 log-sum-exp 트릭에 대해 읽었 지만 (예를 들어 here 와 here ) 이것이 Naive Bayes 분류기에 어떻게 적용되는지에 대한 예를 보지 못했습니다 (예 : 개별 기능과 두 클래스)
이 트릭을 사용하여 수치 언더 플로 문제를 어떻게 정확하게 피할 수 있습니까?
답변:
에서는
가 0에 가까워 질 수 있고 분모 가 서로 곱하기 때문에 분모와 분자가 모두 매우 작아 질 수 있습니다. 언더 플로를 방지하려면 분자의 로그를 간단히 가져갈 수 있지만 분모에 대해 log-sum-exp 트릭을 사용해야합니다.
보다 구체적으로, 언더 플로를 방지하려면 :
입력 어떤 클래스 인지 아는 것에 만 관심이 있다면 최대 MAP (post posteriori) 결정 규칙에 속할 가능성이 높습니다. 이 경우 분모를 계산할 필요가 없으므로 log-sum-exp 트릭을 적용해야합니다 . 분자의 경우 언더 플로를 방지하기 위해 간단히 로그를 취할 수 있습니다. . 더 구체적으로:( X = X 1 , ... , X의 N ) L O g ( P ( X | Y = C ) , P ( Y = C ) )
로그를 가져온 후에 나타납니다.
클래스 확률 를 계산하려면 분모를 계산해야합니다.
소자 때문에 언더있다 는 매우 작을 수 있습니다. 분자에서와 같은 문제이지만 이번에는 로그 안에 요약이 있으므로 를 변환 할 수 없습니다. 0) ( 이므로 음수이며 더 이상 0에 가까워지지 않음 ). 이 문제를 피하기 위해 을 사용하여 다음을 얻을 수 있습니다.
이 시점에서 새로운 문제가 발생합니다. 은 음수 일 수 있습니다. 이는 는 0에 매우 가까워 질 수 있습니다 (예 : 언더 플로). 여기서 log-sum-exp 트릭을 사용합니다 .
와:
변수 를 도입 하면 언더 플로를 피할 수 있습니다. 예를 들어 경우 다음이 있습니다.
log-sum-exp 트릭을 사용하면 와 함께 언더 플로를 피할 수 있습니다 :
이 또는 보다 0에서 훨씬 멀기 때문에 언더 플로를 피했습니다 .
우리는에서 볼 수있는 이 답변 파이썬에서 가장 작은 수입니다 (다만 예를 들어 그것을 가지고) 있음을 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
with discrete features and two classes
필요에 따라 Naive Bayes의 기능을 살펴 보겠습니다 .
간단한 NLP 작업을 통해 해당 기능을 인스턴스화하겠습니다.
우리는 다가오는 이메일이 스팸인지 ( ) 스팸이 아닌지 ( ) 탐지하기로 결정 하고 단어의 크기가 5,000 ( )이고 단어 ( )가 발생 하는 경우 유일한 관심사는 이메일의 ( ) 또는 단순함 ( Bernoulli naive Bayes )의 경우 ( )
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 , 따라서 우리는 제품이보다 작은 것이 있는지입니다 와 우리가 얻을 .
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. Bernoulli Naive Bayes 분류기
2. Naive Bayes를 사용한 스팸 필터링 – 어떤 Naive Bayes?