参考:
从假设检验到AB实验——面试前你要准备什么?
一文入门A/B测试(含流程、原理及示例)
A/B testing(一):随机分配(Random Assignment)里的Why and How
AB测试的简介
我们都或多或少听说过A/B测试,即便没有听过其实也被动的参与过——作为受试者。AB实验是数据分析、产品运营、算法开发在工作中都时常接触到的工作。在网站和APP的设计、产品的运营中,经常会面临多个设计/运营方案的选择。小到按钮的位置、文案的内容、主题的颜色,再到注册表单的设计、不同的运营方案,都有不同的选择。A/B test可以帮助我们做出选择,消除客户体验(UX)设计中不同意见的争执。按钮颜色、广告算法、标签排序,这些互联网产品里常见的功能与展示都是在一次次AB实验中得到优化。
所谓A/B test,其实类似于初中生物说的对照试验。对用户分组,每个组使用一个方案(方案应遵从单变量前提),在相同的时间维度上去观察用户的反应(体现在业务数据和用户体验数据上)。需要注意的是各个用户群组的组成成分应当尽量相似,譬如新老用户很有可能表现出较大的偏好差异。最后根据假设检验的结果,判断哪些版本较之原版有统计意义上的差异,并根据效应量选出其中表现最好的版本。
一个完整的AB测试流程
- 分析现状,建立假设:分析业务,确定最高优先级的改进点,作出假设,提出优化建议。
- 设定指标:设置主要指标来衡量版本的优劣;设置辅助指标来评估其他影响。
- 设计与开发:设计优化版本的原型并完成开发。
- 确定测试时长:确定测试进行的时长。
- 确定分流方案:确定每个测试版本的分流比例及其他分流细节。
- 采集并分析数据:收集实验数据,进行有效性和效果判断。
- 给出结论:①确定发布新版本;②调整分流比例继续测试;③优化迭代方案重新开发,回到步骤1。
注意点
测试时长 : 测试时长不宜过短,否则参与实验的几乎都是该产品的高级用户(短时间新用户流入较少)。
分流(抽样):应该保持同时性、同质性、唯一性、均匀性。
同时性:分流应该是同时的,测试的进行也应该是同时的。
同质性:也可以说是相似性,是要求分出的用户群,在各维度的特征都相似。可以基于用户的设备特征(例如手机机型、操作系统版本号、手机语言等)和用户的其他标签(例如性别、年龄、新老用户、会员等级等)进行分群,每一个A/B测试试验都可以选定特定的用户群进行试验。 控制变量
思考:如何判断是不是真的同质? 可以采用AAB测试。抽出两份流量进行A版本的测试,进行AA测试,并分别与B版本进行AB测试。通过考察A1和A2组是否存在显著性差异,就可以确定试验的分流是否同质了。唯一性: 即要求用户不被重复计入测试。
-
均匀性: 要求各组流量是均匀的。希望把每一个用户随机分配到试验组(treatment group)和控制组(control group),但为什么我们要做随机分配(why we need to do random assignment),和应该怎么做随机分配(how to do random assignment)可以参考:随机分配里的Why and How。(统计学原理上,我没有找到均匀性这一要求的依据,其实双样本的假设检验并不要求两个样本的数量相等或相近。当然从直观上是可以理解,希望分出的用户组越相近越好,包括人数的相近。)
- 1 现在有一个user,他有一个user_id
- 2 每run一个实验,我们都有一个特定的salt,把这个user_id和这个salt拼接起来成一个长字符串;
- 3 把2中得到的长字符串扔进一个哈希函数(可以是MD5或者SHA1),这里用MD5,然后生成一条哈希数据(Hashed data);
- 4 取3中得到的哈希数据的头六位字符,转换成一个十六进制整数;
- 5 拿4中得到的整数去除以最大的六位十六进制数字(0xffffff),注意用浮点数相除,会得到一个介于0和1之间的浮点数。
- 6 根据第5步得到的浮点数是否大于预设阈值,决定这个用户的分组。举个例子,如果我们想得到50-50的平均分配,那么我们会预先设定一个阈值0.5,如果第5步得到的是0.4,那么这个用户被分到控制组,因为它小于0.5,如果第5步得到的是0.6,这个用户被分配到试验组。
理由:
- 我之前做随机的时候都是取随机数和阈值进行比较,超过阈值一组,低于阈值的是另一组。 但是如果发生了错误,对用户精准定位就会比较麻烦(随机数是随机的,需要靠随机数种子控制,而且每次试验取不同的随机数种子)。这种方法是可以对每次试验的用户的分组进行实现的(每个用户的id是确定的,每个试验的salt也是确定的),不仅方便精确定位用户,而且实验出问题了也方便debugging。
- 一个实验对应一个salt,每个实验不一样,这样保证不同实验有完全不一样的随机分配。因为一家公司一天可能做很多实验,如果一个用户老是被分到试验组,他用户体验会比较差
- 最后得到的随机分配结果还是比较'Random'的,虽然本质上都是假Random。
A/B测试只能有两个版本么?
A/B test不是只能A方案和B方案,实际上一个测试可以包含A/B/C/D/E/……多个版本,但是要保证单变量,比如按钮的颜色赤/橙/黄/绿/青/蓝/紫,那么这七个方案是可以做A/B测试的;但如果某方案在旁边新增了另一个按钮,即便实验结果产生了显著差异,我们也无法判断这种差异的成因究竟是谁。
同一段时间内可以做不同的A/B测试么?
比如一个test抽取总体20%的流量做按钮颜色的实验,另一个test也抽取总体20%的流量做布局样式的实验。是否可行?
我认为是可行的。但要求多个方案并行测试,同层互斥。如果从总体里,先后两次随机抽取20%流量,则很有可能会有重叠的用户,既无法满足控制单变量,又影响了用户的使用体验。
- 同层指的是在同一流量层中创建实验,在此层中创建的实验共享此层中的100%流量。
- 互斥指的是在此层中,一个设备有且只能分配到此层多个实验中的某一个实验。
假设检验的示例
数据:对web新旧页面的A/B测试结果,来自Udacity的示例案例
- 用户id
- 时间戳
- 分组(实验组还是对照组)
- 展示页面的新旧版本(新版本还是旧版本)
- 该用户是否发生了转化(0-未转化、1-转化)
ab测试目的:判断新旧两版页面在用户的转化情况上是否有显著区别
数据清洗
- 查看总的数据行数 和 (去重后)的独立用户进行比较
- 发现上述不一致(出现了重复统计的用户)
- 利用duplicated考察重复数据
- 分组group和展示页面的新旧版本landing_page应当是匹配的(treatment-new_page、control-old_page),但是发现存在不匹配的情况。 去掉不匹配的记录。
- 去掉不匹配的记录之后利用drop_duplicates去掉重复值(发现此时只有一条重复值)。
6.检查缺失值,若存在缺失值可以进行直接删除、填充等处理。 - 比较收到新页面的用户占比和老页面的用户占比。
假设检验
1.给出零假设和备择假设:
记旧页面的转化率为,新页面的转化率为
我们的目标是为了说明, 因此作出如下假设:
即
即
这个业务可以抽象为:X是否被转化,也就是两点分布。对于n个个体则为二项分布。
因此有:
和
由于满足两点分布
有以上的铺垫下面求
此刻和都是未知的,所以要通过样本成数进行估计,即设,其中
因此:-
确定检验类型和检验统计量
独立双样本。样本大小n>30,总体的均值和标准差未知,用Z检验。检验统计量为:
为转化率的联合估计,,其中。 给定显著性水平
方法一、直接根据公式计算检验统计量Z
旧版总受试用户数: 145274 旧版转化用户数: 17489 旧版转化率: 0.1204
新版总受试用户数: 145310 新版转化用户数: 17872 新版转化率: 0.1230
转化率的联合估计: 0.12169
检验统计量z: -2.1484
, 拒绝域为。
z=-2.15落入拒绝域。在显著性水平时,拒绝零假设。
假设检验并不能真正的衡量差异的大小,它只能判断差异是否比随机造成的更大。cohen-s-d,因此,我们在报告假设检验结果的同时,给出效应的大小。对比平均值时,衡量效应大小的常见标准之一是Cohen's d,中文一般译作科恩d值:
这里的标准差,由于是双独立样本的,需要用合并标准差(pooled standard deviations)代替。也就是以合并标准差为单位,计算两个样本平均值之间相差多少。双独立样本的并合标准差可以如下计算:
Cohen's d的值约为-0.00797,绝对值很小。两者虽有显著性水平5%时统计意义上的显著差异,但差异的效应量很小。可以简单地理解为显著有差异,但差异的大小不显著。
利用python进行计算:
statsmodels.stats.proportion.proportions_ztest
第一个参数为两个概率的分子
第二个参数为两个概率的分母
第三个参数alternative[‘two-sided’, ‘smaller’, ‘larger’]分别代表[双侧,左尾,右尾]
import statsmodels.stats.proportion as sp
# alternative='smaller'代表左尾
z_score, p_value = sp.proportions_ztest([convert_old, convert_new], [n_old, n_new], alternative='smaller')
可以同时得到检验统计量和P值,得到的z值和前面计算的完全相同,落在拒绝域,故拒绝零假设。同时我们也得到了p值,用p值判断与用检验统计量z判断是等效的,这里p值约等于0.016, ,同样也拒绝零假设。
在python中一般的z检验是这样做的statsmodels.stats.weightstats.ztest
直接输入两组的具体数值即可,同样有alternative参数控制检验方向。
蒙特卡洛模拟
蒙特卡罗法其实就是计算机模拟多次抽样,不过感觉好强啊,结果直观又容易理解,能够很好的帮助初学者理解分布、p值、显著性、分位数等概念。
在零假设成立的前提下( 即 ),为临界情况(零假设中最接近备择假设的情况)。如果连临界的情况都可以拒绝,那么剩下的部分更可以拒绝()。
可以利用样本数据求得整体的总转化率,下面考察临界情况,以为新旧版共同的转化率,即取,分别进行n_old次和n_new次二项分布的抽样。
重复抽样多次,每次抽样之后,都可以得到:旧版本与新版本之间的转化率差值。而此数据的转化率差值为:-0.0026。
可以观测模拟得到转化率差值的分布(由于概率相等,应该接近于正态分布)和此数据的转化率差值的位置关系。
在diffs列表的数值中,有多大比例小于ab_data.csv中观察到的转化率差值?此次模拟的结果为0.0155,每次模拟的结果都不太相同,但都在p值(0.016)上下浮动,且随着样本量的增大,更加接近p值。
上图的含义是,在时进行的10000次随机模拟得到的差值中,只有1.55%比数据集中的差值更极端,说明我们这个数据集在的前提下是小概率事件。如果则得到的差值的分布仍然近似于正态分布但是其均值会右移,此时数据中的差值会更加极端。因此,此数据的结果是零假设中的极端情况,零假设很有可能是不成立的。
若diffs的分布就是标准正态(这里只是近似),则竖线左侧的面积占比其实就是p值(左侧or右侧or双侧要根据备择假设给定的方向
p值到底要多小才算真的小?
这需要我们自己给定一个标准,这个标准其实就是,是犯第一类错误的上界,常见的取值有0.1、0.05、0.01。
所谓第一类错误,即拒真错误,也就是零假设为真,我们却拒绝了。在这个例子里就是——新旧版转化率明明相等,只不过我们很非酋,得到的样本正好比较极端,以至于我们错误地认为新旧版转化率不等。所以要取定一个时,认为原假设在该显著性水平下被拒绝。(如果我们取的是0.01而不是0.05,则这个例子里就拒绝不了零假设了。)
并不是越小越好,这与第二类错误的概率有关,越小,则就越大,而第二类错误的概率也需要控制在一定的范围,因此不能一味地取极小的。