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 的优劣。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,172评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,346评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,788评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,299评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,409评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,467评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,476评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,262评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,699评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,994评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,167评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,499评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,149评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,387评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,028评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,055评论 2 352

推荐阅读更多精彩内容