要想理解GBDT,我们先来看一下基于残差学习的梯度提升树(即,基于残差学习的加法模型+前向分步算法)。为什么要使用残差呢?因为树模型最重要的就是寻找最佳划分点,分类树用纯度来判断最佳划分点使用信息增益(ID3算法),信息增益比(C4.5算法),基尼系数(CART分类树)。但是在回归树中的样本标签是连续数值,可划分点包含了所有特征的所有可取的值。所以再使用熵之类的指标不再合适,取而代之的是平方误差,它能很好的评判拟合程度。
AdaBoost算法中,我们使用分类错误率来修正样本分布以及计算每个模型的权重,但是在GBDT中没有分类错误率。我们可以模仿分类错误率用每个基学习器的残差来表示基学习器没有学习到的那部分。因此,基于残差的加法模型+前向分步算法可以表示如下:
输入训练样本 D = { ( x 1 , y 1 ) , x 2 , y 2 ) , … , ( x N , y N ) } D=\{(x_1,y_1),x_2,y_2),\dots,(x_N,y_N)\} D={(x1,y1),x2,y2),…,(xN,yN)},其中 x i ∈ x ⊆ R d x_i \in \bm{x}\subseteq \bm{R}^d xi∈x⊆Rd, y i ∈ y = { − 1 , 1 } y_i \in \bm{y}=\{-1,1\} yi∈y={−1,1},基本分类器为 h m ( x ) h_m(x) hm(x),最终分类器为 H ( x ) H(\bm{x}) H(x):
(1)初始化 h 0 ( x ) = 0 h_0(x)=0 h0(x)=0
(2)对于基分类器 h m ( x ) , m ∈ { 1 , 2 , … , M } h_m(x),m\in \{1,2,\dots,M\} hm(x),m∈{1,2,…,M}
\qquad 1)计算每个样本的残差 r m , i = y i − h m − 1 ( x i ) , i = 1 , 2 , … , N \bm{r}_{m,i}=y_i-h_{m-1}(x_i),\quad i=1,2,\dots,N rm,i=yi−hm−1(xi),i=1,2,…,N;
\qquad 2)拟合残差 r m , i \bm{r}_{m,i} rm,i学习一颗回归树,得到 T ( x ; θ m ) T(x;\theta_m) T(x;θm);
\qquad 3)更新当前分类器为 h m ( x ) = h m − 1 ( x ) + T ( x ; θ m ) h_m(x)=h_{m-1}(x)+T(x;\theta_m) hm(x)=hm−1(x)+T(x;θm);
(3)构建基本分类器的线性组合,得到最终分类器: H ( x ) = ∑ m = 1 M T ( x , θ m ) H(\bm{x})=\sum_{m=1}^M T(x,\theta_m) H(x)=∑m=1MT(x,θm)
上边是利用残差作为损失,用加法模型和前向分步算法可以很容易的优化每一步,可是当我们使用一般的损失函数呢?往往每一步的优化并不容易。针对这一问题,Freidman提出了梯度提升算法(gradient boosting)。这是利用最速下降法的近似方法,利用损失函数的负梯度在当前模型拟合的值 − [ ∂ L ( y , h ( x i ) ) ∂ h ( x i ) ] h ( x ) = h m − 1 ( x ) -\left[\frac{\partial L\left(y, h\left(x_{i}\right)\right)}{\partial h\left(x_{i}\right)}\right]_{h(x)=h_{m-1}(x)} −[∂h(xi)∂L(y,h(xi))]h(x)=hm−1(x)作为回归问题提升树的残差近似值,以此来拟合回归树。为什么可以这样做呢?
损失函数 L ( y , h ( x ) ) L(y,h(x)) L(y,h(x))中将基学习器 h ( x ) h(x) h(x)看作一个参数,为了使损失 L L L最小,如果采用梯度下降法,那么: h m ( x ) = h m − 1 ( x ) − η d L d h m − 1 ( x ) h_m(x)=h_{m-1}(x)-\eta\frac{dL}{dh_{m-1}(x)} hm(x)=hm−1(x)−ηdhm−1(x)dL,与一般的梯度下降法相同;而对于上边提到的用残差和前向分步来拟合则: h m ( x ) = h m − 1 ( x ) + T ( x ; θ m ) h_m(x)=h_{m-1}(x)+T(x;\theta_m) hm(x)=hm−1(x)+T(x;θm) ,因此 h m − 1 ( x ) − η d L d h m − 1 ( x ) = h m − 1 ( x ) + T ( x ; θ m ) ⇒ T ( x ; θ m ) = − η d L d h m − 1 ( x ) h_{m-1}(x)-\eta\frac{dL}{dh_{m-1}(x)}=h_{m-1}(x)+T(x;\theta_m) \Rightarrow T(x;\theta_m)=-\eta\frac{dL}{dh_{m-1}(x)} hm−1(x)−ηdhm−1(x)dL=hm−1(x)+T(x;θm)⇒T(x;θm)=−ηdhm−1(x)dL由此可以看出当前树要拟合的正是损失函数负梯度在当前函数的值 − [ ∂ L ( y , h ( x i ) ) ∂ h ( x i ) ] h ( x ) = h m − 1 ( x ) -\left[\frac{\partial L\left(y, h\left(x_{i}\right)\right)}{\partial h\left(x_{i}\right)}\right]_{h(x)=h_{m-1}(x)} −[∂h(xi)∂L(y,h(xi))]h(x)=hm−1(x)
输入输入训练样本 D = { ( x 1 , y 1 ) , x 2 , y 2 ) , … , ( x N , y N ) } D=\{(x_1,y_1),x_2,y_2),\dots,(x_N,y_N)\} D={(x1,y1),x2,y2),…,(xN,yN)},其中 x i ∈ x ⊆ R d x_i \in \bm{x}\subseteq \bm{R}^d xi∈x⊆Rd, y i ∈ y = { − 1 , 1 } y_i \in \bm{y}=\{-1,1\} yi∈y={−1,1},基本分类器为 h m ( x ) h_m(x) hm(x),最终分类器为 H ( x ) H(\bm{x}) H(x),损失函数 L ( y , h ( x ) ) L(y,h(x)) L(y,h(x)):
(1)初始化 h 0 ( x ) = arg min c ∑ i = 1 N L ( y i , c ) h_0(x)=\arg \min _{c} \sum_{i=1}^{N} L\left(y_{i}, c\right) h0(x)=argminc∑i=1NL(yi,c)
(2)对于基学习器 h m ( x ) , m ∈ { 1 , 2 , … , M } h_m(x),m\in \{1,2,\dots,M\} hm(x),m∈{1,2,…,M}
\qquad 1)计算每个样本的残差 r m , i = − ∂ L ( y i , h ( x i ) ) ∂ h ( x i ) , i = 1 , 2 , … , N \bm{r}_{m,i}=-\frac{\partial L\left(y_i, h\left(x_{i}\right)\right)}{\partial h\left(x_{i}\right)},\quad i=1,2,\dots,N rm,i=−∂h(xi)∂L(yi,h(xi)),i=1,2,…,N;
\qquad 2)将上步得到的残差作为样本新的真实值,并将数据 ( x i , r m , i ) (x_i,r_{m,i}) (xi,rm,i)作为下一颗树的训练数据拟合一颗回归树,得到第 m m m棵树的叶结点区域 R m , j , j = 1 , 2 , … , J ( J 为 叶 结 点 数 量 ) R_{m,j},\quad j=1,2,\dots,J(J为叶结点数量) Rm,j,j=1,2,…,J(J为叶结点数量);
\qquad 3)对叶子区域 j = 1 , 2 , … , J j=1,2,\dots,J j=1,2,…,J,计算最佳拟合值 c m , j = arg min c ∑ x i ∈ R m , j L ( y i , h m − 1 ( x i ) + c ) c_{m,j}=\arg \min _{c}\sum_{x_i \in R_{m,j}}L(y_i,h_{m-1}(x_i)+c) cm,j=argminc∑xi∈Rm,jL(yi,hm−1(xi)+c);
\qquad 4)更新当前分类器为 h m ( x ) = h m − 1 ( x ) + ∑ j = 1 J c m , j I ( x ∈ R m , j ) h_m(x)=h_{m-1}(x)+\sum_{j=1}^Jc_{m,j}I(x\in R_{m,j}) hm(x)=hm−1(x)+∑j=1Jcm,jI(x∈Rm,j);
(3)得到最终回归树 H ( x ) = h M ( x ) = h 0 ( x ) + ∑ m = 1 M ∑ j = 1 J c m j I ( x ∈ R m j ) H(x)=h_{M}(x)=h_0(x)+\sum_{m=1}^{M} \sum_{j=1}^{J} c_{m j} I\left(x \in R_{m j}\right) H(x)=hM(x)=h0(x)+∑m=1M∑j=1JcmjI(x∈Rmj)
from sklearn.metrics import mean_squared_error
from sklearn.datasets import make_friedman1
from sklearn.ensemble import GradientBoostingRegressor
X, y = make_friedman1(n_samples=1200, random_state=0, noise=1.0)
X_train, X_test = X[:200], X[200:]
y_train, y_test = y[:200], y[200:]
est = GradientBoostingRegressor(n_estimators=100, learning_rate=0.1,
max_depth=1, random_state=0, loss='ls').fit(X_train, y_train)
mean_squared_error(y_test, est.predict(X_test))
初始平方误差为:5.009154859960321
reg = GradientBoostingRegressor(random_state=0)
reg.fit(X_train, y_train)
reg.score(X_test, y_test)
最终准确率为:0.8531316061459552
参考:
《GBDT算法原理以及实例理解》
Kaggle开源内容