|
| 1 | + |
| 2 | +<html> |
| 3 | +<head> |
| 4 | + <script type="text/x-mathjax-config"> |
| 5 | + MathJax.Hub.Config({ |
| 6 | + extensions: ["tex2jax.js", "TeX/AMSsymbols.js", "TeX/AMSmath.js"], |
| 7 | + jax: ["input/TeX", "output/HTML-CSS"], |
| 8 | + tex2jax: { |
| 9 | + inlineMath: [ ['$','$'] ], |
| 10 | + displayMath: [ ['$$','$$'] ], |
| 11 | + processEscapes: true |
| 12 | + }, |
| 13 | + "HTML-CSS": { availableFonts: ["TeX"] } |
| 14 | + }); |
| 15 | + </script> |
| 16 | + <script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js" async></script> |
| 17 | + <script type="text/javascript" src="../.tools/theme/marked.js"> |
| 18 | + </script> |
| 19 | + <link href="http://cdn.bootcss.com/highlight.js/9.9.0/styles/darcula.min.css" rel="stylesheet"> |
| 20 | + <script src="http://cdn.bootcss.com/highlight.js/9.9.0/highlight.min.js"></script> |
| 21 | + <link href="http://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" rel="stylesheet"> |
| 22 | + <link href="https://cdn.jsdelivr.net/perfect-scrollbar/0.6.14/css/perfect-scrollbar.min.css" rel="stylesheet"> |
| 23 | + <link href="../.tools/theme/github-markdown.css" rel='stylesheet'> |
| 24 | +</head> |
| 25 | +<style type="text/css" > |
| 26 | +.markdown-body { |
| 27 | + box-sizing: border-box; |
| 28 | + min-width: 200px; |
| 29 | + max-width: 980px; |
| 30 | + margin: 0 auto; |
| 31 | + padding: 45px; |
| 32 | +} |
| 33 | +</style> |
| 34 | + |
| 35 | + |
| 36 | +<body> |
| 37 | + |
| 38 | +<div id="context" class="container-fluid markdown-body"> |
| 39 | +</div> |
| 40 | + |
| 41 | +<!-- This block will be replaced by each markdown file content. Please do not change lines below.--> |
| 42 | +<div id="markdown" style='display:none'> |
| 43 | +# CTR预估 |
| 44 | + |
| 45 | +## 背景介绍 |
| 46 | + |
| 47 | +CTR(Click-Through Rate)\[[1](https://en.wikipedia.org/wiki/Click-through_rate)\] 是用来表示用户点击一个特定链接的概率, |
| 48 | +通常被用来衡量一个在线广告系统的有效性。 |
| 49 | + |
| 50 | +当有多个广告位时,CTR 预估一般会作为排序的基准。 |
| 51 | +比如在搜索引擎的广告系统里,当用户输入一个带商业价值的搜索词(query)时,系统大体上会执行下列步骤来展示广告: |
| 52 | + |
| 53 | +1. 召回满足 query 的广告集合 |
| 54 | +2. 业务规则和相关性过滤 |
| 55 | +3. 根据拍卖机制和 CTR 排序 |
| 56 | +4. 展出广告 |
| 57 | + |
| 58 | +可以看到,CTR 在最终排序中起到了很重要的作用。 |
| 59 | + |
| 60 | +### 发展阶段 |
| 61 | +在业内,CTR 模型经历了如下的发展阶段: |
| 62 | + |
| 63 | +- Logistic Regression(LR) / GBDT + 特征工程 |
| 64 | +- LR + DNN 特征 |
| 65 | +- DNN + 特征工程 |
| 66 | + |
| 67 | +在发展早期时 LR 一统天下,但最近 DNN 模型由于其强大的学习能力和逐渐成熟的性能优化, |
| 68 | +逐渐地接过 CTR 预估任务的大旗。 |
| 69 | + |
| 70 | + |
| 71 | +### LR vs DNN |
| 72 | + |
| 73 | +下图展示了 LR 和一个 \(3x2\) 的 DNN 模型的结构: |
| 74 | + |
| 75 | +<p align="center"> |
| 76 | +<img src="images/lr_vs_dnn.jpg" width="620" hspace='10'/> <br/> |
| 77 | +Figure 1. LR 和 DNN 模型结构对比 |
| 78 | +</p> |
| 79 | + |
| 80 | +LR 的蓝色箭头部分可以直接类比到 DNN 中对应的结构,可以看到 LR 和 DNN 有一些共通之处(比如权重累加), |
| 81 | +但前者的模型复杂度在相同输入维度下比后者可能低很多(从某方面讲,模型越复杂,越有潜力学习到更复杂的信息)。 |
| 82 | + |
| 83 | +如果 LR 要达到匹敌 DNN 的学习能力,必须增加输入的维度,也就是增加特征的数量, |
| 84 | +这也就是为何 LR 和大规模的特征工程必须绑定在一起的原因。 |
| 85 | + |
| 86 | +LR 对于 DNN 模型的优势是对大规模稀疏特征的容纳能力,包括内存和计算量等方面,工业界都有非常成熟的优化方法。 |
| 87 | + |
| 88 | +而 DNN 模型具有自己学习新特征的能力,一定程度上能够提升特征使用的效率, |
| 89 | +这使得 DNN 模型在同样规模特征的情况下,更有可能达到更好的学习效果。 |
| 90 | + |
| 91 | +本文后面的章节会演示如何使用 PaddlePaddle 编写一个结合两者优点的模型。 |
| 92 | + |
| 93 | + |
| 94 | +## 数据和任务抽象 |
| 95 | + |
| 96 | +我们可以将 `click` 作为学习目标,任务可以有以下几种方案: |
| 97 | + |
| 98 | +1. 直接学习 click,0,1 作二元分类 |
| 99 | +2. Learning to rank, 具体用 pairwise rank(标签 1>0)或者 listwise rank |
| 100 | +3. 统计每个广告的点击率,将同一个 query 下的广告两两组合,点击率高的>点击率低的,做 rank 或者分类 |
| 101 | + |
| 102 | +我们直接使用第一种方法做分类任务。 |
| 103 | + |
| 104 | +我们使用 Kaggle 上 `Click-through rate prediction` 任务的数据集\[[2](https://www.kaggle.com/c/avazu-ctr-prediction/data)\] 来演示模型。 |
| 105 | + |
| 106 | +具体的特征处理方法参看 [data process](./dataset.md) |
| 107 | + |
| 108 | + |
| 109 | +## Wide & Deep Learning Model |
| 110 | + |
| 111 | +谷歌在 16 年提出了 Wide & Deep Learning 的模型框架,用于融合适合学习抽象特征的 DNN 和 适用于大规模稀疏特征的 LR 两种模型的优点。 |
| 112 | + |
| 113 | + |
| 114 | +### 模型简介 |
| 115 | + |
| 116 | +Wide & Deep Learning Model\[[3](#参考文献)\] 可以作为一种相对成熟的模型框架使用, |
| 117 | +在 CTR 预估的任务中工业界也有一定的应用,因此本文将演示使用此模型来完成 CTR 预估的任务。 |
| 118 | + |
| 119 | +模型结构如下: |
| 120 | + |
| 121 | +<p align="center"> |
| 122 | +<img src="images/wide_deep.png" width="820" hspace='10'/> <br/> |
| 123 | +Figure 2. Wide & Deep Model |
| 124 | +</p> |
| 125 | + |
| 126 | +模型左边的 Wide 部分,可以容纳大规模系数特征,并且对一些特定的信息(比如 ID)有一定的记忆能力; |
| 127 | +而模型右边的 Deep 部分,能够学习特征间的隐含关系,在相同数量的特征下有更好的学习和推导能力。 |
| 128 | + |
| 129 | + |
| 130 | +### 编写模型输入 |
| 131 | + |
| 132 | +模型只接受 3 个输入,分别是 |
| 133 | + |
| 134 | +- `dnn_input` ,也就是 Deep 部分的输入 |
| 135 | +- `lr_input` ,也就是 Wide 部分的输入 |
| 136 | +- `click` , 点击与否,作为二分类模型学习的标签 |
| 137 | + |
| 138 | +```python |
| 139 | +dnn_merged_input = layer.data( |
| 140 | + name='dnn_input', |
| 141 | + type=paddle.data_type.sparse_binary_vector(data_meta_info['dnn_input'])) |
| 142 | + |
| 143 | +lr_merged_input = layer.data( |
| 144 | + name='lr_input', |
| 145 | + type=paddle.data_type.sparse_binary_vector(data_meta_info['lr_input'])) |
| 146 | + |
| 147 | +click = paddle.layer.data(name='click', type=dtype.dense_vector(1)) |
| 148 | +``` |
| 149 | + |
| 150 | +### 编写 Wide 部分 |
| 151 | + |
| 152 | +Wide 部分直接使用了 LR 模型,但激活函数改成了 `RELU` 来加速 |
| 153 | + |
| 154 | +```python |
| 155 | +def build_lr_submodel(): |
| 156 | + fc = layer.fc( |
| 157 | + input=lr_merged_input, size=1, name='lr', act=paddle.activation.Relu()) |
| 158 | + return fc |
| 159 | +``` |
| 160 | + |
| 161 | +### 编写 Deep 部分 |
| 162 | + |
| 163 | +Deep 部分使用了标准的多层前向传导的 DNN 模型 |
| 164 | + |
| 165 | +```python |
| 166 | +def build_dnn_submodel(dnn_layer_dims): |
| 167 | + dnn_embedding = layer.fc(input=dnn_merged_input, size=dnn_layer_dims[0]) |
| 168 | + _input_layer = dnn_embedding |
| 169 | + for i, dim in enumerate(dnn_layer_dims[1:]): |
| 170 | + fc = layer.fc( |
| 171 | + input=_input_layer, |
| 172 | + size=dim, |
| 173 | + act=paddle.activation.Relu(), |
| 174 | + name='dnn-fc-%d' % i) |
| 175 | + _input_layer = fc |
| 176 | + return _input_layer |
| 177 | +``` |
| 178 | + |
| 179 | +### 两者融合 |
| 180 | + |
| 181 | +两个 submodel 的最上层输出加权求和得到整个模型的输出,输出部分使用 `sigmoid` 作为激活函数,得到区间 (0,1) 的预测值, |
| 182 | +来逼近训练数据中二元类别的分布,并最终作为 CTR 预估的值使用。 |
| 183 | + |
| 184 | +```python |
| 185 | +# conbine DNN and LR submodels |
| 186 | +def combine_submodels(dnn, lr): |
| 187 | + merge_layer = layer.concat(input=[dnn, lr]) |
| 188 | + fc = layer.fc( |
| 189 | + input=merge_layer, |
| 190 | + size=1, |
| 191 | + name='output', |
| 192 | + # use sigmoid function to approximate ctr, wihch is a float value between 0 and 1. |
| 193 | + act=paddle.activation.Sigmoid()) |
| 194 | + return fc |
| 195 | +``` |
| 196 | + |
| 197 | +### 训练任务的定义 |
| 198 | +```python |
| 199 | +dnn = build_dnn_submodel(dnn_layer_dims) |
| 200 | +lr = build_lr_submodel() |
| 201 | +output = combine_submodels(dnn, lr) |
| 202 | + |
| 203 | +# ============================================================================== |
| 204 | +# cost and train period |
| 205 | +# ============================================================================== |
| 206 | +classification_cost = paddle.layer.multi_binary_label_cross_entropy_cost( |
| 207 | + input=output, label=click) |
| 208 | + |
| 209 | + |
| 210 | +paddle.init(use_gpu=False, trainer_count=11) |
| 211 | + |
| 212 | +params = paddle.parameters.create(classification_cost) |
| 213 | + |
| 214 | +optimizer = paddle.optimizer.Momentum(momentum=0) |
| 215 | + |
| 216 | +trainer = paddle.trainer.SGD( |
| 217 | + cost=classification_cost, parameters=params, update_equation=optimizer) |
| 218 | + |
| 219 | +dataset = AvazuDataset(train_data_path, n_records_as_test=test_set_size) |
| 220 | + |
| 221 | +def event_handler(event): |
| 222 | + if isinstance(event, paddle.event.EndIteration): |
| 223 | + if event.batch_id % 100 == 0: |
| 224 | + logging.warning("Pass %d, Samples %d, Cost %f" % ( |
| 225 | + event.pass_id, event.batch_id * batch_size, event.cost)) |
| 226 | + |
| 227 | + if event.batch_id % 1000 == 0: |
| 228 | + result = trainer.test( |
| 229 | + reader=paddle.batch(dataset.test, batch_size=1000), |
| 230 | + feeding=field_index) |
| 231 | + logging.warning("Test %d-%d, Cost %f" % (event.pass_id, event.batch_id, |
| 232 | + result.cost)) |
| 233 | + |
| 234 | + |
| 235 | +trainer.train( |
| 236 | + reader=paddle.batch( |
| 237 | + paddle.reader.shuffle(dataset.train, buf_size=500), |
| 238 | + batch_size=batch_size), |
| 239 | + feeding=field_index, |
| 240 | + event_handler=event_handler, |
| 241 | + num_passes=100) |
| 242 | +``` |
| 243 | +## 运行训练和测试 |
| 244 | +训练模型需要如下步骤: |
| 245 | + |
| 246 | +1. 下载训练数据,可以使用 Kaggle 上 CTR 比赛的数据\[[2](#参考文献)\] |
| 247 | + 1. 从 [Kaggle CTR](https://www.kaggle.com/c/avazu-ctr-prediction/data) 下载 train.gz |
| 248 | + 2. 解压 train.gz 得到 train.txt |
| 249 | +2. 执行 `python train.py --train_data_path train.txt` ,开始训练 |
| 250 | + |
| 251 | +上面第2个步骤可以为 `train.py` 填充命令行参数来定制模型的训练过程,具体的命令行参数及用法如下 |
| 252 | + |
| 253 | +``` |
| 254 | +usage: train.py [-h] --train_data_path TRAIN_DATA_PATH |
| 255 | + [--batch_size BATCH_SIZE] [--test_set_size TEST_SET_SIZE] |
| 256 | + [--num_passes NUM_PASSES] |
| 257 | + [--num_lines_to_detact NUM_LINES_TO_DETACT] |
| 258 | + |
| 259 | +PaddlePaddle CTR example |
| 260 | + |
| 261 | +optional arguments: |
| 262 | + -h, --help show this help message and exit |
| 263 | + --train_data_path TRAIN_DATA_PATH |
| 264 | + path of training dataset |
| 265 | + --batch_size BATCH_SIZE |
| 266 | + size of mini-batch (default:10000) |
| 267 | + --test_set_size TEST_SET_SIZE |
| 268 | + size of the validation dataset(default: 10000) |
| 269 | + --num_passes NUM_PASSES |
| 270 | + number of passes to train |
| 271 | + --num_lines_to_detact NUM_LINES_TO_DETACT |
| 272 | + number of records to detect dataset's meta info |
| 273 | +``` |
| 274 | + |
| 275 | +## 参考文献 |
| 276 | +1. <https://en.wikipedia.org/wiki/Click-through_rate> |
| 277 | +2. <https://www.kaggle.com/c/avazu-ctr-prediction/data> |
| 278 | +3. Cheng H T, Koc L, Harmsen J, et al. [Wide & deep learning for recommender systems](https://arxiv.org/pdf/1606.07792.pdf)[C]//Proceedings of the 1st Workshop on Deep Learning for Recommender Systems. ACM, 2016: 7-10. |
| 279 | + |
| 280 | +</div> |
| 281 | +<!-- You can change the lines below now. --> |
| 282 | + |
| 283 | +<script type="text/javascript"> |
| 284 | +marked.setOptions({ |
| 285 | + renderer: new marked.Renderer(), |
| 286 | + gfm: true, |
| 287 | + breaks: false, |
| 288 | + smartypants: true, |
| 289 | + highlight: function(code, lang) { |
| 290 | + code = code.replace(/&/g, "&") |
| 291 | + code = code.replace(/>/g, ">") |
| 292 | + code = code.replace(/</g, "<") |
| 293 | + code = code.replace(/ /g, " ") |
| 294 | + return hljs.highlightAuto(code, [lang]).value; |
| 295 | + } |
| 296 | +}); |
| 297 | +document.getElementById("context").innerHTML = marked( |
| 298 | + document.getElementById("markdown").innerHTML) |
| 299 | +</script> |
| 300 | +</body> |
0 commit comments