清雨影的Blog
  • README
  • 机器学习
    • 一般话题
      • 再谈正则化项
      • 论文阅读:“快把卷积神经网络中的平移不变性带回来”
      • 半监督/无监督学习收集
      • 收藏夹
    • 推荐系统
      • Understanding LightGCN in a visualized way
      • Learning To Rank 之 RankNet
      • 随想: BPR Loss 与 Hinger Loss
      • 关于AA测试和AB测试的一些思考
      • 无采样的矩阵分解
      • 收藏夹
    • 强化学习
      • Re:从零开始的Multi-armed Bandit
  • 高级物理引擎实战指南笔记
    • 弹簧质点系统
    • 光滑粒子法
    • 专题:线性方程组求解
  • 有限单元法
    • 1. 引论
    • 2. 基于直接刚度法的杆系有限元方法
    • 3. 针对复杂几何形状变形体的力学描述(1)
  • Web开发相关技术
    • JWT简介
  • 技术杂文
    • React-Script转Vite时引用路径的问题
    • Let's encrypt -- 让我们一起愉快的使用HTTPS
    • 干掉吸血雷,重塑和谐P2P环境
    • 开源CAN总线信号可编程台架
    • Linux下利用mdadm设置软件 RAID
    • 互不联网时代的自给自足
    • 为什么我劝你不要使用云计算?
    • 科学的公司内网连接技术选型
    • 构建家用NAS过程中的碎碎念
    • 简易的Linux迁移指北
    • 记录一次rsync命令引起的异常
    • 为FFMPEG添加Intel QSV支持
    • 备忘录
    • 福冈外免切替(中国驾照换日本驾照)攻略
    • 记一个离谱的MySQL语句的性能问题
    • 拯救变砖的OpenWRT路由器
    • 使用FRP进行内网穿透
  • 政治不正确
    • 吃屎系列:资本家如何喂员工吃屎
      • 华为251事件记忆
    • 吃屎系列:资本家如何喂用户吃屎
      • 互不联网公司是如何强奸用户的(持续更新)
    • 吃屎系列:大学如何喂学生吃屎
    • 推荐系统如何让我们变得极端
    • 互联网政治圈观察日志
    • 中国网络防火长城简史
    • 《线性代数》(同济版)——教科书中的耻辱柱
    • 杂谈
      • 访谈:为什么毛泽东时代工人的积极性很高?
      • 90年代到21世纪初的商业环境
    • 为什么不应该用国产手机
    • “救救孩子”
  • 随园食单
    • ボロネーゼ
    • 甜酒酿的制作
    • 香草与香料
    • 皮塔饼
    • 韭菜鸡蛋饼
    • 牛肉蔬菜汤
由 GitBook 提供支持
在本页
  • 起因
  • 改善
  • 实战
  • AA测试
  • AB测试
  • 展望
  • 更新:关于如何利用Beta分布进行AB测试(Bayesian Approach)

这有帮助吗?

  1. 机器学习
  2. 推荐系统

关于AA测试和AB测试的一些思考

上一页随想: BPR Loss 与 Hinger Loss下一页无采样的矩阵分解

最后更新于2年前

这有帮助吗?

起因

一般的AA/AB测试都怎么做呢?正常来说,取一部分用户,然后把用户分成2组(也可以是多组,本文中我们只讨论最典型的2组的情况),在一段时间的运行以后,比较其结果。

AB测试自不必说,主要测试新的算法在指定的监测指标上又没有达到预期,AA测试则一般用来测试AB测试和用户的稳定性,也可以作为AB测试的一个预演和对AB测试工具的测试。 有了AA测试,可以对AB测试的结果更为自信,当然,也有很多的文章讨论AA测试究竟是不是必要的,例如这一篇:。

就我的实践而言,AB测试中的确容易出现两组天然存在指标差异的情况,尤其是样本或者用户少的时候更容易发生,比如有的时候你就是不幸多分了几个Heavy User到某一个组去,导致你怎么测试都得不到正确的结果……当然,在实践中,这个问题造成的误差并不大,一般CTR误差都在0.1%这个量级,而且误差可以随着时间收敛,基本算法带来的提升都应该能覆盖这个误差。

AB测试中出现两组天然存在指标差异带来的更大问题是,AA测试通不过,有的时候换个分组的Salt Key结果就不一样了。造成这个问题的原因是,随着采样数据的上升,AA测试中两组的方差都逐渐收敛,这个时候两组之间的天然差异会被“固化”,这个时候AA测试就失败了。

这个问题困扰我们挺久,但是到我们之前的AA测试只有Pass/Fail两个结果,所以我们考虑不妨把AA测试变成标定AB测试的工具,故事就从这里开始了。

改善

假设在一次试验(或者试验的某个片段中)收集到如下数据:

Group
Variable
Comment

T(reatment)

测试组的展示次数

T(reatment)

测试组的点击次数

C(ontrol)

对照组的展示次数

C(ontrol)

对照组的点击次数

我们假定点击这个事件是服从Bernoulli分布的,也就是每次展示等价于抛一枚正反面概率固定为p的硬币(这里的假设是错误的,爆款新闻,突发事件,甚至只是单纯的周末或者深夜等等特殊时间都会影响点击率)。 那我们做了N次这样的试验,所以 C∼Bin(N,p)C \sim Bin(N,p)C∼Bin(N,p),C指的就是点击次数这个随机变量,p是二项分布试验的概率,那么C/N自然就是实际观测到的点击(转换)率CTR了,之后我们用符号x表示这个变量。

根据中心极限定理,当n特别大的时候,C是服从正态分布的(注意了,N一定要特别大),表达为 C∼N(Np,Np(1−p))C \sim N(Np, Np(1-p))C∼N(Np,Np(1−p))。 我们稍作转换x∼N(p,p(1−p)N)x \sim N(p, \frac{p(1-p)}{N})x∼N(p,Np(1−p)​)。

我们用表中的数据代入,我们就可以得到对照组和测试组的点击率分布。 记作:x1∼N(μ1,σ12)x_1 \sim N(\mu_1,\sigma_1^2)x1​∼N(μ1​,σ12​) 和 x2∼N(μ2,σ22)x_2 \sim N(\mu_2,\sigma_2^2)x2​∼N(μ2​,σ22​)。 其中,μ=CN\mu=\frac{C}{N}μ=NC​,σ2=μ(1−μ)N=N(N−C)N3\sigma^2=\frac{\mu(1-\mu)}{N}=\frac{N(N-C)}{N^3}σ2=Nμ(1−μ)​=N3N(N−C)​。

这里我们遇到第二个有点脏的点:用观测的CTR取代μ\muμ,好在CTR是μ\muμ的无偏估计,N又特别大,忍了。

直观的画出来,大概长这个样子:

到此为止,我们所有的理论准备完成。

实战

AA测试

AA测试,主要是估计一个提升的下确界,也就是我们最少要提升多少CTR,这个CTR甚至有可能是负数。举个例子,当试验组就是比对照组CTR要小一些的时候,哪怕在之后的测试中两者持平,我也可以判定对照组胜利,这和于老爷子出门没捡着钱就算丢的理论有异曲同工之妙。

在求这个数值之前,我们首先要确定一个单侧的置信度,比如0.05,或者0.01,根据你的容忍程度决定。随后我们可以根据这个数值,计算我们最少应该提升的CTR,如果形象的画个示意图,差不多是这个样子:

简单的写个代码实现一下:

from scipy.stats import norm
​​from math import sqrt

def get_minimal_lift(C_1, N_1, C_2, N_2, confidence_level):
    mean_1 = C_1 / N_1
    mean_2 = C_2 / N_2
    delta_mean = mean_1 - mean_2
    var_1 = mean_1 * (1 - mean_1) / N_1
    var_2 = mean_2 * (1 - mean_2) / N_2
    return norm(delta_mean, sqrt(var_1 + var_2)).ppf(1 - confidence_level)

AB测试

我们用AA测试得到了一个最少提升的参数,不妨记这个最小提升为ϵ\epsilonϵ。

那我们AB测试究竟过了没有(过了的意思是,测试组的算法对于对照组来说有提升),或者说,我们能以怎样的置信度判断AB测试过了。 也就是说我们要求:P(x1−x2≥ϵ)P(x_1-x_2 \ge \epsilon)P(x1​−x2​≥ϵ)。由于x3=x1−x2x_3=x_1-x_2x3​=x1​−x2​,所以也就是判断 P(x3≥ϵ)P(x_3 \ge \epsilon)P(x3​≥ϵ)。

根据公式很容易得到:

P(x1−x2≥ϵ)=P(x3≥ϵ)=1−Φμ1−μ2,σ12+σ22(ϵ)P(x_1-x_2 \ge \epsilon)=P(x_3 \ge \epsilon)=1-\Phi_{\mu_1-\mu_2,\sigma_1^2+\sigma_2^2}(\epsilon)P(x1​−x2​≥ϵ)=P(x3​≥ϵ)=1−Φμ1​−μ2​,σ12​+σ22​​(ϵ)

简单的写个代码实现一下:

from scipy.stats import norm
​​from math import sqrt

def get_passed_prob(C_1, N_1, C_2, N_2, epsilon):
    mean_1 = C_1 / N_1
    mean_2 = C_2 / N_2
    delta_mean = mean_1 - mean_2
    var_1 = mean_1 * (1 - mean_1) / N_1
    var_2 = mean_2 * (1 - mean_2) / N_2
    return 1.0 - norm(delta_mean, sqrt(var_1 + var_2)).cdf(epsilon)

展望

但是我还没搞出来,因为还没有得到CTR误差的解析解。而Thompson Sampling是利用一种类似蒙特卡洛的方式来解决这个问题的。

等我下次无聊的时候,或许会尝试去计算一下吧。

更新:关于如何利用Beta分布进行AB测试(Bayesian Approach)

之前我提到,我没有求得Beta分布的解析解。

而Thompson Sampling是利用一种类似蒙特卡洛的方式来解决这个问题的。

我简直是个蠢货,其实解决方案已经被我写出来了而我不自知。我的同事今天给出了一个不算漂亮但是又极其有效的解决方案:采样。

仔细反省一下为什么我会这么蠢,因为在我看来,AA/AB已经是一种采样了,我们很难想象用预估出来的参数再做采样,这会导致一定程度的失真。

但是换个角度想,如果只是为了求得一个封闭解不存在或者很难求的函数的数值解,蒙特卡洛也不是不能接受,采样频率调高一点就能获取足够的精度。一直自觉是个工程师,但是脑子居然在这种地方卡壳了实在是不应该。

参考资料:

我觉得两个随机变量还是太多,由于我们关注的并不是CTR本身,而是CTR的差异。 不妨设 x3=x1−x2x_3=x_1-x_2x3​=x1​−x2​,我们可以根据轻易得到 x3∼N(μ1−μ2,σ12+σ22)x_3 \sim N(\mu_1-\mu_2,\sigma_1^2+\sigma_2^2)x3​∼N(μ1​−μ2​,σ12​+σ22​)。

其实我们不仅仅可以使用正态分布,我们还可以借鉴Thompson Sampling中的思路,利用Beta分布对CTR进行建模,而Bandit本身,其实可以看作一个实时的,特殊的AB Testing。详细可以参见这篇:。

假设我们现在通过AA/AB测试获得了数据,求出了C组的分布 xc∼Beta(ac,bc)x_c \sim Beta(a_c, b_c)xc​∼Beta(ac​,bc​) 和T组的分布 xt∼Beta(at,bt)x_t \sim Beta(a_t, b_t)xt​∼Beta(at​,bt​) ,我们现在要求期望 E(xt−xc)E(x_t - x_c)E(xt​−xc​) 或者方差标准差之类的统计参数,直接用的 rvs 生成一些样本,将它们相减以后获取新的分布。

N1N_1N1​
C1C_1C1​
N2N_2N2​
C2C_2C2​
A/A Testing: A Waste of Time or Useful Best Practice?
正态分布求和公式
Re:从零开始的Multi-armed Bandit
scipy.stats.beta
scipy.stats.beta
Understanding empirical Bayes estimation (using baseball statistics)
bayesian ab testing