传统推荐算法(六)Facebook的GBDT+LR模型(2)理论浅析+实战

公众号

更多精彩内容请移步公众号:推荐算法工程师
公众号后台回复”进群“,加入学习交流群,和小伙伴们一起学习,一起进步~

前言

介绍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

GBDT+LR模型

首先要说明的是,对点击通过问题,要么点击要么不点击,因此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局限性

GBDT+LR这个模型还是有一些局限性的。首先看GBDT的缺点,上篇文章中我们提到过:GBDT有一些缺点:

  • GBDT在高维稀疏的数据集上,表现不如支持向量机或者神经网络[2]。
  • 训练过程基学习器需要串行训练,只能通过局部并行提高速度。

[3]中凯菜大佬指出,对于高维稀疏的特征,GBDT容易过拟合,表现不理想,甚至LR都比GBDT好,​并给了一个例子:
对于高维稀疏的特征,GBDT容易过拟合,表现不理想,甚至LR都比GBDT好,[3]中凯菜大佬给了一个很好的例子:
在这里插入图片描述在这里插入图片描述

GBDT+LR缺点

关于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

  • 0
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值