当前位置:   article > 正文

推荐系统-排序算法:GBDT+LR_gbdp+lr 做排序

gbdp+lr 做排序

1. GBDT + LR 是什么

本质上GBDT+LR是一种具有stacking思想的二分类器模型,所以可以用来解决二分类问题。这个方法出自于Facebook 2014年的论文 Practical Lessons from Predicting Clicks on Ads at Facebook 。

2. GBDT + LR 用在哪

GBDT+LR 使用最广泛的场景是CTR点击率预估,即预测当给用户推送的广告会不会被用户点击。

在CTR预估问题的发展初期,使用最多的方法就是逻辑回归(LR),LR使用了Sigmoid变换将函数值映射到0~1区间,映射后的函数值就是CTR的预估值。
LR属于线性模型,容易并行化,可以轻松处理上亿条数据,但是学习能力十分有限,需要大量的特征工程来增加模型的学习能力。但大量的特征工程耗时耗力同时并不一定会带来效果提升。因此,如何自动发现有效的特征、特征组合,弥补人工经验不足,缩短LR特征实验周期,是亟需解决的问题。

FM模型通过隐变量的方式,发现两两特征之间的组合关系,但这种特征组合仅限于两两特征之间,后来发展出来了使用深度神经网络去挖掘更高层次的特征组合关系。但其实在使用神经网络之前,GBDT也是一种经常用来发现特征组合的有效思路。

CTR预估的模型训练分为离线训练(offline)、在线训练(online),其中离线部分目标主要是训练出可用模型,而在线部分则考虑模型上线后,性能可能随时间而出现下降,若出现这种情况,可选择使用Online-Learning来在线更新模型:

2.1 离线部分

  1. 数据收集:收集和业务相关的数据,如在app位置进行埋点
  2. 预处理
  3. 构造数据集:切训练、测试、验证集
  4. 特征工程:对原始数据进行基本的特征处理,包括去除相关性大的特征,离散变量one-hot,连续特征离散化
  5. 模型选择:选择合理的机器学习模型来完成相应工作,原则是先从简入深,先找到baseline,然后逐步优化;
  6. 超参选择:利用gridsearch、randomsearch或者hyperopt来进行超参选择,选择在离线数据集中性能最好的超参组合;
  7. 在线A/B Test:选择优化过后的模型和原先模型(如baseline)进行A/B Test,若性能有提升则替换原先模型;

2.2 在线模型

  1. Cache & Logic:设定简单过滤规则,过滤异常数据;
  2. 模型更新:当Cache & Logic 收集到合适大小数据时,对模型进行增量训练finetuning,若在测试集上比原始模型性能高,则更新model server的模型参数;
  3. Model Server:接受数据请求,返回预测结果;

3. GBDT + LR 的结构

正如它的名字一样,GBDT+LR 由两部分组成,其中GBDT用来对训练集提取特征作为新的训练输入数据,LR作为新的训练输入数据的分类器。

GBDT和LR的融合方案,FaceBook的paper中有个例子:

图中共有两棵树,x为一条输入样本,遍历两棵树后,x样本分别落到两颗树的叶子节点上,每个叶子节点对应LR的一个特征。构造的新特征向量(Transformed Features)是取值0/1的。

举例来说:上图有两棵树(即两个弱分类器),左树有三个叶子节点,右树有两个叶子节点,最终的特征即为五维的向量。对于输入x,假设他落在左树第一个节点,编码[1,0,0],落在右树第二个节点则编码[0,1],所以整体的编码为[1,0,0,0,1],这类编码作为input,输入到LR中进行分类。在对原始数据进行GBDT提取为新的数据这一操作之后,数据不仅变得稀疏,而且可能会导致新的训练数据特征维度过大的问题,因此,在Logistic Regression这一层中,可使用正则化来减少过拟合的风险,在Facebook的论文中采用的是L1正则化。

4. GBDT+LR代码实践

本文使用lightgbm包来训练我们的GBDT模型,训练共100棵树(即100个弱分类器),每棵树有64个叶子结点(Transformed Features 有64*100维)

  1. df_train = pd.read_csv('data/train.csv')
  2. df_test = pd.read_csv('data/test.csv')
  3. NUMERIC_COLS = [
  4. "ps_reg_01", "ps_reg_02", "ps_reg_03",
  5. "ps_car_12", "ps_car_13", "ps_car_14", "ps_car_15",
  6. ]
  7. y_train = df_train['target'] # training label
  8. y_test = df_test['target'] # testing label
  9. X_train = df_train[NUMERIC_COLS] # training dataset
  10. X_test = df_test[NUMERIC_COLS] # testing dataset
  11. # create dataset for lightgbm
  12. lgb_train = lgb.Dataset(X_train, y_train)
  13. lgb_eval = lgb.Dataset(X_test, y_test, reference=lgb_train)
  14. params = {
  15. 'task': 'train',
  16. 'boosting_type': 'gbdt',
  17. 'objective': 'binary',
  18. 'metric': {'binary_logloss'},
  19. 'num_leaves': 64,
  20. 'num_trees': 100,
  21. 'learning_rate': 0.01,
  22. 'feature_fraction': 0.9,
  23. 'bagging_fraction': 0.8,
  24. 'bagging_freq': 5,
  25. 'verbose': 0
  26. }
  27. # number of leaves,will be used in feature transformation
  28. num_leaf = 64
  29. # train
  30. gbm = lgb.train(params,
  31. lgb_train,
  32. num_boost_round=100,
  33. valid_sets=lgb_train)
  34. # save model to file
  35. gbm.save_model('model.txt')

在训练得到100棵树之后,我们需要得到的不是GBDT的预测结果,而是每一条训练数据落在了每棵树的哪个叶子结点上,因此需要使用下面的语句:

  1. y_pred = gbm.predict(X_train, pred_leaf=True)
  2. print(np.array(y_pred).shape)
  3. print(y_pred[0])

打印上面结果的输出,可以看到shape是(8001,100),其中8001是训练集的样本个数,100是因为有100棵树(aka. 弱分类器)

  1. (8001, 100)
  2. [[43 26 47 47 47 19 36 19 50 52 29 0 0 0 46 23 13 27 27 13 10 22 0 10
  3. 4 57 17 55 54 57 59 42 22 22 22 13 8 5 27 5 58 23 58 14 16 16 10 32
  4. 60 32 4 4 4 4 4 46 57 48 57 34 54 6 35 6 4 55 13 23 15 51 40 0
  5. 47 40 10 29 24 24 31 24 55 3 41 3 22 57 6 0 6 6 57 55 57 16 12 18
  6. 30 15 17 30]]

每个值就是当前输入x落到了哪个叶子上,是[0,64]之间的值。

我们需要将每棵树的特征进行one-hot处理,如前面所说,假设第一棵树落在43号叶子结点上,那我们需要建立一个64维的向量,除43维之外全部都是0。因此用于LR训练的特征维数共num_trees * num_leaves = 100 * 64

之后我们可以用转换后的训练集特征和label训练我们的LR模型。

 

一些说明

为什么建树采用GBDT而非RF:RF也是多棵树,但从效果上有实践证明不如GBDT。且GBDT前面的树,特征分裂主要体现对多数样本有区分度的特征;后面的树,主要体现的是经过前N颗树,残差仍然较大的少数样本。优先选用在整体上有区分度的特征,再选用针对少数样本有区分度的特征,思路更加合理,这应该也是用GBDT的原因。

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号