최근에 데이터를 활용한 의사 결정이나 서비스가 확산되면서, 주로 웹서비스에서 사용되던 A/B 테스트가 여러 분야로 활용 범위를 넓히고 있다. A/B 테스트 결과를 분석할 때 가장 널리 사용하는 방법은 귀무 가설을 이용한 통계 유의성 검정 기법이다. 피셔에 의해 개발된 이 기법은 가장 널리 사용되는 검정 기법이지만 동시에 여러 가지 문제점이 있어서 최근에는 그 대안으로 베이지안 통계 기법을 사용하는 움직임이 점점 커지고 있다.
그런데 통계 유의성 검정 방법에 대해서는 정리된 글도 많고 실제 구현된 함수나 코드가 많이 있는 반면 베이지안 통계 기법에 대한 자료는 상대적으로 빈약하며 특히 A/B 테스트에서 베이지안 기법을 이용하는 방법에 대해 잘 구현된 코드나 정리된 결과물을 찾기가 힘들다. 그나마 [1]이나 [2]와 같은 자료가 비교적 잘 설명되어 있긴 하지만 수식 유도 위주로 설명이 되어 있어 이해하기가 어렵고 특정 케이스에 대해서만 구현 코드가 나와 있어서 실전에 바로 사용하기 적절하지 않다.
그래서 이 글에서는 우선 A/B 테스트 및 통계 유의성 검정 기법에 대해 간략히 소개를 한 후 A/B 테스트에서 베이지안 통계 기법을 이용해서 결과를 확인하는 방법에 대해 구현 방법 위주로 정리하였다.
A/B 테스트는 유저들을 임의로 분류하여 각 그룹에게 서로 다른 상황을 제시하고 그 반응을 확인하는 실험 기법을 말한다. 주로 웹 서비스에서 많이 사용하던 방법인데, 예를 들어 웹 페이지 디자인 시안이 두 개가 있을 때 어떤 유저들에게는 A 시안의 화면을 보여주고 또다른 유저들에게는 B 시안의 화면을 보여준 후 어떤 화면에 더 잘 반응하는지를 측정하는 것이다. 오바마 대통령의 경우 선거 기간 동안 광고 및 홈페이지 디자인에 A/B 테스트를 적극 도입하여 선거 자금 모금 및 지지율 향상에 효과를 봤다고도 알려져 있다.
A/B 테스트는 위에서 예로 든 웹 페이지 디자인 뿐만 아니라 다양한 분야에서 활용할 수 있다. 가령 구글이나 페이스북, 넷플릭스 등에서는 자신들의 추천 알고리즘을 개선할 때 개선된 버전이 기존 모델에 비해 실제 얼마나 효과가 좋은지를 평가하기 위해 A/B 테스트를 사용한다.
최근에는 모바일 게임이나 앱에서도 A/B 테스트를 도입하는 추세이다. 특히 모바일 게임의 경우 정식 출시 전에 일명 소프트론치(soft launch, 기존 PC용 온라인 게임의 CBT와 비슷한 개념으로 특정 지역이나 국가에만 제한적으로 게임을 출시하여 반응을 살펴보는 서비스)라고 부르는 기간을 두고 이 기간동안 제한된 유저에게 게임을 서비스하면서 다양한 A/B 테스트를 통해 게임 밸런스 조절이나 BM 상품 기획에 활용한다. 기존에는 기획자나 디렉터의 감을 믿고 여러 가지 사안들을 결정했다면, 이제는 A/B 테스트를 통해 데이터에 기반한 의사 결정을 하는 것이다(A/B 테스트에 대해 좀 더 자세히 알고 싶은 분은 [3]을 참고하기 바란다).
한편 테스트를 통해 측정하는 결과값은 크게 두 종류로 나눌 수 있는데, 하나는 흔히 '전환율(conversion rate)'라고 부르는 비율값이고 또 하나는 유저별로 측정된 '정량값'이다. 예를 들어 어떤 구매 아이템에 대해 A/B 테스트를 하기 위해 전체 유저를 A, B 두 그룹으로 나누고 서로 다른 형태의 아이템 패키지를 제시하는 경우를 생각해 보자. 그러면 이 테스트를 통해 우리는 '구매율'과 '구매 금액' 이라는 두 가지 종류의 데이터를 얻을 수 있다. 구매율이 앞서 설명한 '비율값'이고 구매 금액이 '정량값'에 해당한다.
A/B 테스트를 할 때는 어떤 목표를 세우게 되고 그 목표를 좀 더 잘 충족하는 그룹을 선택하는 것이 이 테스트의 목적이다. 앞의 예로 다시 돌아가 보면, 구매율을 높이고 싶은지 혹은 구매 금액을 높이고 싶은지를 테스트 전에 결정한 후 테스트 기간 동안 목표로 하는 값을 측정하게 된다. 그리고 테스트 결과를 평가할 때 이렇게 측정한 데이터의 종류에 따라 다른 통계 기법을 사용한다.
A/B 테스트를 수행하고 나면 어떤 그룹이 더 좋은지를 평가해야 한다. 얼핏 생각하기에는 단순히 결과 데이터를 비교하면 될 것 같다. 예를 들어 측정 대상이 전환율이라면 두 그룹의 전환율을 측정한 후 어느 그룹의 전환율이 더 높은지 비교하는 것이다. 만약 테스트 대상이 충분히 많고 두 그룹의 전환율 차이가 크다면 단순히 두 값을 비교하더라도 큰 문제는 없다. 하지만 두 그룹 간의 테스트 대상수가 차이가 나거나 전환율의 차이가 미미하다면 확률적으로 우연히 발생한 차이를 실제 어느 한 그룹이 더 효과가 좋은 것으로 오해할 수 있다.
가령 아무런 조작이나 문제가 없다면 동전을 던졌을 때 앞면과 뒷면이 나올 확률은 일반적으로 둘 다 50% 일 것이다. 하지만 실제 동전을 10번 정도 던져보면 정확히 앞면과 뒷면이 5번씩 나오는 경우보다는 어느 한쪽 면이 1~2번 더 많이 나오는 경우가 많다. 마찬가지로 위 예에 나온 두 그룹이 정말로 전환율에 차이가 있는 것인지 아니면 이런 우연한 오차로 인해 차이가 발생한 것인지 의심이 들 수 있다. 심지어 테스트 결과가 기존에 이미 적용된 사항을 수정 반영해야 하는 방향으로 나왔다면 전환에 따른 비용을 생각해 볼 때 좀 더 확실한 결정 기준이 필요할 것이다.
이런 문제를 해결하기 위해 널리 사용하는 방법이 귀무 가설을 이용한 통계 유의성 분석(Null Hypothesis Significance Testing)이다. 이것은 둘 혹은 그 이상의 그룹에 대해서 각 그룹의 분포가 같다는 가설(귀무가설)을 세우고 이 가설이 얼마나 맞는지를 측정하는 방식이다. 다시 말하면, 두 그룹이 실제로는 차이가 없는데 관측된 값처럼 차이가 발생할 확률을 계산한다(이것을 'p-value'라고 부른다). 만약 p-value가 매우 낮을 경우 귀무가설을 기각함으로써 두 그룹은 통계적으로도 차이가 있다고 판단한다.
예를 들어 아래와 같은 경우를 살펴 보자.
테스트 1)
테스트 그룹 실험 참가횟수 이벤트 전환횟수 전환율(%)
A 10000 3300 33
B 500 170 34
테스트 2)
테스트 그룹 실험 참가횟수 이벤트 전환횟수 전환율(%)
A 10000 3300 33
B 500000 160000 32
단순히 전환율만 비교하면 테스트 1)에서는 B그룹, 테스트 2)에서는 A그룹이 더 높은 전환율을 가지고 있으므로 해당 그룹에서 사용한 방법을 실전 서비스에 적용하면 된다. 그러나 실제 두 테스트에 대해서 통계 유의성 분석을 해보면 아래와 같은 결과가 나온다.
전통적으로 귀무 가설을 이용한 통계 유의성 검정에서는 p-value가 0.05(혹은 좀 더 보수적으로는 0.01)보다 작아야 통계적으로 유의미하다고 판단한다. 그런데 위 스크린샷을 보면 테스트 2)는 0.03으로 다소 낮은 값이 나온 반면 테스트 1)의 경우 0.05보다 훨씬 높은 값이 나왔다. 이것은 쉽게 말해 두 그룹의 차이가 없을 가능성이 높다는 뜻이다. 따라서 이런 경우에는 비록 어떤 한 그룹이 전환율이 더 높게 측정되더라도 실험 결과를 수용하지 않는 것이 좋다.
그런데 이렇게 귀무 가설에 의한 통계 유의성 검정 방법은 여러 가지 문제점을 갖고 있다. 이에 대해서는 이미 많은 통계 학자나 데이터 분석가들이 잘 정리해 놓은 글이 있기 때문에 다시 언급하는 것이 불필요해 보이지만 간략하게 언급하자면, 가장 큰 문제는 분석 결과가 이해하기 힘들고 직관적이지 못하다는 점이다.
실제 사용자가 A/B 테스트를 통해 알고 싶은 것은 '어떤 그룹이 더 좋은가?' 인데 통계 유의성 검정이 알려 주는 것은 '두 그룹의 분포가 같을 것이라는 귀무 가설을 기각해도 될까?' 이다. 한마디로 말해 (비록 관련성이 있긴 하지만) 원래 문제와 다른 문제에 대한 답을 찾는 것이다. 이는 직접적인 답을 찾기 힘든 상황에서 어쩔 수 없이 통계학자들이 택한 일종의 편법이다. 따라서 해당 내용을 잘 알고 있는 통계 전문가라면 상관없지만 그렇지 못한 사람들에게는 많은 오해를 줄 수 있다. 특히 흔히 하는 오해가 p-value의 의미를 마치 '이 실험 결과가 틀릴 확률'이라고 생각하는 것이다(물론 아니다). 따라서 많은 통계 전문가들이 이러한 p-value에 대한 오해의 심각성을 지적하고 있으며, 이 외에도 몇 가지 문제 때문에 극단적으로는 더 이상 p-value를 사용하지 말자는 주장도 나오고 있다(이와 관련해서는 [4]에 잘 정리되어 있다).
이렇듯 기존에 사용하던 통계 유의성 검정 방법의 문제점 때문에 최근에는 베이지안 확률을 이용한 A/B 테스트 평가 방법이 널리 퍼지고 있다. 이것은 베이지안 이론을 이용해서 어떤 그룹이 다른 그룹보다 실험 목표가 되는 측정치(구매율이나 구매금액)가 더 높게 나올 확률을 직접적으로 구하는 방식이다. 따라서 그 결과가 직관적이라는 장점이 있다.
통계학에서 확률을 계산하는 방법은 크게 두 가지가 있다. 하나는 모든 가능한 경우의 수(혹은 관찰된 전체 사건의 수)에서 원하는 경우의 수(혹은 확률을 계산하고자 하는 사건의 수)가 차지하는 비율이다. 동전의 앞면이 나올 확률이나 주사위에서 눈금 3이 나올 확률 등을 계산할 때 보통 이 방식을 이용해 계산한다. 이것을 '빈도주의 확률'이라고 부른다. 또 다른 방법은 베이즈 이론을 이용해 계산하는 확률이다. 베이즈 이론은 어떤 확률을 계산할 때 '사전 확률'과 '조건부 확률'을 이용해서 계산하는 방법인데 공식은 아래와 같다.
빈도주의 확률에 비해 수식도 복잡할 뿐더러 '사전 확률'이나 '조건부 확률' 같은 개념은 다소 생소한 개념이기 때문에 베이지안 확률을 직관적으로 이해하기는 어렵다. 그럼에도 불구하고 베이지안 이론이 널리 사용되는 이유는 (다소 어거지로 느껴질 수는 있겠지만) 어떤 경우에도 확률을 계산할 수 있기 때문이다.
가령 앞서 예로 든 동전 던지기를 생각해 보자. 내가 현재 어떤 동전을 갖고 있는데 이 동전을 던졌을 때 앞면이 나올 확률을 계산하고 싶다. 빈도주의 확률을 계산하려면 동전을 여러 차례 던진 후 앞면이 나온 횟수를 세보면 된다. 그런데 오차가 있을 수 있기 때문에 실제 확률이 50%라고 하더라도 앞면이 정확하게 시도 횟수의 절반 만큼 나오리라는 보장은 없다. 그럼 몇 번을 던져야 할까? 10번? 100번? 1000번? 설령 1000번을 던지더라도 앞면이 정확히 500번 나오지는 않을 것이다. 다만 10번을 던졌을 때 보다는 근사적으로 절반에 가까운 확률이 나올 것이다. 반대로 얘기해서 시행 횟수가 매우 적다면 정확한 확률을 구하는 것이 불가능하다. 따라서 빈도주의에서는 시행 횟수가 충분히 많아야 정확한 확률을 구할 수 있다. 그래서 빈도주의 통계기법에서는 시행 횟수에 따라 현재 구한 확률이 얼마나 실제 확률에 가까운지를 추정하는 신뢰 구간이나 p-value 같은 수치를 따로 측정해서 제시한다(2장에서 소개한 통계 유의성 분석이 바로 이 빈도주의 확률을 이용한 방법이다).
반면 베이지안 확률에서는 매우 적은 시도만으로도 확률을 비교적 정확히 계산할 수 있다. 단, 이를 위해선 사전 확률을 잘 정해야 한다. 물론 우리는 정확한 사전 확률을 모르기 때문에 이건 말그대로 이상적인 조건이다. 그럼에도 불구하고 이 방법이 쓸만한 이유는 실전에서는 많은 시도는 할 수 없지만 사전 확률은 대략적으로 추정할 수 있는 상황이 그 반대의 상황인 아무런 배경 지식이 없이 많은 시도를 할 수 있는 상황에 비해 더 많기 때문이다.
위 동전 던지기 역시 마찬가지이다. 우리는 동전을 던졌을 때 앞면이 나올 확률이 대개의 경우 50% 라는 것을 이미 알고 있다. 따라서 내가 지금 실험하려는 동전에 대해서 굳이 100번을 던질 필요없이 몇 번만 던져보면 이 동전의 앞면이 나올 확률을 비교적 정확하게 계산할 수 있다.
심지어 빈도주의 확률로는 계산할 수 없는 확률까지도 계산할 수 있다. A/B 테스트에서 구하고자 하는 확률도 바로 이런 예 중 하나이다(그래서 빈도주의에서는 직접적으로 확률을 계산하는 대신 유의성 검정이라는 기법을 사용하는 것이다).
먼저 전환율에 대해서 A/B 테스트의 실험 그룹들에 대한 베이지안 확률을 계산하는 방법을 살펴 보겠다. 2장에서 예로 든 테스트 1) 을 다시 생각해 보자.
테스트 1)
테스트 그룹 실험 참가횟수 이벤트 전환횟수 전환율(%)
A 10000 3300 33
B 500 170 34
여기서 구하려는 값은 각 그룹별로 '어떤 그룹의 전환율이 다른 그룹보다 높을 확률'이다. 즉, 'A그룹의 전환율이 B그룹보다 높을 확률'과 'B그룹의 전환율이 A그룹보다 높을 확률'이다. 이것을 각각 P(A>B), P(B>A)라고 하겠다. 빈도주의 확률에서는 이것을 구할 수 없다. 하지만 베이지안 확률에서는 아래와 같은 공식을 통해 확률을 추정할 수 있다.
위 식에서 P(a|A) 와 P(b|B) 는 각각 'A그룹과 B그룹의 테스트 결과를 관찰한 상태에서 전환율 a, b가 발생할 조건부 확률'을 의미한다. 이것은 R에서 dbeta 함수와 pbeta 함수를 이용해서 아래와 같이 구할 수 있다.
prob.a.better.b <- integrate(function(x) {
dbeta(x, ca+1, ta-ca+1) * pbeta(x, cb+1, tb-cb+1)
}, 0, 1)
prob.b.better.a <- integrate(function(x) {
dbeta(x, cb+1, tb-cb+1) * pbeta(x, ca+1, ta-ca+1)
}, 0, 1)
여기서 ta와 ca는 각각 A그룹의 실험 참가횟수와 이벤트 전환횟수를, tb와 cb는 B그룹의 실험 참가횟수와 이벤트 전환횟수를 의미한다. 그리고 integrate 는 적분함수인데 첫번째 인자로 주어진 함수에 대해서 두번째와 세번째 인자 값의 범위 내에서 적분을 계산한다. 만약 테스트 그룹이 A와 B외에 더 있다면 추가되는 그룹에 대해서 pbeta 함수를 추가로 곱해주면 된다. 위 방법을 이용하여 최종적으로 전환율에 대한 각 그룹별 베이지안 확률을 계산하는 R 코드는 아래와 같다.
bayesian.stat.for.rate <-function (data) {
k <- nrow(data)
result <- numeric(k)
for (i in (1:k)) {
idx <- (1:k)[-i]
f <- function(z) {
r <- dbeta(z,
data[i, 'conversion'] + 1,
data[i, 'trial'] - data[i, 'conversion'] + 1)
for (j in idx) {
r <- r * pbeta(z,
data[j, 'conversion'] + 1,
data[j, 'trial'] - data[j, 'conversion'] + 1)
}
return(r)
}
result[i] = integrate(f, 0, 1)$value
}
return(result)
}
위 코드를 이용해서 테스트 1)과 2)에 대해서 각 그룹별 확률을 계산하면 아래와 같이 나온다.
테스트 1)
테스트 그룹 실험 참가횟수 이벤트 전환횟수 전환율(%) 베이지안확률(%)
A 10000 3300 33 31
B 500 170 34 68
테스트 2)
테스트 그룹 실험 참가횟수 이벤트 전환횟수 전환율(%) 베이지안확률(%)
A 10000 3300 33 98
B 500000 160000 32 2
테스트 1)에서는 비록 B가 더 전환율이 높긴 하지만 정말로 A그룹보다 전환율이 높을 확률이 68%에 불과한 반면 테스트 2)의 경우에는 A그룹의 전환율이 B보다 더 높을 확률이 98%나 된다. 한 가지 눈여겨 봐야할 점은 결과적으로 (정확하게 해석만 한다면) 통계 유의성 검정에 의한 판단 결과와 동일한 결론을 내리게 된다는 점이다. 실제 빈도주의 확률과 베이지안 확률은 적절한 조건(예를 들면, 충분한 시행 횟수)만 주어지면 동일한 결과가 나온다. 다만 앞서 언급했듯이 베이지안 확률은 p-value를 이용한 유의성 검정에 비해 좀 더 직관적이며 오용할 가능성이 더 낮다.
정량값에 대한 확률 계산도 기본적인 개념은 5.2의 전환율 공식과 거의 유사하다. 한가지 차이가 있다면 5.2에서는 비율이기 때문에 이항 분포에 기반하여 확률을 계산했다는 점과 적분을 할 때 0~1까지의 범위로 계산을 했다는 점이다.
정량값의 경우 해당 테스트 데이터의 확률 분포에 기반한 계산이 필요하다. 여기서는 단순함을 위해 측정하는 데이터가 정규분포라고 가정하겠다. 정규분포로 가정할 경우 위에서 사용한 dbeta 와 pbeta 함수 대신 dnorm 과 pnorm 함수를 사용하면 된다. 그리고 이에 맞게 입력 데이터는 시행 횟수와 전환 횟수 대신 각 그룹에서 측정한 값의 평균과 표준편차가 필요하며 마지막으로 적분을 할 때는 0 ~ 1 대신 -Inf ~ Inf 를 주어야 한다(각각 R에서 음의 무한대와 양의 무한대값을 의미한다). 이렇게 수정된 계산 코드는 아래와 같다.
bayesian.stat.for.value <-function (data) {
k <- nrow(data)
result <- numeric(k)
for (i in (1:k)) {
idx <- (1:k)[-i]
f <- function(z) {
r <- dnorm(z, data[i, 'mean'], data[i, 'std.dev'])
for (j in idx) {
r <- r * pnorm(z, data[j, 'mean'], data[j, 'std.dev'])
}
return(r)
}
result[i] = integrate(f, -Inf, Inf)$value
}
return(result)
}
위 두 개의 R코드는 간단한 테스트 상황에서는 잘 동작하지만 실전에서 사용할 때 두 가지 문제가 있다. 첫째, 전환율을 계산할 때 시행 횟수와 전환 횟수가 매우 큰 경우에는 integrate 함수가 정확한 적분값을 계산하지 못한다. 이것은 정밀도 문제 때문에 생기는 현상인데 가령 아래와 같은 경우 bayesian.stat.for.rate 함수는 엉뚱한 값을 출력한다.
비록 integrate 함수에는 rel.tol 과 abs.tol 이라는 파라미터가 있어서 이를 통해 정밀도를 개선할 수 있긴 하지만 한계가 있다. 따라서 실전에서 만약 수십만 명이 넘는 유저에 대해서 테스트를 수행한다면 위 코드로는 정확한 베이지안 확률을 계산하지 못한다. 이 문제를 개선하기 위한 방법 중 하나로 적분을 직접 구현하는 방법이 있다. 아래 코드는 integrate 함수 대신 적분을 직접 구현한 코드이다.
bayesian.stat.for.rate <-function (data, resolution = 0.001) {
k <- nrow(data)
result <- numeric(k)
for (i in (1:k)) {
idx <- (1:k)[-i]
f <- function(z) {
r <- dbeta(z,
data[i, 'conversion'] + 1,
data[i, 'trial'] - data[i, 'conversion'] + 1)
for (j in idx) {
r <- r * pbeta(z,
data[j, 'conversion'] + 1,
data[j, 'trial'] - data[j, 'conversion'] + 1)
}
return(r)
}
#result[i] = integrate(f, 0, 1)$value
xs <- seq(0, 1, resolution)
temp <- numeric(length(xs))
for (x in 1:length(xs)) {
temp[x] <- f(xs[x])
}
result[i] <- mean(temp)
}
return(result)
}
위 코드를 이용해서 아까 오류가 있었던 데이터를 다시 계산해 보면 <그림 5>와 같이 정상적으로 확률이 계산된다. 참고로 수정된 코드에 추가된 파라미터인 resolution의 값을 더 작게할수록 정밀도가 향상되기 때문에 더 정확한 확률이 계산되지만 대신 계산하는데 걸리는 시간은 더 길어진다.
또 다른 문제점은 정량값 확률을 계산하는 bayesian.stat.for.value 함수의 문제인데, 만약 확률을 계산할 그룹의 정량값의 분포가 정규분포와 크게 다르다면(예를 들어 멱함수 분포) 확률이 부정확하게 계산될 수 있다. 이 문제를 해결하려면 해당 그룹의 데이터 분포에 맞게 dnorm과 pnorm 함수 호출 부분을 수정해줘야 한다. 특히 만약 데이터의 분포가 모수적 방법으로 표현할 수 없는 분포라면 비모수적 기법(non-parametric method)을 이용해 확률 분포를 계산해 줘야 한다(이에 대해서는 차후에 따로 다루도록 하겠다).
A/B 테스트는 데이터에 기반한 의사 결정의 가장 대표적인 사례로써 널리 사용되고 있다. 특히 최근에는 기존의 통계 유의성 검정에 기반한 테스트 방법의 문제점을 극복하기 위해 베이지안 기법을 도입하는 추세이다. 이 글에서는 베이지안 A/B 테스트의 기본 개념 및 구현 방법에 대해 개략적으로 정리하였다.
한편, 이미 A/B 테스트를 적극적으로 도입하여 활용하던 업계 전문가를 중심으로 이것의 문제점이나 한계점을 지적하며 그 대안을 제시하는 경우가 있다. 이에 대해서는[5]와 [6]을 참고하기 바란다.
[1] http://www.evanmiller.org/bayesian-ab-testing.html
[2] http://engineering.richrelevance.com/bayesian-ab-tests/
[3] http://www.boxnwhis.kr/2015/01/29/a_b_testing.html
[4] http://www.ibric.org/myboard/read.php?Board=news&id=270293
[5] http://www.ecogwiki.com/A-B_Testing%EC%9D%98_%EB%8B%A8%EC%A0%90%EA%B3%BC_%EA%B0%9C%EC%84%A0%EC%95%88