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

2023-04-21 23:42:53 浏览数 (3)

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=mutimes [frac{mutimes(1-mu)}{sigma^{2}}-1 ]
beta=(1-mu)times [frac{mutimes(1-mu)}{sigma^{2}}-1 ]

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

4.beta分布画图

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

代码语言:python代码运行次数:0复制
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。

0 人点赞