🎓 レベル:標準 | 重要度:A(必須)
📎 関連:第7章 実験と因果推論 目次 | 前提:A/Bテストの設計と分析
要点(BLUF)
- ベイズ A/B は、頻度論の 値の代わりに事後分布から意思決定に直結する量を直接出します——「B が A より優れている確率 」「増分 の 95% 確信区間」「期待損失(B を選んで実は損する見込み)」。 値の「効果がない前提での希少性」より、ずっと素直に読めます。
- 仕掛けは Beta–二項共役。事前 に二項データ(CV 数 、試行 )を掛けると、事後はそのまま 。閉じた形で求まるので MCMC は不要、
numpyのrng.betaで事後からサンプリングするだけです。 - A/Bテストの設計と分析 と同じデータ(CV と )に一様事前 を置くと、(厳密には約 )、増分の事後平均 、95% 確信区間 、B を選ぶ期待損失はほぼ 。よって「迷わず B」。ただしデータが少ない序盤の“のぞき見”では雑音も事前も効く(勝者が逆転して見えることすらある)ので、期待損失の閾値で停止を設計します。
1. 頻度論からベイズへ
A/Bテストの設計と分析 は 値と信頼区間で「偶然か」を判定しました。けれど現場が本当に知りたいのは、たいてい**「結局 B のほうが良いのか、どれくらいの確率で?」「B に切り替えて損する見込みはどれだけか?」**です。 値はこれらに直接答えません( 値は「効果がないと仮定したときの希少性」であって「B が良い確率」ではない)。
ベイズ A/B は発想を逆にします。データを固定し、未知の反応率 のほうを確率変数として、データを見たあとの分布=事後分布を求めます。事後分布さえ手に入れば、意思決定に必要な量は全部そこから読めます。
flowchart LR D["観測データ<br/>(CV数 x, 試行 n)"] --> PA["事後 Beta_A(p_A の分布)"] D --> PB["事後 Beta_B(p_B の分布)"] PA --> S["両事後から大量サンプリング"] PB --> S S --> Q1["P(p_B > p_A)"] S --> Q2["増分 p_B − p_A の確信区間"] S --> Q3["期待損失(意思決定)"]
2. Beta–二項共役(数式)
コンバージョンは「買う/買わない」のベルヌーイ試行で、群の反応率 が成功確率です。 への事前を とすると、その密度は に比例します。 試行で 件 CV したデータの尤度は二項なので に比例。事後はベイズの定理で両者の積に比例し、
これは の密度そのもの。事前と事後が同じ Beta 族にとどまるこの性質を共役(conjugacy)と呼びます。だから事後はパラメータの足し算だけで求まり、MCMC のような数値近似は要りません。 は 上の一様分布で、「何も知らない」に近い弱情報事前です。
意思決定量は、両群の事後 から大量にサンプリングして読みます。
増分 の事後は差のサンプル で、その パーセンタイルが 95% 確信区間です。そして B を選んだときの期待損失は
——B を選んだのに実は A のほうが良かった、その取りこぼし (負なら )の期待値です。これが意思決定の通貨になります。
3. 事後分布から意思決定する(コード)
A/Bテストの設計と分析 と同じ設定・同じ生成順(、各群 )でデータを作り、一様事前 の事後から 万サンプルして、・増分の事後平均と 95% 確信区間・両群の期待損失を出します。
import numpy as np
rng = np.random.default_rng(0)
n_A = n_B = 6000
# 07-01 と同じ設定・同じ生成順なので CV数も一致する
conv_A = rng.binomial(1, 0.10, n_A)
conv_B = rng.binomial(1, 0.12, n_B)
x_A, x_B = conv_A.sum(), conv_B.sum()
# 事前 Beta(1,1)(一様)+二項データ → 事後 Beta(1+CV, 1+非CV)
a0 = b0 = 1.0
postA_a, postA_b = a0 + x_A, b0 + (n_A - x_A)
postB_a, postB_b = a0 + x_B, b0 + (n_B - x_B)
# 各事後から10万サンプル(共役なのでMCMC不要)
M = 100_000
sA = rng.beta(postA_a, postA_b, M)
sB = rng.beta(postB_a, postB_b, M)
p_B_better = np.mean(sB > sA) # P(p_B > p_A)
diff = sB - sA # 増分の事後サンプル
ci_lo, ci_hi = np.percentile(diff, [2.5, 97.5])
loss_B = np.mean(np.maximum(sA - sB, 0)) # Bを選ぶときの期待損失
loss_A = np.mean(np.maximum(sB - sA, 0)) # Aを選ぶときの期待損失
print(f"対照A: CV {x_A}/{n_A} 事後平均CVR = {postA_a / (postA_a + postA_b):.4f}")
print(f"介入B: CV {x_B}/{n_B} 事後平均CVR = {postB_a / (postB_a + postB_b):.4f}")
print(f"P(p_B > p_A) = {p_B_better:.4f}")
print(f"増分の事後平均 = {diff.mean():+.4f}")
print(f"増分の95%確信区間 = [{ci_lo:+.4f}, {ci_hi:+.4f}]")
print(f"Bを選ぶ期待損失 = {loss_B:.6f}")
print(f"Aを選ぶ期待損失 = {loss_A:.6f}")
出力:
対照A: CV 569/6000 事後平均CVR = 0.0950
介入B: CV 724/6000 事後平均CVR = 0.1208
P(p_B > p_A) = 1.0000
増分の事後平均 = +0.0258
増分の95%確信区間 = [+0.0147, +0.0369]
Bを選ぶ期待損失 = 0.000000
Aを選ぶ期待損失 = 0.025839
出力の意味:事後平均 CVR は対照 ・介入 (一様事前なので 、生データ をほぼそのまま)。 は「 万サンプルで B が A に負けた回数がゼロ」という意味で、これは「B が優れている確率がほぼ 」とそのまま読めます—— 値とはここが決定的に違います(厳密には 、A が勝つのは約 万回に1回で、文字どおりの ではありません)。増分の事後平均 、95% 確信区間 は A/Bテストの設計と分析 の信頼区間と数値はほぼ一致しますが、意味は別物(後述)。極めつけは期待損失で、B を選ぶ期待損失はほぼ (取りこぼしの心配なし)、A を選ぶ期待損失は (A に留まると平均 ポイントぶん取りこぼす)。つまり「迷わず B」という意思決定が、確率と損失の言葉で直接出ます。
両群の事後分布と、増分の事後分布を描きます。
import numpy as np
from scipy import stats
import matplotlib.pyplot as plt
import japanize_matplotlib
rng = np.random.default_rng(0)
n_A = n_B = 6000
conv_A = rng.binomial(1, 0.10, n_A)
conv_B = rng.binomial(1, 0.12, n_B)
x_A, x_B = conv_A.sum(), conv_B.sum()
postA = stats.beta(1 + x_A, 1 + n_A - x_A)
postB = stats.beta(1 + x_B, 1 + n_B - x_B)
sA = rng.beta(1 + x_A, 1 + n_A - x_A, 100_000)
sB = rng.beta(1 + x_B, 1 + n_B - x_B, 100_000)
diff = sB - sA
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(9.4, 4.2))
xs = np.linspace(0.085, 0.140, 400)
ax1.plot(xs, postA.pdf(xs), color="C0", lw=2, label="対照A の事後")
ax1.plot(xs, postB.pdf(xs), color="C3", lw=2, label="介入B の事後")
ax1.fill_between(xs, postA.pdf(xs), alpha=0.20, color="C0")
ax1.fill_between(xs, postB.pdf(xs), alpha=0.20, color="C3")
ax1.set_xlabel("コンバージョン率 p"); ax1.set_ylabel("事後密度")
ax1.set_title("各群のCVRの事後分布"); ax1.legend()
ax2.hist(diff, bins=60, color="C2", alpha=0.85, density=True)
ax2.axvline(0, ls="--", color="black", label="差なし")
ax2.axvline(diff.mean(), ls="-", color="C3", lw=2, label=f"事後平均 {diff.mean():+.3f}")
ax2.set_xlabel("増分 p_B − p_A"); ax2.set_ylabel("事後密度")
ax2.set_title("増分の事後分布(ほぼ全域が正)"); ax2.legend()
fig.suptitle("ベイズA/B:事後分布から P(B>A)・増分・期待損失を読む")
fig.tight_layout()
plt.show()
左パネルでは対照 A(青、 中心)と介入 B(赤、 中心)の事後がほとんど重ならず、B が右にずれています。右パネルの増分の事後はほぼ全域が より右で、これが「」を絵にしたものです。区間幅が事後の不確実性、 までの距離が効果の確かさを表します。
4. 事前の影響・のぞき見・意思決定の閾値(コード)
§3 の結論は決定的でしたが、それはデータが十分(各群 )だからです。一様事前 なら、これだけデータがあれば事後はデータがほぼ決め、事前は無視できます。問題はまだデータが少ない序盤に“のぞき見”したとき。同じデータを の途中段階で見て、一様事前と強い事前 (平均 ・事前の重み約 件ぶん)で と期待損失がどう変わるかを見ます。
import numpy as np
rng = np.random.default_rng(0)
n_full = 6000
conv_A = rng.binomial(1, 0.10, n_full)
conv_B = rng.binomial(1, 0.12, n_full)
def decide(n_peek, a0, b0, M=500_000, seed=7):
xa = conv_A[:n_peek].sum()
xb = conv_B[:n_peek].sum()
g = np.random.default_rng(seed)
sA = g.beta(a0 + xa, b0 + n_peek - xa, M)
sB = g.beta(a0 + xb, b0 + n_peek - xb, M)
return xa, xb, np.mean(sB > sA), np.mean(np.maximum(sA - sB, 0))
print("のぞき見:経過途中の n でのベイズ判断(真は B が +0.02 良い)")
for n_peek in (300, 1000, 6000):
xa, xb, p_u, loss_u = decide(n_peek, 1, 1) # 一様 Beta(1,1)
_, _, p_s, _ = decide(n_peek, 200, 1800) # 強い事前 Beta(200,1800)
print(f"n={n_peek:>4}/群 CVR_A={xa / n_peek:.3f} CVR_B={xb / n_peek:.3f} "
f"P(B>A) 一様={p_u:.3f} 強い事前={p_s:.3f} Bの期待損失(一様)={loss_u:.4f}")
出力:
のぞき見:経過途中の n でのベイズ判断(真は B が +0.02 良い)
n= 300/群 CVR_A=0.140 CVR_B=0.127 P(B>A) 一様=0.317 強い事前=0.423 Bの期待損失(一様)=0.0189
n=1000/群 CVR_A=0.096 CVR_B=0.112 P(B>A) 一様=0.879 強い事前=0.753 Bの期待損失(一様)=0.0008
n=6000/群 CVR_A=0.095 CVR_B=0.121 P(B>A) 一様=1.000 強い事前=1.000 Bの期待損失(一様)=0.0000
出力の意味:3つの教訓が同時に出ています。第一にのぞき見は危険—— では雑音で対照 A のほうが高く見え()、 はわずか 。真は B が良いのに、ここで「A が勝っている」と早期停止したら真に優れた B を捨てるところでした。第二に事前の影響——同じ で強い事前 は を と 側に引き戻し、少データの雑音を正則化します(が、 では一様・強い事前とも で差は消滅=データが事前を圧倒)。第三に期待損失が意思決定の通貨——B の期待損失は とともに と縮みます。「B の期待損失が閾値(例:= ポイント)を下回ったら採用」のように閾値で止めれば、のぞき見の害を損失の言葉で制御できます。頻度論ののぞき見が を膨らませるのに対し、ベイズは期待損失という連続量で停止規準を設計できるのが実務上の強みです(ただし後述のとおり、ベイズでものぞき見そのものが無害になるわけではありません)。
⚠️ よくある誤解
- は読めるが 値は読めない: は「B が優れている確率が 」と文字どおり読めます(事後確率)。一方 は「効果がある確率 」では断じてなく、「効果がないと仮定したときに今回以上の差が出る確率」です。この読みやすさがベイズ A/B を選ぶ最大の理由ですが、 は事前とデータに依存する量だという前提は忘れずに。
- 確信区間 信頼区間:本ノートの 95% 確信区間 は「増分が の確率でこの区間にある」と、区間を固定してパラメータに確率を置きます。A/Bテストの設計と分析 の 信頼区間は「同じ実験を無数に繰り返せば の区間が真値を含む」と、真値は固定で区間がランダム。大標本+弱情報事前で数値はほぼ一致しますが、意味は別物なので混同しないこと。
- 事前の影響:弱情報 は、データが十分なら結論を歪めません(§4 で では事前差が消えました)。しかし強い事前や少データでは効きます。事前は必ず明示し、結論が事前にどれだけ動くか(感度)を確かめるのが作法です。
- ベイズでものぞき見は無害ではない:「 が高くなるまで覗いて、超えた瞬間に止める」をやれば、本当は差がなくても偶然そこに達して誤って勝ちを宣言しがちです。ベイズが与えるのは“のぞき見の免罪符”ではなく、期待損失に閾値を引いて停止規準を事前に決める設計の枠組みです(§4)。
- MCMC・階層ベイズとは別物:今回 MCMC が要らなかったのは Beta–二項が共役だから。アウトカムが連続値だったり、多数のセグメントを束ねて情報を共有する階層構造を入れたりすると共役は崩れ、MCMC が必要になります。その一般論はベイズ統計テキストの領域です。
関連ノート
- A/Bテストの設計と分析(前提・同じデータを頻度論の 値/信頼区間で判断した版。確信区間との対比はここ)
- 第7章 実験と因果推論 目次
- 共役でない場合の MCMC・階層ベイズ A/B(連続アウトカム・多群の情報共有)はベイズ統計テキストへ。説得可能層を狙うアップリフト(効果の異質性)は次の 07-03 で扱います
- マーケティング・サイエンス 全体目次