ベイズ推論を用いたA/Bテストを行なってみる(PyMC3)

1.背景

ビジネスにおいて、異なる施策を試す場合、各施策の効果の差を検証し、意思決定を行うことが重要です。しかし、得られるデータは限られており、ばらつきが存在することが多いです。このような状況では、少数のデータのばらつきを適切に考慮しながら効果の比較を行うために、ベイズモデリングが有用です。
ベイズモデリングを使用することで、A/Bテストと呼ばれる2つの施策の効果を比較してみます。これにより、データの限定的な性質やばらつきを考慮しながら、効果の比較をより妥当な方法で行うことができます。

2.A/Bテスト

A/Bテストは、マーケティングやウェブデザインなどの領域で広く使用される実験手法です。この手法では、2つのバリエーション(AとB)を比較し、それぞれの効果や効果の差異を評価します。
マーケティングの分野では、元の施策をコントロール群(A)として、別の施策(B)を実施した時に元の施策と比べて、効果があるのかどうかを施策効果を示す評価指標を設定し、統計的手法により検証を行います。
ウェブデザインにおいては、テスト要素(タイトル、レイアウト、ボタンの色等)を選択し、テスト要素を変えたバリエーション(AとB)を用意し、ウェブサイトの訪問者数、クリック数、コンバージョン数などの指標を設定し、データを取得し、テスト要素のバリエーションによって、評価指標に違いがあるのか統計的手法により検証を行います。

3.ベイズ推論

ベイズ推論は、統計的な推論や予測を行うための強力な手法です。ベイズ推論は、ベイズの定理に基づいて確率的なモデルを構築し、データ(尤度)と事前知識を組み合わせて事後分布を推定する手法です。ベイズ推論のは以下の特徴があります。

  1. 不確実性の表現: ベイズ推論では、パラメータの事前分布や事後分布を通じて不確実性を明示的に表現することができます。これにより、データが限られている場合でも、推定結果の信頼性や不確実性を評価することが可能です。例えば、二つの異なる施策によるコンバージョン率を比較する際に、データから算出されるコンバージョン率を確率的な幅をもったものとして推定できます。これにより、推定されるコンバージョン率の信頼度も把握できます。

  2. 逐次的な更新: ベイズ推論はデータが逐次的に入手される場合にも適用することができます。新たなデータが得られるたびに、事前分布を事後分布として更新し、推定結果を更新することができます。これにより、逐次的な学習や予測が可能となります。

  3. 柔軟性と統合性: ベイズ推論は、異なるデータやモデルを柔軟に組み合わせることができます。複数のデータセットや異なるモデルを統合する際に、ベイズ推論は有用な手法となります。また、事前分布や尤度関数を自由に設定することで、モデルの複雑さやドメイン知識を組み込むことも可能です。例えば、二つの異なるコンバージョン率を比較する際に、施策によらない平均的なコンバージョン率を事前知識として設定することで、少数のサンプルしか得られず、サンプルに偏りがある際でも事前知識を活かした妥当なコンバージョン率の推定を行うことができます。

  4. ベイズモデルの解釈: ベイズ推論によって構築されたベイズモデルは、パラメータの確率分布として表現されます。これにより、推定結果や予測結果を直感的に解釈することができます。A/Bテストにベイズ推論を導入することでt検定等の統計的検定と異なり、各群が他の群よりも優れている(劣っている)確率を直接的に算出できるメリットがあります。

4.PyMC3について

PyMC3は、ベイズ統計モデリングを行うためのPythonベースのライブラリです。PyMC3は、確率モデルの定義から事後分布の推定まで、ベイズ推論のさまざまな手法を提供します。確率変数や確率分布、尤度関数などの関数を提供しており、これらを組み合わせることで、さまざまなモデルを構築することが可能です。また、MCMC(マルコフ連鎖モンテカルロ法)等の事前分布と尤度から事後分布を計算するためのソルバーを提供しており、ベイズモデリング及びその推論が可能です。

PyMC3 Documentation — PyMC3 3.11.5 documentation

5.ベイズ推論を使用したA/Bテスト

今回は二つの異なる施策を行った時のコンバージョン率の差をベイズ推論を用いて比較してみます。
本事例では、過去のデータからコンバージョン率が平均して3%であったという事前情報を事前知識として与えます。
コンバージョン数が施行回数N, 確率pの二項分布Binominal(N, p)に従うとしてモデル化を行います。この場合、事前分布としてベータ分布を設定します。詳細には、事前知識としてpが0.03(3%)になるように(2, 98)ベータ分布に設定します。事前分布(ベータ分布)とモデル(二項分布)を用いて、MCMCにより事後分布(ベータ分布)を推定します。この事後分布からの確率的なサンプリングとして二項分布のパラメータ(=コンバージョン率)を考えることで、コンバージョン率の比較を行います。
以下に、上述したベイズモデリングを行い、事後分布のサンプリングを行うコードを記載します。

import pymc3 as pm
import numpy as np

# Aグループのデータ(コンバージョンの成果数とサンプル数)
data_A = 10
sample_A = 200

# Bグループのデータ(コンバージョンの成果数とサンプル数)
data_B = 20
sample_B = 250

#事前分布(ベータ分布のパラメータ)
alpha_prior = 2
beta_prior = 98

with pm.Model() as model:

    # ベータ事前分布を指定
    p_A = pm.Beta('p_A', alpha=alpha_prior, beta=beta_prior)
    p_B = pm.Beta('p_B', alpha=alpha_prior, beta=beta_prior)

    # ベータ分布からのデータ生成を指定
    obs_A = pm.Binomial('obs_A', n=sample_A, p=p_A, observed=data_A)
    obs_B = pm.Binomial('obs_B', n=sample_B, p=p_B, observed=data_B)

    #新たな生成量として行動を起こす確率の差を定義
    delta_prob = pm.Deterministic('delta_prob', p_B - p_A)

    # MCMCサンプリング
    trace = pm.sample(2000, tune=1000, cores=1)  # サンプリング数: 2000, バーンイン: 1000

# 推定結果の表示
pm.summary(trace).round(2)

上記のプログラムを実行すると、コンバージョン率を比較したいA,B群の架空のコンバージョンデータを生成し、事前知識を反映させたベータ分布を事前分布、ベータ分布により決定されるパラメータpに基づく二項分布(モデル)からMCMCにより事後分布をサンプリングします。サンプリングした事後分布はtrace変数に格納されます。 以下のコードで構築したベイズモデルを可視化することができます。

pm.model_to_grahviz(model)

また、以下のコードを実行することで事後分布の可視化が行えます。

_ = pm.traceplot(trace)
_ = pm.plot_posterior(trace)

続いて、以下のコードを実行することで事後分布に基づくA,B群のコンバージョン率の分布をヒストグラムで可視化します。取得データだけで平均的なコンバージョン率を計算するとそれぞれ、5%(=10/ 200)、8%(=20/ 250)となりますが、過去のデータに基づく平均的なコンバージョン率3%を事前知識として設定しているため、データのみから決まるコンバージョン率よりも小さくなっています。今回のように取得データのサンプルが小さく上振れが考えられる際に過去の事前分知識を取り込んでおくことで妥当な推定を行いやすくなります。

import matplotlib.pyplot as plt

# プロット
plt.hist(trace['p_A'], bins=30, density=True, alpha=0.7)
plt.hist(trace['p_B'], bins=30, density=True, alpha=0.7)
plt.xlabel('Conversion Rate')
plt.ylabel('Density')
plt.legend()
plt.show()

コンバージョン率の差の分布も可視化してみます。

# AグループとBグループの差の分布をサンプリング

# ヒストグラムの表示
plt.hist(trace['delta_prob'], bins=30, density=True, alpha=0.7)
plt.xlabel('Difference in Conversion Rate')
plt.ylabel('Density')
plt.title('Difference between A and B Groups')
plt.show()

最後に、A,B群とを比較して確率的にどちらが効果的か見積もってみます。
ベイズモデリングでは推定値を幅を持った値として表現するので、各群が他の群よりも優れている確率を直接的に算出できます。

(trace['p_B'] - trace['p_A'] > 0).mean()
# グループBの方がグループAに比べてコンバージョン率が高いと90%の確率でいえる。

6.参考