Naive Bayes에서 log-sum-exp 트릭이 작동하는 예


14

나는 많은 곳에서 log-sum-exp 트릭에 대해 읽었 지만 (예를 들어 herehere ) 이것이 Naive Bayes 분류기에 어떻게 적용되는지에 대한 예를 보지 못했습니다 (예 : 개별 기능과 두 클래스)

이 트릭을 사용하여 수치 언더 플로 문제를 어떻게 정확하게 피할 수 있습니까?


2
순진한 Bayes에 대해 반드시 명시 적으로는 아니지만 여기에는 몇 가지 사용 예가 있습니다 . 그러나 트릭에 대한 아이디어가 매우 간단하고 쉽게 적용 가능하기 때문에 그다지 중요하지 않습니다.
Glen_b-복지국 모니카

오버플로보다 문제가 더 많이 발생할 수 있습니다.
Henry

underflow 에서 검색을 시도한 다음 아직 다루지 않은 내용을보다 구체적으로 다루기 위해 질문을 업데이트하십시오.
Glen_b-복귀 모니카

당신은 또한 명확히 할 수 있습니까-이것은 Bernoulli 모델 순진한 Bayes입니까? 다른 것?
Glen_b-복귀 모니카

예를 참조하십시오 여기에 바로 단지 그들이 로그를 취할 경우 '참조'전에 바닥 (에서, 로그의 예 것) 양쪽을 제곱 승 그러나 로그의 합의 특급으로 ( "있는 그대로"는 우를 떠나 -sum-exp 트릭 더 구체적인 질문을하기 위해 Naive Bayes에서의 사용과 관련하여 충분한 정보를 제공합니까?
Glen_b -Reinstate Monica

답변:


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 (post posteriori) 결정 규칙에 속할 가능성이 높습니다. 이 경우 분모를 계산할 필요가 없으므로 log-sum-exp 트릭을 적용해야합니다 . 분자의 경우 언더 플로를 방지하기 위해 간단히 로그를 취할 수 있습니다. . 더 구체적으로:( 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)

    로그를 가져온 후에 나타납니다.

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(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 트릭을 사용합니다 .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.

    변수 를 도입 하면 언더 플로를 피할 수 있습니다. 예를 들어 경우 다음이 있습니다.Ak=2,a1=245,a2=255

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

    log-sum-exp 트릭을 사용하면 와 함께 언더 플로를 피할 수 있습니다 : A=max(245,255)=245logkeak=logkeakeAA=A+logkeakA=245+logkeak+245=245+log(e245+245+e255+245)=245+log(e0+e10)

    이 또는 보다 0에서 훨씬 멀기 때문에 언더 플로를 피했습니다 .e103.96143×101071.798486×10111


2

두 데이터베이스 중 어느 구가 구문을 생성했을 가능성이 더 높은지 (예를 들어,이 구가이 소설에서 유래했을 가능성이 높은 소설)를 식별하려고한다고 가정하십시오. 데이터베이스에서 조건부 단어의 독립성을 가정 할 수 있습니다 (Naive Bayes 가정).

이제 게시 한 두 번째 링크를 찾으십시오. 이 데이터베이스 주어진 문장 관찰의 결합 확률 대표 것이다 의 문장에서 단어의 각을 관찰 확률을 나타내는 것입니다.aebt


1

우리는에서 볼 수있는 이 답변 파이썬에서 가장 작은 수입니다 (다만 예를 들어 그것을 가지고) 있음을 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의 기능을 살펴 보겠습니다 .

p(S=1|w1,...wn)=p(S=1)i=1np(wi|S=1) s={0,1}p(S=s)i=1np(wi|S=s)

간단한 NLP 작업을 통해 해당 기능을 인스턴스화하겠습니다.

우리는 다가오는 이메일이 스팸인지 ( ) 스팸이 아닌지 ( ) 탐지하기로 결정 하고 단어의 크기가 5,000 ( )이고 단어 ( )가 발생 하는 경우 유일한 관심사는 이메일의 ( ) 또는 단순함 ( Bernoulli naive 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)

우리는 확률로 인해 가 매우 작다는 것을 알 수 있습니다 ( 및 것)에 0 ~ 1 , 따라서 우리는 제품이보다 작은 것이 있는지입니다 와 우리가 얻을 .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

그러면 문제가 발생합니다. 전자 메일이 스팸 가능성을 어떻게 계산할 수 있습니까? 아니면 분자와 분모를 어떻게 계산할 수 있습니까?p(S=1|w1,...wn)

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

공동 로그 우도를 추가하여 두 개의 공동 확률을 추가 할 수 없으므로 로그 공간에서 확률 공간으로 나가야합니다. 그러나 두 개의 실제 확률은 너무 작기 때문에 추가 할 수 없으며 및 결과를 다시 넣어야합니다. 로그 공간 다시 하십시오. 로그 공간에 추가하여 .s={0,1}ejllsmax_jlllogs={0,1}ejllsmax_jllmax_jll+logs={0,1}ejllsmax_jllmax_jll

그리고 여기에 파생물이 있습니다 :

logs={0,1}ejlls=logs={0,1}ejllsemax_jllmax_jll=logemax_jll+logs={0,1}ejllsmax_jll=max_jll+logs={0,1}ejllsmax_jll

여기서 은 코드 의 입니다.max_jlla_max

로그 공간에서 분자와 분모를 모두 얻으면 분자에서 분모를 빼서 로그 조건부 확률 ( )을 얻을 수 있습니다 . logp(S=1|w1,...wn)

return jll - np.atleast_2d(log_prob_x).T

희망이 도움이됩니다.

참조 :
1. Bernoulli Naive Bayes 분류기
2. Naive Bayes를 사용한 스팸 필터링 – 어떤 Naive Bayes?

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.