2

基于PaddleRec的用户点击率预测

 9 months ago
source link: https://blog.51cto.com/u_16011718/6852187
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

基于PaddleRec的用户点击率预测

精选 原创

基于PaddleRec的用户点击率预测

  • 推荐系统的痛点
  • 二、推荐系统的数据获取
  • 获取数据的三种方法
  • 1.使用现成的数据集
  • 2.网络爬虫
  • 3.调查问卷
  • 三、数据处理
  • PaddleRec推荐数据集格式
  • 1.slot:value的格式说明
  • 2.只有value的输入数据格式
  • 四、完成点击率预测的具体实现
  • 1.首先安装PaddleRec及相关依赖
  • 2.参数配置
  • data——存放数据集
  • config.yaml——配置训练参数
  • sparse_slots——稀疏参数
  • dense_slots——稠密参数
  • model.py——网络结构
  • 3.模型训练
  • 4.测试模型效果
  • 五、总结与展望
  • 六、个人介绍

不会吧,不会吧,都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

我以前也做过一些爬虫程序,可以参考参考:

  • 抓取王者荣耀英雄列表以及对应图片——
  • 抓取中国天气网当前时段所有城市的天气数据——

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

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

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

这两个项目中用的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.首先安装PaddleRec及相关依赖

请确保paddlepaddle版本 >= 1.7.2

PaddleRec的安装包已上传至本项目work目录下。

将PaddleRec解压到work目录下:

# 解压PaddleRec
!tar -xvf data/data40302/PaddleRec.tar.gz -C work

如果该命令卡很久,可以把work/PaddleRec/setup.py里requires的paddlepaddle==1.7.1去掉:

# 环境部署
# 安装PaddleRec
!cd work/PaddleRec/ && python setup.py install

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的部分流量,测试集则对应训练数据后一天的广告点击流量。 每一行数据格式如下所示:

<integer feature 1> … <integer feature 13> <categorical feature 1> … <categorical feature 26>

其中表示广告是否被点击,点击用1表示,未点击用0表示。代表数值特征(连续特征),共有13个连续特征。代表分类特征(离散特征),共有26个离散特征。相邻两个特征用\t分隔,缺失特征用空格表示。测试集中特征已被移除。

# 一键下载训练及测试数据
!cd work/PaddleRec/models/rank/logistic_regression/data && sh run.sh

进入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中做如下配置:

基于PaddleRec的用户点击率预测_深度学习

则在model.py中:

基于PaddleRec的用户点击率预测_深度学习_02

这里是对应的,sparse_slot的第一个特征是label,于是model中就有self.label = self._sparse_data_var[0]这句话来取label

model.py文件里的某些代码在训练或预测时可以进行简单的微调。

比如在做infer时需要看一下某一个数据对应的预测结果,那么可以做如下修改:

基于PaddleRec的用户点击率预测_PaddleRec_03

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

训练结束后,模型文件保存在work/PaddleRec/models/rank/logistic_regression/increment里

4.测试模型效果

# 模型测试
!cd work/PaddleRec/models/rank/logistic_regression && python -m paddlerec.run -m ./infer_config.yaml

做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 # 模型预测结果

某个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]

当然,除了上面这个方法,也可以用这个op查看运算过过程中变量的值—— paddle.fluid.layers.Print

paddle.fluid.layers.Print可以打印正在访问的tensor的值

五、总结与展望

我在接触PaddleRec时,用的环境是Python3,但PaddleRec内有部分代码是用Python2写的,所以可能会有报错。因此,这里比较推荐各位开发者使用Python2的环境使用PaddleRec开发推荐系统。

对于新手来说,推荐系统可能略有困难,特别是数据处理部分可能会劝退很多人。但是!别灰心!虽然数据很难获取,但是搭建整个推荐系统的流程还是需要弄明白的。

如果你对数据获取、数据处理部分有疑问,不妨利用这个小长假好好补一补吧~


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK