背景
在电商领域中,经常要计算或者CTR(点击通过率),CVR。以点击率CTR为例,CTR根据统计指标 CTR = 点击量/ 曝光量;具体需要看公司要求,有的是点击uv/ 曝光uv ,有的是点击pv/曝光pv ;然而实际应用中,会遇到两个问题:
- 【新品问题】新商品点击率的预测和计算问题
对于新上线的商品,其曝光为0,点击量也为0,此时这件商品的CTR应该为0 还是赋值一个初始值呢? - 【数据不可信问题】不同商品点击率之间的比较
有两件商品A和B ,商品A曝光了10次点击了5次,商品B曝光了100次点击率50次
Click_a = Click_b 这样合理吗?从置信度的角度来说,Click_b更加可信。
理论基础
对于一件商品或者或者一个广告,对于某次曝光,用户要么点击,要么不点击,符合二项分布。因此下文对于点击率的贝叶斯平滑,都是基于以下假设:
对于某件商品或者广告X ,其是否点击是一个伯努利分布(Bernoulli):
x
∈
B
e
r
(
r
)
x\in Ber(r)
x∈Ber(r)
X表示是否点击,当X=1时表示点击,当X=0时表示未点击,r表示某件商品被点击的概率,即点击率。
bayes & Wilson
| 指标 | 含义 |
|---|---|
| bayes | 贝叶斯平滑的最终落脚点是要估计Beta分布(点击率r服从的分布)中的参数α和β |
| Wilson | 指在一定置信度下,真实的 CTR 范围是多少 |
Bayes 分布
共轭先验
-
如果能够找到一个分布:π®,它是f(x│r)的共轭先验,那么r的后验分布π(r|x) 和先验分布π(r)会有一样的形式。这里的共轭指的是π® 和π(r|x) 通过 f(x|r) 联系起来了。
-
假设广告是否点击服从伯努利分布,参数为r ;对于点击次数服从二项分布,即 f(click,expose|r)∼Bin®。二项分布的共轭先验是beta分布,beta分布的参数是
α和β,即π® =Beat(α,β) 。根据共轭先验的定义,r的后验分布π(r|x)的形式和其先验分布π®是一样的.- π(r|x)=Beta(α, β)
-
为什么很多文章会假设点击率服从Beta分布的理由,因为最终的平滑的因子是Beta分布(先验分布)中的两个参数。
Beta 参数α和β的估计
贝叶斯平滑的最终落脚点是要估计Beta分布(点击率r服从的分布)中的参数α和β
利用矩估计可以得到:
- α = X ^ ( X ^ ( 1 − X ^ ) S 2 − 1 ) \alpha = \hat X(\frac{\hat X(1-\hat X)}{S^2}-1) α=X^(S2X^(1−X^)−1)
- β = ( 1 − X ^ ) ( X ^ ( 1 − X ^ ) S 2 − 1 ) \beta = (1-\hat X)(\frac{\hat X(1-\hat X)}{S^2}-1) β=(1−X^)(S2X^(1−X^)−1)
- 取连续一段时间的数据,比如一周,然后在每天的数据中计算每件商品或者广告的点击率,之后求这些点击率的均值和方差,然后带入到公式中。
CTR的计算
c t r = c l i c k + α e x p o s e + α + β ctr = \frac{click+\alpha}{expose+\alpha+\beta} ctr=expose+α+βclick+α
import numpy
import random
import scipy.special as special
class BayesianSmoothing(object):
def __init__(self, alpha, beta):
self.alpha = alpha
self.beta = beta
def sample(self, alpha, beta, num, imp_upperbound):
# 先验分布参数
clicks = []
exposes = []
for clk_rt in numpy.random.beta(alpha, beta, num):
imp = imp_upperbound
clk = int(imp * clk_rt)
exposes.append(imp)
clicks.append(clk)
return clicks, exposes
def update(self, imps, clks, iter_num=1000, epsilon=1e-5):
for i in range(iter_num):
new_alpha, new_beta = self.__fixed_point_iteration(imps, clks, self.alpha, self.beta)
if abs(new_alpha-self.alpha)<epsilon and abs(new_beta-self.beta)<epsilon:
break
self.alpha = new_alpha
self.beta = new_beta
def __fixed_point_iteration(self, imps, clks, alpha, beta):
numerator_alpha = 0.0
numerator_beta = 0.0
denominator = 0.0
for i in range(len(imps)):
numerator_alpha += (special.digamma(clks[i]+alpha) - special.digamma(alpha))
numerator_beta += (special.digamma(imps[i]-clks[i]+beta) - special.digamma(beta))
denominator += (special.digamma(imps[i]+alpha+beta) - special.digamma(alpha+beta))
return alpha*(numerator_alpha/denominator), beta*(numerator_beta/denominator)
def main():
bs = BayesianSmoothing(1, 1)
# clk, exp = bs.sample(500, 500, 10, 1000)
clk = [5, 50, 500, 5000]
exp = [10, 100, 1000, 10000]
print('原始数据')
for i, j in zip(clk, exp):
print(i, j)
bs.update(exp, clk)
print('bayes光滑先验分布参数:', bs.alpha, bs.beta)
fixed_ctr = []
for i in range(len(clk)):
origin_ctr = clk[i] / exp[i]
new_ctr = (clk[i] + bs.alpha) / (exp[i]+bs.alpha+bs.beta)
print('修正前{}, 修正后{}'.format(round(origin_ctr, 3), round(new_ctr, 3)))
if __name__ == '__main__':
main()
Wilson方法
参数
| 指标 | 含义 |
|---|---|
| p | 概率,即点击率(ctr) |
| n | 样本总数,即曝光数(expose) |
| z | 在正态分布里, μ + z × σ \mu+z×\sigma μ+z×σ会有一定的置信度。例如z=1.96,就有95%的置信度 |
公式

import numpy as np
def wilson_ctr(clks, imps, z=1.96):
origin_ctr = clks * 1.0 / imps
if origin_ctr > 0.9:
return 0.0
n = imps
first_part_numerator = origin_ctr + z**2 / (2*n)
second_part_numerator_2 = np.sqrt(origin_ctr * (1-origin_ctr) / n + z**2 / (4*(n**2)))
common_denominator = 1 + z**2 / n
second_part_numerator = z * second_part_numerator_2
new_ctr = (first_part_numerator-second_part_numerator)/common_denominator
return new_ctr
test_case = [(5, 10), (50, 100), (500, 1000), (5000, 10000)]
for item in test_case:
print(wilson_ctr(*item))
1099

被折叠的 条评论
为什么被折叠?



