提升树(Boosting Tree)原理与案例

1. 提升树模型

提升方法实际采用加法模型(即基函数的线性组合)与前向分步算法。以决策树为基函数的提升方法称为提升树(boosting tree)。对分类问题决策树是二叉分类树,对回归问题决策树是二叉回归树。提升树模型可以表示为决策树的加法模型:
f M ( x ) = ∑ m = 1 M T ( x ; θ m ) f _ { M } ( x ) = \sum _ { m = 1 } ^ { M } T ( x ; \theta _ { m } ) fM(x)=m=1MT(x;θm)
其中, T ( x ; θ m ) T ( x ; \theta _ { m } ) T(x;θm)表示决策树; θ m \theta _ { m } θm为决策树参数; M M M为树的个数。

2. 提升树算法

提升树算法采用前向分步算法。首先确定初始提升树 f 0 ( x ) = 0 f_0(x)=0 f0(x)=0 ,第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)=fm1(x)+T(x;Θm)
其中, f m − 1 ( x ) f_{m-1}(x) fm1(x) 为当前模型,通过经验风险极小化确定下一棵决策树的参数 Θ m \Theta_m Θm
Θ ^ m = argmin ⁡ ( Θ n ) ∑ i = 1 N L ( y i , f m − 1 ( x i ) + T ( x i ; Θ m ) ) \hat{\Theta}_m=\operatorname{argmin}_{\left(\Theta_n\right)} \quad \sum_{i=1}^N L\left(y_i, f_{m-1}\left(x_i\right)+T\left(x_i ; \Theta_m\right)\right) Θ^m=argmin(Θn)i=1NL(yi,fm1(xi)+T(xi;Θm))
由于树的线性组合可以很好地拟合训练数据,即使数据中的输入与输出之间的关系很复杂也是如此,所以提升树是一个高功能的学习算法。

下面讨论针对不同问题的提升树学习算法,其主要区别在于使用的损失函数不同。包括用平方误差 损失函数的回归问题,用指数损失函数的分类问题,以及用一般损失函数的一般决策问题。

2.1 二叉分类提升树

对于二分类问题,提升树算法只需将AdaBoost算法中的基本分类器限制为二类分类树即可,可以说这时的提升树算法是AdaBoost算法的特殊情况,这里不再细述。下面叙述回归问题的提升树。

2.2 二叉回归提升树

已知一个训练数据集 T = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , … , ( x N , y N ) } , x i ∈ X ⊆ R n , x T=\left\{\left(x_1, y_1\right),\left(x_2, y_2\right), \ldots,\left(x_N, y_N\right)\right\}, x_i \in X \subseteq R^n , \mathrm{x} T={(x1,y1),(x2,y2),,(xN,yN)},xiXRnx 为输入空 间, y i ∈ Y ⊆ R , y y_i \in Y \subseteq R , \mathrm{y} yiYRy 为输出空间。如果将输入空间 x x x划分为 J J J个互不相交的区域 R 1 , R 2 , … , R J R_1, R_2, \ldots, R_J R1,R2,,RJ , 并且在每个区域上确定输出的常量 c j c_j cj ,那么树可表示为:
T ( x ; Θ ) = ∑ j = 1 J c j I ( x ∈ R j ) T(x ; \Theta)=\sum_{j=1}^J c_j I\left(x \in R_j\right) T(x;Θ)=j=1JcjI(xRj)
其中,参数 Θ = { ( R 1 , c 1 ) , ( R 2 , c 2 ) , … , ( R J , c J ) } \Theta=\left\{\left(R_1, c_1\right),\left(R_2, c_2\right), \ldots,\left(R_J, c_J\right)\right\} Θ={(R1,c1),(R2,c2),,(RJ,cJ)} 表示树的区域划分和各区域上的常数。J 是回归树的复杂度即叶结点个数。
回归问题提升树使用以下前向分步算法:
f 0 ( x ) = 0 f m ( x ) = f m − 1 ( x ) + T ( x ; Θ m ) , m = 1 , 2 , … , M f M ( x ) = ∑ m = 1 M T ( x ; Θ m ) \begin{aligned} &f_0(x)=0 \\ &f_m(x)=f_{m-1}(x)+T\left(x ; \Theta_m\right), m=1,2, \ldots, M \\ &f_M(x)=\sum_{m=1}^M T\left(x ; \Theta_m\right) \end{aligned} f0(x)=0fm(x)=fm1(x)+T(x;Θm),m=1,2,,MfM(x)=m=1MT(x;Θm)
在前向分步算法的第 m m m 步,给定当前模型 f m − 1 ( x ) f_{m-1}(x) fm1(x) ,需求解:
Θ ^ m = argmin ⁡ ( Θ n ) ∑ i = 1 N L ( y i , f m − 1 ( x i ) + T ( x i ; Θ m ) ) \hat{\Theta}_m=\operatorname{argmin}_{\left(\Theta_n\right)} \sum_{i=1}^N L\left(y_i, f_{m-1}\left(x_i\right)+T\left(x_i ; \Theta_m\right)\right) Θ^m=argmin(Θn)i=1NL(yi,fm1(xi)+T(xi;Θm))
得到 Θ ^ m \hat{\Theta}_m Θ^m ,即第 m \mathrm{m} m 棵树的参数。
当采用平方误差损失函数时, L ( y , f ( x ) ) = ( y − f ( x ) ) 2 L(y, f(x))=(y-f(x))^2 L(y,f(x))=(yf(x))2 ,其损失变为:
L ( y , f m − 1 ( x ) + T ( x ; Θ m ) ) = [ y − f m − 1 ( x ) − T ( x ; Θ m ) ] 2 = [ r − T ( x ; Θ m ) ] 2 L\left(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,fm1(x)+T(x;Θm))=[yfm1(x)T(x;Θm)]2=[rT(x;Θm)]2
这里, r = y − f m − 1 ( x ) r=y-f_{m-1}(x) r=yfm1(x) ,是当前模型拟合数据的残差 (residual) 。所以,对回归问题的提升树算法来说,只需简单地拟合当前模型的残差。这样,算法是相当简单的。

回归提升树算法步骤:
输入:训练数据集 T = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , … , ( x N , y N ) } , x i ∈ X , y i ∈ R T=\left\{\left(x_1, y_1\right),\left(x_2, y_2\right), \ldots,\left(x_N, y_N\right)\right\}, \quad x_i \in X, \quad y_i \in R T={(x1,y1),(x2,y2),,(xN,yN)},xiX,yiR
输出:提升树 f M ( x ) f_M(x) fM(x)
(1) 初始化 f 0 ( x ) = 0 f_0(x)=0 f0(x)=0
(2)对 m = 1 , 2 , … , M ( M m=1,2, \ldots, \mathrm{M}(\mathrm{M} m=1,2,,M(M 棵树,对每个树进行遍历)
(a)计算残差:
r m i = y i − f m − 1 ( x i ) , i = 1 , 2 , … , N r_{m i}=y_i-f_{m-1}\left(x_i\right), \quad i=1,2, \ldots, N rmi=yifm1(xi),i=1,2,,N
(b) 拟合残差 r m i r_{m i} rmi 学习一个回归树,得到 T ( x ; Θ m ) T\left(x ; \Theta_m\right) T(x;Θm)
(c)更新 f m ( x ) = f m − 1 ( x ) + T ( x ; Θ m ) f_m(x)=f_{m-1}(x)+T(x ; \Theta m) fm(x)=fm1(x)+T(x;Θm)
(3) 得到回归问题的提升树
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=1MT(x;Θm)

3. 回归提升树示例

提升树计算实例,以下实例来自于《统计机器学习》
数据集:

x i x_i xi12345678910
y i y_i yi5.565.705.916.406.807.058.908.709.009.05

对于上述训练样本,设定基本模型为只有一个节点回归树模型(树桩)。x的取值范围为区间[0.5, 10.5],y的取值范围为区间[5.0, 10.0]。可能的切分点为1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5。

树桩是由一个根节点直接连接两个叶结点的简单决策树。

首先通过以下优化问题寻找回归树的最佳切分点:

3.1 求 f 1 ( x ) f_1(x) f1(x) 即回归树 T 1 ( x ) T_1(x) T1(x)

样本输入空间划分的基本步骤如下:
首先通过以下优化问题:
min ⁡ ( 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 ] \min _{(s)}\left[\min _{\left(c_1\right)} \sum_{x_i \in R_1}\left(y_i-c_1\right)^2+\min _{\left(c_2\right)} \sum_{x_i \in R_2}\left(y_i-c_2\right)^2\right] min(s)[min(c1)xiR1(yic1)2+min(c2)xiR2(yic2)2]
求解训练数据的切分点 s : \mathrm{s}: s:
R 1 = { x ∣ x ≤ s } , R 2 = { x ∣ x > s } R_1=\{x \mid x \leq s\}, R_2=\{x \mid x>s\} R1={xxs},R2={xx>s}
容易求得在 R 1 , R 2 R_1, R_2 R1,R2 内部使平方损失误差达到最小的 c 1 , c 2 c_1, c_2 c1,c2 为:
c 1 = 1 N 1 ∑ x i ∈ R 1 y i , c 2 = 1 N 2 ∑ x i ∈ R 2 y i c_1=\frac{1}{N_1} \sum_{x_i \in R_1} y_i, c_2=\frac{1}{N_2} \sum_{x_i \in R_2} y_i c1=N11xiR1yi,c2=N21xiR2yi
这里 N 1 , N 2 N_1, N_2 N1,N2 R 1 , R 2 R_1, R_2 R1,R2 的样本点数。
(1) 求训练数据的切分点
这里的切分点指的是将x值划分界限,数据中x的范围是 [ 1 , 10 ] [1,10] [1,10] ,假设我们考虑如下切分点:
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), m(s)计算公式如下: 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 _{\left(c_1\right)} \sum_{x_i \in R_1}\left(y_i-c_1\right)^2+\min _{\left(c_2\right)} \sum_{x_i \in R_2}\left(y_i-c_2\right)^2 m(s)=min(c1)xiR1(yic1)2+min(c2)xiR2(yic2)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, \ldots, 10\} R1={1},R2={2,3,,10} ,那么:
c 1 = 5.56 c 2 = 1 9 ( 5.70 + 5.91 + 6.40 + 6.80 + 7.05 + 8.90 + 8.70 + 9.00 + 9.05 ) = 7.50 c_1=5.56\\ c_2=\frac{1}{9}(5.70+5.91+6.40+6.80+7.05+8.90+8.70+9.00+9.05)=7.50 c1=5.56c2=91(5.70+5.91+6.40+6.80+7.05+8.90+8.70+9.00+9.05)=7.50
m ( 1.5 ) = 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 m(1.5)=\min _{\left(c_1\right)} \sum_{x_i \in R_1}\left(y_i-c_1\right)^2+\min _{\left(c_2\right)} \sum_{x_i \in R_2}\left(y_i-c_2\right)^2=0+15.72 m(1.5)=min(c1)xiR1(yic1)2+min(c2)xiR2(yic2)2=0+15.72 = 15.72 =15.72 =15.72

现将s及m(s)的计算结果列表如下:

s1.52.53.54.55.56.57.58.59.5
m ( s ) m(s) m(s)15.7212.078.365.783.911.938.0111.7315.74

(2)求回归树
由上表可知,当 s = 6.5 \mathrm{s}=6.5 s=6.5 时, m ( s ) \mathrm{m}(\mathrm{s}) m(s) 达到最小值,此时 R 1 = { 1 , 2 , … , 6 } , R 2 = { 7 , 8 , 9 , 10 } R_1=\{1,2, \ldots, 6\}, R_2=\{7,8,9,10\} R1={1,2,,6},R2={7,8,9,10} , 且:
c 1 = 1 6 ( 5.56 + 5.70 + 5.91 + 6.40 + 6.80 + 7.05 ) = 6.24 c 2 = 1 4 ( 8.90 + 8.70 + 9.00 + 9.05 ) = 8.91 \begin{aligned} &c_1=\frac{1}{6}(5.56+5.70+5.91+6.40+6.80+7.05)=6.24 \\ &c_2=\frac{1}{4}(8.90+8.70+9.00+9.05)=8.91 \end{aligned} c1=61(5.56+5.70+5.91+6.40+6.80+7.05)=6.24c2=41(8.90+8.70+9.00+9.05)=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)= \begin{cases}6.24, & x<6.5 \\ 8.91, & x \geq 6.5\end{cases} T1(x)={6.24,8.91,x<6.5x6.5
(3)求当前加法模型 f 1 ( x ) f_1(x) f1(x)
当前的加法模型为:
f 1 ( x ) = T 1 ( x ) f_1(x)=T_1(x) f1(x)=T1(x)
(4)求当前加法模型的残差
f 1 ( x ) f_1(x) f1(x) 拟合训练数据的残差如表所示 ,表中 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, \ldots, 10 r2i=yif1(xi),i=1,2,,10

x i x_i xi12345678910
r 2 i r_{2i} r2i-0.68-0.54-0.330.160.560.81-0.01-0.210.090.14

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=110(yif1(xi))2=1.93
这里的误差为 1.93 1.93 1.93 ,如果我们定义终止时候的误差比这个误差要小,那么算法继续执行以上步骤, 直到满足误差为止。

3.2 求回归树 T 2 ( x ) T_2(x) T2(x)

方法与求 T 1 ( x ) T_1(x) T1(x) 一样,只是拟合的数据是残差表。
(1)求解数据的切分点
仍然对区域 R = { 1 , 2 , … , 10 } R=\{1,2, \ldots, 10\} R={1,2,,10} 求解数据的切分点。当 s = 1.5 \mathrm{s}=1.5 s=1.5 时, R 1 ′ = { 1 } , R 2 ′ = { 2 , 3 , … , 10 } R_{1^{\prime}}=\{1\}, R_{2^{\prime}}=\{2,3, \ldots, 10\} R1={1},R2={2,3,,10} ,那么:
c 1 ′ = − 0.68 c 2 ′ = 1 9 ( − 0.54 − 0.33 + 0.16 + 0.56 + 0.81 − 0.01 − 0.21 + 0.09 + 0.14 ) = 0.07 m ( 1.5 ) = min ⁡ ( c 1 ) ∑ x i ∈ R 1 ( r 2 i − c 1 ) 2 + min ⁡ ( c 2 ) ∑ x i ∈ R 2 ( r 2 i − c 2 ) 2 = 0 + 1.42 = 1.42 \begin{aligned} &c_{1^{\prime}}=-0.68 \\ &c_{2^{\prime}}=\frac{1}{9}(-0.54-0.33+0.16+0.56+0.81-0.01-0.21+0.09+0.14)=0.07 \\ &m(1.5)=\min _{\left(c_1\right)} \sum_{x_i \in R_1}\left(r_{2 i}-c_1\right)^2+\min _{\left(c_2\right)} \sum_{x_i \in R_2}\left(r_{2 i}-c_2\right)^2=0+1.42 \\ &=1.42 \end{aligned} c1=0.68c2=91(0.540.33+0.16+0.56+0.810.010.21+0.09+0.14)=0.07m(1.5)=(c1)minxiR1(r2ic1)2+(c2)minxiR2(r2ic2)2=0+1.42=1.42
现将 s s s m ( s ) m(s) m(s) 的计算结果列表如下 (见表4):
表4  s 、 m(s) \text{ s 、 m(s)}  s  m(s)计算结果

s1.52.53.54.55.56.57.58.59.5
m ( s ) m(s) m(s)1.421.010.801.141.671.931.931.901.91

(2)求回归树
由表4可知,当 s = 3.5 s=3.5 s=3.5 m ( s ) m(s) m(s) 达到最小值,此时
R 1 ′ = { 1 , 2 , 3 } , R 2 ′ = { 4 , 5 , … , 10 } , c 1 = − 0.52 , c 2 = 0.22 R_{1^{\prime}}=\{1,2,3\}, R_{2^{\prime}}=\{4,5, \ldots, 10\}, c_1=-0.52, c_2=0.22 R1={1,2,3},R2={4,5,,10},c1=0.52,c2=0.22 ,所以回归树 T 2 ( x ) T_2(x) T2(x) 为:
T 2 ( x ) = { − 0.52 , x < 3.5 0.22 , x ≥ 3.5 T_2(x)= \begin{cases}-0.52, & x<3.5 \\ 0.22, & x \geq 3.5\end{cases} T2(x)={0.52,0.22,x<3.5x3.5
(3) 求当前加法模型 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)= \begin{cases}5.72, & x<3.5 \\ 6.46, & 3.5 \leqslant x<6.5 \\ 9.13, & x \geqslant 6.5\end{cases} f2(x)=f1(x)+T2(x)= 5.72,6.46,9.13,x<3.53.5x<6.5x6.5
(4)求当前加法模型的残差
f 2 ( x ) f_2(x) f2(x) 拟合训练数据的残差如表5,表中 r 3 i = y i − f 2 ( x i ) , i = 1 , 2 , … , 10 r_{3 i}=y_i-f_2\left(x_i\right), i=1,2, \ldots, 10 r3i=yif2(xi),i=1,2,,10

x i x_i xi12345678910
r 3 i r_{3i} r3i-0.16-0.020.19-0.060.340.59-0.23-0.43-0.13-0.08

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=110(yif2(xi))2=0.79

之后的过程同步骤2一样,我就不在这里故述啦! 最后,给出完整的回归提升树模型。
T 3 ( x ) = { 0.15 , x < 6.5 − 0.22 , x ⩾ 6.5 L ( y , f 3 ( x ) ) = 0.47 , T 4 ( x ) = { − 0.16 , x < 4.5 0.11 , x ⩾ 4.5 L ( y , f 4 ( x ) ) = 0.30 , T 5 ( x ) = { 0.07 , x < 6.5 − 0.11 , x ⩾ 6.5 L ( y , f 5 ( x ) ) = 0.23 , T 6 ( x ) = { − 0.15 , x < 2.5 0.04 , x ⩾ 2.5 f 6 ( x ) = f 5 ( x ) + T 6 ( x ) = T 1 ( x ) + ⋯ + T 5 ( x ) + T 6 ( x ) = { 5.63 , x < 2.5 5.82 , 2.5 ⩽ x < 3.5 6.56 , 3.5 ⩽ x < 4.5 6.83 , 4.5 ⩽ x < 6.5 8.95 , x ⩾ 6.5 \begin{aligned} T_3(x) &=\left\{\begin{array}{ll} 0.15, & x<6.5 \\ -0.22, & x \geqslant 6.5 \end{array} \quad L\left(y, f_3(x)\right)=0.47,\right.\\ T_4(x) &=\left\{\begin{array}{ll} -0.16, & x<4.5 \\ 0.11, & x \geqslant 4.5 \end{array} \quad L\left(y, f_4(x)\right)=0.30,\right.\\ T_5(x) &=\left\{\begin{array}{ll} 0.07, & x<6.5 \\ -0.11, & x \geqslant 6.5 \end{array} \quad L\left(y, f_5(x)\right)=0.23,\right.\\ T_6(x) &= \begin{cases}-0.15, & x<2.5 \\ 0.04, & x \geqslant 2.5\end{cases} \\ f_6(x) &=f_5(x)+T_6(x)=T_1(x)+\cdots+T_5(x)+T_6(x) \\ &= \begin{cases}5.63, & x<2.5 \\ 5.82, & 2.5 \leqslant x<3.5 \\ 6.56, & 3.5 \leqslant x<4.5 \\ 6.83, & 4.5 \leqslant x<6.5 \\ 8.95, & x \geqslant 6.5\end{cases} \end{aligned} T3(x)T4(x)T5(x)T6(x)f6(x)={0.15,0.22,x<6.5x6.5L(y,f3(x))=0.47,={0.16,0.11,x<4.5x4.5L(y,f4(x))=0.30,={0.07,0.11,x<6.5x6.5L(y,f5(x))=0.23,={0.15,0.04,x<2.5x2.5=f5(x)+T6(x)=T1(x)++T5(x)+T6(x)= 5.63,5.82,6.56,6.83,8.95,x<2.52.5x<3.53.5x<4.54.5x<6.5x6.5
f 6 ( x ) f_6(x) f6(x) 拟合训练数据的平方损失误差是
L ( y , f 6 ( x ) ) = ∑ i = 1 10 ( y i − f 6 ( x i ) ) 2 = 0.17 L\left(y, f_6(x)\right)=\sum_{i=1}^{10}\left(y_i-f_6\left(x_i\right)\right)^2=0.17 L(y,f6(x))=i=110(yif6(xi))2=0.17
假设此时已满足误差要求, 那么 f ( x ) = f 6 ( x ) {f}(\mathrm{x})={f}_6(\mathrm{x}) f(x)=f6(x) 即为所求提升树。

4. python代码

import numpy as np

label = np.array([5.56, 5.7, 5.91, 6.4, 6.8, 7.05, 8.9, 8.7, 9, 9.05])

# 已经排好序了。实际情况中单一特征的数据或者多特征的数据,选择切分点的时候也像决策树一样选择
feature = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]


class Tree_model:
    def __init__(self, stump, mse, left_value, right_value, residual):
        '''
        :param stump: 为feature最佳切割点
        :param mse: 为每棵树的平方误差
        :param left_value: 为决策树左值
        :param right_value: 为决策树右值
        :param residual: 为每棵决策树生成后余下的残差
        '''
        self.stump = stump
        self.mse = mse
        self.left_value = left_value
        self.right_value = right_value
        self.residual = residual


'''根据feature准备好切分点。例如:
feature为[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
切分点为[1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5]
'''


def Get_stump_list(feature):
    # 特征值从小到大排序好,错位相加
    tmp1 = list(feature.copy())
    tmp2 = list(feature.copy())
    tmp1.insert(0, 0)
    tmp2.append(0)
    stump_list = ((np.array(tmp1) + np.array(tmp2)) / float(2))[1:-1]
    return stump_list


# 此处的label其实是残差
def Get_decision_tree(stump_list, feature, label):
    best_mse = np.inf
    best_stump = 0  # min(stump_list)
    residual = np.array([])
    left_value = 0
    right_value = 0
    for i in range(np.shape(stump_list)[0]):
        left_node = []
        right_node = []
        for j in range(np.shape(feature)[0]):
            if feature[j] < stump_list[i]:
                left_node.append(label[j])
            else:
                right_node.append(label[j])
        left_mse = np.sum((np.average(left_node) - np.array(left_node)) ** 2)
        right_mse = np.sum((np.average(right_node) - np.array(right_node)) ** 2)
        # print("decision stump: %d, left_mse: %f, right_mse: %f, mse: %f" % (i, left_mse, right_mse, (left_mse + right_mse)))
        if best_mse > (left_mse + right_mse):
            best_mse = left_mse + right_mse
            left_value = np.average(left_node)
            right_value = np.average(right_node)
            best_stump = stump_list[i]
            left_residual = np.array(left_node) - left_value
            right_residual = np.array(right_node) - right_value
            residual = np.append(left_residual, right_residual)
            # print("decision stump: %d, residual: %s"% (i, residual))
    Tree = Tree_model(best_stump, best_mse, left_value, right_value, residual)
    return Tree, residual


# Tree_num就是树的数量
def BDT_model(feature, label, Tree_num=100):
    feature = np.array(feature)
    label = np.array(label)
    stump_list = Get_stump_list(feature)
    Trees = []
    residual = label.copy()
    # 产生每一棵树
    for num in range(Tree_num):
        # 每次新生成树后,还需要再次更新残差residual
        Tree, residual = Get_decision_tree(stump_list, feature, residual)
        Trees.append(Tree)
    return Trees


def BDT_predict(Trees, feature):
    predict_list = [0 for i in range(np.shape(feature)[0])]
    # 将每棵树对各个特征预测出来的结果进行相加,相加的最后结果就是最后的预测值
    for Tree in Trees:
        for i in range(np.shape(feature)[0]):
            if feature[i] < Tree.stump:
                predict_list[i] = predict_list[i] + Tree.left_value
            else:
                predict_list[i] = predict_list[i] + Tree.right_value
    return predict_list


# 计算误差
def Get_error(predict, label):
    predict = np.array(predict)
    label = np.array(label)
    error = np.sum((label - predict) ** 2)
    return error


Trees = BDT_model(feature, label)
predict = BDT_predict(Trees, feature)
print("The error is ", Get_error(predict, label))
print(predict)
  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值