前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >浅谈贝叶斯平滑在CTR上的实践

浅谈贝叶斯平滑在CTR上的实践

原创
作者头像
千万别过来
修改2023-04-21 23:42:53
5K5
修改2023-04-21 23:42:53
举报
文章被收录于专栏:推荐算法学习推荐算法学习

0. 前言

item得分的计算通常用于召回并且配合用户兴趣画像一同使用。item得分计算的方式可以归为三类:

  1. 千人一面(最常用):通常是CTR热度,按照整个用户来算会有变动但波动不大
  2. 千人百面:折中方案,通过用户分群找代表用户。
  3. 千人千面:资源消耗较大,一般做不到。

本文重点针对“千人一面”的item得分计算方式来浅谈一下贝叶斯平滑在CTR上的实践。

1. 问题

以内容分发为例,在计算item得分的时候,可以有很多维度,比如item的互动热度、变化率、创建时间、CTR、负反馈等。若取CTR作为得分依据来对item进行召回,则我们希望可以召回点击率较高且数据相对置信的一批item。这时候可能就会有以下两个问题:

  1. 新的item没有曝光点击数据,那么就没有得分。(当然这里也可以通过一些冷启策略解决)
  2. 两个item,第一个CTR=10/20,第二个CTR=1000/2000。此时CTR得分一模一样,但是很明显后者数据更加置信。

2. 简单的思考

最简单的思考,我们可以人为的定两个值,a和b,将CTR改造成:

CTR=\frac{click+a}{imp+b}

但是这样的改造只能解决问题1,对于问题2的解决效果并不是很好。因此我们思考这里可不可以做的不那么“暴力”?简单一点的改造是取历史日志的平均点击、曝光,但是还是不够优雅,并且若给的a、b值太大会导致所有item的打分向平均值集中

我们知道,一个item是否被点击是符合伯努利分布的,而伯努利分布中只有一个参数P,我们可以通过取一些日志算出P,继而得到后验,接着后验又可以作为下一次迭代的先验,但是这样不好计算。

3. beta分布改进

因此我们希望先验和后验同属于一个分布,只是其中的参数不一样,这样我们在实现的时候只用更新里面的参数,不需要通过大量的计算。而伯努利分布共轭分布beta分布,因此可以利用beta分布做贝叶斯平滑,并将CTR得分改造成:

CTR=\frac{click+\alpha}{imp+\alpha+\beta}

其中α和β通过矩估计来获得,具体的:

\alpha=\mu\times [\frac{\mu\times(1-\mu)}{\sigma^{2}}-1 ]
\beta=(1-\mu)\times [\frac{\mu\times(1-\mu)}{\sigma^{2}}-1 ]

其中μ为均值,σ为方差。

4.beta分布画图

Beta分布的横轴和纵轴分别表示随机变量的取值和概率密度函数(Probability Density Function,PDF)的取值。横轴表示Beta分布中随机变量的取值,取值范围为[0,1],可以理解为某个事件发生的概率,比如CTR。在Beta分布中,横轴的取值范围是由Beta分布的参数α和β决定的。纵轴表示在Beta分布中某个随机变量取某个特定值的概率密度,取值范围为[0,∞),表示在横轴某一点处的概率密度。用python画图:

代码语言:python
复制
import numpy as np
from scipy.stats import beta
import matplotlib.pyplot as plt

ab_pairs = [(2.81,21.92), (14.19,123.57)]

x = np.linspace(0, 1, 1002)[1:-1]

for a, b in ab_pairs:
    print(a, b)
    dist = beta(a, b)
    y = dist.pdf(x)
    plt.plot(x, y, label=r'$\alpha=%.1f,\ \beta=%.1f$' % (a, b))

# 设置标题
plt.title(u'Beta Distribution')
# 设置 x,y 轴取值范围
plt.xlim(0, 1)
plt.ylim(0, 25)
plt.legend()
plt.savefig("./beta.svg", format="svg")

4.1 beta分布示例图
4.1 beta分布示例图
  • 当beta分布的α和β参数很小时:意味着分布的概率密度函数在中心点处较高,但是尾部的概率密度函数下降得很快。这通常被解释为分布的方差很大,也就是说,数据的差异很大,没有一个明显的趋势或者结论。因此,当α和β参数很小时,通常表示我们对数据的先验知识很少,或者数据来源不可靠。
  • 当beta分布的α和β参数很大时:说明该分布的峰值比较尖锐,方差较小,而且分布趋于对称。这反映了样本数据非常准确地估计了真实参数,使得数据置信度非常高。(这里先埋个坑,后面细说)

5. 工程上的实践

接下来我们就要来计算α和β了,到这里我们根据粒度大脑袋一拍可以想到三种方案:

  1. 对于所有的item只算一套α和β作为平滑参数(粗粒度)
  2. 对每个类别分桶下分别计算一套α和β作为每一个类别的平滑参数(折中)
  3. 对于每一个item,都计算一套α和β作为每一个平滑参数(细粒度)

很显然,方法3根本就是错的,无论是从实现还是贝叶斯平滑的角度出发都不正确。

【我们希望的是】:能用一批“有代表性”的item求出其beta分布,来做所有item的平滑。同时,若一个item曝光点击数据较小,我们认为它数据不够置信,那么我的先验(α和β)应该起到主导作用;若一个item曝光点击数据足够多,我们认为它已经足够置信,则先验(α和β)作用几乎没用。

因此可以采用的是方法1和方法2。针对这两个方法下面也会展开讨论。

5.1 方法1

对于所有的item只算一套α和β作为平滑参数(粗粒度),具体在实践中,通常取一个周期(比如7天),然后在每天,按uid、itemid、traceid进行去重,接着分别对每一个item计算CTR,然后根据这些CTR求出当天的方差和均值,并根据方差和均值计算出每天的α和β,再求7天的平均α和β。用SparkSQL来实现,如下代码所示:

代码语言:sql
复制
SELECT AVG(alpha) AS avg_alpha
    , AVG(beta) AS avg_beta
FROM(
    SELECT ftime
        , mean
        , variance
        , mean*(mean*(1-mean)/variance-1) AS alpha
        , (1-mean)*(mean*(1-mean)/variance-1) AS beta
    FROM(
        SELECT ftime
            , AVG(ctr) AS mean
            , VARIANCE(ctr) AS variance
        FROM (
            SELECT ftime
                , item_id
                , imp
                , clk
                , ctr
            FROM mid_tb
            WHERE imp>500
        )
        GROUP BY ftime
    )
    WHERE NOT isnan(variance)
)
WHERE alpha IS NOT NULL AND beta IS NOT NULL

这里需要注意的是,在计算方差和均值的时候可以卡一个曝光阈值剔除长尾数据,比如我这里是WHERE imp>500。如果不剔除长尾数据,那么CTR的方差会过大,导致矩估计计算得出的平滑参数过小,进而导致平滑效果失效。下图反应的是卡阈值之前的beta分布,以及卡阈值之后的beta分布:

5.1 卡阈值前后beta分布
5.1 卡阈值前后beta分布

上图可以看出黄色曲线没卡阈值,有很多长尾数据,比如曝光3次点击2次,CTR=2/3。导致计算出的α=0.1,β=2.3,基本上就没有平滑的作用了,其原因是长尾数据的CTR不置信增大了方差。但是其实不难发现,方法1即使是卡了曝光阈值,计算出的α和β依然没有很大,平滑力度还是有限

5.2 方法2

对每个类别分桶下分别计算一套α和β作为每一个类别的平滑参数(折中)。方法1实现起来较为简单,线上工程部署也比较方便,因为只需要求得一组α和β就可以了。但是其也拥有一定的局限性:不同品类(category)的item的CTR分布天然就会有差异,这个差异和产品形态有关系。例:一个主打交友的产品,那“交友”类的帖子CTR可能就会普遍高于“体育”类的帖子。下图展示的是一个产品中不同品类的CTR差异,横轴为品类,纵轴为CTR:

5.2 不同品类下的CTR(纵轴CTR)
5.2 不同品类下的CTR(纵轴CTR)

基于上面的情况,若直接用方法1,计算出的方差仍然较大,导致平滑粒度不够。针对这个问题一个简单的方法是人工放大平滑参数,增强平滑力度,但是会改变其原始分布。比如针对方法1中的α=1.7,β=35.9,人为放大4倍,变成α=6.8,β=143.6。

5.3 放大4倍后的beta分布
5.3 放大4倍后的beta分布

更优雅的方法则是:由于每个品类的CTR差异较大,因此可以对每一个品类下分别计算α和β。一方面可以减少方差,增加平滑力度,另一方面也是考虑的每个品类本身的CTR分布不同。

不过到这里有个坑(第四章末提到),如果品类(或者tag)的数目过多,也就是分的太细,可能导致每一个品类(或者tag)下的item个数较少,出现长尾品类。这时候若计算每个品类下的α和β很可能会求出特别大的α和β,因为数据可能只有一两条,方差很小导致beta分布以为数据足够置信,实际上并不是。针对这种情况的解决方法是:

  • 采用粗粒度的品类体系,保证每个品类下的数据量足够。
  • 或者退而求其次采用5.1的方法1。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0. 前言
  • 1. 问题
  • 2. 简单的思考
  • 3. beta分布改进
  • 4.beta分布画图
  • 5. 工程上的实践
    • 5.1 方法1
      • 5.2 方法2
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档