数据分析业务场景 | CTR预估

news2024/11/19 17:47:58

一.概况

定义

是推荐中最核心的算法之一

对每次广告的点击情况做出预测,预测用户是点击还是不点击

就是预测点击与否的而分类算法,成功的关键之一就是样本的准确性

对于正样本,一般可发挥的空间不是很大,最多就是卡一个停留时长,将属于误点击的正样本剔除

对于负样本,CTR预估是非常讲究真实负样本的,即一定是给用户真实曝光过而被用户忽略的item,才能作为负样本

作用

CTR是为广告排序用的,而排序是竞价广告的核心,连接了点击和曝光

广告平台关心自己的流量价值,即自己的流量曝光卖的贵还是便宜,一般用ECPM(earning cost per mille)这个指标来衡量,即每1000次曝光带来收入

广告主一般按点击进行扣费,即广告主通常关心结果,出价原则就看一个点击需要花多少钱

广告平台需要把点击出价转化成ECPM进行扣费和排序

CTR架起了从点击到曝光的一座桥梁,为排序提供基础

推荐系统也需要CTR预估来排序

意义

有针对性地对个体单独预估,通过某个模型用交叉特征看每一个个体的点击率,从而下一个拥有该特征的人群来访问的时候,就能相对更加准确地预估了

过程

决定广告点击率的因素有三类

广告主侧:比如广告创意,广告文案,广告的表现形式,广告主行业

用户侧:比如人群属性(年龄,性别,地域,兴趣,活跃度等)

广告平台侧:比如不同的广告位,投放时间,竞价策略,流量分配机制,频次控制策略等

这些决定因素在CTR预估中被称为特征,CTR预估的第一步就是特征工程,即把这些特征找到并数据化,列出不同的特征可能对CTR产生的影响

确定了特征之后就需要对这些特征进行处理,即把特征数据化,比如把所有的特征变成0和1的二值化,把连续的特征离散化,把特征的值平滑化,把多个特征向量化

特征完成后就开始建立模型:尝试新模型,训练不同的模型参数

模型

逻辑回归:使用最广泛的模型,一般结合onehot encoding之后的特征使用

GBDT:一般配合连续值特征使用

FM:输入的每个特征除了学到一个对应的权重外,还能得到一组权重存在于vector中,当需要把特征a和特征b进行组合时,就把a的vector和b的vector做内积

FFM:是FM的增强版,也能做特征组合,而且对于每个特征,可以得到好几个vector

DeepFM:可以从原始特征中学习低阶和高阶特征交互,不需要做大量专业的特征工作

DNN:一般使用连续值特征,否则离线训练时间难以承受

传统CTR模型演化关系图

深度学习CTR模型演化关系图

二.案例

1.背景

广告点击率预估是在线广告交易的核心环节之一,公司想确定CTR以确定将它们的钱花在数字广告上是否值得。如何有效利用积累的海量广告数据和用户数据去预测用户的广告点击率,是大数据应用在精准营销中的关键问题,也是所有智能营销平台必须具备的核心技术

通过人工智能技术构建预测模型预估用户的广告点击概率,即给定广告点击相关的广告,媒体,用户,上下文内容等信息的条件下预测广告点击概率

由于是CTR预估问题,所以可以简单抽象为样本分布不均衡的二分类问题,需要预测的内容为用户是否会点击该商品

2.数据预处理

对于CTR问题而言,广告是否被点击的主导因素是用户,其次是广告信息

数据探索一般先从活动特征,商品历史特征,时间序列特征入手

活动特征是外部导致(一般来源于业务本身)

商品历史特征(统计比例,权重设置)

时间序列特征(可以做的处理比较多)

缺失值处理:用-1填充,模型能够识别缺失值与非缺失值的不同

df = df.fillna(-1)

时间提取

#时间特征:可以构造时间差,平均用时,总用时特征,不同分类组合的平均用时,总用时特征
df['day'] = df['time'].apply(lambda x: int(time.strftime("%d",time.localtime(x)))
df['hour'] = df['time'].apply(lambda x: int(time.strftime("%H",time.localtime(x)))

布尔型数据转换

list(filter(lambda s: s=='bool',[df[i].dtype for i in df.columns]))
bool_feature = list(filter(lambda s: s!=0,[i if df[i].dtype == 'bool' else 0 for i in df.columns]))

#转换bool
for i in bool_feature:
  df[i] = df[i].astype(int)

advert_industry_inner特征提取

df['advert_industry_inner_1'] = df['advert_industry_inner'].apply(lambda x: x.split('-')[0])

将广告相关特征放到一个列表

ad_cate_feature = ['adid','advert_id','orderid','advert_industry_inner_1','advert_industry_inner','advert_name','compaign_id','creative_id','creative_type','creative_tp_dnf','creative_ha_deeplink','creative_is_jump','creative_is_download']

df['creative_is_js'].value_counts()

将媒体相关特征放到一个列表

media_cate_feature = ['app_cate_id','f_channel','app_id','inner_slot_id']

df['app_paid'].value_counts()

上下文特征(用户信息)

content_cate_feature = ['city','carrier','province','nnt','devtype','osv','os','make']
df['os_name'].value_counts()
df['os'].value_counts()

列表合并

origin_cate_list = ad_cate_feature + media_cate_feature + content_cate_feature

将分类特征labelencode

for i in origin_cate_list:
  df[i] = df[i].map(dict(zip(df[i].unique(),range(0,df[i].nunique()))))

df['os_name'].map(dict(zip(df['os_name'].unique(),range(0,df['os_name'].nunique())))).value_counts()
pd.DataFrame(df['os_name'].value_counts()).reset_index()

3.特征工程

遍历所有特征做个新的统计特征

#函数参数
features = ['day','hour','creative_id']
data = df.copy()
is_feature = True
n = df[features].nunique()

#改变特证名
new_feature = 'count'
nunique = []
for i in features:
  nunique.append(data[i].nunique())
  new_feature += '_' + i.replace('add','')

#不符合以下条件就是无效的交叉特征,不作处理
#条件:features列表里不止1个元素,并且features列的独立元素特别少的情况
if len(features) > 1 and len(data[feature].drop_duplicates()) <= np.max(nunique):
  print(new_feature,'is_unvalid cross feature:')

temp = data.groupby(features).size().reset_index().rename(columns={0:new_feature})
data = data.merge(temp,'left',on=features)
data.groupby(features2).size().reset_index().rename(columns={0:new_features})

count_feature_list = []
if is_feature:
  count_feature_list.append(new_feature)
if 'day_' in new_feature:
  data.loc[data.day==3,new_feature] = data[data.day==3][new_feature]*4

df['day'].value_counts()

遍历两个交叉元素做个比例特征

ratio_feature_list = []
for i in media_cate_feature:
  for j in content_cate_feature + ad_cate_feature:
    new_feature = 'inf_' + i + '_' + j
    df = feature_count(df,[i,j])
    if df[i].nunique() > 5 and df[j].nunique() > 5:
      df['ratio_' + j + '_of_' + i] = df['count_' + i + '_' + j] / df['count_' + i]
      df['ratio_' + i + '_of_' + j] = df['count_' + i + '_' + j] / df['count_' + j]
      ratio_feature_list.append('ratio_' + j + '_of_' + i)
      ratio_feature_list.append('ratio_' + i + '_of_' + j)
      print(i,'&',j)

特征列表组合,新增统计特征和比例特征字段

cate_feature = origin_cate_list
num_feature = ['creative_width','creative_height','hour'] + count_feature_list + ratio_feature_list
feature = cate_feature + num_feature

低频过滤:把只有一个元素的特征变成-1,然后再+1,变成0,只有一个元素的特征做onehotencode会造成维度爆炸

for feature in cate_feature:
  #它这里不是一个DataFrame了,所以没有columns()属性,它是一个table
  if 'count_' + feature in df.keys():
    df.loc[df['count_' + feature] < 2,feature] = -1
    df[feature] = df[feature] + 1

构建训练集和测试集

label = list(click) + [-1]*(len(df) - len(click))
df['label'] = label
#测试集数据
predict = df[df.label == -1]
predict_result = predict[['instance_id']]
predict_result['predict_score'] = 0
predict_x = predict.drop('label',axis=1)
#建立训练集,里面全是正例样本,drop=True一定要写,意思是返回一个DataFrame
train_x = df[df.label != -1].reset_index(drop=True)
#pop()的功能就是删除并返回所删除的内容,将之传给训练集
train_y = train_x.pop('label').values

train_x.shape
predict_x.shape

建立稀疏矩阵:压缩矩阵对象的内存空间和加速多数机器学习程序

base_train_csr = sparse.csr_matrix((len(train_x),0))
base_predict_csr = sparse.csr_matrix((len(predict_x),0))

分类变量onehot encode

将多个特征对应的编码向量链接在一起构成特征向量

作用:分类编码变量,用来解决类别型数据的离散值问题;将每一个类可能取值的特征变换为二进制特征向量,每一类特征向量只有一个地方是1,其余位置都是0

#这个sparse.hstack非常有必要,代表粗细粒度
#使用hstack时要从粗粒度往细粒度加,否则细粒度的特征就会被压缩,信息损失很多,更省内存
enc = OneHotEncoder()
for feature in cate_feature:
  enc.fit(df[feature].values.reshape(-1,1))
  base_train_csr = sparse.hstack((base_train_csr,enc.transform(train_x[feature].values.reshape(-1,1))),'csr','bool')
  base_predict_csr = sparse.hstack((base_predict_csr,enc.transforms(predict_x[feature].values.reshape(-1,1)))
 
base_train_csr.shape
base_predict_csr.shape

 user_tags特征

#建立一个文本提取器
cv = CountVectorizer(min_df=20)
#对于特征user_tags,对其进行文本特征提取并且跟之前的矩阵进行水平方向上的合并
#adtype(str)非常有必要,因为它可能原来是obj类型,反正这样不会错
for feature in ['user_tags']:
  df[feature] = df[feature].astype(str)
  cv.fit(df[feature])
  base_train_csr = sparse.hstack((base_train_csr,cv.transform(train_[feature].astype(str))),'csr','bool')
  base_predict_csr = sparse.hstack((base_predict_csr,cv.transform(predict_x[feature].astype(str))),'csr','bool')

#忽略少于20个文档中出现的术语
cv = CountVectorizer(min_df=20)

base_train_csr.shape
base_predict_csr.shape

4.特征选择

方差选择法

from sklearn.feature_selection import SelectKBest,SelectPercentile
from sklearn.feature_selection import chi2
from sklearn.feature_selection import VarianceThreshold

sel_var = VarianceThreshold(threshold=0.001)
sel_var.fit(base_train_csr)
base_train_csr = sel_var.transform(base_train_csr)
base_predict_csr = sel_var.transform(base_predict_csr)

稀疏矩阵从布尔转换为float

#训练集,预测集压缩稀疏矩阵:将数字特征列表num_feature跟之前的矩阵进行水平方向上的合并
train_csr = sparse.hstack((sparse.csr_matrix(train_x[num_feature]),base_train_csr),'csr').astype('float32')
predict_csr = sparse.hstack((sparse.csr_matrix(predict_x[num_feature]),base_predict_csr),'csr').astype('float32')

sys.getsizeof(train_csr)

5.构建模型及交叉验证

StratifiedKFold:分层采样,训练集和测试集中各类别样本的比例与原始数据相同(分类问题)

KFold:分层采样,将数据分成训练集和测试集,不考虑训练集和测试集中各类别数据是否相同(回归问题)

#建立一个lgb_model,LGBM分类器
lgb_model = lgb.LGBMClassifier(
  boosting_type='gbdt',num_leaves=61,reg_alpha=3,reg_lambda=1,
  max_depth=-1,n_etimators=5000,objective='binary',
  subsample=0.8,colsample_bytree=0.8,subsample_freq=1,
  learning_rate=0.035,random_state=2022,n_jobs=10
)
#建立一个分层k折采样器,为5折
skf = StratifiedKFold(n_split=5,random_state=2022,shuffle=True)

6.模型训练与评估

这类问题评估函数常用logloss和AUC

logloss:更关注和观察数据的吻合程度,用于评估模型输出概率与训练数据概率的一致程度,logloss越小,模型预估的ctr越准

AUC:更关注rank order,主要预估模型对于整体样本的排序能力

如果是按照概率期望来进行收费投放的话就用logloss,如果定投一定量就用AUC,主要和业务相关

best_score = []
for index,(train_index,test_index) in enumerate(skf.split(train_csr,train_y)):
  lgb_model.fit(train_csr[train_index],train_y[train_index],eval_set=[(train_csr[train_index],train_y[train_index],(train_csr[test_index],train_y[test_index])],early_stopping_rounds=200,verbose=10)
  best_score.append(lgb_model.best_score_['valid_1']['binary_logloss'])
  print(best_score)
  #如果在训练期间启用了早期停止,可以通过best_iteration方式从最佳迭代中获得预测
  test_pred = lgb_model.predict_proba(predict_csr,num_iteration=lgb_model.best_iteration_)[:,1]
  predict_result['predicted_score'] = predict_result['predicted_score'] + best_pred
predict_result['predicted_score'] = predict_result['predicted_score']/5
mean = predict_result['predicted_score'].mean()
print('mean',mean)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/82108.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LinkedList源码解析

LinkedList源码解析 简介 LinkedList 是一个双向链表&#xff08;内部是 Node 节点&#xff09;实现的 List&#xff0c;并且还实现了 Deque 接口&#xff0c;它除了作为 List 使用&#xff0c;还可以作为队列或者栈来使用。 这样看来&#xff0c;LinkedList 简直就是个全能…

【文字版】津津有味:感染新冠后没食欲,那咱也得吃饭啊!

点击文末“阅读原文”即可收听本期节目剪辑、音频 / 朱峰 编辑 / 姝琦 SandLiu监制 / 姝琦 文案 / 粒粒 产品统筹 / bobo你阳了吗&#xff1f;你嗓子疼吗&#xff1f;你的食欲还好吗&#xff1f;没有什么比好好吃饭更重要&#xff0c;生病的时候尤其是。营养对健康而言是预防…

浏览器上的Cookies有什么用?超级浏览器防关联如何实现?

Cookies是浏览器的指纹信息之一&#xff0c;它是一种文本文件&#xff0c;是网站为了辨别用户身份&#xff0c;对用户进行记录并由户客户端计算机进行暂时或永久保存的信息。一般情况下&#xff0c;网站对浏览器的指纹的记录主要是依靠Cookies来实现的。因为超级浏览器来可以生…

[附源码]JAVA毕业设计英语网站(系统+LW)

[附源码]JAVA毕业设计英语网站&#xff08;系统LW&#xff09; 项目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xf…

[附源码]Python计算机毕业设计大学生健康系统Django(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等…

c#和Python交互,完美解决Python调用OpenCV等第三方库以及分发时需配置python环境的问题

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言一、问题分析二、解决方案第一个问题第二个问题三、结果及源码四、总结前言 关于C#如何调用Python&#xff0c;网上提供了很多解决方案&#xff0c;有用ironPyt…

react组件深度解读

五、React 核心是组件 在 React 中&#xff0c;我们使用组件&#xff08;有状态、可组合、可重用&#xff09;来描述 UI 。 在任何编程语言中&#xff0c;你都可以将组件视为简单的函数。 React 组件也一样&#xff0c; 它的输入是 props&#xff0c;输出是关于 UI 的描述。我…

Win11 WSL Linux子系统安装与注销 配置conda环境 启动jupyter

1 前言 本篇博客讲解如何在Windows11系统中安装与注销Linux子系统&#xff0c;并配置conda环境、jupyter环境&#xff0c;实现在Local浏览器启动jupyter并运行项目。 2 安装Linux子系统&#xff08;参考文章[1]&#xff09; 1.1 WSL 在任务栏中的搜索功能中&#xff0c;搜索…

合并多文件后分组再结构化

【问题】 Heres the problem statement: In a folder in HDFS, therere a few csv files with each row being a record with the schema (ID, attribute1, attribute2, attribute3). Some of the columns (except ID) could be null or empty strings, and no 2 records wi…

汇编语言常用DOS功能调用示例

1.利用DOS功能调用输出响铃&#xff08;响铃的ASCII码为07H&#xff09;。建立源程序文件HELLO.ASM&#xff0c;通过汇编程序得到目标文件RING.OBJ以及列表文件RING.LST&#xff0c;通过连接程序得到可执行文件性文件 RING.EXE。对可执行性文件进行调试。 &#xff08;1&…

【数据结构】——栈和队列

目录 1.栈 1.1栈的概念及结构 1.2栈的实现 1.2.1具体实现 Stack.h 栈初始化 栈销毁 入栈 出栈 取栈顶数据 判空 栈中有效元素的个数 全部Stack.c的代码 测试Test.c代码 2.队列 2.1队列的概念及结构 2.2队列的实现 Queue.h 队列初始化 队列销毁 队尾入队列…

Tomcat8.0使用tomcat-redis-session-manager共享session【开源】,tomcat实现session共享

前言 【可跳过&#xff0c;比较简单】 由于以前的项目配置了多个tomcat服务使用了nginx代理&#xff0c;但是关闭某个tomcat的时候登录用户信息丢失&#xff0c;用户得重新登录&#xff0c;这就让人体验不好了&#xff1b;我们可以复制各个tomcat服务的session来实现的sessio…

【供给需求优化算法】基于适应度-距离-平衡供给需求优化算法FDB-SDO附matlab代码

​✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法…

毕设选题推荐基于python的django框架的学生课程管理系统

&#x1f496;&#x1f525;作者主页&#xff1a;计算机毕设老哥&#x1f525; &#x1f496; 精彩专栏推荐订阅&#xff1a;在 下方专栏&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; Java实战项目专栏 Python实…

前端二面常见手写面试题(必备)

用正则写一个根据name获取cookie中的值的方法 function getCookie(name) {var match document.cookie.match(new RegExp((^| ) name ([^;]*)));if (match) return unescape(match[2]); }获取页面上的cookie可以使用 document.cookie 这里获取到的是类似于这样的字符串&…

新一代推理部署工具FastDeploy与十大硬件公司联合打造:产业级AI模型部署实战课...

人工智能产业应用发展的越来越快&#xff0c;开发者需要面对的适配部署工作也越来越复杂。层出不穷的算法模型、各种架构的AI硬件、不同场景的部署需求、不同操作系统和开发语言&#xff0c;为AI开发者项目落地带来极大的挑战。为了解决AI部署落地难题&#xff0c;我们发布了新…

(附源码)springboot校园跳蚤市场 毕业设计 646515

基于Springboot校园跳蚤市场 摘 要 科技进步的飞速发展引起人们日常生活的巨大变化&#xff0c;电子信息技术的飞速发展使得电子信息技术的各个领域的应用水平得到普及和应用。信息时代的到来已成为不可阻挡的时尚潮流&#xff0c;人类发展的历史正进入一个新时代。现代社会越来…

Python解题 - CSDN周赛第14期 - 单词编码

本期其实没啥好写的&#xff0c;都是数学题&#xff0c;和算法关系不大&#xff0c;唯手熟尔。而且又出现了同一天的每日一练中包含了赛题&#xff0c;这算不算官方泄题呢&#xff1f;看来下次在竞赛之前先做完每日一练大有益处呢。 第一题&#xff1a;字符串全排列 对K个不同字…

算法leetcode|21. 合并两个有序链表(rust重拳出击)

文章目录21. 合并两个有序链表&#xff1a;样例 1&#xff1a;样例 2&#xff1a;样例 3&#xff1a;提示&#xff1a;原题传送门&#xff1a;分析&#xff1a;题解&#xff1a;rustgoccpythonjava21. 合并两个有序链表&#xff1a; 将两个升序链表合并为一个新的 升序 链表并…

一款基于SpringBoot+layui 开源的固定设备资产管理系统源码 源码免费分享

淘源码&#xff1a;国内专业的免费源码下载平台 分享一款开源的固定设备资产管理系统源码&#xff0c;系统可对常用资产设备进行信息化管理&#xff0c;包含自定义支持各类设备、自带导入导出、维护工作统计、采购管理、文档管理、合同管理等功能&#xff0c;包含对资产的登记、…