一、auc选特征的原理
在优化模型的时候,我们期望能够加入足够多有典型区分度的特征。特征有良好的区分度,有助于在筛选广告阶段进行准确的排序。准确的排序意味着,在排序好的候选广告中,可以选择top1,或者top2等这样高预估ctr的广告进行展现。也就是说,加入的特征能够触发高ctr高的广告排在前面。
二、ctr的auc计算方法
(1)定义法:
根据discrimination threshold阈值,每次在一个pCTR
上进行划分,取划分这个pCTR
为阈值,高于这个阈值的是预估的正样本,低于这个阈值,是预估的负样本。在这种情况下,计算当前这个划分的tpr
和fpr
。得到一个点对(fpr
, tpr
)。每个划分都会得到一个点对,那么就可以绘制出ROC,进而计算出AUC。
tpr = tp/p = 预测是正样本实际也是正样本的个数(把正样本预测为正样本的个数)/正样本的个数
fpr = fp/N = 预测是正样本实际是负样本的个数(把负样本预测为正样本的个数)/负样本的个数
给定一组样本如下,其中正样本4个,负样本4个:
样本真实值 | pctr |
1 | 0.9 |
1 | 0.8 |
0 | 0.7 |
1 | 0.5 |
0 | 0.4 |
1 | 0.3 |
0 | 0.2 |
0 | 0.1 |
在本例中,tp即为预测为正样本中,1的个数,fp即为预测为正样本中,0的个数
根据阈值0.9进行划分,大于等于0.9预测为正样本,小于0.9预测为负样本,tpr=1/4=0.25,fpr=0/4=0;
根据阈值0.8进行划分,大于等于0.8预测为正样本,小于0.8预测为负样本,tpr=2/4=0.5,fpr=0/4=0;
根据阈值0.7进行划分,大于等于0.7预测为正样本,小于0.7预测为负样本,tpr=2/4=0.5,fpr=1/4=0.25;
根据阈值0.5进行划分,大于等于0.5预测为正样本,小于0.5预测为负样本,tpr=3/4=0.75,fpr=1/4=0.25;
根据阈值0.4进行划分,大于等于0.4预测为正样本,小于0.4预测为负样本,tpr=3/4=0.75,fpr=2/4=0.5;
根据阈值0.3进行划分,大于等于0.3预测为正样本,小于0.3预测为负样本,tpr=4/4=1,fpr=1/4=0.25;
根据阈值0.2进行划分,大于等于0.2预测为正样本,小于0.2预测为负样本,tpr=4/4=1,fpr=3/4=0.75;
根据阈值0.1进行划分,大于等于0.1预测为正样本,小于0.1预测为负样本,tpr=4/4=1,fpr=4/4=1;
(2)Wilcoxon-Mann-Witney Test惠特尼检验的方法;
Wilcoxon-Mann-Witney Test就是测试任意给一个正类样本和一个负类样本,正类样本的score有多大的概率大于负类样本的score.首先对score从大到小排序,然后令最大score对应的sample 的rank为n,第二大score对应sample的rank为n-1,以此类推。然后把所有的正类样本的rank相加,再减去正类样本的score为最 小的那M个值的情况。得到的就是所有的样本中有多少对正类样本的score大于负类样本的score。然后再除以M×N
还以上面的例子来计算,(M为正样本的个数,N为负样本的个数):
样本真实值 | pctr | rank |
1 | 0.9 | 8 |
1 | 0.8 | 7 |
0 | 0.7 | 6 |
1 | 0.5 | 5 |
0 | 0.4 | 4 |
1 | 0.3 | 3 |
0 | 0.2 | 2 |
0 | 0.1 | 1 |
auc = (正样本的各个rank的和 - M*(M+1)/2)/M*N=[(8+7+5+3)-4*5/2]/4*4=13/16
也即总共有13的(正,负)的点对,正样本的ctr高于负样本的ctr
rank之和: 8 +7 + 5 + 3
4 3 2 1
实际: 4 + 4 + 3 + 2
实验方法:
1、从AUC统计意义去计算。所有的正负样本对中,正样本排在负样本前面占样本对数的比例,即这个概率值。
具体的做法就是它也是首先对prob score从大到小排序,然后令最大prob score对应的sample 的rank为n,第二大score对应sample的rank为n-1,以此类推。然后把所有的正类样本的rank相加,再减去M-1种两个正样本组合的情况。得到的就是所有的样本中有多少对正类样本的score大于负类样本的score。最后再除以M×N。公式如下
def calAUC(prob,labels):
f = list(zip(prob,labels))
rank = [values2 for values1,values2 in sorted(f,key=lambda x:x[0])]
rankList = [i+1 for i in range(len(rank)) if rank[i]==1]
posNum = 0
negNum = 0
for i in range(len(labels)):
if(labels[i]==1):
posNum+=1
else:
negNum+=1
auc = 0
auc = (sum(rankList)- (posNum*(posNum+1))/2)/(posNum*negNum)
print(auc)
return auc
其中:其中输入prob是得到的概率值[0.5,0.6,04,0.3,0.1],labels是分类的标签[1,0,1,0,1]
2、通过计算梯形的面积
注意:(1)首先按ctr从大到小排序;(2)对于正样本来说,每个ctr的正样本,需要对前面的进行累加
得出当前的正样本,因为每次ctr划分。比如:>ctr1的为正样本,小于ctr1为负样本,前面的ctr都比ctr1大,
因此需要把前面的正样本累加到本次当中,负样本no_click不用累加。(3)横坐标为FPR=no_click/no_click_sum,
纵坐标为TPR = click_num/click_num_sum。
def scoreClickAUC(predicted_ctr):
"""
利用梯形面积计算auc
@predictd_ctr: {score:[show,click]}
"""
i_sorted = sorted(predicted_ctr.iteritems(),key=lambda x:x[0],reverse=True)
auc_temp = 0.0
click_sum = 0.0
old_click_sum = 0.0
no_click = 0.0
no_click_sum = 0.0
for (pscore, [show, click]) in i_sorted:
no_click = show - click
#每次ctr的划分,需要对前面的正样本进行累加,前面的正样本对当前ctr来说也是
#正样本
click_sum += click
auc_temp += (click_sum + old_click_sum) * no_click / 2.0
old_click_sum = click_sum
#累加负样本
no_click_sum += show - click
auc = auc_temp / (click_sum * no_click_sum)
return auc
# coding=utf-8
import sys
import math
import time
import datetime
import numpy as np
class get_single_feature_auc(object):
def __init__(self):
self.thresh_hold_nums = 5 #最大序列长度
def process(self,data_str,thresh_hold_nums):
"""
返回每个区块周边的区块特征拼接
"""
#feature_size = 0
#if data_str is None: return
score_dict = {}
for elem in data_str.split(';'):
p = elem.split(',')
if len(p) != 2:continue
score_dict.setdefault(float(p[0]),[])
score_dict[float(p[0])].append(int(p[1]))
feature_distribute = {}
score_list = sorted(score_dict.iteritems(), key=lambda x:x[0],reverse = False)
#将实值列表,分到thresh_hold_nums个桶里
seed = len(score_list) / thresh_hold_nums
for i in range(len(score_list)):
bucket_num = i / seed
click_ = len([e for e in score_list[i][1] if e == 1])
no_click_ = len([e for e in score_list[i][1] if e == 0])
feature_distribute.setdefault(bucket_num, [0,0])
feature_distribute[bucket_num][0] += click_ + no_click_
feature_distribute[bucket_num][1] += click_
#基于每个桶,统计正负样本,并计算不同的阈值
predicted_ctr = dict()
for key in feature_distribute:
show_,click_ = feature_distribute[key][:]
pscore = click_ * 1.0/show_
predicted_ctr.setdefault(pscore, [0,0])
predicted_ctr[pscore][0] += show_
predicted_ctr[pscore][1] += click_
#计算梯形面积,累计出auc的值
i_sorted = sorted(predicted_ctr.iteritems(), key=lambda x:x[0],reverse = True)
auc_temp = 0.0
click_sum = 0.0
old_click_sum = 0.0
no_click = 0.0
no_click_sum = 0.0
for (pscore, [show, click]) in i_sorted:
no_click = show - click
click_sum += click
auc_temp += (click_sum + old_click_sum) * no_click /2.0
old_click_sum = click_sum
no_click_sum += show - click
auc = auc_temp / (click_sum * no_click_sum)
print auc
if __name__ == '__main__':
op = get_single_feature_auc()
op.process("0.3,1;0.45,0;0.56,1;0.3,0;0.21,1;0.33,0;0.43,1;0.78,0;0.65,1;0.98,0;0.75,0",5)