今天是梯度提升树GBDT的理论学习和细节补充, 之前整理过XGBOOST和Lightgbm, 在那里面提到了GBDT, 但是只是简单的一过, 并没有关注太多GBDT的细节, 所以这次借着整理推荐系统里面的GBDT+LR模型的机会, 重新过了一遍GBDT和LR的基础知识, 确实发现忽略了很多知识, 而GBDT和逻辑回归模型都是作为面试考核的大点, 所以有必要细一些了。
关于逻辑回归的细节, 在这篇文章中进行了补充, 今天的重点是GBDT, GBDT全称梯度提升决策树,在传统机器学习算法里面是对真实分布拟合的最好的几种算法之一,在前几年深度学习还没有大行其道之前,gbdt在各种竞赛是大放异彩。原因大概有几个,一是效果确实挺不错。二是即可以用于分类也可以用于回归。三是可以筛选特征。这三点实在是太吸引人了,导致在面试的时候大家也非常喜欢问这个算法, 下面就从算法的原理与公式推导, 算法如何选择特征, 如何进行回归和分类等几方面进行一个整理。
PS: 这个算法非常重要, 现在机器学习算法最常用的XGBOOST, Lightgbm, catboost这几大巨头算法都是基于这个算法的基础上进行发展起来的, 面试里面一般会问到的关于这个算法的问题, 大致有下面几个, 由于我也刚开始接触细节部分, 先整理其中的几个, 后面再慢慢加:
大纲如下:
Ok let’s go!
在介绍GBDT之前, 先简单的介绍一下BDT, 也就是Boosting Decision Tree, 这是以CART决策树为基学习器的集成学习方法, 关于集成学习, 这里就不过多赘述了, 可以参考白话机器学习算法理论+实战之AdaBoost算法。 提升树模型可以表示为决策树的加法模型:
f M ( x ) = ∑ m = 1 M T ( x ; Θ m ) f_{M}(x)=\sum_{m=1}^{M} T\left(x ; \Theta_{m}\right) fM(x)=m=1∑MT(x;Θm)
其中, T ( x ; Θ m ) T(x;\Theta_m) T(x;Θm)表示决策树; Θ m \Theta_m Θm为决策树的参数, M M M表示树的个数, 即M棵树的结果相加。
提升树采用的是前向分布算法, 首先确定初始提升树 f 0 ( x ) = 0 f_0(x)=0 f0(x)=0, 第 m m m步的模型是:
f m ( x ) = f m − 1 ( x ) + T ( x ; Θ m ) f_{m}(x)=f_{m-1}(x)+T\left(x ; \Theta_{m}\right) fm(x)=fm−1(x)+T(x;Θm)
通过经验风险极小化确定下一棵树的参数(让残差尽可能的小找到最优划分点):
Θ ^ m = arg min Θ m ∑ i = 1 N L ( y i , f m − 1 ( x i ) + T ( x i ; Θ m ) ) \hat{\Theta}_{m}=\arg \min _{\Theta_{m}} \sum_{i=1}^{N} L\left(y_{i}, f_{m-1}\left(x_{i}\right)+T\left(x_{i} ; \Theta_{m}\right)\right) Θ^m=argΘmmini=1∑NL(yi,fm−1(xi)+T(xi;Θm))
这里的 L ( ) L() L() 是损失函数,回归算法选择的损失函数一般是均方差(最小二乘)或者绝对值误差;而在分类算法中一般的损失函数选择对数函数来表示。 这是李航老师《统计学习方法》里面的原内容, 也是对提升树比较好的总结。
如果对上面的公式一脸懵逼, 那么我们拿一个图来看一下BDT的一个学习流程, 然后再回顾一下上面的这些公式, 就会有一种豁然开朗的感觉(初极狭, 才通人, 复行数十步, 豁然开朗哈哈)
boosting方法之前已经提到过, 是由多个弱学习器进行组合得到的一个强学习器, 而每个弱学习器之间是相互关联的, AdaBoost是boosting家族的一员, 它与BDT不同, AdaBoost中弱学习器之间的关联关系是前一轮学习器表现不行的样本, 而GDT中弱学习器之间的关联是残差。
给我一些训练样本, 我们先训练第一个弱学习器, BDT里面的话就是决策树了, 关于决策树的问题这里依然不多说, 可以参考白话机器学习算法理论+实战之决策树, 训练完了第一个学习器, 就可以对样本进行一个预测, 此时会得到一个与真实标签的一个残差, 那么就可以用这个残差来训练后面的学习器, 也就是第二个分类器关注于与前面学习器与真实标签的差距, 这样依次类推, 最后会得到n个弱分类器。 那么我这n个分类器进行加和, 就得到了最终的学习器。最后就是用这个东西进行预测。 关于这个拟合残差的这部分, 在白话机器学习算法理论+实战番外篇之Xgboost做了比较详细的赘述, 这里就不再重复了, 因为这篇文章内容也很多, 不要再冗余了哈哈。
根据上面的这个简单过程, 我们再来理解一下《统计学习方法》里面的公式, 提升树实际上是加法模型和前向分布算法, 表示为:
在前向分布算法的第 m m m步时, 给定当前的模型 f m − 1 ( x ) f_{m-1}(x) fm−1(x), 求解:
这样就可以得到第 m m m棵决策树 T ( x , Θ m ) T(x, \Theta_m) T(x,Θm)。只不过不同问题的提升树, 损失函数不同。如果我们解决的一个回归问题, 我们用平方损失函数的话, 第 m m m次迭代的损失函数为:
L ( y , f m − 1 ( x ) + T ( x , Θ m ) ) = ( y − f m − 1 ( x ) − T ( x , Θ m ) ) 2 = ( r − T ( x , Θ m ) ) 2 \mathrm{L}\left(\mathrm{y}, f_{m-1}(x)+T\left(x, \Theta_{m}\right)\right)=\left(y-f_{m-1}(x)-T\left(x, \Theta_{m}\right)\right)^{2} = \left(r-T\left(x, \Theta_{m}\right)\right)^{2} L(y,fm−1(x)+T(x,Θm))=(y−fm−1(x)−T(x,Θm))2=(r−T(x,Θm))2
这里的 r r r就是残差, 所以第 m m m棵决策树 T ( x , Θ m ) T(x, \Theta_m) T(x,Θm)是对该残差的拟合。 但是要注意的是提升树算法中的基学习器是CART树的回归树。 关于CART树, 可以参考决策树那篇文章。
这就是BDT算法的一般流程, 简单总结就是初始化一棵树, 计算残差, 根据残差拟合一棵树, 然后更新。下面就是完整的提升树算法:
关于回归问题的提升树算法, 李航老师书上有个比较好的例子, 由于比较细致, 这里只摘一部分, 但是在摘之前, 需要先整理一下CART回归树的生成方式, 因为之前整理决策树全是分类任务, 而这次要整理的BDT或者是下面的GBDT都是用CART回归树作为的基分类器, 所以有必要了解一下CART回归树的生成过程, 这也对应着面试过程中的一个问题, 如何选择特征? 这个细节其实就是CART回归树的生成过程, 因为CART回归树生成的过程就是一个特征选择的过程。(这里PS一下:gbdt的弱分类器默认选择的是CART TREE。其实也可以选择其他弱分类器的,选择的前提是低方差和高偏差。框架服从boosting 框架即可)
一棵回归树对应着特征空间的一个划分以及在划分单元上的输出值。 假设已将输入空间划分为M个单元 R 1 , R 2 , . . . , R M R_1, R_2, ..., R_M R1,R2,...,RM, 并且在每个单元 R m R_m Rm上有一个固定的输出值 c m c_m cm, 于是回归树模型表示为:
f ( x ) = ∑ m = 1 M c m I ( x ∈ R m ) f(x)=\sum_{m=1}^{M} c_{m} I\left(x \in R_{m}\right) f(x)=m=1∑McmI(x∈Rm)
当输入空间的划分确定时, 可以用平方误差 ∑ x i ∈ R m ( y i − f ( x i ) ) 2 \sum_{x_{i} \in R_{m}}\left(y_{i}-f\left(x_{i}\right)\right)^{2} ∑xi∈Rm(yi−f(xi))2来表示回归树训练数据的误差, 用平方误差最小的准则求解每个单元上的最优输出值。 单元 R m R_m Rm上的 c m c_m cm的最优输出值 c ^ m \hat c_m c^m是 R m R_m Rm上的所有输入实例 x i x_i xi对应的 y i y_i yi的均值, 即
c ^ m = ave ( y i ∣ x i ∈ R m ) \hat{c}_{m}=\operatorname{ave}\left(y_{i} \mid x_{i} \in R_{m}\right) c^m=ave(yi∣xi∈Rm)
现在问题是如何对输入空间进行划分? 这里才用的启发方法, 选择第 j j j个变量(特征) x ( j ) x^{(j)} x(j)和它的取值 s s s, 作为切分变量和切分点, 并定义两个区域:
R 1 ( j , s ) = { x ∣ x ( j ) ⩽ s } 和 R 2 ( j , s ) = { x ∣ x ( j ) > s } R_{1}(j, s)=\left\{x \mid x^{(j)} \leqslant s\right\} \quad \text { 和 } \quad R_{2}(j, s)=\left\{x \mid x^{(j)}>s\right\} R1(j,s)={ x∣x(j)⩽s} 和 R2(j,s)={ x∣x(j)>s}
然后寻找最优切分点 j j j和最优切分点 s s s。 这个是关键, 如何寻找?
具体的求解
min j , s [ min c 1 ∑ x i ∈ R 1 ( j , s ) ( y i − c 1 ) 2 + min c 2 ∑ x i ∈ R 2 ( j , s ) ( y i − c 2 ) 2 ] \min _{j, s}\left[\min _{c_{1}} \sum_{x_{i} \in R_{1}(j, s)}\left(y_{i}-c_{1}\right)^{2}+\min _{c_{2}} \sum_{x_{i} \in R_{2}(j, s)}\left(y_{i}-c_{2}\right)^{2}\right] j,smin⎣⎡c1minxi∈R1(j,s)∑(yi−c1)2+c2minxi∈R2(j,s)∑(yi−c2)2⎦⎤
对固定输入变量 j j j可以找到最优切分点 s s s。
c ^ 1 = ave ( y i ∣ x i ∈ R 1 ( j , s ) ) 和 c ^ 2 = ave ( y i ∣ x i ∈ R 2 ( j , s ) ) \hat{c}_{1}=\operatorname{ave}\left(y_{i} \mid x_{i} \in R_{1}(j, s)\right) \quad \text { 和 } \quad \hat{c}_{2}=\operatorname{ave}\left(y_{i} \mid x_{i} \in R_{2}(j, s)\right) c^1=ave(yi∣xi∈R1(j,s)) 和 c^2=ave(yi∣xi∈R2(j,s))
遍历所有输入变量, 找到最优切分变量 j j j, 构成一个对 ( j , s ) (j,s) (j,s)。 依此将输入空间划分为两个区域。 重复上面的过程, 直到满足条件, 这样就生成了一棵回归树。
如果感觉上面的内容比较头大, 那么可以看下面的这个例子, 这个例子既说明了一下回归树是如何生成的, 又解释了回归问题提升树的原理, 这是李航老师书上的一个例子:
假设这里有10个训练样本的某个特征取值范围区间[0.5, 10.5], y y y的取值范围[5.0, 10.0], 我们学习一个提升树模型。
首先通过下面的优化问题:
min j , s [ min c 1 ∑ x i ∈ R 1 ( j , s ) ( y i − c 1 ) 2 + min c 2 ∑ x i ∈ R 2 ( j , s ) ( y i − c 2 ) 2 ] \min _{j, s}\left[\min _{c_{1}} \sum_{x_{i} \in R_{1}(j, s)}\left(y_{i}-c_{1}\right)^{2}+\min _{c_{2}} \sum_{x_{i} \in R_{2}(j, s)}\left(y_{i}-c_{2}\right)^{2}\right] j,smin⎣⎡c1minxi∈R1(j,s)∑(yi−c1)2+c2minxi∈R2(j,s)∑(yi−c2)2⎦⎤
求训练数据的切分点 s s s. 根据所给数据, 我们考虑如下切分点:
1.5 , 2.5 , 3.5 , 4.5 , 5.5 , 6.5 , 7.5 , 8.5 , 9.5 1.5, 2.5, 3.5, 4.5,5.5, 6.5, 7.5, 8.5, 9.5 1.5,2.5,3.5,4.5,5.5,6.5,7.5,8.5,9.5
对于每个切分点, 很容易求出 R 1 , R 2 , c 1 , c 2 R_1, R_2, c_1, c_2 R1,R2,c1,c2及
m ( s ) = min c 1 ∑ x i ∈ R 1 ( y i − c 1 ) 2 + min c 2 ∑ x i ∈ R 2 ( y i − c 2 ) 2 m(s)=\min _{c_{1}} \sum_{x_{i} \in R_{1}}\left(y_{i}-c_{1}\right)^{2}+\min _{c_{2}} \sum_{x_{i} \in R_{2}}\left(y_{i}-c_{2}\right)^{2} m(s)=c1minxi∈R1∑(yi−c1)2+c2minxi∈R2∑(yi−c2)2
比如, 当 s = 1.5 s=1.5 s=1.5, R 1 = 1 , R 2 = 2 , 3 , . . . 10 R_1={1}, R_2={2, 3,...10} R1=1,R2=2,3,...10, 对应 y y y的平均 c 1 = 5.56 , c 2 = 7.5 c_1=5.56, c_2=7.5 c1=5.56,c2=7.5, 这时候根据上面这个公式得到
m ( s ) = min c 1 ∑ x i ∈ R 1 ( y i − c 1 ) 2 + min c 2 ∑ x i ∈ R 2 ( y i − c 2 ) 2 = 0 + 15.72 = 15.72 m(s)=\min _{c_{1}} \sum_{x_{i} \in R_{1}}\left(y_{i}-c_{1}\right)^{2}+\min _{c_{2}} \sum_{x_{i} \in R_{2}}\left(y_{i}-c_{2}\right)^{2}=0+15.72=15.72 m(s)=c1minxi∈R1∑(yi−c1)2+c2minxi∈R2∑(yi−c2)2=0+15.72=15.72
然后, 在当 s = 2.5 s=2.5 s=2.5的时候, 再求一个m, 得到如下表:
可以发现 s = 6.5 s=6.5 s=6.5的时候 m ( s ) m(s) m(s)最小, 此时 R 1 = { 1 , 2 , . . . 6 } R_1=\{1, 2, ...6\} R1={ 1,2,...6}, R 2 = { 7 , 8 , 9 , 10 } R_2=\{7, 8, 9, 10\} R2={ 7,8,9,10}, c 1 = 6.24 , c 2 = 8.91 c_1=6.24, c_2=8.91 c1=6.24,c2=8.91, 所以这样就得到了第一棵回归树 T 1 ( x ) T_1(x) T1(x)为
T 1 ( x ) = { 6.24 , x < 6.5 8.91 , x ⩾ 6.5 T_{1}(x)=\left\{\begin{array}{ll} 6.24, & x<6.5 \\ 8.91, & x \geqslant 6.5 \end{array}\right. T1(x)={ 6.24,8.91,x<6.5x⩾6.5
即 f 1 ( x ) = T 1 ( x ) f_1(x)=T_1(x) f1(x)=T1(x)。 这就是回归树的建树过程了, 然后谈到BDT, 就会发现根据第一棵树, 每个训练数据会有一个残差, 也就是经过第一轮的预测, 与样本的真实值还是有些差距的, 即
其中, r 2 i = y i − f 1 ( x i ) , i = 1 , 2 , ⋯ , 10 r_{2 i}=y_{i}-f_{1}\left(x_{i}\right), i=1,2, \cdots, 10 r2i=yi−f1(xi),i=1,2,⋯,10
用 f 1 ( x ) f_1(x) f1(x)拟合训练数据的平方损失误差:
L ( y , f 1 ( x ) ) = ∑ i = 1 10 ( y i − f 1 ( x i ) ) 2 = 1.93 L\left(y, f_{1}(x)\right)=\sum_{i=1}^{10}\left(y_{i}-f_{1}\left(x_{i}\right)\right)^{2}=1.93 L(y,f1(x))=i=1∑10(yi−f1(xi))2=1.93
第二步, 就是求 T 2 ( x ) T_2(x) T2(x), 方法和上面一样, 只是这次我们的训练集上图的残差的这个,把这个当做训练数据, 同理就会得到
T 2 ( x ) = { − 0.52 , x < 3.5 0.22 , x ⩾ 3.5 T_{2}(x)=\left\{\begin{array}{ll} -0.52, & x<3.5 \\ 0.22, & x \geqslant 3.5 \end{array}\right. T2(x)={ −0.52,0.22,x<3.5x⩾3.5
这样, 我们就得到了 f 2 ( x ) f_2(x) f2(x)
f 2 ( x ) = f 1 ( x ) + T 2 ( x ) = { 5.72 , x < 3.5 6.46 , 3.5 ⩽ x < 6.5 9.13 , x ⩾ 6.5 f_{2}(x)=f_{1}(x)+T_{2}(x)=\left\{\begin{array}{ll} 5.72, & x<3.5 \\ 6.46, & 3.5 \leqslant x<6.5 \\ 9.13, & x \geqslant 6.5 \end{array}\right. f2(x)=f1(x)+T2(x)=⎩⎨⎧5.72,6.46,9.13,x<3.53.5⩽x<6.5x⩾6.5
用 f 2 ( x ) f_2(x) f2(x)拟合训练数据的平方损失误差:
L ( y , f 2 ( x ) ) = ∑ i = 1 10 ( y i − f 2 ( x i ) ) 2 = 0.79 L\left(y, f_{2}(x)\right)=\sum_{i=1}^{10}\left(y_{i}-f_{2}\left(x_{i}\right)\right)^{2}=0.79 L(y,f2(x))=i=1∑10(yi−f2(xi))2=0.79
就会发现误差小了一些了。 然后我们可以再次求残差, 再进行下面树的建立, 再求误差。 但误差小到了我们允许的范围内, 停。
这时候, f ( x ) = f n ( x ) f(x)=f_n(x) f(x)=fn(x)就是我们求得最终的提升树了。
好了, 有了上面的基础, 相信下面的GBDT就比较容易了。 当然上面的如何选择特征, 其实就是考察CART Tree建立的过程, 也是上面这个了。
提升树利用加法模型和前向分布算法实现学习的优化过程, 但损失函数是平方损失或者指数损失时, 优化比较简单, 但是对于一般的损失函数而言, 往往每一步优化不容易, 针对这个问题, 所以Friedman大神提出了利用最速下降的近似方法, 即利用损失函数的负梯度来拟合基学习器。就是它了
− [ ∂ L ( y i , F ( x i ) ) ∂ F ( x i ) ] F ( x ) = F t − 1 ( x ) -\left[\frac{\partial L\left(y_{i}, F\left(\mathbf{x}_{\mathbf{i}}\right)\right)}{\partial F\left(\mathbf{x}_{\mathbf{i}}\right)}\right]_{F(\mathbf{x})=F_{t-1}(\mathbf{x})} −[∂F(xi)∂L(yi,F(xi))]F(x)=Ft−1(x)
用这个东西直接作为残差的近似值,拟合回归树。
怎么来理解这个近似呢? 如果是平方损失函数的话, 就一目了然了:
L ( y i , F ( x i ) ) = 1 2 ( y i − F ( x i ) ) 2 L\left(y_{i}, F\left(\mathbf{x}_{\mathbf{i}}\right)\right)=\frac{1}{2}\left(y_{i}-F\left(\mathbf{x}_{\mathbf{i}}\right)\right)^{2} L(yi,F(xi))=21(yi−F(xi))2
这时候对 F ( X i ) F(X_i) F(Xi)求导, 得:
∂ L ( y i , F ( x i ) ) ∂ F ( x i ) = F ( x i ) − y i \frac{\partial L\left(y_{i}, F\left(\mathbf{x}_{\mathrm{i}}\right)\right)}{\partial F\left(\mathbf{x}_{\mathrm{i}}\right)}=F\left(\mathbf{x}_{\mathrm{i}}\right)-y_{i} ∂F(xi)∂L(yi,F(xi))=F(xi)−yi
就会发现, 这个残差正是梯度的相反数, 即:
r t i = y i − F t − 1 ( x ) = − [ ∂ L ( y i , F ( x i ) ) ∂ F ( x i ) ] F ( x ) = F t − 1 ( x ) r_{t i}=y_{i}-F_{t-1}(\mathbf{x})=-\left[\frac{\partial L\left(y_{i}, F\left(\mathbf{x}_{\mathbf{i}}\right)\right)}{\partial F\left(\mathbf{x}_{\mathbf{i}}\right)}\right]_{F(\mathbf{x})=F_{t-1}(\mathbf{x})} rti=yi−Ft−1(x)=−[∂F(xi)∂L(yi,F(xi))]F(x)=Ft−1(x)
所以在GBDT中使用负梯度作为残差进行拟合, 当然这是平方损失函数, 其他损失的话, 也会得到近似的结论, 只不过不是完全相等, 这里放张图体会一下:
这其实就是GBDT的核心了, 即利用损失函数的负梯度在当前模型的值作为回归问题提升树算法中的残差的近似值去拟合一个回归树。gbdt 每轮迭代的时候,都去拟合损失函数在当前模型下的负梯度。这样每轮训练的时候都能够让损失函数尽可能快的减小,尽快的收敛达到局部最优解或者全局最优解。
下面就是GBDT的梯度提升流程(这里是宏观的角度, 即分类器是个大类), 和BDT差不多,只不过残差这里直接用负梯度进行了替代。
输入: 训练集 { ( x 1 , y 1 ) , ( x 2 , y 2 ) , . . . ( x N , y N ) } , y i 属 于 { + 1 , − 1 } \{(x_1, y_1), (x_2, y_2), ...(x_N, y_N)\}, y_i属于\{+1, -1\} { (x1,y1),(x2,y2),...(xN,yN)},yi属于{ +1,−1} 。
- 初始化: F 0 ( x ) = arg min h 0 ∑ i = 1 N L ( y i , h 0 ( x ) ) F_{0}(\mathbf{x})=\arg \min _{h_{0}} \sum_{i=1}^{N} L\left(y_{i}, h_{0}(\mathbf{x})\right) F0(x)=argminh0∑i=1NL(yi,h0(x))
- for t=1 to T do
- 计算负梯度: y ~ i = − [ ∂ L ( y i , F ( x i ) ) ∂ F ( x i ) ] F ( x ) = F t − 1 ( x ) , i = 1 , 2 , ⋯ , N \tilde{y}_{i}=-\left[\frac{\partial L\left(y_{i}, F\left(\mathbf{x}_{i}\right)\right)}{\partial F\left(\mathbf{x}_{i}\right)}\right]_{F(\mathbf{x})=F_{t-1}(\mathbf{x})}, i=1,2, \cdots, N y~i=−[∂F(xi)∂L(yi,F(xi))]F(x)=Ft−1(x),i=1,2,⋯,N
- 拟合残差得到基学习器:
w t = arg min w t ∑ i = 1 N ( y ~ i − h t ( x ; w t ) ) 2 w_{t}=\arg \min _{w_{t}} \sum_{i=1}^{N}\left(\tilde{y}_{i}-h_{t}\left(\mathbf{x} ; \mathbf{w}_{\mathbf{t}}\right)\right)^{2} wt=argwtmini=1∑N(y~i−ht(x;wt))2- 得到基学习器的权重:
α t = arg min α t ∑ i = 1 N L ( y i , f t − 1 ( x i ) + α t h t ( x ; w t ) ) \alpha_{t}=\arg \min _{\alpha_{t}} \sum_{i=1}^{N} L\left(y_{i}, f_{t-1}\left(\mathbf{x}_{\mathbf{i}}\right)+\alpha_{t} h_{t}\left(\mathbf{x} ; \mathbf{w}_{t}\right)\right) αt=argαtmini=1∑NL(yi,ft−1(xi)+αtht(x;wt))- 更新 F t ( x ) = F t − 1 ( x i ) + α t h t ( x ; w t ) F_{t}(\mathbf{x})=F_{t-1}\left(\mathbf{x}_{\mathbf{i}}\right)+\alpha_{t} h_{t}\left(\mathbf{x} ; \mathbf{w}_{\mathbf{t}}\right) Ft(x)=Ft−1(xi)+αtht(x;wt)
如果公式不好看, 那就看图, 这个图应该也是了然:
可以发现GBDT和提升树的区别是残差使用了梯度来替代, 且每个基学习器有了对应的参数权重。gbdt通过多轮迭代,每轮迭代产生一个弱分类器,每个分类器在上一轮分类器的残差基础上进行训练。对弱分类器的要求一般是足够简单,并且是低方差和高偏差的(因为训练的过程是通过降低偏差来不断提高最终分类器的精度)。 弱分类器一般会选择为CART TREE(也就是分类回归树)。由于上述高偏差和简单的要求每个分类回归树的深度不会很深。最终的总分类器是将每轮训练得到的弱分类器加权求和得到的(也就是加法模型)。
这就是GBDT的训练流程了, 当然要明确一点, gbdt无论用于分类还是回归一直都是使用CART回归树, 这个原因后面会说, 既然是用的回归树, 我们就有:
f ( X ) = ∑ k = 1 K c k I ( X ∈ R k ) f(\mathbf{X})=\sum_{k=1}^{K} c_{k} I\left(\mathbf{X} \in R_{k}\right) f(X)=k=1∑KckI(X∈Rk)
这时候, 流程可以化成下面的形式了,
算法第 1 步初始化,估计使损失函数极小化的常数值, 它是只有一个根结点的树。第 2(a) 步计算损失函数的负梯度在当前模型的值,将它作为残差的估计。对于平方损失函数,它就是通常所说的残差,对于一般损失函数,它就是残差的近似值。第 2(b) 步估计回归树叶结点区域,以拟合残差的近似值。第 2(c )步利用线性搜索估计叶结点区域的值,使损失函数极小化。第 2(d) 步更新回归树。第 3 步得到输出的最终 模型 f ^ ( x ) \hat f(x) f^(x)。
这就是GBDT算法的流程了。上面的第一个问题。下面进行下一个问题, GBDT如何构建特征?
其实说gbdt能够构建特征并非很准确,gbdt 本身是不能产生特征的,但是我们可以利用gbdt去产生特征的组合。在CTR预估中,工业界一般会采用逻辑回归去进行处理,在逻辑回归那篇文章中已经说过,逻辑回归本身是适合处理线性可分的数据,如果我们想让逻辑回归处理非线性的数据,其中一种方式便是组合不同特征,增强逻辑回归对非线性分布的拟合能力。
长久以来,我们都是通过人工的先验知识或者实验来获得有效的组合特征,但是很多时候,使用人工经验知识来组合特征过于耗费人力,造成了机器学习当中一个很奇特的现象:有多少人工就有多少智能。关键是这样通过人工去组合特征并不一定能够提升模型的效果。所以我们的从业者或者学界一直都有一个趋势便是通过算法自动,高效的寻找到有效的特征组合。Facebook 在2014年 发表的一篇论文便是这种尝试下的产物,利用gbdt去产生有效的特征组合,以便用于逻辑回归的训练,提升模型最终的效果。
这本应该是推荐系统那边的模型, 后面会在那边再详细整理一下这个, 这里简单整理一下这个过程:看论文里面的这个图:
我们 使用 GBDT 生成了两棵树,两颗树一共有五个叶子节点。我们将样本 X 输入到两颗树当中去,样本X 落在了第一棵树的第二个叶子节点,第二颗树的第一个叶子节点,于是我们便可以依次构建一个五维的特征向量,每一个纬度代表了一个叶子节点,样本落在这个叶子节点上面的话那么值为1,没有落在该叶子节点的话,那么值为 0.
于是对于该样本,我们可以得到一个向量[0,1,0,1,0] 作为该样本的组合特征,和原来的特征一起输入到逻辑回归当中进行训练。实验证明这样会得到比较显著的效果提升。
关于这个模型的详细整理和代码实践部分, 可以参考推荐系统专栏里面的LR+GBDT模型, 这里不多说, 但是这里会有一个问题, 就是LR+GBDT模型一般是用于点击率预测里面, 而点击率预测是个典型的分类问题(点击或者不点击), 而我们上面一直说GBDT用的是CART回归树, 那么我们上面那部分进行样本类别划分的时候, 是怎么划分的呢? 回归树不是输出连续值吗? 我们怎么训练上面的GBDT模型?
所以下面才是重点, GBDT如何用于分类?
首先明确一点,gbdt 无论用于分类还是回归一直都是使用的CART 回归树。不会因为我们所选择的任务是分类任务就选用分类树,这里面的核心是因为gbdt 每轮的训练是在上一轮的训练的残差基础之上进行训练的。这里的残差就是当前模型的负梯度值 。这个要求每轮迭代的时候,弱分类器的输出的结果相减是有意义的。残差相减是有意义的。
如果选用的弱分类器是分类树,类别相减是没有意义的。上一轮输出的是样本 x 属于 A类,本一轮训练输出的是样本 x属于 B类。 A 和 B 很多时候甚至都没有比较的意义,A 类- B类是没有意义的。 那么我们如何进行分类呢?
假设样本X总共有K类, 来了一个样本, 我们使用gbdt来判断样本x属于哪一类? 流程如下:
[0, 1, 0]
来表示。 0表示样本不属于该类, 1表示属于该类, 因为样本属于第二类嘛。(x, 0)
, 第二棵是针对样本x的第二类, 输入(x, 1)
, 第三棵针对样本x的第三类, 输入(x, 0)
, 这里的具体训练过程就是我们上面CART 回归树生成过程了, 按照上面的生成过程, 我们就可以解出这三棵树以及三棵树上x类别的预测值 f 1 ( x ) , f 2 ( x ) , f 3 ( x ) f_{1}(x), f_{2}(x), f_{3}(x) f1(x),f2(x),f3(x)。p 1 ( x ) = exp ( f 1 ( x ) ) ∑ l = 1 3 exp ( f l ( x ) ) p_{1}(\mathbf{x})=\frac{\exp \left(f_{1}(\mathbf{x})\right)}{ \sum_{l=1}^{3} \exp \left(f_{l}(\mathbf{x})\right)} p1(x)=∑l=13exp(fl(x))exp(f1(x))
如果感觉上面的理论比较难理解, 那么依然是来一个例子, 鸢尾花分类的数据集作为例子, 看一下gbdt多分类的过程。
数据集如下:
这是一个有6个样本的三分类问题。我们需要根据这个花的花萼长度,花萼宽度,花瓣长度,花瓣宽度来判断这个花属于山鸢尾,杂色鸢尾,还是维吉尼亚鸢尾。具体应用到gbdt多分类算法上面。我们用一个三维向量来标志样本的label。[1,0,0]
表示样本属于山鸢尾,[0,1,0]
表示样本属于杂色鸢尾,[0,0,1]
表示属于维吉尼亚鸢尾。
以样本1为例, 第一轮要训练3棵CART树, 对于第一棵CART回归树, 训练样本是[5.1, 3.5, 1.4, 0.2], label是1,第二棵 训练样本是[5.1, 3.5, 1.4, 0.2], label是0, 第三棵训练样本是[5.1, 3.5, 1.4, 0.2], label是0。
下面我们看CART 1是如何生成的, CART 1生成过程是从这四个特征中找一个特征作为CART 1的节点, 比如花萼长度作为节点, 6个样本当中花萼长度 大于5.1 cm的就是 A类,小于等于 5.1 cm 的是B类。生成的过程其实非常简单,但是有两个问题 :
即使我们已经确定了花萼长度做为节点。花萼长度本身也有很多值。在这里我们的方式是遍历所有的可能性,找到一个最好的特征和它对应的最优特征值可以让当前式子的值最小。
min j , s [ min q ∑ x i ∈ R 1 ( j , s ) ( y i − c 1 ) 2 + min a i ∑ x i ∈ R 2 ( j , s ) ( y i − c 2 ) 2 ] \min _{j, s}\left[\min _{q} \sum_{x_{i} \in R_{1}(j, s)}\left(y_{i}-c_{1}\right)^{2}+\min _{a_{i}} \sum_{x_{i} \in R_{2}(j, s)}\left(y_{i}-c_{2}\right)^{2}\right] j,smin⎣⎡qminxi∈R1(j,s)∑(yi−c1)2+aiminxi∈R2(j,s)∑(yi−c2)2⎦⎤
还记得这个吗? 我们以第一个特征的第一个特征值为例。 R 1 R_1 R1 为所有样本中花萼长度小于 5.1 cm 的样本集合, R 2 R_2 R2 为所有样本当中花萼长度大于等于 5.1cm 的样本集合。所以 R 1 = { 2 } , R 2 = { 1 , 3 , 4 , 5 , 6 } R_1=\{2\}, R_2=\{1,3,4,5,6\} R1={ 2},R2={ 1,3,4,5,6}
解释一下上面这个图就是 y 1 y_1 y1是 R 1 R_1 R1label的均值, 由于 R 1 R_1 R1只有一个元素且label为1, 则均值为1, 即 y 1 = 1 y_1=1 y1=1, y 2 y_2 y2是 R 2 R_2 R2的label均值, 由于 R 2 R_2 R2里面只有1的label是1, 3, 4, 5, 6label是0, 故均值是0.2, 下面就是用上面的公式计算的损失函数了。 就会发现山鸢尾类型在特征1的第一个特征值上的损失为0.8。
同样的方式, 我们可以计算特征1上第二个特征值上的损失, R 1 R_1 R1为所有样本花萼长度小于4.9cm的样本集合, R 2 R_2 R2为所有样本中花萼长度大于等于4.9cm的样本集合。 所以 R 1 = { } , R 2 = { 1 , 2 , 3 , 4 , 5 , 6 } R_1=\{\}, R_2=\{1, 2, 3, 4, 5, 6\} R1={ },R2={ 1,2,3,4,5,6}, 再计算得到特征1的第二个特征上的损失:
这样我们可以遍历所有特征的所有特征值,找到让这个式子最小的特征以及其对应的特征值。 在这里我们算出来让这个式子最小的特征花萼长度,特征值为5.1 cm。这个时候损失函数最小为 0.8。
于是我们的预测函数为:
f ( x ) = ∑ x ϵ R 1 y 1 ∗ I ( x ϵ R 1 ) + ∑ x ϵ R 2 y 2 ∗ I ( x ϵ R 2 ) f(x)=∑_{xϵR1}y1∗I(xϵR1)+∑_{xϵR2}y2∗I(xϵR2) f(x)=xϵR1∑y1∗I(xϵR1)+xϵR2∑y2∗I(xϵR2)
此处 R 1 = { 2 } , R 2 = { 1 , 3 , 4 , 5 , 6 } R_1=\{2\}, R_2=\{1, 3, 4, 5, 6\} R1={ 2},R2={ 1,3,4,5,6}, y 1 = 1 , y 2 = 0.2 y_1=1, y_2=0.2 y1=1,y2=0.2, 训练完以后的最终式子为:
f 1 ( x ) = ∑ x ϵ R 1 1 ∗ I ( x ϵ R 1 ) + ∑ x ϵ R 2 0.2 ∗ I ( x ϵ R 2 ) f1(x)=∑_{xϵR1}1∗I(xϵR1)+∑_{xϵR2}0.2∗I(xϵR2) f1(x)=xϵR1∑1∗I(xϵR1)+xϵR2∑0.2∗I(xϵR2)
因此, 我们得到对样本属于类别1的预测值
f 1 ( x ) = 1 + 0.2 ∗ 5 = 2 f 1 ( x ) = 1 + 0.2 ∗ 5 = 2 f1(x)=1+0.2∗5=2f1(x)=1+0.2∗5=2 f1(x)=1+0.2∗5=2f1(x)=1+0.2∗5=2
同理, 我们也可以构建CART 2和CART 3计算得到样本属于类别2和类别3的预测值 f 2 ( x ) , f 3 ( x ) f_2(x), f_3(x) f2(x),f3(x), 那么我们就可以根据softmax得到样本属于类别1的概率:
p 1 ( x ) = exp ( f 1 ( x ) ) ∑ l = 1 3 exp ( f l ( x ) ) p_{1}(\mathbf{x})=\frac{\exp \left(f_{1}(\mathbf{x})\right)}{ \sum_{l=1}^{3} \exp \left(f_{l}(\mathbf{x})\right)} p1(x)=∑l=13exp(fl(x))exp(f1(x))
类别2和类别3的也能算出来, 然后就可以得到残差, 每个样本都是如此, 就可以进行第二轮的训练了。
这就解决了上面的问题4, 最后再整理一个。
GBDT的优势 首先得益于 Decision Tree 本身的一些良好特性,具体可以列举如下:
Decision Tree 可以很好的处理 missing feature,这是他的天然特性,因为决策树的每个节点只依赖一个 feature,如果某个 feature 不存在,这颗树依然可以拿来做决策,只是少一些路径。像逻辑回归,SVM 就没这个好处。
Decision Tree 可以很好的处理各种类型的 feature,也是天然特性,很好理解,同样逻辑回归和 SVM 没这样的天然特性。
对特征空间的 outlier 有鲁棒性,因为每个节点都是 x < 的形式,至于大多少,小多少没有区别,outlier 不会有什么大的影响,同样逻辑回归和 SVM 没有这样的天然特性。
如果有不相关的 feature,没什么干扰,如果数据中有不相关的 feature,顶多这个 feature 不出现在树的节点里。逻辑回归和 SVM 没有这样的天然特性(但是有相应的补救措施,比如逻辑回归里的 L1 正则化)。
数据规模影响不大,因为我们对弱分类器的要求不高,作为弱分类器的决策树的深 度一般设的比较小,即使是大数据量,也可以方便处理。像 SVM 这种数据规模大的时候训练会比较麻烦。
当然 Decision Tree 也不是毫无缺陷,通常在给定的不带噪音的问题上,他能达到的最佳分类效果还是不如 SVM,逻辑回归之类的。但是,我们实际面对的问题中,往往有很大的噪音,使得 Decision Tree 这个弱势就不那么明显了。而且,GBDT 通过不断的叠加组合多个小的 Decision Tree,他在不带噪音的问题上也能达到很好的分类效果。换句话说,通过GBDT训练组合多个小的 Decision Tree 往往要比一次性训练一个很大的 Decision Tree 的效果好很多。因此不能把 GBDT 理解为一颗大的决策树,几颗小树经过叠加后就不再是颗大树了,它比一颗大树更强。
关于GBDT的理论和细节, 先到这里, 更详细的可以参考下面的第三个链接,这里就先不都整理过来了, 有了上面的GBDT的这些知识, 对于了解推荐系统里面的GBDT+LR模型, 或者GBDT+FM模型就比较轻松了, 对于了解XGBOOST和Lightgbm应该也会比较轻松了。
铺垫已经完成, 下一个就要一睹GBDT+LR模型的真容了
参考: