diff --git a/ctr/README.md b/ctr/README.md index 50eadab4fe7e10eff3f0abe825526f5cb6ac591d..653e599382676284b07277caa0bb1390979b592b 100644 --- a/ctr/README.md +++ b/ctr/README.md @@ -1,32 +1,6 @@ -
-

Table of Contents

-
- -
-
- - - - -# 背景介绍 +# CTR预估 + +## 背景介绍 CTR(Click-through rate) 是用来表示用户点击一个特定链接的概率, 通常被用来衡量一个在线广告系统的有效性。 @@ -40,7 +14,7 @@ CTR(Click-through rate) 是用来表示用户点击一个特定链接的概率 4. 展出广告 可以看到,CTR 在最终排序中起到了很重要的作用。 - +### 发展阶段 在业内,CTR 模型经历了如下的发展阶段: - Logistic Regression(LR) / GBDT + 特征工程 @@ -51,9 +25,7 @@ CTR(Click-through rate) 是用来表示用户点击一个特定链接的概率 逐渐地接过 CTR 预估任务的大旗。 - - -## LR vs DNN +### LR vs DNN 下图展示了 LR 和一个 \(3x2\) 的 NN 模型的结构: @@ -73,9 +45,7 @@ LR 对于 NN 模型的优势是对大规模稀疏特征的容纳能力,包括 本文后面的章节会演示如何使用 PaddlePaddle 编写一个结合两者优点的模型。 - - -# 数据和任务抽象 +## 数据和任务抽象 我们可以将 `click` 作为学习目标,具体任务可以有以下几种方案: @@ -90,16 +60,12 @@ LR 对于 NN 模型的优势是对大规模稀疏特征的容纳能力,包括 具体的特征处理方法参看 [data process](./dataset.md) - - -# Wide & Deep Learning Model +## Wide & Deep Learning Model 谷歌在 16 年提出了 Wide & Deep Learning 的模型框架,用于融合适合学习抽象特征的 DNN 和 适用于大规模稀疏特征的 LR 两种模型的优点。 - - -## 模型简介 +### 模型简介 Wide & Deep Learning Model 可以作为一种相对成熟的模型框架使用, 在 CTR 预估的任务中工业界也有一定的应用,因此本文将演示使用此模型来完成 CTR 预估的任务。 @@ -112,9 +78,7 @@ Wide & Deep Learning Model 可以作为一种相对成熟的模型框架使用 而模型右边的 Deep 部分,能够学习特征间的隐含关系,在相同数量的特征下有更好的学习和推导能力。 - - -## 编写模型输入 +### 编写模型输入 模型只接受 3 个输入,分别是 @@ -132,13 +96,9 @@ lr_merged_input = layer.data( type=paddle.data_type.sparse_binary_vector(data_meta_info['lr_input'])) click = paddle.layer.data(name='click', type=dtype.dense_vector(1)) - - ``` - - -## 编写 Wide 部分 +### 编写 Wide 部分 Wide 部分直接使用了 LR 模型,但激活函数改成了 `RELU` 来加速 @@ -147,13 +107,9 @@ def build_lr_submodel(): fc = layer.fc( input=lr_merged_input, size=1, name='lr', act=paddle.activation.Relu()) return fc - - ``` - - -## 编写 Deep 部分 +### 编写 Deep 部分 Deep 部分使用了标准的多层前向传导的 NN 模型 @@ -169,13 +125,9 @@ def build_dnn_submodel(dnn_layer_dims): name='dnn-fc-%d' % no) _input_layer = fc return _input_layer - - ``` - - -## 两者融合 +### 两者融合 两个 submodel 的最上层输出加权求和得到整个模型的输出,输出部分使用 `sigmoid` 作为激活函数,得到区间\((0,1)\) 的预测值, 来逼近训练数据中二元类别的分布,最终作为 CTR 预估的值使用。 @@ -191,13 +143,9 @@ def combine_submodels(dnn, lr): # use sigmoid function to approximate ctr rate, a float value between 0 and 1. act=paddle.activation.Sigmoid()) return fc - - ``` - - -## 训练任务的定义 +### 训练任务的定义 ```python dnn = build_dnn_submodel(dnn_layer_dims) @@ -240,16 +188,10 @@ trainer.train( feeding=field_index, event_handler=event_handler, num_passes=100) - - ``` - - -# 引用 - -- [1] -- [2] Mikolov, Tomáš, et al. "Strategies for training large scale neural network language models." Automatic Speech Recognition and Understanding (ASRU), 2011 IEEE Workshop on. IEEE, 2011. -- [3] -- [4] Cheng, Heng-Tze, et al. "Wide & deep learning for recommender systems." Proceedings of the 1st Workshop on Deep Learning for Recommender Systems. ACM, 2016. - +## 引用 +1. +2. Mikolov T, Deoras A, Povey D, et al. Strategies for training large scale neural network language models[C]//Automatic Speech Recognition and Understanding (ASRU), 2011 IEEE Workshop on. IEEE, 2011: 196-201. +3. +4. Cheng H T, Koc L, Harmsen J, et al. Wide & deep learning for recommender systems[C]//Proceedings of the 1st Workshop on Deep Learning for Recommender Systems. ACM, 2016: 7-10. diff --git a/ctr/data_provider.py b/ctr/data_provider.py index a004a3e547a5d6d933167956c72dddfca7606f79..f02d3d33e75163cf772921ef54729a3fc8da022b 100644 --- a/ctr/data_provider.py +++ b/ctr/data_provider.py @@ -150,8 +150,8 @@ for key in id_features: fields[key] = IDfeatureGenerator(10000) # used as feed_dict in PaddlePaddle -field_index = dict( - (key, id) for id, key in enumerate(['dnn_input', 'lr_input', 'click'])) +field_index = dict((key, id) + for id, key in enumerate(['dnn_input', 'lr_input', 'click'])) def detect_dataset(path, topn, id_fea_space=10000): @@ -175,7 +175,7 @@ def detect_dataset(path, topn, id_fea_space=10000): feature_dims[key] = item.size() #for key in id_features: - #feature_dims[key] = id_fea_space + #feature_dims[key] = id_fea_space feature_dims['hour'] = 24 feature_dims['click'] = 1 @@ -222,8 +222,7 @@ class AvazuDataset(object): def train(self): self.mode = self.TRAIN_MODE - return self._parse( - self.train_path, skip_n_lines=self.n_records_as_test) + return self._parse(self.train_path, skip_n_lines=self.n_records_as_test) def test(self): self.mode = self.TEST_MODE @@ -257,7 +256,8 @@ class AvazuDataset(object): else: fea0 = fields[key].cross_fea0 fea1 = fields[key].cross_fea1 - record.append(fields[key].gen_cross_fea(row[fea0], row[fea1])) + record.append( + fields[key].gen_cross_fea(row[fea0], row[fea1])) sparse_input = concat_sparse_vectors(record, id_dims) diff --git a/ctr/dataset.md b/ctr/dataset.md index 7ea9b59f7cdba6803b16634986924c6c990b7727..62511be2e99de38a3f6bcdc4d1b204d2e098aba5 100644 --- a/ctr/dataset.md +++ b/ctr/dataset.md @@ -1,44 +1,12 @@ - - - - - -# 数据集介绍 +# 数据及处理 +## 数据集介绍 数据集使用 `csv` 格式存储,其中各个字段内容如下: - `id` : ad identifier - `click` : 0/1 for non-click/click - `hour` : format is YYMMDDHH, so 14091123 means 23:00 on Sept. 11, 2014 UTC. -- `C1` – anonymized categorical variable +- `C1` : anonymized categorical variable - `banner_pos` - `site_id` - `site_domain` @@ -51,45 +19,33 @@ - `device_model` - `device_type` - `device_conn_type` -- `C14-C21` – anonymized categorical variables +- `C14-C21` : anonymized categorical variables - - -# 特征提取 +## 特征提取 下面我们会简单演示几种特征的提取方式。 原始数据中的特征可以分为以下几类: 1. ID 类特征(稀疏,数量多) -```python - `id` - `site_id` - `app_id` - `device_id` -``` - 2. 类别类特征(稀疏,但数量有限) -```python + - `C1` - `site_category` - `device_type` - `C14-C21` -``` - 3. 数值型特征转化为类别型特征 -```python -- hour (可以转化成数值,也可以按小时为单位转化为类别) - - -``` - +- hour (可以转化成数值,也可以按小时为单位转化为类别) -## 类别类特征 +### 类别类特征 类别类特征的提取方法有以下两种: @@ -97,9 +53,7 @@ 2. 类似词向量,用一个 Embedding Table 将每个类别映射到对应的向量 - - -## ID 类特征 +### ID 类特征 ID 类特征的特点是稀疏数据,但量比较大,直接使用 One-hot 表示时维度过大。 @@ -111,25 +65,17 @@ ID 类特征的特点是稀疏数据,但量比较大,直接使用 One-hot 上面的方法尽管存在一定的碰撞概率,但能够处理任意数量的 ID 特征,并保留一定的效果[2]。 - - - -## 数值型特征 +### 数值型特征 一般会做如下处理: - 归一化,直接作为特征输入模型 - 用区间分割处理成类别类特征,稀疏化表示,模糊细微上的差别 +## 特征处理 - -# 特征处理 - - - - -## 类别型特征 +### 类别型特征 类别型特征有有限多种值,在模型中,我们一般使用 embedding table 将每种值映射为连续值的向量。 @@ -171,15 +117,11 @@ class CategoryFeatureGenerator(object): def __repr__(self): return '' % len(self.dic) - ``` 本任务中,类别类特征会输入到 DNN 中使用。 - - - -## ID 类特征 +### ID 类特征 ID 类特征代稀疏值,且值的空间很大的情况,一般用模操作规约到一个有限空间, 之后可以当成类别类特征使用,这里我们会将 ID 类特征输入到 LR 模型中使用。 @@ -201,13 +143,9 @@ class IDfeatureGenerator(object): def size(self): return self.max_dim - - ``` - - -## 交叉类特征 +### 交叉类特征 LR 模型作为 Wide & Deep model 的 `wide` 部分,可以输入很 wide 的数据(特征空间的维度很大), 为了充分利用这个优势,我们将演示交叉组合特征构建成更大维度特征的情况,之后塞入到模型中训练。 @@ -218,139 +156,33 @@ LR 模型作为 Wide & Deep model 的 `wide` 部分,可以输入很 wide 的 def gen_cross_fea(self, fea1, fea2): key = str(fea1) + str(fea2) return self.gen(key) - ``` 比如,我们觉得原始数据中, `device_id` 和 `site_id` 有一些关联(比如某个 device 倾向于浏览特定 site), 我们通过组合出两者组合来捕捉这类信息。 - - - -## 特征维度 - - - - -### Deep submodel(DNN)特征 - - - - --- -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
featuredimention
`app_category`21
`site_category`22
`device_conn_type`5
`hour`24
`banner_pos`7
Total79
- - - - -### Wide submodel(LR)特征 - - - - --- -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FeatureDimention
`id`100000
`site_id`100000
`app_id`100000
`device_id`100000
`device_id` X `site_id`10000000
Total10,400,000
- - - - -# 输入到 PaddlePaddle 中 +### 特征维度 +#### Deep submodel(DNN)特征 +| feature | dimention | +|------------------+-----------| +| app_category | 21 | +| site_category | 22 | +| device_conn_type | 5 | +| hour | 24 | +| banner_pos | 7 | +| **Total** | 79 | + +#### Wide submodel(LR)特征 +| Feature | Dimention | +|---------------------+-----------| +| id | 10000 | +| site_id | 10000 | +| app_id | 10000 | +| device_id | 10000 | +| device_id X site_id | 1000000 | +| **Total** | 1,040,000 | + +## 输入到 PaddlePaddle 中 Deep 和 Wide 两部分均以 `sparse_binary_vector` 的格式[1]输入,输入前需要将相关特征拼合,模型最终只接受 3 个 input, 分别是 @@ -379,8 +211,6 @@ def concat_sparse_vectors(inputs, dims): res.append(v + start) start += dims[no] return res - ``` -[1] - +1.