传统的广告点击率预估场景中,提取特征的流程如下:
在广告点击率预估这个场景中,由于经常就是上亿量的数据,经常使用LR模型来预估(它是将函数值映射到0,1区间,映射后的函数值就是预估值)因为这个模型简单,容易实现并行,但是正因为它简单,所以它的学习能力有限,所以这个时候,特征工程就很重要了,如何选择一些比较有效的特征,以及特征组合就是一个关键了,而传统的采用人工去筛选重要特征的话,比较麻烦,耗时耗力
所以,为了解决这个问题,它引入了GDBT,利用GDBT来为它选择一些区分性的特征以及特征组合,并且它的效果也不亚于人工
那么这里有亮点
1 选择一些有区分性的特征
2 有效的特征组合
为什么会选gbdt呢?
我们知道gbdt每一轮都是学习上一轮的错误或者残差,每一轮都会构建一棵树,那么其实它在前面几轮的树其实就能够区分大部分的样本了,
后面的树能够区分少量的错误样本,所以它前面的几棵树其实对应的特征就是那种能够区分大部分样本的特征了,这就是体现区分性;至于特征组合呢,我的理解是它的每轮树的构建用的划分特征都不一样,并且每棵树的复杂度也不是很高,所以每棵树的构建相当于不同的特征组合
上面的x就是一个样本x,假如它落到了第一颗树的第一个叶子结点,它的编码就是[1,0,0],落到了第二颗树的第二个叶子结点,它的编码就是[0,1],那么它的整体编码就是[1,0,0,0,1]了,然后这个就是新的x了,输入到模型中,进行训练
但是注意,上面的对应每棵树的编码其实就是哑编码,实际上这个哑编码是需要我们自己编的,真正x落入到每棵树上的某个叶子结点,GDBT只有这个叶子结点的索引,然后我们拿到x落到每棵树的叶子结点索引后,然后是自己进行哑编码的,下面的例子中会有
选择它的理由
1.为什么采用ensemble决策树?
一棵树的表达能力很弱,不足以表达多个有区分性的特征组合,而多棵树的表达能力更强些
2.为什么采用GBDT,而不是RF?
主要是由于GBDT,对于大部分样本,前面的n棵树就能很好的把它们区分开,而只有少量的分类错误样本会被留到后面的树进行区分,所以相比较RF来说,采用GBDT更能选择出具有区分性的特征
建树方案
1.非id树
由于广告的id有很多,所以不太可能针对每个广告id建一颗树,如果某个广告id曝光量比较少的话,那么没必要为它单独建一颗树,这类广告采用base树
2.id树
对于一些曝光量比较充分的广告id,由于需要发掘出这类广告的一些有区分性的特征,所以需要为这类广告id单独建树
如下:
代码实现:
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.preprocessing import OneHotEncoder
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_curve
import numpy as np
弱分类器的数目
n_estimator = 10
随机生成分类数据。
X, y = make_classification(n_samples=80000)
print(X)
切分为测试集和训练集,比例0.5
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5)
将训练集切分为两部分,一部分用于训练GBDT模型,另一部分输入到训练好的GBDT模型生成GBDT特征,然后作为LR的特征。这样分成两部分是为了防止过拟合。
X_train, X_train_lr, y_train, y_train_lr = train_test_split(X_train, y_train, test_size=0.5)
调用GBDT分类模型。
grd = GradientBoostingClassifier(n_estimators=n_estimator)
#调用one-hot编码。
grd_enc = OneHotEncoder()
调用LR分类模型。
grd_lm = LogisticRegression()
‘’‘使用X_train训练GBDT模型,后面用此模型构造特征’’’
grd.fit(X_train, y_train)
print(grd.apply(X_train))
print(grd.apply(X_train)[:, :, 0])
fit one-hot编码器
#每个样本落到GDBT每棵树叶子结点的索引
x_train_leaf_indexs = grd.apply(X_train)[:, :, 0]
grd_enc.fit(x_train_leaf_indexs)
print(grd_enc.transform(x_train_leaf_indexs))
‘’’
使用训练好的GBDT模型构建特征,然后将特征经过one-hot编码作为新的特征输入到LR模型训练。
‘’’
x_train_transform = grd_enc.transform(grd.apply(X_train_lr)[:, :, 0])
x_test_transform = grd_enc.transform(grd.apply(X_test)[:, :, 0])
grd_lm.fit(x_train_transform, y_train_lr)
#用训练好的LR模型多X_test做预测
y_predict = grd_lm.predict(x_test_transform)
根据预测结果输出
fpr_grd_lm, tpr_grd_lm, _ = roc_curve(y_test, y_pred_grd_lm)
训练数据
每个样本落到GBDT每棵树叶子结点索引的数组
[[ 6. 6. 4. … 6. 3. 3.] ————-》对应一个样本
[ 3. 4. 3. … 3. 4. 6.]
[ 3. 3. 3. … 4. 3. 3.]
…
[11. 11. 11. … 10. 11. 10.]
[ 3. 4. 3. … 3. 4. 3.]
[11. 11. 11. … 13. 11. 11.]]
每个样本哑编码后的数组,用LR对这个数组进行训练
[[0. 0. 1. … 0. 0. 0.]
[1. 0. 0. … 0. 0. 0.]
[1. 0. 0. … 0. 0. 0.]
…
[0. 0. 0. … 0. 0. 0.]
[1. 0. 0. … 0. 0. 0.]
[0. 0. 0. … 1. 0. 0.]]
测试数据和这个流程一样,也是先转化为这个哑编码数组,然后进行预测
实现过程总结:
先将训练数据X输入到GDBT模型中进行训练,通过apply函数得到了每个样本落到每个棵树叶子结点对应的索引,对于每个样本,将它的所有索引合并为一个数组,然后对它进行哑编码,将所有样本的索引集合哑编码后,输入到LR模型中进行训练,得到LR模型
预测也是一样,首先将测试数据输入到GDBT,转化为测试数据索引集合,然后转化为哑编码集合,然后用刚才训练的LR模型对它进行预测
参考链接:
https://blog.csdn.net/lilyth_lilyth/article/details/48032119