原本应该是年后就要写的一篇博客,由于考完试后忙了一段时间课设和实验,而后回家后又在摸鱼,就一直没开动。趁着这段时间只能呆在家里来把这些博客补上。在以前的文章中介绍了 Random Forest 和 AdaBoost,这篇文章将介绍介绍在数据挖掘竞赛中,最经常使用的算法之一 —— GBDT(Gradient Boosting Decision Tree)。python
GBDT
其实是 GBM(Gradient Boosting Machine)
中的一种,采用 CART 树做为基学习器,因此称为 GBDT。与 AdaBoost 同样,GBDT 为 Boosting 家族中的一员。其表达式为
\[ f_m(x) = \sum_{m=1}^{M} T(x;\Theta_m) \]
其中\(T(x;\Theta_m)\)表示决策树;\(\Theta_m\)为决策树参数;M为树的个数。面试
这里来回顾下 AdaBoost,AdaBoost 经过不断调整样本权重,使得上一次分类错误的样本权重变大,最后训练出 m 个弱分类器,而后对 m 个弱分类器加权结合造成强分类器。算法
而 GBDT 又是另外一思想,当咱们假设前一轮迭代获得的学习器为 \(f_{m-1}(x)\) ,损失函数为 \(L(y, f_{m-1}(x))\) ,那么,本轮迭代的目标就是使损失函数 \(L(y, f_{m-1}(x) + h_m(x))\) 的值尽量的小。app
咱们先将损失函数假设为最经常使用的平方损失dom
令 \(r = y - f_{m-1}(x)\) ,那么第 m 步的目标就是最小化 \(L(y, f_m(x)) = \frac{1}{2}(y-f_m(x))^2=\frac{1}{2}(r-h_m(x))^2\)函数
到这里,彷佛发现了点什么,咱们对损失函数求导,发现:
\[ \frac{\partial{L}}{\partial{h_m(x)}}=h_m(x)-r \]
看出什么来了没?对其取个负号就变成 \(r-h_m(x)\) ,即咱们常说的残差
,因此,当为平方损失函数时,弱学习器拟合的目标为以前拟合结果的残差。那到这里代码就很好写了,可是,咱们实际中有不少其它的损失函数,并且在不少问题中,这些损失函数比平方损失要好得多。那这时候,若是咱们还采用一样的思路,那就没办法像上面那样直接展开并拟合残差了,这时候该怎么办?学习
这里别忘了,咱们最终的目标是使得 \(L(y, f_m(x))\) 最小,那么只须要保证 \(L(y, f_{m-1}(x)+h_m(x))\) 的值比 \(L(y, f_{m-1}(x))\) 小就行了。spa
即
\[ max[L(y,f_{m-1}(x))-L(y,f_{m-1}(x)+h(x))] \]
检验大一高数学的怎么样的时候到了 orzrest
咱们前面说了第 m 轮迭代的损失函数为 \(L(y, f_{m-1}(x) + h_m(x))\) ,换一种形式,写成 \(L(f_{m-1}(x) + h_m(x))\) ,对其进行一阶泰勒展开,得
\[ L(f_{m-1}(x)+h_m(x)) \approx L(f_{m-1}(x)) + L'(f_{m-1}(x))h_m(x) \]
因此,咱们只需使得知足
\[ \max L'(f_{m-1}(x))h_m(x) \\ L'(f_{m-1}(x))h_m(x)<0 \]
那咱们的 \(h_m(x)\) 到底要拟合什么呢?别忘了,咱们是要求梯度的,在这里咱们已知的是 \(L'(f_{m-1}(x))\) ,咱们确定是根据上一次的拟合的结果来拟合这一次的结果,因此,要使得结果最大,天然就是梯度方向。那么 \(h_m(x)=-L'(f_{m-1}(x))\) , 这样原先的 \(r\) 也就变成了梯度。这里若是把损失函数看做是平方损失,咱们获得的结果也刚好就是咱们所说的残差!!code
此时也总算明白了以前面腾讯的时候我说 GBDT 是拟合残差的时候面试官让我再回去从新康康这个算法的缘由了。
输入: 训练数据集 \(T = {(x_1, y_1),(x_2, y_2), ..., (x_N, y_N)}, x_i \in X \subset R^n, y_i \in Y \subset R\); 损失函数 L(y,f(x)),树的个数M.
输出: 梯度提高树\(F_M(x)\)
(1) 初始化 \(f_0(x) = argmin_c \Sigma_{i=1}^N L(y_i,c)\).
(2) 对 \(m=1,2,...,M\)
(a) 对\(i =1,2,...,N\),计算, \(r_{mi} = - [\frac{\partial L(y_i, f(x_i))}{\partial f(x_i)}]_{f(x) = F_{m-1}(x)}\).
(b) 拟合残差\(r_{mi}\)学习一个回归树,获得\(f_m(x)\).
(c) 更新\(F_m(x) = F_{m-1}(x) + f_m(x)\).
(3) 获得回归问题提高树 \(F_M(x) = \Sigma_{i=0}^M f_i(x)\)
这里代码是采用了平方损失的方法来写的,且解决的是分类问题
def sigmoid(x): """ 计算sigmoid函数值 """ return 1 / (1 + np.exp(-x)) def gbdt_classifier(X, y, M, max_depth=None): """ 用于分类的GBDT函数 参数: X: 训练样本 y: 样本标签,y = {0, +1} M: 使用M个回归树 max_depth: 基学习器CART决策树的最大深度 返回: F: 生成的模型 """ # 用0初始化y_reg y_reg = np.zeros(len(y)) f = [] for m in range(M): # 计算r r = y - sigmoid(y_reg) # 拟合r # 使用DecisionTreeRegressor,设置树深度为5,random_state=0 f_m = DecisionTreeRegressor(max_depth=5, random_state=0) # 开始训练 f_m.fit(X, r) # 更新f f.append(f_m) y_reg += f_m.predict(X) def F(X): num_X, _ = X.shape reg = np.zeros((num_X)) for t in f: reg += t.predict(X) y_pred_gbdt = sigmoid(reg) # 以0.5为阈值,获得最终分类结果0或1 one_position = y_pred_gbdt >= 0.5 y_pred_gbdt[one_position] = 1 y_pred_gbdt[~one_position] = 0 return y_pred_gbdt return F
到这里 GBDT 也就讲完了,从决策树 ID3 开始一直到 GBDT,后面终于要迎来最开始想要梳理的数据挖掘的两大杀器 XGBoost 和 LightGBM 了,下一篇将介绍 XGBoost。