1 - 加法模型
加法模型,就是通过训练集不断的得到不同的分类器(回归),然后将这些分类器组合成一个新的分类器的过程。
假设有
其中
可以看出,在给定这样一个加法模型的思路以及固定的训练集后,我们要做的就是最小化该模型的损失,即:
对于这样一个模型,训练的思路是:因为是个加法模型,所以可以从前向后,每一步只学习一个分类器(即基函数)和系数,让它逐步的逼近目标函数,直到达到我们能够接受的误差为止。该方法也叫做前向分步算法。
前向分布算法过程如下:
假设目前训练集
1)得到第一个分类器
2)对于
i)如数学归纳法一样,假设目前已经得到了前
那么,对于第
这样就得到了第
ii)然后更新旧加法模型,得到新的加法模型:
这样,就是将整个模型转换成了一步求得一个模型的参数和系数的优化问题上。
2 - adaboost
adaboost是加法模型的一个特例。假设目前有训练集
1)先设定初始时的训练样本的权值:
2)对
i)使用具有权值分布
ii)然后计算基于当前分类器
ps:当然是选择当前权值基础上分类误差率最低的那个分类器作为当前分类器。所以其实是通过ii)来确定i)的分类器
iii)计算该分类器
这里对数是自然对数。
ps:可以发现,当
iv)更新训练集的权值为下一个分类器做准备:
这里
ps:权值更新的式子可以写成如下形式:
可以看出,当样本分类正确时,其权值在变小,而当样本分类错误时,其权值被放大,由此,误分类样本在下一轮学习中会起更大作用,也就是下一个分类器会更关注那些误分类的样本。
3)构建基本分类器的线性组合:
得到最终分类器:
2.1 这里举个例子:
假设分类器是一个阈值分类器,即
1)最开始每个样本的权重为相等,即:
2)然后计算基于
i)因为当2.5时,错误率最低,则第一个分类器:
ii)当前误差率为0.3
iii)计算第一个分类器系数:
iv)更新训练集的权值分布:
d1 = [0.1]*10
y = [1,1,1,-1,-1,-1,1,1,1,-1]
z = []
def g1(x):
return 1 if x < 2.5 else -1
for ind,(d1Item,yItem) in enumerate(zip(d1,y)):
z.append(d1Item*np.exp(-0.4236*yItem*g1(ind)))
d2 = [ zItem/sum(z) for zItem in z ]
此时分类器
3)接着处理
同上述方法计算每个阈值
#python 计算误分类率代码
weight=np.asarray([0.0715,0.0715,0.0715,0.0715,0.0715,0.0715,0.1666,0.1666,0.1666,0.0715])
eInd = np.asarray([4,5,6])-1
weight[eInd].sum() #0.2145
最低的分类器:
i)此时误差率为0.2143
ii)系数为0.6496
iii)训练集权值分布:
iv)此时分类器
//scala计算误分类代码
def g1(x:Int):Int = if (x<2.5) 1 else -1
def g2(x:Int):Int = if (x<8.5) 1 else -1
def f2(x:Int):Double = 0.4236*g1(x) + 0.6496*g2(x)
val X = List(0,1,2,3,4,5,6,7,8,9)
X.foreach(x=>println(f"${f2(x)}%.0f"))
4)如上述计算过程,第三轮得到的分类器:
此时误分类样本为0个,所以无需训练所谓第四个基分类器。
最终分类器为:
3 - boosting tree
提升树是以回归树或者分类树作为基本分类器的模型,被认为统计学习中性能最好的方法之一(当然还有svm了)。
该类方法是采用了加法模型(基函数的线性组合)和前向分布算法,用决策树为基函数的提升方法叫提升树,分类问题 时,可采用二叉分类树,回归问题时,可采用二叉回归树。提升树模型可以表示为决策树的加法模型:
其中,
对于整个过程,如上面的加法模型一样,先确定初始提升树
其中
ps:因为树的线性组合可以很好的拟合训练数据,即使数据中的输入与输出的关系很复杂也如此。所以提升树是一个高功能的学习算法
通常来说,不同的提升树学习算法,主要区别在于使用的损失函数:1)平方误差损失函数的回归问题;2)指数损失函数的分类问题;3)一般损失函数的一般决策问题。
对于二类分类问题,即将adaboost中的基本分类器限制为二类分类树(即树桩:一个根节点,两个叶子节点)。这里主要介绍回归问题的提升树。
假设训练集
其中参数
前向分步步骤:
假设训练集
1)初始化
2)对于轮数
i)当第
得到的
代入得:
其中
ii)对于每个样本
求得他们的残差:
iii)拟合这轮得到的残差
iv)更新
3)得到最后结果模型:
3.1 回归提升树例子
如下表数据:
这里略过了
通过优化下面的问题:
来求解切分点
从而计算在当前切分点基础上,在
1)上述训练集样本的切分点有
对应的最小误差值如下:
k发现当
从而
X = [1,2,3,4,5,6,7,8,9,10]
Y0 = [5.56, 5.70, 5.91, 6.40, 6.80, 7.05, 8.90, 8.70, 9.00, 9.05]
X_splits = [1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5]
def cost_split_point(ind,xs,YT):
R1 = YT[:ind+1]
R2 = YT[ind+1:]
c1 = round(mean(R1),3)
c2 = round(mean(R2),3)
ans0 = [(x-c1)*(x-c1) for x in R1]
ans1 = [(x-c2)*(x-c2) for x in R2]
return [sum(ans0+ans1), c1, c2]
cost = [[xs]+cost_split_point(ind,xs,Y0) for ind,xs in enumerate(X_splits)]
#[[1.5, 15.723, 5.559, 7.501],
# [2.5, 12.083, 5.629, 7.726],
# [3.5, 8.365, 5.723, 7.985],
# [4.5, 5.775, 5.892, 8.25],
# [5.5, 3.911, 6.073, 8.540],
# [6.5, 1.930, 6.236, 8.912],
# [7.5, 8.009, 6.617, 8.916],
# [8.5, 11.735, 6.877, 9.025],
# [9.5, 15.738, 7.113, 9.050]]
2)计算
用
r_2i = [-0.68, -0.54, -0.33, -0.16, 0.56, 0.81, -0.01, -0.21, 0.09, 0.14]
r_2i = np.array(r_2i)
loss = (r_2i*r_2i).sum()#1.9301
3)在计算新的回归树时,选择的训练集是图3.1.3 而不是原来的3.1.1,即是基于上一轮模型结果之后的残差。从而得到这一轮的回归树:
Y1 = [-0.68, -0.54, -0.33, 0.16, 0.56, 0.81, -0.01, -0.21, 0.09, 0.14]
cost = [[xs]+cost_split_point(ind,xs,Y1) for ind,xs in enumerate(X_splits)]
#[[1.5, 1.417, -0.680, 0.073],
# [2.5, 1.002, -0.609, 0.151],
# [3.5, 0.790, -0.517, 0.22],
# [4.5, 1.129, -0.347, 0.230],
# [5.5, 1.657, -0.166, 0.164],
# [6.5, 1.930, -0.003, 0.003],
# [7.5, 1.929, -0.004, 0.007],
# [8.5, 1.896, -0.029, 0.115],
# [9.5, 1.908, -0.017, 0.140]]
在得到这一轮的回归树之后,将前面得到的模型与当前模型相加,且重合区域需要注意相加减,如当
用
如此得到最后结果:
当前模型拟合训练集平方损失为0.17. 假设此时误差已经满足要求,那么就可以将
3.2 gradient boosting tree
当提升树中选取的损失函数是平方损失和指数损失函数时,训练过程如上述所属,可是当一般损失函数而言,优化相对就较为麻烦了,这时候可以用freidma提出的gradient boosting算法,这是与最速下降法相似的方法,使用了损失函数的负梯度:
将其作为3.1中每一轮计算的平方损失残差值,即这一轮所谓的”训练集”
其他部分的计算不变。
参考资料:
[] 李航,统计学习方法
2017/04/07 第一次修改!