前言:我在github上建立了一個新的repo:PaddleAI, 準備用Paddle做的一系列有趣又實用的案例,所有的案例都會上傳資料程式碼和預訓練模型,下載後可以在30s內上手,跑demo出結果,讓大家儘快看到訓練結果,用小批量資料除錯,再用全量資料跑模型,當然,也可以基於我上傳的預訓練模型進行遷移學習,如果大家有需要的話。今天剛寫好第一個專案,用Paddle做廣告CTR預估,來源於Kaggle的比賽Display Advertising Challenge, 感興趣的讀者往下看~(也可以留言期待的todo案例)
github:https://github.com/huxiaoman7/PaddleAI
Paddle models:https://github.com/PaddlePaddle/models
歡迎大家star、fork、提issue和貢獻新案例~
資料準備
資料說明
-
資料來源:Kaggle公司舉辦的展示廣告競賽中所使用的Criteo資料集。該資料包含數百萬展示廣告的特徵值和點選反饋,目的是對點選率(CTR)的預測做基準預測。
-
資料背景:Criteo是線上效果類數字營銷廠商,於2005年在法國巴黎成立,目前的核心業務是重定向廣告(retargeting)。Criteo在全球範圍內共有31間辦事處,有6間位於歐洲,有5間位於北美,有1間在巴西,在亞太地區總共有5間辦事處。Criteo是線上效果類展示廣告廠商於2014年5月13日宣佈啟動在中國的業務和運營,並將北京設為中國區總部所在地。Criteo的核心產品主要包括訪客廣告、流失客戶廣告、移動應用內效果型廣告和AD-X 移動廣告跟蹤分析產品等。Criteo擁有世界領先的自主學習式推薦引擎和預測引擎,能夠通過其對於市場的洞察提供可評估的結果,因而能夠在正確的時間通過推送廣告,將對的產品推薦給對的使用者。並且,隨著每一條廣告的交付,Criteo的引擎在預測和推薦方面的精確性也不斷提高。
-
資料格式:
- 格式:
-
訓練資料:train.txt:Criteo 公司在七天內的部分流量。每行對應的是Critio的展示廣告,第一列代表該廣告是否被點選。我們對正樣本(已點選)的和負樣本(未點選)均做了子取樣來減少資料量。類別特徵的值已經過雜湊處理為64位來進行脫敏。特徵的語義沒有公開,並且有些特徵有缺失值。行按照時間排序。
- 示例(只顯示了前20列)
- 測試資料:test.txt:測試集於訓練集的計算方式相同,但對應的是訓練集時間段的後一天的事件。並且第一列(label)已被移除。
- 示例(只顯示了前20列)
資料處理
-
下載資料
cd data && ./download.sh && cd ..
-
資料讀取
-
code:reader.py
-
原始資料中前13個feature為int型,通過reader.py將其做了資料歸一化處理為float型,避免過大和過小的資料在模型訓練中的影響。
.── CriteoDataset │ ├── train │ ├── test │ ├── infer
-
具體程式碼:
1 class CriteoDataset(Dataset):
2 def __init__(self, sparse_feature_dim):
3 self.cont_min_ = [0, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
4 self.cont_max_ = [20, 600, 100, 50, 64000, 500, 100, 50, 500, 10, 10, 10, 50]
5 self.cont_diff_ = [20, 603, 100, 50, 64000, 500, 100, 50, 500, 10, 10, 10, 50]
6 self.hash_dim_ = sparse_feature_dim
7 # here, training data are lines with line_index < train_idx_
8 self.train_idx_ = 41256555
9 self.continuous_range_ = range(1, 14)
10 self.categorical_range_ = range(14, 40)
11
12 def _reader_creator(self, file_list, is_train, trainer_num, trainer_id):
13 def reader():
14 for file in file_list:
15 with open(file, 'r') as f:
16 line_idx = 0
17 for line in f:
18 line_idx += 1
19 if is_train and line_idx > self.train_idx_:
20 break
21 elif not is_train and line_idx <= self.train_idx_:
22 continue
23 if line_idx % trainer_num != trainer_id:
24 continue
25 features = line.rstrip('\n').split('\t')
26 dense_feature = []
27 sparse_feature = []
28 for idx in self.continuous_range_:
29 if features[idx] == '':
30 dense_feature.append(0.0)
31 else:
32 dense_feature.append((float(features[idx]) - self.cont_min_[idx - 1]) / self.cont_diff_[idx - 1])
33 for idx in self.categorical_range_:
34 sparse_feature.append([hash(str(idx) + features[idx]) % self.hash_dim_])
35
36 label = [int(features[0])]
37 yield [dense_feature] + sparse_feature + [label]
38
39 return reader
模型訓練
網路結構
- code: network_conf.py (只用到ctr_dnn_model) 詳細講解
訓練方式
單機訓練
python train.py \
--train_data_path data/raw/train.txt \
2>&1 | tee train.log
分散式訓練
執行方式
sh cluster_train.sh
呼叫介面
1 pe = fluid.ParallelExecutor(
2 use_cuda=False,
3 loss_name=loss.name,
4 main_program=train_program,
5 build_strategy=build_strategy,
6 exec_strategy=exec_strategy)
7 logger.info("run dist training")
8 t = fluid.DistributeTranspiler()
9 t.transpile(args.trainer_id, pservers=args.endpoints, trainers=args.trainers)
10 if args.role == "pserver" or args.role == "PSERVER":
11 logger.info("run pserver")
12 prog = t.get_pserver_program(args.current_endpoint)
13 startup = t.get_startup_program(args.current_endpoint, pserver_program=prog)
14 exe = fluid.Executor(fluid.CPUPlace())
15 exe.run(startup)
16 exe.run(prog)
17 elif args.role == "trainer" or args.role == "TRAINER":
18 logger.info("run trainer")
19 train_prog = t.get_trainer_program()
20 train_loop(args, train_prog, py_reader, loss, auc_var, batch_auc_var,
21 args.trainers, args.trainer_id)
注:batch_size由預設的1000修改為64,可提高auc
訓練結果
-
單機訓練
- 速度太慢,迭代到第1輪batch= 4919時就停住了
-
分散式訓練
-
設定:2pserver、2trainer
-
訓練日誌:alldata/log/trainer0.log 、alldata/log/trainer1.log
-
訓練結果:
-
2019-05-11 08:34:19,678-INFO: TRAIN --> pass: 9 batch: 2577 loss: 0.467225006104 auc: 0.787909292672, batch_auc: 0.797377570934
pass_id: 0, pass_time_cost: 3150.447569
pass_id: 1, pass_time_cost: 3177.322331
pass_id: 2, pass_time_cost: 3174.676812
pass_id: 3, pass_time_cost: 3209.558880
pass_id: 4, pass_time_cost: 3134.910369
pass_id: 5, pass_time_cost: 3202.956675
pass_id: 6, pass_time_cost: 3169.575809
pass_id: 7, pass_time_cost: 3210.294044
pass_id: 8, pass_time_cost: 3039.102302
pass_id: 9, pass_time_cost: 3036.933163
模型預測
預測方式
python infer.py \
--model_path models/pass-0/ \
--data_path data/raw/valid.txt
預測結果:
- log:alldata/log/infer.txt
2019-05-13 09:35:49,177-INFO: TEST --> batch: 4500 loss: [0.46127334] auc: [0.78797872]
實驗對比
原始資料情況
label | 數量 | 比例 | |
---|---|---|---|
負樣本 | 0 | 34095179 | 0.74377662 |
正樣本 | 1 | 11745438 | 0.25622338 |
實驗資料:
- mini-demo:1%的全量資料
- demo:10%的全量資料
- raw:全量資料
BaseLine實驗
-
資料說明: mini-data(1%全量資料)
-
網路配置:一層網路,batch_size =1000
-
修改方式:
-
在network_conf.py 第151行修改,如下如所示,將input=fc3 修改為input=fc1
-
在cluster_train.sh 裡第35行和第47行將batch_size=64 修改為batch_size=1000
-
-
執行方式:
- 修改完以上兩個檔案後,執行:sh cluster_train.sh
-
輸出日誌:
- pserver:pserver0.log、pserver1.log 引數伺服器的輸出日誌
- trainer:trainer0.log、trainer1.log 每個trainer的輸出日誌 可以通過 tail -f trainer0.log -n 999 檢視輸出結果
-
實驗效果
- 訓練時間:33s(一輪迭代)
- auc:0.50234167
優化實驗(一):優化網路層
-
資料說明: mini-data(1%全量資料)
-
網路配置:三層網路,batch_size =1000
-
修改方式:
-
在network_conf.py 第151行修改,如下如所示,將input=fc1 修改為input=fc3
-
在cluster_train.sh 裡第35行和第47行將batch_size=64 修改為batch_size=1000
-
-
執行方式:
- 修改完以上兩個檔案後,執行:sh cluster_train.sh
-
輸出日誌:
- pserver:pserver0.log、pserver1.log 引數伺服器的輸出日誌
- trainer:trainer0.log、trainer1.log 每個trainer的輸出日誌 可以通過 tail -f trainer0.log -n 999 檢視輸出結果
-
實驗效果
- 訓練時間:35s(一輪迭代)
- auc:0.54893279
優化實驗(二):調整batch_size
-
資料說明: mini-data(1%全量資料)
-
網路配置:三層網路,batch_size =64
-
修改方式:
-
在network_conf.py 第151行修改,如下如所示,將input=fc1 修改為input=fc3
-
在cluster_train.sh 裡第35行和第47行將batch_size=1000 修改為batch_size=64
-
-
執行方式:
- 修改完以上兩個檔案後,執行:sh cluster_train.sh
-
輸出日誌:
- pserver:pserver0.log、pserver1.log 引數伺服器的輸出日誌
- trainer:trainer0.log、trainer1.log 每個trainer的輸出日誌 可以通過 tail -f trainer0.log -n 999 檢視輸出結果
-
實驗效果
- 訓練時間:103s(一輪迭代)
- auc:0.74322927
優化實驗(三):增加資料集
-
資料說明: 全量資料
-
網路配置:三層網路,batch_size =64,資料量由10%資料(demo_data)擴充到全量資料
-
修改方式:
-
在network_conf.py 第151行修改,如下如所示,將input=fc1 修改為input=fc3
-
在cluster_train.sh 裡第35行和第47行將batch_size=1000 修改為batch_size=64
-
在cluster_train.sh 裡將連個pserver和兩個trainer的train_data_path地址修改為raw_data的地址,如下圖所示,注意:一共需要修改四個地址
-
-
執行方式:
- 修改完以上兩個檔案後,執行:sh cluster_train.sh
-
輸出日誌:
- pserver:pserver0.log、pserver1.log 引數伺服器的輸出日誌
- trainer:trainer0.log、trainer1.log 每個trainer的輸出日誌 可以通過 tail -f trainer0.log -n 999 檢視輸出結果
-
實驗效果
- 訓練時間:3150s(一輪迭代)
- auc:0.81093872
優化實驗對比結果
- 表格1:對mini_demo(1%全量資料)做訓練,採取不同的優化方式,得到最後的優化方案的實驗結果
評估 | batch_size | batch_1000 | batch_1000 | batch_64 | batch_64 |
---|---|---|---|---|---|
優化方式 | 評估 | 一層網路 | 三層網路 | 一層網路 | 三層網路 |
mini_demo | time | 33s | 35s | 97s | 103s |
auc | 0.50234167 | 0.54893279 | 0.721332392 | 0.74322927 |
- 表格2: 增加資料集對模型精度的提升
batch_size | time | auc | |
---|---|---|---|
demo | 64 | 1133s | 0.73777626 |
全量 | 64 | 3150s | 0.81093872 |
優化方案總結
由以上兩個表格可知:
- batch 大小的改變:在資料集和其他引數不變的情況下,batch_size由1000變為64.可以提升20%的模型精度
- 網路結構的改變:在資料集和batch_size等引數不變的情況,由一層網路變為三層網路結構,大約可提升2~4%的模型精度
- 資料集的改變:由demo資料(10%全量資料)擴充到全量資料,採用同樣的batch_size,同樣的迭代次數和其他超引數,大約可提升7%的精度