1.提升树的简介
提升树是以分类树或回归树为基本分类器的提升方法。提升树被认为是统计学习中性能最好的方法之一
1.1提升方法思路
采用加法模型(即基函数的线性组合)与前向分布算法。
1.2提升树
以决策树为基函数的提升方法称为提升树(boosting tree)
1.3二叉分类树
对分类问题决策树是二叉分类树
1.4二叉回归树
对回归问题决策树是二叉回归树
1.5决策树桩
在AdaBoosting算法中用到的基本分类器base_generator:
base_generator = lambda x,v,f1,f2:(x<y and [f1] or [f2])[0]
可以看做有一个根节点v直接连接两个叶子f1和f2,如果x小于根节点v,则x的分类标签取f1,如果x大于根节点v,则x的分类标签取f2。则base_generator就可以看做一个简单的决策树,即所谓的决策树桩(decision stump)
1.6提升树模型公式
提升树模型表示为决策树的加法模型:
2.提升树(BoostingTree)学习算法详解
注:针对不同问题的提升树学习算法,其主要区别在于使用的损失函数不同。比如用平方误差损失函数的回归问题,用指数损失函数的分类问题,还有用一般损失函数的一般决策问题。
如果提升树学习算法使用AdaBoosting算法中的基本分类器作为二类分类树,这是的提升树学习算法就演化为AdaBoosting算法。这里我们讲解回归问题的提升树学习算法~
2.1提升树学习算法中的基本操作简介
已知一个训练数据集train_samples =[[x1,y1],[x2,y2],[,x3,y3],[x4,y4],[x5,y5]]
则X=[x1,x2,x3,x4,x5]是输入空间,Y=[y1,y2,y3,y4,y5]是输出空间,Y[i]是X[i]的回归结果
下面以一个具体训练数据集来讲解
#训练数据集 Train Samples
#train_samples[:,0]为数据集,train_samples[:,1]为回归结果集
train_samples = np.array([[1, 5.56],
[2, 5.70],
[3, 5.91],
[4, 6.40],
[5, 6.80],
[6, 7.05],
[7, 8.90],
[8, 8.70],
[9, 9.00],
[10,9.05]])
2.1.1数据X
则X=[1,2,3,4,5,6,7,8,9,10]是输入空间,Y=[5.56, 5,70, 5.91, 6.40, 6.80, 7.05, 8.90, 8.70, 9.00, 9.05]是输出空间(即回归结果)
python代码:
X,Y =train_samples[:,0],train_samples[:,1]
2.1.2回归值
比如X[0]=1的回归值是Y[0]=5.56,X[5]=6的回归值是Y[5]=6.80
即在训练数据集中,数据值回归值的回归函数为:f(X[i]) = Y[i]
2.1.3切分点s和切分点集合S
数据集合的切分点s:将X切分成两个子数据集的数据,比如1.5可以将X切分为如下两个子数据集RX1和RX2
s=1.5,RX1 = [1],RX2 = [2, 3, 4, 5, 6, 7, 8, 9, 10]
求训练数据集的切分点集合S的python代码:
'''计算所有可能切分点的集合,S'''
X,N,S= train_samples[:,0], np.shape(train_samples)[0],[]
for i in (np.arange(N)[0:-1]:
S.append((X[i] + X[i+1])/2.0)
2.1.4切分点集合S
所有可能的切分点的集合,称为切分点(segmentation point)集合S
则S=[1.5, 2.5, 3.5, 4.5, 5.6, 6.5 7.5, 8.5, 9.5]
2.1.5切分区域R
切分区域有数据切分区域(RX1,RX2),和回归值切分区域(RY1,RY2)
when 切分点s=1.5
数据切分区域 :RX1 = [1],RX2 = [2, 3, 4, 5, 6, 7, 8, 9, 10]
回归值切分区域:RY1 = [5.56],RY2 = [5,70, 5.91, 6.40, 6.80, 7.05, 8.90, 8.70, 9.00, 9.05]
python代码:
RX = [X[0:i+1], X[i+1:]]
RY = [Y[0:i+1], Y[i+1:]]
2.1.6平均回归值c1和c2
在RX1内部使得平方损失误差达到最小值的回归值,记为c1
在RX2内部使得平方损失误差达到最小值得回归值,记为c2
可以很容易知道,c1=mean(RY1),c2=mean(RY2)
用上面的RY1和RY2计算就是:c1 =mean(RY1) = 5.56,c2 = mean(RY2) = 7.50
cur_c1 = np.mean(RY[0])
cur_c2 = np.mean(RY[1])
2.1.7切分点的回归误差平方和m
when 切分点s=1.5
数据切分区域 :RX1 = [1],RX2 = [2, 3, 4, 5, 6, 7, 8, 9, 10]
回归值切分区域:RY1 = [5.56],RY2 = [5,70, 5.91, 6.40, 6.80, 7.05, 8.90, 8.70, 9.00, 9.05]
切分区域的平均回归值:c1 = 5.56,c2 = 7.50
切分点s=1.5的回归误差平方和m:
m=(5.56-c1)^2 + (5.70-7.50)^2 + (5.91-7.50)^2 + (6.40-7.50)^+……+(9.00-7.50)^2+(9.05-7.50)^2
=15.72
m1_ufunc = lambda y : np.power(y-c1 ,2)
m2_ufunc = lambda y : np.power(y-c2, 2)
m1_func = np.frompyfunc(m1_ufunc, 1, 1)
m2_func = np.frompyfunc(m2_ufunc, 1, 1)
cur_m = m1_func(RY[0]).sum() + m2_func(RY[1]).sum()
2.1.8最佳切分点s
现在我们选取最佳切分点作为当前即将要生成的回归树的根节点
最佳切分点:求切分点集合S中每个切分点的回归误差平方和m,m值最小的切分点s就是最佳切分点
还是用使用面的数据,可以依次求得每个切分点的回归误差平方和m
S=[1.5, 2.5, 3.5, 4.5, 5.6, 6.5 7.5, 8.5, 9.5]
M=[m(s1), m(s2), m(s3), m(s4), m(s5), m(s6), m(s7), m(s8), m(s9)]
=[m(1.5), m(2.5), m(3.5), m(4.5), m(5.5), m(6.5), m(7.5), m(8.5), m(9.5)]
=[15.72, 12.07, 8.36, 5.78, 3.91, 1.93, 8.01, 11.73, 15.74]
mean(M) = 1.93 = mean(M(6.5)) => 最佳切分点s=6.5
i = np.argmin(M)
s,m,c1,c2 = S[i], M[i], C[i][0], C[i][1]
2.1.9回归树T(x)
回归树T(x) = T(x|{v,f1,f2})
根节点v为最佳切分点s,左叶子f1=c1=6.24,右叶子f2=c2=8.91
def Tx(x, s, c1, c2):
if x < s:
return f1
else:
return f2
建议使用lambda+(and-or)定义回归树T(x)
Tx = lambda x,s,c1,c2 :(x<s and [c1] or [c2])[0]
2.1.10残差(residual)
有训练数据输入空间X,输出空间Y,以及学习得到的提升树f(x)
Y[i] - f(X[i])称为f(x)拟合训练数据X[i]的残差,记作r(X[i])=Y[i] - f(X[i])
2.1.11残差表(Residual Table)
经过计算我们学习生成了训练数据集的第一个回归树T(x) = T(x | {s=6.5, f1=6.24, f2=8.91})
因为此T(x)是我们求得的第一个回归树,则学习到的提升树f(x)=T(x)
如果T(x)是我们求得的第K个回归树,则学习到的提升树更新为f(x) = f(x) + T(x)
X[i]残差r(X[i]) = Y[i] - f(X[i])
我们可以求得更新提升树后的残差表Rsd
R = [r(X[0]), r(x[1]), r(X[2]), r(X[3]), r(X[4]), r(X[5]), r(X[6]), r(X[7]), r(X[8]), r(X[9])]
=[Y[0]-f(X[0]),Y[1]-f(X[1]),Y[2]-f(X[2]),Y[3]-f(X[3]),Y[4]-f(X[4]), Y[5]-f(X[5]), Y[6]-f(X[6]), Y[7]-f(X[7]), Y[8]-f(X[8]), Y[9]-f(X[9])]
=[5.56-f(1), 5.70-f(2), 5.91-f(3), 6.40-f(4), 6.80-f(5), 7.05-f(6), 8.90-f(7), 8.70-f(8), 9.00-f(9), 9.05-f(10)]
=[5.56-f(1), 5.70-f(2), 5.91-f(3), 6.40-f(4), 6.80-f(5), 7.05-f(6), 8.90-f(7), 8.70-f(8), 9.00-f(9), 9.05-f(10)]
注:f(x|{v=6.5, f1=6.24, f2=8.91}) = 如果x<v回归值取f1=6.24,如果x>=6.5回归值取f2=8.91
=[5.56-6.24, 5.70-6.24, 5.91-6.24, 6.40-6.24, 6.80-6.24, 7.05-6.24, 8.90-8.91, 8.70-8.91, 9.00-8.91, 9.05-8.91]
=[-0.68, -0.54, -0.33, 0.16, 0.56, 0.81, -0.01, -0.21, 0.09, 0.14]
python代码:
#更新残差表(用提升树拟合训练数据的残差表)
rsd_ufunc = lambda x,y : (x<s and [y-c1] or [y-c2])[0]
rsd_func = np.frompyfunc(rsd_ufunc, 2, 1)
rsd = rsd_func(X, Y)
2.1.12f(x)训练数据的平方损失误差
如果有提升树f(x),并求出了用f(x)拟合训练数据的残差表Rsd
rsd =[-0.68, -0.54, -0.33, 0.16, 0.56, 0.81, -0.01, -0.21, 0.09, 0.14]
用f(x)拟合训练数据的平方损失误差e=残差表中每个残差平方的和
e = (0.68)^2 + (-0.54)^2 + (0.33)^2 + (0.16)^2 + (0.56)^2 + (0.81)^2 + (-0.01)^2 + (0.09)^2 + (0.14)^2
= 1.93
python代码:
#计算用提升树f(x)拟合训练数据的平方损失误差
e_ufunc = lambda r : np.power(r,2)#r为残差
e_func = np.frompyfunc(e_ufunc, 1, 1)
e = e_func(rsd).sum() #rsd为当前提升树f(x)的残差表
2.1.13提升树
我们学习得到一个新的回归树T(x),如果已有学习得到的提升树f(x),则新学习到的提升树更新为:
f(x) = f(x) + T(x)
通过提升树学习,我们得到了提升树f(x)的存储体FX为:
[[ 6.5 6.24 8.91]
[ 3.5 -0.52 0.22]
[ 6.5 0.15 -0.22]
[ 4.5 -0.16 0.11]
[ 6.5 0.07 -0.11]
[ 1.5 -0.22 0.02]]
先解释说明,如何理解f(x)的这个存储体FX:
FX[0] = [6.5 6.24 8.91],表示这是第一个学习得到的回归树T(x),根节点v=6.5,左叶子f1=6.24,右叶子f2=8.91
FX[5] = [1.5 -0.22 0.02],表示这是最后一个学习得到的回归时T(x),根节点v=1.5,左叶子=-0.22,右叶子f2=0.02
可知我们一共学习到6个回归树,提升树f(x)等于这6个回归树的加:
f(x) = T1(x) + T2(x) + T3(x) + T4(x) + T5(x) + T6(x)
=T(x|FX[0]) + T(x|FX[1]) + T(x|FX[2]) + T(x|FX[3]) + T(x|FX[4]) +T(x|FX[5])
提升树的回归函数python代码:
def Regress(self, test_samples):
'''提升树的回归函数
使用学习得到的提升树,对给定的单个测试数据test_x进行回归
:param test_samples:测试数据集
:return 回归结果
'''
base_tree = lambda x,v,f1,f2 : (x<v and [f1] or [f2])[0]
reg_efunc = lambda x : sum([base_tree(x,v,f1,f2) for v,f1,f2 in self.FX])
reg_func = np.frompyfunc(reg_efunc, 1, 1)
return reg_func(test_samples)
2.1.14提升树学习终止
如果用提升树f(x)拟合训练数据的平方损失误差e小于允许的最大平方损失误差max_e,则提升树学习终止,f(x)为最终的提升树
3.2回归问题的提升树学习算法步骤
输入:训练数据集T={(x1,y1), (x2,y2), ..., (xn, yn)},其中X是输入空间,Y是输出空间
输出:提升树f(x)