基于PaddleRec的用户点击率预测
不会吧,不会吧,都0202年了,不会有AI连用户喜欢什么都不知道吧!AI比你更懂自己!
一、前言
推荐系统在我们的日常生活中,可谓是无处不在,比如我们平常逛的淘宝、刷的抖音,这些应用的背后都离不开推荐系统。
说起推荐,大家应该都不陌生:
- “哪家的月饼好吃?推荐一下!”
- “今年国庆放8天假,去哪玩比较好呢?”
- … …
今年是特殊的一年,因为疫情,也许会有一部分人会选择留在家中过中秋,中秋节自然少不了月饼,月饼这么多,哪一家的月饼符合你的心意,这就需要推荐系统了(总不能每一家月饼都尝一边吧);庆国庆,去哪玩也许是一个令你头疼的问题,每个人的喜好不同,选择的地方也会不同,这也需要推荐系统的帮助。总之,我们的生活离不开推荐。
CTR(Click Through Rate),即点击率,是“推荐系统/计算广告”等领域的重要指标,对其进行预估是商品推送/广告投放等决策的基础。简单来说,CTR预估对每次广告的点击情况做出预测,预测用户是点击还是不点击。CTR预估模型综合考虑各种因素、特征,在大量历史数据上训练,最终对商业决策提供帮助。
推荐系统的痛点
但是,话说回来,搭建一个推荐系统,真的有这么简单吗?
我们都知道,在深度学习里,数据对模型的效果在一定程度上起到了不可小觑的作用。推荐系统更是如此,它主要解决的是信息过载的问题,目标是从海量物品筛选出不同用户各自喜欢的物品,从而为每个用户提供个性化的推荐。因此,如果数据量很少,或数据质量不高,那么做出来的效果可能就不会很理想。
尽管如此,现在也有很多推荐系统的数据集:
- MovieLens——数据地址:https://grouplens.org/datasets/movielens/
MovieLens数据集中,用户对自己看过的电影进行评分,分值为1~5。 - Book-Crossings——数据地址:http://www2.informatik.uni-freiburg.de/~cziegler/BX/
该包含90000个用户的270000本书的110万个评分。评分范围从1到10,包括显式和隐式的评分。 - Last.fm——数据地址:https://grouplens.org/datasets/hetrec-2011/
Last.fm提供音乐推荐的数据集。 对于数据集中的每个用户,包含他们最受欢迎的艺术家的列表以及播放次数。它还包括可用于构建内容向量的用户应用标签。
我们可以先使用这些现成的数据集,学习如何处理数据,并搭建一个推荐系统。
二、推荐系统的数据获取
当今时代,是大数据时代,谁拥有数据,谁就有筹码。在搭建推荐系统的全流程中,获取数据是第一步,也是最关键的一步。
在做这个项目之前,我想做中秋节赏月地点个性化推荐的,但是苦于没有数据,所以我暂时放弃了这个想法。
获取数据的三种方法
我目前能想到的比较可行的数据获取方案有三种:
- 最简单的当然是找现成的数据集,这类数据集往往是一些研究机构公开的,一般也比较好;
- 退而求其次就是自己去一些公开网站上用自动化的方法采集数据,但这些数据往往缺少一些特征,更何况爬虫有风险;
- 最后一种是最实在的方法,但这种方法收集到的数据量往往很少,对个人来说真的很困难。
1.使用现成的数据集
前面已经给大家提供了一些现成数据集,除了电影、图书、音乐数据集之外,还有一些可用于推荐系统的数据集:
- MovieLens——数据地址:https://grouplens.org/datasets/movielens/
MovieLens数据集中,用户对自己看过的电影进行评分,分值为1~5。 - Book-Crossings——数据地址:http://www2.informatik.uni-freiburg.de/~cziegler/BX/
该包含90000个用户的270000本书的110万个评分。评分范围从1到10,包括显式和隐式的评分。 - Last.fm——数据地址:https://grouplens.org/datasets/hetrec-2011/
Last.fm提供音乐推荐的数据集。 对于数据集中的每个用户,包含他们最受欢迎的艺术家的列表以及播放次数。它还包括可用于构建内容向量的用户应用标签。 - Wikipedia——数据地址:https://en.wikipedia.org/wiki/Wikipedia:Database_download#English-language_Wikipedia
该数据集已广泛用于社交网络分析,图形和数据库实现测试,以及维基百科用户行为研究 - OpenStreetMap——数据地址:http://planet.openstreetmap.org/planet/full-history/
OpenStreetMap是一个协作的地图项目,类似于维基百科。它的数据由用户提供,数据集中的对象包括道路,建筑物,兴趣点,以及您可能在地图上找到的任何其他内容。
2.网络爬虫
使用网络爬虫的话,首先需要一定的Python基础,并且对前后端的知识有一定的了解。
AI Studio上也有一些关于爬虫的教程,可以去学习学习:
- Python入门-豆瓣电影TOP250爬取——https://aistudio.baidu.com/aistudio/projectdetail/70149
- 《青春有你2》数据爬取与分析——https://aistudio.baidu.com/aistudio/projectdetail/396959
我以前也做过一些爬虫程序,可以参考参考:
- 抓取王者荣耀英雄列表以及对应图片——https://blog.csdn.net/zbp_12138/article/details/101595246
- 抓取中国天气网当前时段所有城市的天气数据——https://blog.csdn.net/zbp_12138/article/details/101617083
3.调查问卷
调查问卷就简单多了,不需要代码基础,像问卷星就能帮你制作采集表单。
当然,如果想要自己做采集表单的话,也可以自己尝试搭一个服务器用于存放数据,这也需要一定的前后端基础,有兴趣的同学可以学一下Django。
三、数据处理
一般来说,直接采集到的数据是不能直接拿来训练的,因此,我们还需要一定的数据处理功底。对于数据处理,我首先推荐Excel,如果有能力的同学,可以尝试Python代码。
PaddleRec推荐数据集格式
GitHub文档:https://github.com/PaddlePaddle/PaddleRec/blob/master/doc/slot_reader.md
PaddleRec对于数据输入有两种方式。一种是数据已经处理成slot:value的格式可以直接输入模型,我们就用这种方式输入;另一种方式是配置一个reader,在reader中对数据进行处理,再用yield的方式输入。
1.slot:value的格式说明
当你的数据集格式为slot:feasign这种格式,或者可以预处理为这种格式时,可以直接使用PaddleRec内置的Reader。
- Slot直译是槽位,在推荐工程中,是指某一个宽泛的特征类别,比如用户ID、性别、年龄就是Slot.
- Feasign则是具体值,比如:12345,男,20岁。
在实践过程中,很多特征槽位不是单一属性,或无法量化并且离散稀疏的,比如某用户兴趣爱好有三个:游戏/足球/数码,且每个具体兴趣又有多个特征维度,则在兴趣爱好这个Slot兴趣槽位中,就会有多个Feasign值。
PaddleRec在读取数据时,每个Slot ID对应的特征,支持稀疏,且支持变长,可以非常灵活的支持各种场景的推荐模型训练。
在一条数据中,每个特征用slot:feasign表示,相邻两个特征用\t分隔,如下所示:
logid:100009 age:49 bookid:60392452 label:8
logid:100009 age:49 bookid:60502258 label:6
logid:100009 age:49 bookid:60977337 label:9
logid:100009 age:49 bookid:312289871 label:0
logid:100009 age:49 bookid:312981589 label:6
logid:100009 age:49 bookid:312982518 label:8
logid:100009 age:49 bookid:312983654 label:9
… …
电影推荐数据集部分训练数据:
logid:100000548 time:976672993 userid:53793338 gender:54713968 age:23292885 occupation:32989794 movieid:26774464 title:57847355 title:44022005 title:36622433 title:3467516 genres:58455809 label:5
logid:100001212 time:974716889 userid:52427656 gender:40715500 age:38930457 occupation:113722 movieid:51981119 title:48706141 title:41846042 title:33234546 genres:28043405 genres:51532872 label:2
logid:100002309 time:971972222 userid:28238127 gender:54713968 age:50367871 occupation:113722 movieid:31124213 title:41573393 title:37130060 title:33234546 genres:17697846 genres:16423920 label:2
logid:100005308 time:975112166 userid:4949303 gender:54713968 age:50367871 occupation:113722 movieid:26896829 title:48110462 title:57345835 title:24278372 title:2549750 genres:7891961 genres:51532872 label:2
2.只有value的输入数据格式
slot:value这种格式的数据是PaddleRec推荐的数据集格式,当然,这种格式不是唯一的。
假设数据A、B、C在文本数据中,每行以这样的形式存储:
0.1,0.2,0.3…3.0,3.1,3.2 \t 99999,99998,99997 \t 1 \n
则示例代码如下:
from paddlerec.core.utils import envs class Reader(ReaderBase): def init(self): self.avg = envs.get_global_env("avg", None, "hyper_parameters.reader")
def generator_sample(self, line): def reader(self, line): # 先分割 '\n', 再以 '\t'为标志分割为list variables = (line.strip('\n')).split('\t') # A是第一个元素,并且每个数据之间使用','分割 var_a = variables[0].split(',') # list var_a = [float(i) / self.avg for i in var_a] # 将str数据转换为float # B是第二个元素,同样以 ',' 分割 var_b = variables[1].split(',') # list var_b = [int(i) for i in var_b] # 将str数据转换为int # C是第三个元素, 只有一个元素,没有分割符 var_c = variables[2] var_c = int(var_c) # 将str数据转换为int var_c = [var_c] # 将单独的数据元素置入list中 # 将数据与数据名结合,组织为dict的形式 # 如下,output形式为{ A: var_a, B: var_b, C: var_c} variable_name = ['A', 'B', 'C'] output = zip(variable_name, [var_a] + [var_b] + [var_c]) # 将数据输出,使用yield方法,将该函数变为了一个可迭代的对象 yield output
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
四、完成点击率预测的具体实现
如果说手写数字识别是分类问题中最简单的项目,那么,电影推荐就算是推荐系统里最简单的项目了:
- 乘风破浪的调参侠!玩转特征重要性~从此精通LR——PaddleRec-master
- 十分钟!全流程!从零搭建推荐系统——PaddleRec
这两个项目中用的PaddleRec版本不同,但对于电影推荐这个项目来说,两个用的是同一个数据集,PaddleRec-master下的linear regression只是线性回归这一个模型;PaddleRec的demo下面的movie recommend是电影推荐这个项目包含找回和排序两个模型。
点击率预测相对于电影推荐来说,数据的特征更多一些。
以下是本例的简要目录结构及说明:
├── sample_data #样例数据
├── train
├── sample_train.txt #训练数据样例
├── preprocess.py #数据处理程序
├── run.sh #数据一键处理脚本
├── download_preprocess.py #数据下载脚本
├── get_slot_data.py #格式整理程序
├── __init__.py
├── README.md #文档
├── model.py #模型文件
├── config.yaml #配置文件
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
1.首先安装PaddleRec及相关依赖
请确保paddlepaddle版本 >= 1.7.2
PaddleRec的安装包已上传至本项目work目录下。
将PaddleRec解压到work目录下:
# 解压PaddleRec
!tar -xvf data/data40302/PaddleRec.tar.gz -C work
- 1
- 2
如果该命令卡很久,可以把work/PaddleRec/setup.py里requires的paddlepaddle==1.7.1去掉:
# 环境部署
# 安装PaddleRec
!cd work/PaddleRec/ && python setup.py install
- 1
- 2
- 3
2.参数配置
PaddleRec是一个专门做推荐系统的PaddlePaddle生态下的套件,使用方法跟PaddleDetection、PARL这些大同小异。
在PaddleRec里已经有了很多案例,在:PaddleRec-master/models里,但无论案例有多少,大体结构是不变的。
PaddleRec里的每个项目里都有几个文件,这里给大家梳理一下,其他项目的配置方法也是类似的:
- data——用于存放数据集的文件夹,里面还有2个子文件夹,训练集存放在train_data,测试集存放在test_data
- config.yaml——配置训练参数,训练集修改后,里面的参数也要做相应的调整
- model.py——网络结构,config.yaml的参数改变时,model.py里的某些代码也需要做相应调整
下面我们一起来看一下这三个文件怎么配置。
data——存放数据集
data文件夹下用来存放数据集,data文件下有train_data和test_data,里面分别存放着训练集和测试集。
因为电影推荐的数据集ml-1m原本不是key:value这样的格式,因此官方提供了一键数据处理脚本data/run.sh,通过运行该脚本,就可以将ml-1m数据集转换成PaddleRec推荐数据集格式。
在点击率预测中也是类似的,官方也已经提供了一键数据处理脚本,所以我们直接用就可以了。
训练及测试数据集选用Display Advertising Challenge所用的Criteo数据集。该数据集包括两部分:训练集和测试集。训练集包含一段时间内Criteo的部分流量,测试集则对应训练数据后一天的广告点击流量。 每一行数据格式如下所示:
其中
# 一键下载训练及测试数据
!cd work/PaddleRec/models/rank/logistic_regression/data && sh run.sh
- 1
- 2
进入models/rank/logistic_regression/data目录下,执行该脚本,会从国内源的服务器上下载Criteo数据集,并解压到指定文件夹,然后自动处理数据转化为可直接进行训练的格式。
- 解压后全量训练数据放置于./train_datal
- 全量测试数据放置于./test_data
- 可以直接输入的训练数据放置于./slot_train_datal
- 可直接输入的测试数据放置于./slot_test_datal
config.yaml——配置训练参数
官方提供的config.yaml参数配置说明:https://github.com/PaddlePaddle/PaddleRec/blob/master/doc/yaml.md
这里需要注意的是dataset下sparse_slots和dense_slots的配置。
sparse_slots——稀疏参数
稀疏参数就好似embedding层,本身有很多参数,但每次更新的时候只需要更新其中一小部分。
稀疏参数的格式如下:
sparse_slots: “userid gender age occupation movieid title genres”
dense_slots——稠密参数
稠密参数就类似全链接层,每次更新都要更新全部的参数。
稠密参数的格式如下:
dense_slots: “label:1”
与稀疏参数不同的是,稠密参数每个特征之后需要指定该特征的维度,上面这个例子就表示label这个特征的维度是1
model.py——网络结构
model.py跟config.yaml的配置是紧密关联的。
在config.yaml中做如下配置:
则在model.py中:
这里是对应的,sparse_slot的第一个特征是label,于是model中就有self.label = self._sparse_data_var[0]这句话来取label
model.py文件里的某些代码在训练或预测时可以进行简单的微调。
比如在做infer时需要看一下某一个数据对应的预测结果,那么可以做如下修改:
3.模型训练
对于点击率预测,有很多解决方法,这里就拿逻辑回归举例。
本案例代码已在AI Studio公开:
https://aistudio.baidu.com/aistudio/projectdetail/1044265?shared=1
# 召回模型离线训练
# 每轮训练耗时约1分钟,训练5轮
!cd work/PaddleRec/models/rank/logistic_regression && python -m paddlerec.run -m ./config.yaml
- 1
- 2
- 3
训练结束后,模型文件保存在work/PaddleRec/models/rank/logistic_regression/increment里
4.测试模型效果
# 模型测试
!cd work/PaddleRec/models/rank/logistic_regression && python -m paddlerec.run -m ./infer_config.yaml
- 1
- 2
做infer时,我将batchsize调整为1,这样就能看到每条数据的输出,然后对model.py的代码做出如下修改:
if is_infer:
self._infer_results["AUC"] = auc_var # 模型效果指标
self._infer_results["raw_feat_value"] = raw_feat_value # 一条预测数据
self._infer_results["label"] = self.label # 预测数据对应的label,即label=0表示没点击,label=1表示点击
self._infer_results["predict"] = self.predict # 模型预测结果
- 1
- 2
- 3
- 4
- 5
某个batch的输出如下:
AUC auc_0.tmp_0 lod: {}
dim: 1
layout: NCHW
dtype: double
data: [0.453176]
raw_feat_value feat_value lod: {
{0, 39, 78, 117, 156, 195}}
dim: 5, 39
layout: NCHW
dtype: float
data: [0.00017316 1.55233e-05 7.62951e-05 0 5.96732e-05 9.27995e-06 0.000266378 0.000330743 0.00623729 0.0217391 0.00865801 0 0.000270526 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 0 1 1 1 1 0.00034632 1.16424e-05 0.000671397 0.00103199 4.40425e-06 1.85599e-05 3.5517e-05 0.000330743 0.000137841 0.0217391 0.004329 0 0.000541052 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 0.00034632 1.16424e-05 1.5259e-05 0.0144479 3.31182e-05 0.000206479 7.10341e-05 0.000330743 0.00844274 0.0217391 0.012987 0.000748503 0.00608684 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 0 0 0 0.00347721 0 0 0.000189642 0 0 0 0 0 0 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 0 1 1 0 0 0 0 1 1 0 0 0.00051948 7.76163e-06 0 0 8.63578e-08 0 5.32756e-05 0 0 0.0217391 0.004329 0 0 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 0 1 1 0 0]
label label lod: {
{0, 1, 2, 3, 4, 5}}
dim: 5, 1
layout: NCHW
dtype: int64_t
data: [0 0 0 0 0]
predict sigmoid_0.tmp_0 lod: {}
dim: 5, 1
layout: NCHW
dtype: float
data: [0.403716 0.664969 0.532565 0.646387 0.706548]
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
当然,除了上面这个方法,也可以用这个op查看运算过过程中变量的值——paddle.fluid.layers.Print
paddle.fluid.layers.Print可以打印正在访问的tensor的值
五、总结与展望
我在接触PaddleRec时,用的环境是Python3,但PaddleRec内有部分代码是用Python2写的,所以可能会有报错。因此,这里比较推荐各位开发者使用Python2的环境使用PaddleRec开发推荐系统。
对于新手来说,推荐系统可能略有困难,特别是数据处理部分可能会劝退很多人。但是!别灰心!虽然数据很难获取,但是搭建整个推荐系统的流程还是需要弄明白的。
如果你对数据获取、数据处理部分有疑问,不妨利用这个小长假好好补一补吧~
最后,非常感谢PaddleRec的浩峰同学,在这段时间内给予我技术上的支持!
六、个人介绍
- 北京联合大学 机器人学院 自动化专业 2018级 本科生 郑博培
- 百度飞桨开发者技术专家 PPDE
- 深圳柴火创客空间 认证会员
- 百度大脑 智能对话训练师
来AI Studio互粉吧,等你哦~ https://aistudio.baidu.com/aistudio/personalcenter/thirdview/147378
欢迎大家fork喜欢评论三连,感兴趣的朋友也可互相关注一下啊~
原文链接:https://blog.csdn.net/zbp_12138/article/details/109253998