广告CTR预估中真实CTR的离散化方法

来源

在广告CTR预估的过程中,有一个十分重要的特征,那就是广告本身的CTR,从直观上也能明白,对于一个广告,在曝光次数充分的前提下,基于统计的CTR很大程度上反应了广告本身的信息。

作为一个特征,很自然的,我们可以直接把它作为向量的某一维的数值放入样本,这样是有一定的作用的,但作用还不够:首先,对于点击率差距较小的广告,它无法捕捉到相似性;其次,对于点击率差距较大的广告,也很难捕捉到差异性。这是因为一维数值特征的表达力欠缺引起的,那么,怎么解决这个问题呢?

设想,假如我们能有一种手段,能对CTR进行分类,那么,我们就可以借助One Hot Encoding的方法对这个特征进行处理,这样处理之后的特征往往对线性模型更加友好,那么怎么实现这件事呢,我们有以下几种考虑:

1.直接人工分段,人为的根据CTR的分布,来分区间;

2.使用GBDT,K-means等分类算法作映射

可以看到上述两种方法,都有一个明显的缺陷:依赖于先前观测到的数据的分布,这也就意味着,在特征转化的过程中,需要维护额外的数据结构,有没有什么稍微更加简单的方式能做这件事呢?

我们希望存在一个函数满足:

index=f(CTR)

最简单的 f=round(CTR * (N-1)) ,其中N是我们预设的维度的数目,比如N=500,那么CTR会被均匀的映射到0~499共500个维度上,这样做有一定的效果,但是还不够。

我们知道,CTR的分布区间是在[0,1]上,但实际情况中却往往不是这样,实际的CTR往往非常的小,能够超过10%点击率的广告几乎是九牛一毛,所以,基于这样的事实,我们是可以对CTR进行区间放缩的,定义一个 upperBound,一个lowerBound:

CTR_{new}=\frac{CTR_{original}-lowerBound}{upperBound-lowerBound}

实践中也证明,在做完这样一个放缩之后,CTR的分布变得更加的均匀,实际中也取得了更好的效果


那么该怎么来评估这件事情呢?

对于一个一般的Category类型的特征,要衡量它的好坏,我们会选择卡方检验或者使用信息增益,对于信息增益,我们知道一般有如下定义:

IG(T)=H(C)-H(C|T)= -\sum_{i=1}^n{P(C_i)log_2(P(C_i))} +P(t)\sum_{i=1}^n{P(C_i|t)log_2{P(C_i|t)}} +P(\overline{t})\sum_{i=1}^nP(C_i|\overline{t})log_2P(C_i|\overline{t})

信息增益表征了从总体中去掉该类特征时,总体中丢失掉的信息量,也就客观的反映了该特征的重要性。

对于一份包含CTR与是否点击(点击为1,否则为0)的样本,采用不同的CTR离散化方法,计算信息增益,信息增益大的,也就意味着这种方式可能比较有效。

计算信息增益的Python代码如下:

import math

def entropy(p):
    if p <= 0 or p >= 1:
        return 0.0
    return - p * math.log(p, 2) - (1 - p) * math.log(1 - p, 2)


def calc_gain(lables, indices):
    max_index = max(indices)

    index_map = [[0, 0] for x in range(max_index + 1)]
    i = 0
    for index in indices:
        if lables[i] == 1:
            index_map[index][0] += 1
        else:
            index_map[index][1] += 1
        i += 1
    index_map = filter(lambda t: t[0] > 0 or t[1] > 0, index_map)

    # print "index map:", index_map

    total_positive_count = 0.0
    total_count = 0.0
    for x in index_map:
        total_positive_count += x[0]
        total_count += (x[0] + x[1])

    total_entropy = entropy(total_positive_count / total_count)

    conditional_entropy = 0.0
    for x in index_map:
        index_count = float(x[0] + x[1])
        conditional_entropy += (index_count / total_count) * entropy(x[0] / index_count)

    return total_entropy - conditional_entropy

同时,对于离散化之后的Index在每个桶内的分布,我们可以画一个频率分布直方图,直觉上,这个直方图越平缓越好,而不是像CTR分布一样,大家都挤在一堆,彼此不可分。以下是一个示例:

直接将CTR乘上500后数值分布情况,可以看到数据分布非常极端


仔细观察这个频率分布直方图,再联系到我们想要达到的目的:想要原本密集的区间的值尽可能的分散,原本分散的区间的值尽量的聚合。在图像处理领域,有一类算法天然是为了这类目的存在的:对比度增强算法

常用的对比度增强算法有对数变换、指数变换(伽马变换)、灰度拉升等,至于直方图均衡化甚至是小波变换等一些更加复杂的算法,由于它们均依赖于数据的先验分布,因此我们暂时不作考虑:

对数变换:

s=c\cdot{log_{v+1}(1+v\cdot{r})} , r\in[0,1]

指数变换:

s=c\cdot{r^\gamma},r\in[0,1]

灰度拉伸:

s=\frac{1}{1+(\frac{m}{r+eps})^E}

我们尝试着三种算法,其中指数变换的部分我们用开方实现,三种算法均事先作值域变换,默认与值域变换作对比,相关Python代码如下:

def domain_transform(ctr, func):
    a = 0.002
    b = 0.3
    ctr = max(a, ctr)
    ctr = min(b, ctr)
    ctr = (ctr - a) / (b - a)
    fa, fb = func(0), func(1)
    assert fa >= 0
    return (func(ctr) - fa) / (fb - fa)


def map_func1(ctrs):
    return [domain_transform(ctr, lambda x: x) for ctr in ctrs]


def map_func2(ctrs):
    return [domain_transform(ctr, lambda x: math.log(1 + 100 * x)) for ctr in ctrs]


def map_func3(ctrs):
    return [domain_transform(ctr, lambda x: math.sqrt(x)) for ctr in ctrs]


def map_func4(ctrs):
    return [mat2gray(ctr) for ctr in ctrs]


def mat2gray(x):
    return x / (x + 0.01)

我们得到了如下的结果:

各种变换后CTR的分布及对应的信息增益

图中的gain为信息增益,cv为下标的覆盖率,显然是越高越好。

实际工程中,采用对数变换,取得了不错的效果。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值