본문 바로가기
베이지안 A/B 테스트와 신뢰구간.도 있어?

빈도론을 다루거나 베이지안을 다루거나 말이죠 뭐니 뭐니 해도 제일 많이 다루게 되는 응용문제는 확률적으로 차이가 나는가? 에 대한 문제이고, 이 확률적인 차이가 나는가를 다루는 상업적인 Application은 A/B테스트에 가장 많이 활용됩니다. 
빈도론에 근거한 A/B테스트는 "그 유명한 A/B 테스트 결과를 신뢰구간을 이용해서 분석해 보는 이야기" 편에서 이미 살펴보았으니까, 이번에는 베이지안 관점의 A/B테스트를 해 보는 것이 당연한 수순이라고 생각합니다. 베이지안적인 A/B 테스트를 하기 전에 간단하게 신뢰구간등 몇 가지를 한번 들여다 보도 가려고 합니다. 

다음과 같은 앱설치에 관련한 데이터가 있다고 한다면 말이죠.

 

일단 베이지안이라는 건 Prior와 Likelihood를 정의해야 Posterior를 만들어 낼 수 있잖습니꽈?  이런 경우에는 Likelihood가 이항분포가 되면 좋겠군요. Likelihood가 이항분포가 되면? 곧바로 타라락 Beta 분포를 이용하면 Conjugate관계를 이용할 수 있겠습니다아아. 흐뭇.

Prior를 무정보분포 즉, Unifrom(Beta(1,1))로 두고, 앱설치를 Trial, 재방문을 성공, 나머지를 재방문 실패라고 치고요, 암산으로 한번 분포를 암산해 보시죠. 

이거는 Beta(1+56, 1+612-56) = Beta(57,557) 이 되겠습니다. 그쵸?

 

이런 식의 Posterior가 됩니다. Conjugate관계를 이용해서 손쉽게 하긴 했는데요, 항상 Conjugate 관계가 될 순 없잖아요? 그래서 시뮬레이션을 통해서 이걸 일반화된 정공법으로 한번 구해 보도록 해 보겠습니다. 

➊ Prior와 Likelihood로 직접 Posterior 샘플을 만들어 보자구요. 

어떤 식으로 할 거냐 하면, (MCMC방법론도 있긴 하지만) 일단 Prior를 정하고, 그 Prior는 모수에 관한 함수이니까 모든 prior값에 대하여 Likelihood를 계산한 후에 그 값이 성공 값과 같은 경우를 뽑아내서 Posterior의 분포를 확인해 보도록 하시죠.

코드는 말 그대로 간단합니다.

import numpy as np

n_draws = 100000 # ① Prior의 대이터 개수 

prior = np.random.uniform(0, 1, n_draws) # ② Uniform으로 prior를 만들어내고,

def bayesian_model(parameters):  # parameter : θ
    result = np.random.binomial(612, parameters, 1) # ④ 전체 612회 시도에 대하여 성공확률 parameter 확률로 확률변수 채취 
    return result 

simdata = np.empty(n_draws)  
for i in range(n_draws): # ③  모든 prior의 값에 대해서 
    simdata[i] = bayesian_model(prior[i]) # ⑤ prior에 대한 model의 결과값을 넣어둡니다. 

posterior = prior[sim_data == 56] # ⑥ 결과값이 성공 회수인 56인 경우의 parameter만 추려냄


다음과 같은 느낌으로 histogram을 그려보면 말이죠 

plt.hist(prior, bins=30, range=(0, 1))
plt.hist(posterior, bins=30, range=(0, 0.2)



 

이런 식으로 그 결과를 볼 수 있습니다. 머 딱 똑같진 않지만, 암산으로 구한 Posterior와 비~슷하죠?

➋ 그렇다면 이번엔 Posterior 결과에 대한 신뢰구간도 구해보면 좋겠군요. - 신뢰구간이라고 표현했지만 이해하기 좋은 수준에서 차용한 것이고 베이지안에서는 엄밀하게 이야기 하면 신용구간이라고 합니다.

잠시만요, 잠깐 예전 이야기를 떠올려보면, BootStrap 했을 때 기억할는지 모르겠는데, 신뢰구간을 샘플을 가지고 직접 세어서 구했었거든요? 그것과 똑같은 방법으로 신용구간을 따질 수 있습니다.  - 조금 더 이야기 한다면 신뢰구간은 어떤 모수가 포함될 가능성을 이야기 하는 것이고, 신용구간은 모수가 어떤 값일 가능성을 이야기합니다. 조금 더 직관적이죠. 용어는 직관적이지 않은 것 같지만 말이죠. -

np.percentile(posterior, [2.5, 97.5]) # 양쪽 2.5%씩 빼서 95% 신용구간
> 
array([0.06992351, 0.11948172])

 

이런 식으로 95%의 샘플이 있는 구간을 세어볼 수 있는데 결국 θ가 0.069~0.119 사이에 95%의 확률로 있다는 의미이고, 이것은 612명 중 56명이 재방문(성공)일 때, θ인 재방문율을 95% 신용구간으로 따진다면 [6.9%, 11.9%]으로 추정할 수 있다고 주장할 수 있습니다. 이때 평균 재방문율도 메디안으로 주장할 수 있겠는데,

np.median(posterior)
>
0.0929984274207789

 

즉, 9.2%의 평균 재방문율이라고 주장할 수 있다는 의미입니다. 방금 해 본 것은 Beta(1,1)의 무정보 Uniform을 Prior로 했는데,

prior = np.random.uniform(0, 1, n_draws) # ② Uniform으로 prior를 만들어내고,

 

Prior를 사전정보가 포함된 Beta로 설정해서 볼 수도 있겠습니다. 이런 경우의 예를 든다면, 기존의 통계가 22명 중 2명이 재방문하고, 20명이 재방문을 안 하더라..라는 경험적 통계가 있다고 하고 Prior를 설정해 보면,

prior = np.random.beta(2, 20, n_draws)

 

이런 식으로 설정할 수도 있겠네요. 참고로 이 결과를 보면 

 

이런 식으로 Posterior결과를 볼 수도 있겠습니다. 후후. 그러니까 사전에 Parameter에 대한 정보가 있다면 이런 식으로도 시뮬레이션 가능합니다. 

➌ 그러면, 일반적으로 알려져 있는 상식적 통계와 비교해 보는 것도 가능하겠습니다. 

보통 업계에서 Retention에 대한 통계는 8% 정도라고 합디다. 그렇다면 지금 들여다보고 있는 데이터는 그것보다 결과가 좋을까요?라고 질문한다면, 이것은 다음과 같은 방법으로 따져볼 수 있겠습니다. 

(posterior > 0.08).sum()/(len(posterior))
>
0.8277777777777777

 

오, 업계의 일반적인 Retention보다 좋을 확률이 82.7% 정도 되는군요? 야~ 이거 시뮬레이션으로 Posterior 샘플들을 구해 놓고 나니, 별의별걸 다 할 수 있군요! 편리합니다. 

자, 이제는 본격 A/B테스트를 해 보시죠.

자, 이제는 여러 가지를 들여다보니 시뮬레이션에 대한 용기가 생겼을 거라고 생각합니다. 자, 다음과 같은 A/B 테스트 결과가 있다고 해 보시죠.

 

➍ 먼저 AB테스트를 암산으로 도전!

앱이 A, B 버전이 있다고 했을 때, A와 B를 각각 설치한 사용자수와 설치한 후에 재방문한 사용자수라고 하고, 역시나 Likelihood는 이항분포, Prior는 무정보 분포 Uniform = Beta(1,1)이라고 모형을 만들어서 문제를 풀어보시죠. 결국 그렇다면 Posterior는 각각 다음과 같겠죠. (성공은 재방문, 실패는 재방문 안 한 사람수)

• Posterior A : Beta(1+50, 1+600-50) = Beta(51, 551),
• Posterior B : Beta(1+56, 1+612-56) = Beta(57, 557) 

이런 식이 되겠습니다. 이건 또 암산이 가능합니다. 

이 두 개의 암산한 Posterior를 그려보면 

 

이런 식 이거든요? 이것만 보면 두 개의 분포가 얼마나 차이가 나는지... B가 A보다 조금 나아 보이긴 합니다만, 어떤 식으로 알 수 있을까요?

$$ p_A \sim \mbox{Beta}(\alpha_A, \beta_A) \\
 p_B \sim \mbox{Beta}(\alpha_B, \beta_B) $$

인 경우에 B의 확률이 A의 확률보다 클 확률은..?  아주 복잡한 과정을 거쳐 구하게 되면 다음과 같습니다. 

$$ P(p_B > p_A) = \sum_{i=0}^{\alpha_B-1}\frac{B(\alpha_A+i,\beta_A+\beta_B)}{(\beta_B+i) B(1+i, \beta_B) B(\alpha_A, \beta_A) } $$

졸라 복잡하죠. 야.. 도대체 이런 걸 외우고 다니는 사람이 있느냐 하면.. 당연히 없지 않을까요? 이 방법을 위해서 계산해 볼 수는 있겠지만, 이걸 샘플을 통해서 간단하게 계산해 볼 수 있습니다. 휴. 다행. 어떤 식이냐면 Posteriror A 분포를 가진 샘플을 만들어내고, Posterior B분포를 가진 샘플을 만들어 낸 다음에 Posterior A와 Posterior B의 샘플을 비교해서 B보다 A가 클 경우의 비율을 계산해 낼 수 있습니다. 자, 볼까요?

➎ Posterior Beta 분포의 샘플을 만들어서 AB테스트의 암산결과를 대신해 보시죠.

from scipy import stats 
import numpy as np

installA = 600  # A 설치 
installB = 612  # B 설치

retentionA = 50  # A 재방문 (성공) 
retentionB = 56  # B 재방문 (성공)

# Prior parameter
alpha = 1  
beta  = 1   
samples = 1000

# 각 분포에 대한 샘플을 만듦
posterior_samplesA = stats.beta(alpha+retentionA,beta+installA-retentionA).rvs(samples)
posterior_samplesB = stats.beta(alpha+retentionB,beta+installB-retentionB).rvs(samples)

# 전체 샘플중 B가 A보다 큰 경우의 수 / 전체 샘플 수
(posterior_samplesA < posterior_samplesB).sum()/len(posterior_samplesA < posterior_samplesB)

>
0.686

 

B가 A보다 클 확률은 68.6% 정도로 볼 수 있겠습니다. 이 값은 샘플을 이용한 것이기 때문에 할 때마다 조금씩 다르긴 하니까 그 점은 참고해 주세요. 참고로 이거 잘 보니까 사실상 전체 샘플을 하나씩 서로 비교한 후에 B가 A보다 큰 경우의 값들의 합을 B가 A보다 큰 경우의 수를 나누니까 평균이라는 의미와 같군요. 그러면 

print((posterior_samplesA < posterior_samplesB).mean())
>
0.686

 

이런 식으로 평균을 이용해서 계산할 수도 있겠습니다. 이건 팁이니까 알아두면 편리합니다. 헤헤.

➏ 마지막으로 암산없이 애초에 하고 싶었던 샘플 시뮬레이션을 통해 얻은 A와 B안의 비교를 통해서 확률적 의사결정을 해 보죠.

코드는 다음과 같습니다. 

install = [600, 612] # 설치 수
retention = [50, 56] # 재방문자 수 

def bayesian_model(parameters, i):
    return np.random.binomial(install[i], parameters, 1)

for i in range(2) :
    sim_data = np.empty(n_draws)
    for j in range(n_draws):
        sim_data[j] = bayesian_model(prior[j], i)
    posterior.append(prior[sim_data == success[i]])

min_length = min(len(posterior[0]), len(posterior[1]))
posterior[0] = posterior[0][:min_length]
posterior[1] = posterior[1][:min_length]

 

이렇게 하면 posterior[0]에는 A 안이, posterior[1]에는 B 안이 담깁니다. 이걸 histogram으로 그려보면, 

 

어때요? 암산으로 했던 것과 얼추 비슷~해 보이는군요. 

이전에 했던 방법과 마찬가지로 B안이 A안보다 나을 확률을 구하는 것은 

(posterior[1]>posterior[0]).mean()
>
0.708118556701031

 

이런 방식으로 계산할 수 있겠습니다. 이전에 계산했던 68.6%와 거의 비~슷하군요. 

두서없이 좀 복잡하긴 했지만, 여러 가지 해 본 것들을 정리해 보면, 

시뮬레이션을 통해서 Posterior를 만들어내는 것과 Posterior를 만들어 낸 후에 그것에 대한 신용구간도 계산해 낼 수 있었고, A, B 두 안에 대해서 Posterior 비교를 통해서 A/B 테스팅도 해 봤습니다요. 뭐 요 정도 해 보면 베이지안에 대해서 꽤나 자신감이 붙지 않았을까 생각합니다. 베이지안 A/B테스트는 이런 식으로 흘러갑니다. 

베이지안의 교훈은 일반적으로 어렵게 생각될 수 있을 수도 있겠지만, 그렇게 생각만큼 딱딱하지만도 않습니다. 

MAB(멀티암드밴딧)이라는 것도 있는데 말이죠, 그건 그것대로 이제는 각자 그럭저럭 이해할 수 있는 수준이라고 생각하고 은근슬쩍 넘어가려고 합니다. 

친절한 데이터 사이언스 강좌 글 전체 목차 (정주행 링크) -



댓글





친절한 데이터 사이언스 강좌 글 전체 목차 (정주행 링크) -