赞
踩
更多精彩内容请移步公众号:推荐算法工程师
公众号后台回复”进群“,加入学习交流群,和小伙伴们一起学习,一起进步~
介绍GBDT+LR背景
点击率预估模型涉及的训练样本一般是上亿级别,样本量大,模型常采用速度较快的LR(logistic regression)。LR虽然是线性模型线性模型,但是在业界广泛使用。为什么呢?虽然模型本身表达能力差,但是可以通过特征工程不断减少问题的非线性结构。又由于模型计算复杂度低,可以吞吐超大规模的特征空间和样本集合,这样就为效果优化打开了空间。同时,他可以学习id化特征,从而减少了特征工程的环节,可以提高特征的实时性[1]。但正因为LR学习能力有限,此时特征工程尤其重要。
在深度学习大行其道之前,一般采用人工或一些一些传统的方法,人工成本高就不说了,传统的方法像FM,FFM,只能挖掘两个特征间的特征交互关系,作用有限。GBDT是解决这个问题的一种不错方案。回顾我们上篇文章所讲的,GBDT有以下优点:
显而易见,GBDT对于处理特征有很多优点。而LR虽然是线性模型,但是Facebook探索出一种将GBDT和LR结合的方案,来预测广告的点击通过率(Click Trough Rate,CTR)的预测问题。结果显示融合方案比单个的GBDT或LR的性能高3%左右。
论文地址:
http://quinonero.net/Publications/predicting-clicks-facebook.pdf
代码地址:
https://github.com/wyl6/Recommender-Systems-Samples/tree/master/RecSys%20Traditional/DecisionTree/LRGBDT
首先要说明的是,对点击通过问题,要么点击要么不点击,因此y∈{1,_1},是个二分类的问题。因此LR+GBDT中的GBDT是上篇文章中所说的L2-Treeboost方案,上篇文章介绍过了这里不多说。
那么,GBDT与LR是如何结合的呢?看懂论文上的一张图就够了:
如上图所示,这个强学习器由两个弱学习器前向加和组成。假设x输入GBDT后,落到左边回归树的第一个节点,落到右边回归树的第一个节点。则GBDT对样本x的特征进行工程处理得到的转换特征,就可以表示为:(1,0,0)串联(1,0)==》(1,0,0,1,0)。然后将特征输入LR即可进行分类。
GBDT+LR这个模型还是有一些局限性的。首先看GBDT的缺点,上篇文章中我们提到过:GBDT有一些缺点:
[3]中凯菜大佬指出,对于高维稀疏的特征,GBDT容易过拟合,表现不理想,甚至LR都比GBDT好,并给了一个例子:
对于高维稀疏的特征,GBDT容易过拟合,表现不理想,甚至LR都比GBDT好,[3]中凯菜大佬给了一个很好的例子:
关于GBDT+LR的缺点,[4]中屈伟大佬给出了一些看法:
我们分析模型的优缺点,是为了从中借鉴,也是为了更好地使用模型。没有哪个模型可以解决所有问题,不同模型都有自己的优点和缺点。GBDT+LR这种组合,有局限性,但也提供了一种不错的思路。实际业务中还是根据不同情况选择最合适的模型,扬长补短。
我们实战一个GBDT对Iris数据集做分类的小例子。改自[4]。参数的使用可以参考官网说明:https://github.com/microsoft/LightGBM/blob/master/docs/Parameters.rst。
首先,加载数据:
from sklearn.datasets import load_iris
import numpy as np
import lightgbm as lgb
import pandas as pd
import warnings
warnings.filterwarnings('ignore')
## build data
iris = pd.DataFrame(load_iris().data)
iris.columns = ['SepalLengthCm','SepalWidthCm','PetalLengthCm','PetalWidthCm']
iris['Species'] = load_iris().target%2
由于Iris的labels有三类setosa,versicolor和virginica,而GBDT+LR做CTR是做二分类,因此为了一致,第四行将第三类和第一类合为一类。然后做训练和测试数据划分:
## train test split
train=iris[0:130]
test=iris[130:]
X_train=train.filter(items=['SepalLengthCm','SepalWidthCm','PetalLengthCm','PetalWidthCm'])
X_test=test.filter(items=['SepalLengthCm','SepalWidthCm','PetalLengthCm','PetalWidthCm'])
y_train=train[[train.Species.name]]
y_test=test[[test.Species.name]]
样本总共有150个,每类50个,简单划分下得了。然后构建和训练GBDT模型:
## build lgb model lgb_train = lgb.Dataset(X_train.as_matrix(), y_train.values.reshape(y_train.shape[0],)) lgb_eval = lgb.Dataset(X_test.as_matrix(), y_test.values.reshape(y_test.shape[0],), reference=lgb_train) params = { 'task': 'train', 'boosting_type': 'gbdt', 'objective': 'binary', 'metric': {'binary_logloss'}, 'num_leaves': 16, 'num_trees': 10, 'learning_rate': 0.1, 'feature_fraction': 0.9, 'bagging_fraction': 0.8, 'bagging_freq': 5, 'verbose': 0 } gbm = lgb.train(params=params, train_set=lgb_train, num_boost_round=3000, valid_sets=None)
使用GBDT做二分类,因此这里我们指定’num_trees’参数.而如果做k(k>2)分类,就要使用’num_class’参数,此时num_trees=num_class*k。下面GBDT输出转换特征,构建LR训练和测试数据,注意细节:
# build train matrix
num_leaf = 16
y_pred = gbm.predict(X_train,raw_score=False,pred_leaf=True)
transformed_training_matrix = np.zeros([len(y_pred),
len(y_pred[0]) * num_leaf],
dtype=np.int64)
for i in range(0,len(y_pred)):
temp = np.arange(len(y_pred[0])) * num_leaf + np.array(y_pred[i]);
transformed_training_matrix[i][temp] += 1
由于我们需要知道每棵树中输出到了那个点上,因此设置参数pred_leaf=True
,打印一下看看print(y_pred[0], y_pred.shape)
:
[0 0 0 0 0 0 0 0 0 0 0 4 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 3 0
3 0 0 0 0 0 0 0 3 4 3 3 3 0 0 0 0 2 2 3 3 2 3 3 3 1 1 3 3 2 3 1 3 0 3 3 3
0 2 3 2 3 2 2 2 2 1 1 3 3 0 1 1 3 2 3 0 3 2 0 1 3 3] (130, 100)
如果想知道强学习器的预测值y,则需要设置raw_score=True',打印一下看看
print(y_pred[0], y_pred.shape)`:
-9.0297726841214 (130,)
预测数据同理:
# build test matrix
y_pred = gbm.predict(X_test,pred_leaf=True)
transformed_testing_matrix = np.zeros([len(y_pred),
len(y_pred[0]) * num_leaf],
dtype=np.int64)
for i in range(0,len(y_pred)):
temp = np.arange(len(y_pred[0])) * num_leaf + np.array(y_pred[i])
transformed_testing_matrix[i][temp] += 1
然后输入到逻辑回归中分类:
# logistic regression
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
label_train = y_train.values.reshape(y_train.shape[0],)
label_test = y_test.values.reshape(y_test.shape[0],)
c = np.array([1,0.5,0.1,0.05,0.01,0.005,0.001])
for t in range(0,len(c)):
lm = LogisticRegression(penalty='l2',C=c[t]) # logestic model construction
lm.fit(transformed_training_matrix,y_train.values.reshape(y_train.shape[0],)) # fitting the data
y_pred_est = lm.predict(transformed_testing_matrix) # Give the probabilty on each label
acc =accuracy_score(label_test, y_pred_est)
print('Acc of test', acc)
100行代码不到,有兴趣可以调调代码:
https://github.com/wyl6/Recommender-Systems-Samples/tree/master/RecSys%20Traditional/DecisionTree/LRGBDT
[1] https://www.zhihu.com/question/62109451
[2] http://f.dataguru.cn/thread-935853-1-1.html
[3] https://www.zhihu.com/question/35821566
[4] https://github.com/NearXdu/gbdt_lr/blob/master/gbdt_lr.py
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。