103贝叶斯方法数据分析实战--网站转换率评估

网站转换率评估

贝叶斯 A/B 测试

场景模拟

使用贝叶斯解决问题的原因
接下来,让我们使用贝叶斯方法来解决这个问题。

image.png

真实数据可以理解为一件事情发生的概率,而观测频率只是频率而已。举个例子,众所周知,骰子的数字 1 朝上的真实频率为1/6。但是,事实上,就算我们实验六次,也不一定能观测到数字为 1 的那一面(这就是观测频率)。
在现实生活中,真实频率的前面经常会出现很多的噪音以及其他复杂情况的干扰。因此,我们最好是使用观测到的数据以及合理的先验知识,来推断真实频率的可能值。这样得到的转化率才更加接近真实。
网站 A 的观测数据的模拟
image.png

我们需要用计算机来模拟真实数据的产生。
image.png

image.png

我们可以通过真实的概率,模拟观测数据。代码如下:

import scipy.stats as stats
import numpy as np
# 请记住,这个真实概率其实是未知的
# 也就是说,后面我们其实是需要使用贝叶斯推断来得到这个 p_true 的值的
p_true = 0.05
N = 1500

occurrences = stats.bernoulli.rvs(p_true, size=N)

print("每个人是否发生转化:", occurrences)  # 0表示未发生转化,1表示发生转化
print("发生转化行为的人数:", np.sum(occurrences))

接下来,让我们利用观测数据计算观测频率:

print("A 网站的观测频率为: %.4f" % np.mean(occurrences))
print("该转化率和真实的转化率相同吗? %s" % (np.mean(occurrences) == p_true))

利用贝叶斯推断真实转化率
还记得上一个试验中,贝叶斯推断的步骤吗?在建立贝叶斯模型时,我们首先需要对未知变量赋予一个先验分布(即在进行任何实验前,我们所认为的网站 A 的转化率),然后利用已知数据与模型对它的后验分布(即真实转化率)进行求取。

image.png

import pymc3 as pm

model = pm.Model()
with model:
    # 0-1 的均匀分布
    p = pm.Uniform('p', lower=0, upper=1)
model

接下来,我们将观测值放入 PyMC 中的 observed 的变量中。并打开模型的学习开关,进行贝叶斯推断。运行时间 3~5 min,代码如下:

with model:
        # 将数据与模型相结合
        obs = pm.Bernoulli("obs", p, observed=occurrences)

        # 打开模型学习开关
        step = pm.Metropolis()

        # 获得结果分布的样例
        trace = pm.sample(18000, step=step)
        burned_trace = trace[1000:]

接下来,我们通过得到的样本做出未知变量 P_A 的后验分布图:

from IPython.core.pylabtools import figsize
import matplotlib.pyplot as plt
%matplotlib inline

figsize(12.5, 4)
plt.title("Posterior distribution of $p_A$, the true effectiveness of site A")
plt.vlines(p_true, 0, 90, linestyle="--", label="true $p_A$ (unknown)")
plt.hist(burned_trace["p"], bins=25, histtype="stepfilled", normed=True)
plt.legend()

从上图可以看出,出现频率最多的PA的值距离真实频率的值很近。当然由于产生数据的随机性,我们可以通过调整 N 的大小,使PA的后验概率,更加接近网站 A 的真实转化频率。

A 与 B 的结合

image.png

同样,第一步我们需要分别模拟网站 A 和网站 B 的用户行为数据。代码如下:

# 两个网站的真实转化率
true_p_A = 0.05
true_p_B = 0.04

# 两个网站的访问人数
N_A = 1500
N_B = 750

# 产生观测数据
observations_A = stats.bernoulli.rvs(true_p_A, size=N_A)
observations_B = stats.bernoulli.rvs(true_p_B, size=N_B)
print("Obs from Site A: ", observations_A[:30], "...")
print("Obs from Site B: ", observations_B[:30], "...")

接下来,让我们定义先验概率,并将观测数据传入模型中,最后按下模型学习的按钮,对模型进行训练:

model = pm.Model()
with model:
    p_A = pm.Uniform("p_A", 0, 1)
    p_B = pm.Uniform("p_B", 0, 1)

    # 定义两个转化率的差距
    delta = pm.Deterministic("delta", p_A - p_B)

    # 真实数据传入模型s
    obs_A = pm.Bernoulli("obs_A", p_A, observed=observations_A)
    obs_B = pm.Bernoulli("obs_B", p_B, observed=observations_B)

    # 模型训练,将在在后面的实验进行解释
    step = pm.Metropolis()
    trace = pm.sample(20000, step=step)
    burned_trace = trace[1000:]

接下里,根据得到的分布函数,提取出这三个变量的样本集合。

p_A_samples = burned_trace["p_A"]
p_B_samples = burned_trace["p_B"]
delta_samples = burned_trace["delta"]
p_B_samples.shape

最后,同样让我们,利用得到的分布样本,做出这 3 个未知变量的分布图:

figsize(12.5, 10)

# 利用 histogram 做出每个值的取值概率

ax = plt.subplot(311)

plt.xlim(0, .1)
plt.hist(p_A_samples, histtype='stepfilled', bins=25, alpha=0.85,
         label="posterior of $p_A$", color="#A60628", normed=True)
plt.vlines(true_p_A, 0, 80, linestyle="--", label="true $p_A$ (unknown)")
plt.legend(loc="upper right")
plt.title("Posterior distributions of $p_A$, $p_B$, and delta unknowns")

ax = plt.subplot(312)

plt.xlim(0, .1)
plt.hist(p_B_samples, histtype='stepfilled', bins=25, alpha=0.85,
         label="posterior of $p_B$", color="#467821", normed=True)
plt.vlines(true_p_B, 0, 80, linestyle="--", label="true $p_B$ (unknown)")
plt.legend(loc="upper right")

ax = plt.subplot(313)
plt.hist(delta_samples, histtype='stepfilled', bins=30, alpha=0.85,
         label="posterior of delta", color="#7A68A6", normed=True)
plt.vlines(true_p_A - true_p_B, 0, 60, linestyle="--",
           label="true delta (unknown)")
plt.vlines(0, 0, 60, color="black", alpha=0.2)
plt.legend(loc="upper right")

从第三张deta图中可以发现,deta>0 的阴影部分面积远远大于小于零的面积。(由于采样的随机性,也可能有很小很小的概率造成,面积相差不大,这时请重复运行上上上段,模型学习的代码)。因此,可以说明,网站 A 确实比网站 B 的转化率更好。这种推断也可以转换成具体的概率值,如下所示:

print("A 网站的转化率比 B 差的概率: %.3f" %
      np.mean(delta_samples < 0))

print("A 网站的转化率比 B 好的概率: %.3f" %
      np.mean(delta_samples > 0))

如果这个概率值过高,我们也可以对网站 B 进行更多的试验。因为网站 B 的样本比较少,这将导致网站 B 的每个新的点击数据会比网站 A 的每个新的点击数据有更高的贡献度。
我们也可以对 true_p_A、true_p_B、N_A 和 N_B 设置不同的值,观察delta的后验分布情况,进而能够很好的量化网站 A 和网站 B 的优劣。

你可能感兴趣的:(103贝叶斯方法数据分析实战--网站转换率评估)