A/B 검정은 통계 가설 검정을 상업적 영역으로 확대한 것으로 구글에 의해 일반화 되었고, 2011년 기준 구글에서 7,000 가지가 넘는 A/B 검정을 돌리는 것으로 보고되고 있다.
웹사이트를 개발할 때, 두가지 실험군(treatment)과 대조군(control)으로 나눠 실험군에는 대조군과 한가지만 다른 설정을 하고 유입되는 고객을 임의로 예를 들어 50:50으로 나눠 배치한다. 그리고 나서 CTR(Click-Through-Rate)을 비교하여 광고 연결률의 유의미한 차이를 검정한다.
좀더 나은 웹사이트, 광고 캠페인, 버튼 위치, 매출 전략등 다양한 아이디어가 많고, 특정 아이디어는 다른 아이디어 보다 더 좋다. 그럼 특정 아이디어가 다른 아이디어보다 더 좋다는 것을 어떻게 선별할 수 있을까?
A/B 검정을 통한 전통적인 주제는 다음과 같다.
게임에 있어 A/B 검정 최적화 주제로 검토가능한 것은 다음과 같다.
A/B 검정 작업흐름은 실험에 대한 주제선정과 새로운 아이디어를 도출하는 것부터 시작한다. 실험 실행을 하기 전에 먼저 “실험전 기준값 설정”과 필요한 검정력 분석도 사전에 실시해야 한다. 사전 준비가 끝나면 실험을 실행하고 나서 차곡차곡 쌓이는 데이터를 통해 실험결과를 통계적으로 분석해야 한다. 통계분석은 탐색적 데이터분석과 더불어 통계검정도 병행하여 실시한다. 그리고 나서, 데이터 분석에 대한 결과를 통해 처음 생각해 낸 아이디어가 제대로 먹혔는지 반추하고 나서 아이디어를 갱신한 후에 또 다른 실험을 기획한다. 이 과정을 반복적으로 돌려 최선의 결과가 나오도록 과정을 반복한다.
효과적인 광고집행을 통해 사업목표 달성에 추진력을 갖을 수 있다. 온라인 사업분야에서 광고를 활용할 수 있는 도구는 단계별로 구분되어진다.
\(LTV = \begin{equation} \sum_{t=1}^{N} \frac{(R_t -C_t) \times r_t}{(1+i)^t} -\text{AC} \end{equation}\)
A/B 검정을 구성하는 요소는 다음과 같다.
광고 연결률(CTR, click through rates)을 예측하는데 디바이스, 날짜/시간, 웹사이트, 네트워크 연결, 배너 배치, 앱 정보등을 수집하여 이를 바탕으로 광고 클릭을 연결과 실패로 구분하여 예측한다.
카지노에 슬롯머신이 쭉 배치되어 있는데, 각 슬롯머신 별로 승률이 달리 정해져 있지만 승률은 알지 못한다. 한정된 예산을 가지고 가장 많은 돈을 벌기 위해서 어떤 전략을 취하면 될까? 결국 탐색과 획득 딜레마(Explore/Exploit Dilemma)를 통해 우선 가장 승률이 좋은 슬롯머신을 찾아내는 과정과 본격적으로 돈을 버는 과정을 거치게 된다. 한정된 탐색과 획득과정 모두 비용과 시간이 들여 가장 많은 돈을 벌게 되는 최적화 문제를 푸는 것으로 귀결된다.
2차 세계대전 당시 다중 슬롯머신 문제가 매우 중요하지만 수학적으로 어려워서 이 문제를 잘 정리해서 독일에 전달하여 연구자원을 분산시키자고 Peter Whittle이 주장하기도 했다. 하지만, 오프라인에서는 탐색 비용이 매우 커서 비현실적이지만, 온라인 인터넷서비스에서는 상대적으로 비용이 적게 소요되어 밴딧 알고리즘이 A/B 검정의 한계를 극복하는 대안으로 구글, 야후 등에서 활발히 활용되고 있다. 밴딧 알고리즘은 실시간 온라인 학습(Online learning) 알고리즘이면서 동시에 강화학습과 같이 액티브 학습(Active Learning) 알고리즘이기도 하다.
머릿속으로 그리는 A/B 검정은 기존 A안에 새로운 대안을 넣어 그것이 더 좋은 결과를 얻은 경우 새로운 대안을 취하고, B안이 더 좋지 못한 결과를 얻게 되었을 경우 다시 A안으로 회귀하는 것이지만, A/B 검정 결과 더 좋은 것으로 나온 실험결과가 실제로 적용했을 경우 더 좋지 못한 결과가 되기도 한다. MAB 알고리즘은 A/B 검정과 달리 연속적으로 가장 좋은 대안을 찾아가는 방법론을 제시하고 있다.
웹사이트(예를 들어, http://www.korea.me)에서 많은 방문을 유도하고자 웹사이트 시안에서 고양이 사진과 강아지 사진을 두고 A/B 검정을 수행할 수 있다. 모든 조건이 동일하고 웹사이트에서 사진만 달리하고 이를 한동안 실험을 지속한다.
특정일을 기준으로 사용자별로 체류시간을 총합내고 좋아요
, 공유
, 기사
클릭을 했는지 기록한다. 또한, 체류시간
과 교락요인(Confounding effect)와 부작용(Side effect) 측정을 위해서 페이지 로딩시간과 체류시간도 함께 기록한다. 이를 바탕으로 A/B 검정의 효과를 추정한다.
# 0. 환경설정 ----
library(tidyverse)
library(DT)
library(lubridate)
library(extrafont)
library(broom)
loadfonts()
# 1. 데이터 ----
ab_df <- read_csv("data/website_sample.csv")
ab_df %>%
sample_n(10) %>%
datatable()
월별 좋아요 클릭율 (like_click
)을 산출하고 이를 시각화하여 추세를 살펴본다.
# 2. 탐색적 데이터 분석 ----
ab_df %>%
mutate(day = day(date)) %>%
group_by(day) %>%
summarise(conversion = mean(like)) %>%
ggplot(aes(x=day, y=conversion)) +
geom_line() +
geom_point(size=2) +
scale_y_continuous(limits = c(0,0.2), labels = scales::percent) +
labs(x="일자별", y="전환율(%)", title="강아지/고양이 사진 A/B 검정 일별 전환율 추세",
color="A/B검정") +
theme_minimal(base_family = "NanumGothic")
한달 동안 실험한 데이터를 불러와서 실험군(test)과 대조군(control) 유의적인 차이가 있는지 즉, 개와 고양이를 바꾼 사진의 차이가 전환율을 높이는데 기여를 했는지 통계적 데이터 분석을 통해 확인한다.
실험군(test)과 대조군(control) 두 집단을 담고 있는 testing
에 따라 전환율을 산출해본다.
# 3. A/B 검정: 범주형 -----
## 3.1. 기술 통계량 -----
ab_df %>%
group_by(testing) %>%
summarise(conversion = mean(like))
# A tibble: 2 x 2
testing conversion
<chr> <dbl>
1 control 0.0441
2 test 0.137
실험군(test)과 대조군(control) 두 집단을 담고 있는 testing
에 따라 전환율을 일자별로 시각화해서 시각으로 다시 한번 확인한다.
## 3.2. 시각화 -----
ab_df %>%
mutate(day = day(date)) %>%
group_by(day, testing) %>%
summarise(conversion = mean(like)) %>%
ggplot(aes(x=day, y=conversion, color=testing, group=testing)) +
geom_line() +
geom_point(size=2) +
scale_y_continuous(limits = c(0,1), labels = scales::percent) +
labs(x="일자별", y="전환율(%)", title="강아지/고양이 사진 A/B 검정 일별 전환율 추세",
color="A/B검정") +
theme_minimal(base_family = "NanumGothic")
t.test()
함수로 통계검정을 수행해도 되지만, 일반화 선형모형(GLM)을 통해 두집단간의 차이가 유의적인 차이가 있는지를 확인한다.
## 3.3. 통계검정 -----
glm(like ~ testing, family = "binomial", data = ab_df) %>%
tidy()
term estimate std.error statistic p.value
1 (Intercept) -3.075924 0.1007672 -30.52505 1.212265e-204
2 testingtest 1.234924 0.1175121 10.50891 7.859969e-26
좋아요, 공유, 해당 글을 클릭했는지 않았는지 못지않게 페이지 체류시간 혹은 결재금액 등 연속형 변수를 검정하는 경우도 있다. 두집단일 경우 t.test
혹은 동일집단 A/A 검정일 경우 짝(paired) t-검정이 되기도 하고, 실험을 두개이상 진행하는 경우 분산분석 모형이 되기도 하지만 동일하게 일반화 선형모형을 사용한다.
실험군(test)과 대조군(control) 두 집단을 담고 있는 testing
에 따라 전환율을 산출해본다.
# 4. A/B 검정: 연속형 -----
## 4.1. 기술 통계량 -----
ab_df %>%
group_by(testing) %>%
summarise(time_spent = mean(time_spent))
# A tibble: 2 x 2
testing time_spent
<chr> <dbl>
1 control 7.00
2 test 6.10
실험군(test)과 대조군(control) 두 집단을 담고 있는 testing
에 따라 체류시간변화를 일자별로 시각화해서 시각으로 다시 한번 확인한다.
## 4.2. 시각화 -----
ab_df %>%
mutate(day = day(date)) %>%
group_by(day, testing) %>%
summarise(time_spent = mean(time_spent))%>%
ggplot(aes(x=day, y=time_spent, color=testing, group=testing)) +
geom_line() +
geom_point(size=2) +
labs(x="일자별", y="체류시간(분)", title="강아지/고양이 사진 A/B 검정 일별 체류시간",
color="A/B검정") +
theme_minimal(base_family = "NanumGothic")
범주형 변수에 대한 회귀분석 대신 연속형 변수 체류시간(time_spent
)에 대한 회귀분석을 실시한다.
## 4.3. 통계검정 -----
glm(time_spent ~ testing, family = "gaussian", data = ab_df) %>%
tidy()
term estimate std.error statistic p.value
1 (Intercept) 6.9954405 0.01572796 444.77737 0.000000e+00
2 testingtest -0.8948908 0.02229068 -40.14641 7.820734e-303
A/B 검정의 효과를 베이지안 시계열 팩키지를 활용하여 계량화한다. CausalImpact 팩키지에 나온 예제는 ARIMA 모형에서 표본을 추출하여 ‘2014-01-01’ 이후 100일을 기간으로 놓고, 70 일을 사전 기간, 30 일을 사후 기간을 비교하여 CausalImpact
함수에 넣어 A/B 검정 사전,사후 기간 효과를 시각화하고 보고서로 요약한다.
# 0. 환경설정----------------------------------------------
# install.packages("BoomSpikeSlab") # CausalImpact 의존성으로 사전 설치
# devtools::install_github("google/CausalImpact")
library(CausalImpact)
library(xts)
# 1. 데이터 생성----------------------------------------------
set.seed(1)
x1 <- 100 + arima.sim(model = list(ar = 0.999), n = 100)
y <- 1.2 * x1 + rnorm(100)
y[71:100] <- y[71:100] + 10
data <- cbind(y, x1)
time_points <- seq.Date(as.Date("2014-01-01"), by = 1, length.out = 100)
data_zoo <- zoo(data, time_points)
head(data_zoo)
y x1
2014-01-01 105.2950 88.21513
2014-01-02 105.8943 88.48415
2014-01-03 106.6209 87.87684
2014-01-04 106.1572 86.77954
2014-01-05 101.2812 84.62243
2014-01-06 101.4484 84.60650
# 2. A/B 검정 기간 설정----------------------------------------------
pre_period <- as.Date(c("2014-01-01", "2014-03-11"))
post_period <- as.Date(c("2014-03-12", "2014-04-10"))
# 3. A/B 검정 효과 분석----------------------------------------------
impact <- CausalImpact(data_zoo, pre_period, post_period)
plot(impact)
summary(impact, "report")
Analysis report {CausalImpact}
During the post-intervention period, the response variable had an average value of approx. 117.05. By contrast, in the absence of an intervention, we would have expected an average response of 106.53. The 95% interval of this counterfactual prediction is [105.80, 107.26]. Subtracting this prediction from the observed response yields an estimate of the causal effect the intervention had on the response variable. This effect is 10.52 with a 95% interval of [9.78, 11.25]. For a discussion of the significance of this effect, see below.
Summing up the individual data points during the post-intervention period (which can only sometimes be meaningfully interpreted), the response variable had an overall value of 3.51K. By contrast, had the intervention not taken place, we would have expected a sum of 3.20K. The 95% interval of this prediction is [3.17K, 3.22K].
The above results are given in terms of absolute numbers. In relative terms, the response variable showed an increase of +10%. The 95% interval of this percentage is [+9%, +11%].
This means that the positive effect observed during the intervention period is statistically significant and unlikely to be due to random fluctuations. It should be noted, however, that the question of whether this increase also bears substantive significance can only be answered by comparing the absolute effect (10.52) to the original goal of the underlying intervention.
The probability of obtaining this effect by chance is very small (Bayesian one-sided tail-area probability p = 0.001). This means the causal effect can be considered statistically significant.
A/B 실험을 진행하고자 할 때 표본크기를 설정해야한다. 표본크기는 검정력(Statistical Power)와 밀접한 연관이 있다.
Power Calculations – relationship between test power, effect size and sample size 내용을 참조하여 검정력(power), 유의수준(Significance Level), 효과크기(Effect Size)에 따른 표본크기를 살펴보고 나서 가장 많이 사용하는 검정력 0.8(\(1-\beta\)), 유의수준 \(\alpha\) 0.05, 중간 효과크기 0.5를 두고 표본크기도 산출한다.
효과크기, 검정력, 유의수준을 정의하고 나서 pwr
팩키지 power.t.test()
함수를 사용해서 표본크기를 계산한다. 표본크기가 효과크기, 검정력, 유의수준에 따라 어떤 변화가 있는지 살펴보고 표본크기를 효과적으로 증가시키기 위한 지표로 사용한다. stackoverflow, Power.t.test error 오류에 대해서 작업이 필요하여 try()
함수로 간단히 처리하고 표본크기를 후처리한다.
library(pwr)
# 1. 검정력 데이터 생성 ----
d <- seq(.1, 2, by=.1) # 효과크기(Effect Size)
power <- seq(.1, 0.9, by=.1) # 검정력(Power)
alpha <- c(0.01, seq(0.05, 0.5, 0.05)) # 유의수준(Significance Level)
sample_size_df <- expand.grid(power=power, effect_size=d, alpha=alpha) %>%
tbl_df
for(i in 1:nrow(sample_size_df)) {
sample_size_df$sample_size[i] <- try(power.t.test(n = NULL,
d = sample_size_df$effect_size[i],
sig.level= sample_size_df$alpha[i],
power = sample_size_df$power[i],
type = "two.sample")$n)
}
# https://stackoverflow.com/questions/33379962/power-t-test-error
sample_size_df <- sample_size_df %>%
mutate(sample_size = as.integer(sample_size))
유의수준과 효과크기를 고정한 후에 검정력을 강화할수록 표본크기도 증가한다.
# 2. 검정력 ----
sample_size_df %>%
filter(alpha == 0.05, effect_size == 0.5) %>%
ggplot(aes(x=power, y=sample_size)) +
geom_line() +
geom_point() +
theme(legend.position = "none") +
labs(x="검정력(Power)", y="표본크기(Sample Size",
title="검정력 강화에 따른 표본크기 변화",
subtitle="유의수준(0.05)과 효과크기(0.5)를 고정") +
theme_minimal(base_family = "NanumGothic")
효과크기와 검정력을 고정한 후에 유의수준을 높게할수록 표본크기는 감소한다.
# 2. 유의수준 ----
sample_size_df %>%
filter(effect_size == 0.5, power == 0.80) %>%
ggplot(aes(x=alpha, y=sample_size)) +
geom_line() +
geom_point() +
theme(legend.position = "none") +
labs(x="유의수준(Significance Level)", y="표본크기(Sample Size",
title="검정력과 효과크기를 고정한 뒤 유의수준별 표본크기",
subtitle="유의수준(0.05)과 효과크기(0.8)를 고정") +
theme_minimal(base_family = "NanumGothic")
유의수준과 검정력을 고정한 후에 효과크기를 높게 할수록 표본크기는 감소한다.
# 3. 효과크기 ----
sample_size_df %>%
filter(alpha == 0.01, power ==0.8) %>%
ggplot(aes(x=effect_size, y=sample_size, group=1)) +
geom_line() +
geom_point() +
theme(legend.position = "none") +
labs(x="효과크기(Effect Size)", y="표본크기(Sample Size",
title="검정력과 유의수준을 고정한 뒤 효과크기별 표본크기",
subtitle="유의수준(0.05)과 검정력(0.8)를 고정") +
theme_minimal(base_family = "NanumGothic")
큰 효과크기에 필요한 표본크기
pwr.t.test(power = 0.8,
sig.level = 0.05,
d = 0.8)
Two-sample t test power calculation
n = 25.52458
d = 0.8
sig.level = 0.05
power = 0.8
alternative = two.sided
NOTE: n is number in *each* group
작은 효과크기에 필요한 표본크기
pwr.t.test(power = 0.8,
sig.level = 0.05,
d = 0.1)
Two-sample t test power calculation
n = 1570.733
d = 0.1
sig.level = 0.05
power = 0.8
alternative = two.sided
NOTE: n is number in *each* group
순차분석은 데이터를 수집하면서 반복적으로 유의성 통계검정을 수행하는 절차로 다음 세가지 중 하나를 택일한다.
gsDesign 팩키지를 사용해서 순차분석을 통해 실험에 대한 결론도 도출하고 표본크기도 산정한다.
k = 4
는 3번 중간 분석을 수행하고 마지막 4번째 모든 데이터를 가지고 분석을 수행한다. test.type
은 단축, 양측 검정 등을 설정하는 것이고, alpha
는 유의수준, beta
는 검정력과 관계되어 0.2는 검정력이 0.8을 나타내고 sfu
는 Spending 함수로 여러 방법론이 있지만, Pocock
을 지정하여 순차분석을 수행한다.
최대 가능 표본수가 1000개라고 가정하면 두집단이 되어 이를 seq_analysis$timing
에 반영하면 중간중간 살펴봐야하는 표본을 산출할 수 있다.
library(gsDesign)
# 1. 중단 규칙(stopping rule) ----
seq_analysis <- gsDesign(k = 4,
test.type = 2,
alpha = 0.05,
beta = 0.2,
sfu = "Pocock")
max_n <- 1000
max_n_per_group <- max_n / 2
stopping_points <- max_n_per_group * seq_analysis$timing
stopping_points
[1] 125 250 375 500
plot(seq_analysis)