使用Pyro进行贝叶斯思维和AB测试的入门指南

本文是使用Python概率编程语言(Pyro)进行AB测试的介绍,Pyro是PyMC的一个替代品。撰写本文的动机是进一步理解使用Pyro框架进行贝叶斯统计推断,并帮助他人进行学习。因此,欢迎并鼓励提供反馈。
介绍
我以前在Python中进行贝叶斯建模的经验是使用PyMC。然而,我发现最新版本的使用有些麻烦。在研究其他概率编程语言时,我发现了Pyro,这是由Uber构建的通用PPL,并由PyTorch支持后端。以下是Pyro和PyTorch的文档链接。
Pyro
深度通用概率编程
pyro.ai
PyTorch文档 – PyTorch 2.1文档
PyTorch是一个优化的张量库,用于使用GPU和CPU进行深度学习。此文档中描述的功能…
pytorch.org
在探索Pyro时,我发现很难找到使用该软件包进行AB测试的端到端教程。本文旨在填补这一空白。
本文分为五个部分。在第一部分中,我将介绍贝叶斯思维的基础,以及该方法论所代表的哲学背景。我将简要介绍Pyro和用于进行统计推断的贝叶斯方法的技术背景。接下来,我将使用Pyro执行AB测试并讨论结果。然后,我将解释在商业环境中进行贝叶斯AB测试的理由。最后一节将总结。
贝叶斯思维入门
贝叶斯过程在高层次上相对简单。首先,你有一个感兴趣的变量。我们陈述我们对该变量的当前理解,将其表示为概率分布(在贝叶斯世界中的一切都是概率分布)。这称为先验概率。在贝叶斯推断中,概率是认识论的,这意味着它通过我们的知识程度而产生。因此,我们陈述的先验概率分布既是不确定性的陈述,也是理解的陈述。然后,我们观察现实世界中的事件。然后,我们使用这些观察结果来更新我们对感兴趣的变量的理解。我们对这个新的理解进行表征的概率分布被称为后验概率。我们也用概率分布来描述后验,即我们感兴趣的变量在给定所观察到的数据的情况下的概率。这个过程在下面的流程图中有所体现,其中theta是我们感兴趣的变量,data是我们观察到的事件的结果。

贝叶斯过程实际上是循环的,因为我们随后重复这个过程,重新开始但使用我们在观察世界之后获得的新知识。我们旧的后验概率成为我们的新先验概率,我们观察新的事件,并再次更新我们的理解,就像Nate Silver在他的书《信号与噪音》中所说的“越来越少错”的状态。我们不断减少对变量的不确定性,朝着一种确定性的状态收敛(但我们永远无法确定)。在数学上,这种思维方式被贝叶斯定理所概括。

在实践中,贝叶斯定理在高维大模型中很快变得难以计算。有很多方法来解决这个问题,但我们今天要使用的方法叫做马尔可夫链蒙特卡洛(MCMC)。MCMC是一类算法(我们将使用一种叫做哈密顿蒙特卡洛的算法),它智能地从概率分布中抽样。本文无法深入探讨这个问题,但我会在结尾提供一些有用的参考资料以供进一步阅读。现在只需要知道,贝叶斯定理太复杂无法计算,所以我们利用先进的算法,如MCMC来帮助。本文将重点介绍使用Pyro进行MCMC。然而,Pyro强调使用随机变分推断(SVI)这种基于近似的方法,本文将不涉及。
使用Pyro进行AB测试
考虑一个公司设计了一个新的网站落地页,并希望了解这对转化率的影响,即访问者在着陆页后是否继续网站会话。在测试组A中,网站访问者将看到当前的落地页。在测试组B中,网站访问者将看到新的落地页。本文中,我将把测试组A称为对照组,B组称为治疗组。公司对这个变化持怀疑态度,并选择了80/20的会话流量分配。每个测试组的访问者总数和页面转化数如下所示。

AB测试的零假设是两个测试组的页面转化率不会发生变化。在频率派的框架下,这可以表示为双侧检验的以下形式,其中r_c和r_t分别是对照组和治疗组的页面转化率。

显著性检验将寻求拒绝或无法拒绝零假设。在贝叶斯框架下,我们略微不同地表达零假设,即对测试组的先验相同。
让我们暂停一下,概述一下我们测试期间发生的情况。我们感兴趣的变量是页面转化率。这个变量可以通过计算转化的访问者数除以总访问者数来计算。产生此转化率的事件是访问者是否点击页面。对于每个访问者,这里只有两种可能的结果,要么访问者点击页面并转化,要么不转化。你们中的一些人可能会认识到,对于每个不同的访问者,这是一个伯努利试验的例子;有一个试验和两种可能的结果。现在,当我们收集一组这些伯努利试验时,我们就有了一个二项分布。当随机变量X服从二项分布时,我们给它以下符号:

其中n是访问者数(或伯努利试验的数量),p是每次试验上事件的概率。在这里我们感兴趣的是p,我们想要了解每个测试组中的访问者在页面上转化的概率是多少。我们观察到了一些数据,但正如前一节中提到的,我们首先需要定义我们的先验。在贝叶斯统计中,我们总是需要将这个先验定义为一个概率分布。正如之前提到的,这个概率分布是我们不确定性的一个表征。贝塔分布常用于建模概率,因为它在区间[0,1]之间定义。此外,使用贝塔分布作为我们二项式似然函数的先验具有共轭性这一有用的属性,这意味着我们的后验将由与我们的先验相同的分布生成。我们说贝塔分布是共轭先验。贝塔分布由两个参数来定义,分别是alpha和令人困惑的beta。

通过访问历史数据,我们可以断定一个有根据的先验信息。我们不一定需要历史数据,我们可以使用直觉来了解情况,但是现在假设我们两者都没有(在本教程的后面部分,我们将使用有根据的先验信息,但为了演示影响,我将从无信息的情况开始)。假设我们对公司网站的转化率没有了解,因此将我们的先验定义为Beta(1, 1)。这被称为均匀先验。该函数的概率分布看起来像下面的图形,与在区间[0,1]之间定义的均匀分布相同。通过断定Beta(1,1)先验,我们认为页面转化率的所有可能值是等概率的。

现在我们拥有所需的所有信息,先验和数据。让我们进入代码部分。提供的代码将为使用Pyro进行AB测试提供一个起点框架;因此,它忽略了该软件包的一些特性。为了进一步优化您的代码并充分利用Pyro的功能,我建议参考官方文档。
首先,我们需要导入我们的软件包。最后一行是一个很好的做法,特别是在使用笔记本时,可以清除我们积累的参数存储。
import pyroimport pyro.distributions as distfrom pyro.infer import NUTS, MCMCimport torchfrom torch import tensorimport matplotlib.pyplot as pltimport seaborn as snsfrom functools import partialimport pandas as pdpyro.clear_param_store()
Pyro中的模型被定义为普通的Python函数。这很有帮助,因为它使得代码更具直观性。
def model(beta_alpha, beta_beta): def _model_(traffic: tensor, number_of_conversions: tensor): # 定义随机原语 prior_c = pyro.sample('prior_c', dist.Beta(beta_alpha, beta_beta)) prior_t = pyro.sample('prior_t', dist.Beta(beta_alpha, beta_beta)) priors = torch.stack([prior_c, prior_t]) # 定义观测到的随机原语 with pyro.plate('data'): observations = pyro.sample('obs', dist.Binomial(traffic, priors),\ obs = number_of_conversions) return partial(_model_)
这里有一些要解释和解释的东西。首先,我们有一个包装在外部函数内的函数,外部函数返回内部函数的部分函数。这使我们能够更改我们的先验信息,而无需更改代码。我将在内部函数中定义的变量称为原语,可以把原语想象为模型中的变量。我们的模型中有两种类型的原语,随机原语和观测到的随机原语。在Pyro中,我们无需明确定义差异,我们只需在样本方法中添加obs参数,当它是观测到的原语时,Pyro会据此进行解释。观测到的原语包含在上下文管理器pyro.plate()中,这是最佳实践,使我们的代码看起来更清晰。我们的随机原语是我们的两个先验,由从外部函数传递的alpha和beta参数所决定的Beta分布来描述。如前所述,我们通过将它们定义为相等来断定零假设。然后,我们使用tensor.stack()将这两个原语堆叠在一起,tensor.stack()执行的操作类似于连接一个Numpy数组。这将返回一个tensor,这是Pyro中进行推理所需的数据结构。我们已经定义了我们的模型,现在让我们进入推理阶段。
如前所述,该教程将使用MCMC。下面的函数将以参数的形式接受我们上面定义的模型和我们希望用来生成后验分布的样本数量。我们还将我们的数据传递给该函数,就像我们为模型做的那样。
def run_infernce(model, number_of_samples, traffic, number_of_conversions): kernel = NUTS(model) mcmc = MCMC(kernel, num_samples = number_of_samples, warmup_steps = 200) mcmc.run(traffic, number_of_conversions) return mcmc
该函数内的第一行定义了我们的核心。我们使用NUTS类来定义我们的核心,NUTS代表No-U-Turn Sampler,是Hamiltonian Monte Carlo的自动调整版本。这告诉Pyro如何从后验概率空间中进行采样。再次强调,本文的范围无法对这个主题进行更深入的介绍,但暂时了解NUTS允许我们智能地从概率空间中进行采样即可。然后,在第二行使用核心来初始化MCMC类,指定使用NUTS。我们在MCMC类中传递number_of_samples参数,这是用于生成后验分布的样本数量。我们将初始化的MCMC类赋值给mcmc变量,并调用run()方法,传递我们的数据作为参数。该函数返回mcmc变量。
这就是我们所需要的全部内容;以下代码定义了我们的数据,并调用了我们刚刚创建的函数,使用Beta(1,1)先验。
traffic = torch.tensor([5523., 1379.])conversions =torch.tensor([2926., 759.])inference = run_inference(model(1,1), number_of_samples = 1000, \ traffic = traffic, number_of_conversions = conversions)
traffic和conversions张量的第一个元素是控制组的计数,而每个张量的第二个元素是治疗组的计数。我们传递模型函数,以及用于控制我们先验分布的参数,以及我们定义的张量。运行此代码将生成我们的后验样本。然后,我们运行以下代码来提取后验样本,并将它们传递给Pandas的数据帧。
posterior_samples = inference.get_samples()posterior_samples_df = pd.DataFrame(posterior_samples)
请注意,此数据帧的列名是我们在模型函数中定义原始元素时传递的字符串。我们数据帧中的每一行包含从后验分布中抽取的样本,而每个样本代表页面转化率的估计值,即控制我们二项分布的概率值p。现在我们已经返回了样本,我们可以绘制我们的后验分布。
结果
一种展示AB测试两个测试组结果的有见地的方法是使用联合核密度图。它允许我们在概率空间中可视化样本的密度分布。下面的图表可以从我们刚刚构建的数据帧生成。

上图所示的概率空间可以沿对角线进行划分,线上方表示估计的转化率在治疗组中高于控制组,线下方反之。就图表中所示,从后验中抽取的样本在会表示转化率更高的区域中密集分布,这表明了治疗组的性能更好。需要强调的是,治疗组的后验分布比控制组更宽,反映了更高的不确定性。这是因为在治疗组观察到的数据较少。然而,该图表明确表明治疗组的效果优于控制组。通过收集从后验中抽取的样本并逐个元素地进行差异计算,我们可以说治疗组优于控制组的概率为90.4%。这个数字表明,从后验中抽取的样本中有90.4%位于联合密度图中对角线以上的区域。
这些结果是通过使用平坦(未知)先验获得的。使用知情先验可能有助于改进模型,特别是在观察数据有限的情况下。一个有用的练习是探索使用不同先验的影响。下图展示了Beta(2,2)概率密度函数以及重新运行模型时产生的联合图形。我们可以看到,使用Beta(2,2)先验对于两个测试组的后验分布产生了非常相似的结果。

从后验中抽取的样本表明,有91.5%的概率治疗组的表现优于对照组。因此,我们相对使用平坦先验更有确定性地认为治疗组优于控制组。然而,在这个例子中差异可以忽略不计。
我想强调这些结果中的另一件事。当我们运行推断时,我们告诉Pyro从后验中生成1000个样本。这是一个任意的数字,选择不同数量的样本可以改变结果。为了突出增加样本数量的影响,我进行了一个AB测试,其中控制组和治疗组的观测结果相同,每个组的整体转化率为50%。使用Beta(2,2)先验生成以下后验分布,随着样本数量的逐步增加。

当我们仅使用10个样本进行推断时,控制组和治疗组的后验分布相对较宽且形状不同。随着我们抽取的样本数量的增加,分布会收敛,最终生成几乎相同的分布。此外,我们观察到统计分布的两个属性:中心极限定理和大数定律。中心极限定理指出,随着样本数量的增加,样本均值的分布会收敛到正态分布,我们可以在上面的图中看到这一点。另外,大数定律指出,随着样本量的增长,样本均值会收敛到总体均值。我们可以看到右下角的瓷砖中的分布均值大约为0.5,这是每个测试样本中观察到的转化率。
贝叶斯AB测试的商业案例
贝叶斯AB测试可以帮助提升企业的测试和学习文化。贝叶斯统计推断不依赖于长期概率来得出结论,可以快速检测到测试中的小幅提升。测试结论可以更快地得出,并且学习速度增加。贝叶斯AB测试还允许在测试早期停止测试,如果通过”偷看”获得的结果表明测试组表现明显不如对照组,则可以停止测试。因此,测试的机会成本可以大大降低。这是贝叶斯AB测试的一项主要好处;结果可以不断监测,并且我们的后验分布可以不断更新。相反,对测试对象的早期检测可以帮助企业更快地实施变化,减少实施增收变化的延迟。面向客户的企业必须能够快速实施和分析测试结果,这是贝叶斯AB测试框架的便利之处。
总结
本文简要介绍了贝叶斯思维的背景,并使用Pyro探讨了贝叶斯AB测试的结果。希望您在本文中有所收获。正如在介绍中提到的,欢迎并鼓励反馈。如承诺的那样,我在下面链接了一些进一步阅读材料。
推荐阅读材料
以下图书对贝叶斯推断提供了良好的见解:
- 《信息与噪声:预测的艺术和科学》- Nate Silver
- 《原因和效应之书:因果关系的新科学》- Judea Pearl和Dana Mackenzie。尽管这本书主要关注因果关系,但第3章对贝叶斯推理是值得一读的。
- 《黑客的贝叶斯方法:概率编程与贝叶斯推断》- Cameron Davidson-Pilon。这本书也可在Git上获取,我在下面链接了它。
GitHub – CamDavidsonPilon/Probabilistic-Programming-and-Bayesian-Methods-for-Hackers: aka“贝叶斯…
aka“贝叶斯黑客方法”:贝叶斯方法+概率编程的介绍…
github.com
这些VoAGI文章详细解释了MCMC。
蒙特卡洛马尔可夫链(MCMC)解释
MCMC方法是一类使用马尔可夫链进行蒙特卡洛估计的算法。
towardsdatascience.com
贝叶斯推断问题、MCMC和变分推断
贝叶斯推断问题在统计学中的概述。
towardsdatascience.com