基于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

四、完成点击率预测的具体实现

如果说手写数字识别是分类问题中最简单的项目,那么,电影推荐就算是推荐系统里最简单的项目了:

这两个项目中用的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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值