简介
GBDT的原理分析和LR的公式推导已经不需要我再去再添拙笔,这里我将用一个推荐系统系列来从推荐系统发展的角度来看这些在推荐系统不算长久的发展史上重要的或者是具有代表性的经典算法。
其中包括了2000年初的系统过滤,矩阵分解等,还有面向CTR问题的LR为发展起点的FM,FFM,GBDT+LR等,当然后面还会有深度网络的大行其道。且容我不断整理,慢慢落笔。
2004
就是在这一年,FaceBook提出了GBDT+LR这个模型。
有的人认为只要我明白了这个模型的原理不就得了,能用不就完事了,其实我们应当知道为什么Facebook要提出这样一个模型?他到底要解决什么问题?
我们回顾当时在还没有GBDT+LR的推荐系统,当时最新的是FM和FFM,主流的仍然是LR模型,当然传统方法也还在工业系统中发挥总用。
但是从FM和FFM的提出已经可以发现,人们正在关注模型的特征交叉能力,并由此希望模型有更强的特征表达能力。说的简单一点,模型需要不断提高特征交叉的维度。
- 但是难道FM和FFM不够吗?
FM和FFM有他的优点,但是缺点也过于明显,FM的阶数一旦超过2阶,参数量暴增,复杂度和训练代价非常高。而对于FFM计算复杂度达到了kn^2。在生产当中并不能满足效率的要求,怎么做高阶特征工程呢,GBDT+LR横空出世。
GBDT+LR
一句话来说,就是GBDT能够自动的进行特征工程,其中包括了特征组合、交叉、筛选,然后得到新的特征表示向量。再利用LR进行分类或者回归。
- 具体做法
我会尽量讲的简单,如果对于原理还有问题的人可以看我原来写的或者知乎上的文章。
对于一个样本,n棵树就会每次都将这个样本分到一个节点,那么一个样本就可以用n个节点的位置所组成的向量来进行表示了。
举个例子:
我们设定GBDT有三颗树,对于输入X(单个样本)
- X在第一棵树的10号节点
- X在第二棵树的33号节点
- X在第三棵树的4号节点
那么我们就得到了X的新的特征向量表示[10, 33, 4],当然在输入LR之前还会进行一次onehot,但是这里不讨论。
Note:
每棵树生成的过程都是回归树的生成,所以每一棵树的节点分叉都是一次特征选择,原理可以参考决策树的原理,那么多层节点的结构就可以将特征进行组合的操作,由此就解决了我们一直希望的高维特征组合、交叉、筛选。
缺点
GBDT的这种方式其实也存在缺点。
GBDT可能会出现过拟合的情况,因为这种特征选择和筛选其实就会忽略到一些特征之间的距离特性,也就是存在信息的损失。
一个老生常谈的问题:GBDT+LR 和 FFM 谁更好?
我认为二者都是一种进步,都是经典的推荐基础算法,都是成功的算法。
其次推荐系统不是 非黑即白 的问题,因为推荐场景非常多,同时影响的因素更是难以计量,不同的场景下,曾经表现好的算法也可能出现问题,不那么好的算法可能出现惊人的效果,这都是正常的。
正所谓忠于数据,不断探求数据的特征,并智慧的选取算法和处理方法才是上上策呀。
实现
因为这是我很久以前根据简书和知乎上的某几篇文章写得,但是确实找不到链接了,没有加引用,这里说明一下。
- 创建模型
lgb_train = lgb.Dataset(X_train, y_train)
lgb_eval = lgb.Dataset(X_test, y_test)
params = {
'task': 'train',
'boosting_type': 'gbdt',
'objective': 'binary',
'metric': {'binary_logloss'},
'num_leaves': 64,
'num_trees': 100,
'learning_rate': 0.01,
'feature_fraction': 0.9,
'bagging_fraction': 0.8,
'bagging_freq': 5,
'verbose': 0
}
print('training...')
lgb_model = lgb.train(
params,
lgb_train,
num_boost_round=100,
valid_sets=lgb_train
)
其中num_tree决定了输出的特征向量V维度,然后num_leaves决定了对V进行onehot编码的维度。
- 模型存储
初学的人总是没有存储自己训练好的模型的习惯,这非常不好。
from sklearn.externals import joblib
## 存模型
joblib.dump(lgb_model, './Data/GBDT_model.pkl')
## 取模型
lgb_model = joblib.load('./Data/GBDT_model.pkl')
- 转化特征向量
y_pred_train = lgb_model.predict(X_train, pred_leaf=True)
print(y_pred_train.shape)
y_pred_test = lgb_model.predict(X_test, pred_leaf=True)
array([37, 39, 37, 36, 38, 55, 3, 48, 51, 47])
说明一共有八千个样本,100棵树,
eg. 第一个样本落在了第一课数的No.37 节点 以此类推
- One-Hot
## num_leaf = 64
num_leaf = 64
transformed_training_matrix = np.zeros([len(y_pred_train), len(y_pred_train[0]) * num_leaf],dtype=np.int64) # N * num_tress * num_leafs
transformed_training_matrix.shape
for i in range(len(y_pred_train)):
idx = np.arange(len(y_pred_train[0])) * num_leaf + np.array(y_pred_train[i])
transformed_training_matrix[idx] += 1
transformed_testing_matrix = np.zeros([len(y_pred_test), len(y_pred_test[0]) * num_leaf], dtype=np.int64)
for i in range(0, len(y_pred_test)):
temp = np.arange(len(y_pred_test[0])) * num_leaf + np.array(y_pred_test[i])
transformed_testing_matrix[i][temp] += 1
- LR回归
一般输出概率,所以使用回归。
lr = LogisticRegression(penalty='l2', C=0.05)
lr.fit(transformed_training_matrix, y_train)
y_pred_lr_test = lr.predict_proba(transformed_testing_matrix)
- 输出评价指标
我们使用Normalized Cross Entropy
num = (-1.0/len(y_test))*sum(y_test*np.log((y_pred_lr_test[:,1]) + (1-y_test)*np.log(1-(y_pred_lr_test[:,1]))
avg_prob = sum(y_pred_lr_test[:,1]) / len(y_pred_lr_test)
den = -1.0*(avg_prob*np.log(avg_prob) + (1-avg_prob)*np.log(1-avg_prob))
GBDT+LR 历史地位
- 第一次实现特征工程的模型化、自动化,在当时一个端到端的自动特征工程模型确实是值得大书特书的进步。
即使是现在2020年,很多的传统风控企业(是传统的,比如银行什么的),依然是在使用GBDT+LR的基础模型。
- 为Embedding的提出埋下了伏笔,
怎么说呢,GBDT+LR的出现让更多人看到了自动化的特征工程模型的研究和发展方向,GBDT+LR为后面的阿里LS-PLM的出现打下了基础,同时也引领我们进入了深度网络推荐时代的大门。
大家共勉~~