読者です 読者をやめる 読者になる 読者になる

廿TT

譬如水怙牛過窓櫺 頭角四蹄都過了 因甚麼尾巴過不得

検定いらずの AB テスト:φ 係数を用いたサンプルサイズ設計

AB テストと呼ばれるものや、AB テストをやる目的は様々だろう。

ここではウェブ系施策において、「A と B を比べてみて、どっちか良さそうなほうを使いたい」という目的で行われる AB テストで、かつ CTR(クリック率)とか CVR(コンバージョンレート)とかの比率を比べるものに対象を限定して議論する。

この場合、統計的仮説検定はテストの評価に不向きだとぼくは思う。

検定は A と B を対等に評価するものではないからだ。

たとえば、こんなシミュレーションをやってみる。

今使ってるのはランディングページ A で、もしランディングページ B のほうがコンバージョン率(CVR)が高いなら、そっちに変えたいなと思っている。

これに対して有意水準 5% で比率の差の検定(カイ二乗検定)を行う。

対立仮説は A < B にしたいので片側検定にしておく。

ランディングページ A は真のコンバージョン率が 2%、ランディングページ B は真のコンバージョン率が 3%だとする。

ランディングページ訪問者の述べ人数はどちらも 1000 としよう。

このシミュレーションを 1 万回やるコードを R で書くとこうなる。

set.seed(123)
ss <-1000
CV_A <- rbinom(10000,ss,0.02)
CV_B <- rbinom(10000,ss,0.03)
test1 <-mapply(function(a,b){prop.test(c(a,b),c(ss,ss),alternative ="less")$p.value},CV_A,CV_B)

結果、帰無仮説が棄却された割合は 35% だった。

> round(mean(test1<0.05),2)
[1] 0.35

有意になったときだけランディングページ B を採用するとしたら、本当は B のほうがコンバージョン率が高いのに、65% の割合で A を選ぶことになる。

一方、検定とかしないで、単純に A のコンバージョン率より B のコンバージョン率が大きければ B を採用するという方針で AB テストをやったとする。

このとき、B が選ばれる割合は 92% だった。

> round(mean(CV_A<CV_B),2)
[1] 0.92

本当は B のほうがコンバージョン率が高いのに A を選んでしまう割合は 8% である。

検定とかしないほうが楽だし正答率高くなってしまう。なぜ?

それはこの方法が伝統的な仮説検定の手順に則っていないからである。

伝統的な仮説検定の手順に乗っ取ってサンプルサイズ(この場合訪問数)を設定すると、対立仮説が正しいときに、正しく帰無仮説を棄却する確率(検出力)をコントロールできる。

そのやり方はたとえば ズバリ! 必要なサンプルサイズはいくつ? A/Bテストのための例数設計入門 - 廿TT に書いた。

しかし、この場合興味があるのは本当に「有意差」なのか。

本来興味があったのは「本当は B のほうがコンバージョン率が高いのに A を選んでしまう確率」じゃないのか。

問題設定をこのように置き換える:

  • 本当は A のほうが割合(CTR とか CVR)が大きいのに、誤って B の方を選んでしまう確率はどのくらいか
  • 誤って B の方を選んでしまう確率を \gamma くらいに抑えたい場合、どのくらい分母の数(インプレッションとか訪問数)が必要か

φ 係数

ところで、φ(ファイ)係数というのがあってそれは以下のような分割表が与えられたとき、

コンバージョンした訪問 コンバージョンしなかった訪問
A a b a+b
B c d c+d
a+c b+c n=a+b+c+d

\displaystyle  \phi =\frac{ad-bc}{\sqrt{(a+b)(c+d)(a+c)(b+d)}}

で与えられます。

これは -1 から 1 の範囲の値をとり、値の絶対値が大きいほどランディングページとコンバージョンとの連関が強いと解釈できます。

より詳しい説明は以下に書きましたが、若いときの文なのでいま読むと恥ずかしい。

以降、3種類の φ 係数が出てくるので、以下のような書き方で区別します。

  •  \hat \phi:実際に AB テストを行って得られたデータから求めた φ 係数。
  •  \phi_{\rm true}:真のコンバージョン率(無限回実験したら求まる)から求めた φ 係数。
  •  \phi_{\rm set}:少なくてもこれくらいの差があれば、実質的に意味のある差とみなそうと定めた φ 係数。

 \hat \phi は平均が  \phi_{\rm true}標準偏差1/\sqrt{n}正規分布に近似的にしたがう。

この性質を利用すると、「本当は A のほうが割合(CTR とか CVR)が大きいのに、誤って B の方を選んでしまう確率を \gamma くらいに抑えたい場合、どのくらい分母の数(インプレッションとか訪問数)が必要か」が計算できる。

サンプルサイズの決め方

\gamma \phi_{\rm set} を定めたら、

 (\Phi^{-1}(\gamma)/\phi_{\rm set})^2

で必要なサンプルサイズ n が求まる。ここで  \Phi^{-1} は標準正規分布の分布関数の逆関数

なぜなら「本当は A のほうが割合(CTR とか CVR)が大きいのに、誤って B の方を選んでしまう確率 \gamma」とは、\hat \phi が 0 を下回る確率なので、 標準正規分布の分布関数  \Phi を用いて

 \Phi ( (0-\phi_{\rm set})\sqrt{n} ) < \gamma

と書ける。

以下のような式変形をする。

 -\phi_{\rm set}\sqrt{n}  < \Phi^{-1} (\gamma)
\sqrt{n}  > \Phi^{-1} (\gamma)/ \phi_{\rm set}
\sqrt{n}  > \Phi^{-1} (\gamma)/ \phi_{\rm set}
 n  > (\Phi^{-1} (\gamma)/ \phi_{\rm set})^2

おわり。

A と B のふたつを比べるので、n を 2 で割って小数点以下を切り上げたものを A とB の分母の数(インプレッションとか訪問数)とする。

両者で n/2 以上の分母の数が得られたら、単純に CVR がちょっとでも大きい方を選べば良い。

このやり方で「本当は A のほうが割合(CTR とか CVR)が大きいのに、誤って B の方を選んでしまう確率」は \gamma より下に抑えられる。

注意点

ただし、真の φ 係数(\phi_{\rm true})が事前に定めたφ係数( \phi_{\rm set} )より小さい場合は、 \gamma は保たれない。

その場合、A と B の違いは微差だから気にしないことにするしかない。

また、\gamma を 0 にすることはできないので、どこか適当なところで見切るしかない。

求めた n が、予算や時間の制約から見て大きすぎる場合は、より大きな \gamma を許容するか、 \phi_{\rm set} をより大きくして、「微差だから気にしない」という範囲を広げるしかない。

シミュレーション

\phi_{\rm set}\phi_{\rm true} を 0.01 に固定し、\gamma = 0.2, 0.15, 0.1, 0.05, 0.01 として、A の CVR をいろいろ変えて「本当は A のほうが CVR が大きいのに、誤って B の方を選んでしまう割合」をシミュレーションしてみる。

このとき \gamma に対応する n は、それぞれ 7083.263、10741.942、16423.744、27055.435、54118.944 だった。

黒い棒グラフが設定した \gamma、灰色がシミュレーションで「本当は A のほうが CVR が大きいのに、誤って B の方を選んでしまった割合」である。

f:id:abrahamcow:20170505035326p:plain

f:id:abrahamcow:20170505035332p:plain

f:id:abrahamcow:20170505035351p:plain

f:id:abrahamcow:20170505035354p:plain

f:id:abrahamcow:20170505035357p:plain

いずれの場合も、誤って B を選ぶ割合が設定した \gamma に近い数字になっている。

なお、 \phi_{\rm set} を大きめにすると、実際に誤って B を選ぶ割合は設定した \gamma よりだいぶ小さめにでるようです。

#シミュレーションした R のコード
N_calc <- function(phi,error_rate){
  ((qnorm(error_rate)/phi)^2)
}

simfunc <- function(iter,p1,phi,error_rate){
  p2 <-(-p1*(phi^2)+(phi^2)-sqrt((phi^4)-4*(phi^2)*(p1^2)+4*(phi^2)*p1) +p1)/(phi^2+1)
  N <- N_calc(phi,error_rate)
  N2 <- ceiling(N/2)
  A <-rbinom(iter,N2,p1)
  B <-rbinom(iter,N2,p2)
  mean(A<B)
}

iter <-10000
er <- c(0.2,0.15,0.1,0.05,0.01)
sapply(er,function(x)N_calc(0.01,x))

simres_002<-sapply(er,function(x)simfunc(iter,0.02,0.01,x))
barplot(rbind(er,simres_002),names.arg = er,beside = TRUE,
        legend.text=c("nominal","simulation"),
        main=expression(list(CVR[A]==0.02,phi==0.01)))

simres_01<-sapply(er,function(x)simfunc(iter,0.1,0.01,x))
barplot(rbind(er,simres_005),names.arg = er,beside = TRUE,
        legend.text=c("nominal","simulation"),
        main=expression(list(CVR[A]==0.1,phi==0.01)))

simres_02<-sapply(er,function(x)simfunc(iter,0.2,0.01,x))
barplot(rbind(er,simres_02),names.arg = er,beside = TRUE,
        legend.text=c("nominal","simulation"),
        main=expression(list(CVR[A]==0.2,phi==0.01)))

simres_05<-sapply(er,function(x)simfunc(iter,0.5,0.01,x))
barplot(rbind(er,simres),names.arg = er,beside = TRUE,
        legend.text=c("nominal","simulation"),
        main=expression(list(CVR[A]==0.5,phi==0.01)))

simres_08<-sapply(er,function(x)simfunc(iter,0.8,0.01,x))
barplot(rbind(er,simres_08),names.arg = er,beside = TRUE,
        legend.text=c("nominal","simulation"),
        main=expression(list(CVR[A]==0.8,phi==0.01)))

メリット・デメリット

伝統的な仮説検定の枠組みで例数設計を行うには、

を想定する必要がある。

しかし冒頭に書いた目的で AB テストを行う場合、A の CVR、B の CVR を想定するのは難しいし、有意水準とか検出力とかの解釈も難しい。

また「検定して有意にならなかった場合、どうすればいいのか」という指針が与えられない。

「確実な結果が欲しいので有意になるまで検定を続けます」とやったらそれはもはや検定ではない。(たとえば A/Bテストをくり返すために知っておきたい標本の大きさと多重比較の話 - 廿TT を参照。)

これに対して、提案手法(φ 係数を用いたサンプルサイズ設計)では、 \phi_{\rm set} (相対的に見て、どれくらいの差だったら実質的に意味のある差とみなすか)と、「本当は A のほうが比率(CTR とか CVR)が大きいのに、誤って B の方を選んでしまう確率 \gamma をどのくらいに抑えたいか」の二点を定めるだけでいい。

そして厳格な検定をやるよりも、ずっと小さいサンプルサイズで済む。

十分なサンプルサイズが得られたら、単純に CVR がちょっとでも大きい方を選べば良いので結果の解釈も楽である。

デメリットは φ 係数に対する感覚がつかみづらいという点だろうか。

これはいろんな数字から φ 係数を出してみて、慣れていただくしかない。

φ 係数の計算と、φ 係数に基づくサンプルサイズの計算ができるエクセルファイルを以下に置いておきます。

http://zisatsu.web.fc2.com/monooki/excel/phi_coef.xlsx

おすすめ書籍

品質管理のための統計手法 (日経文庫)

品質管理のための統計手法 (日経文庫)

サンプルサイズの決め方 (統計ライブラリー)

サンプルサイズの決め方 (統計ライブラリー)