第八章 集成学习
1.谈谈集成学习的概念和思想。
集成学习(ensemble learning)通过构建并结合多个学习器来完成学习任务. 下图显示出集成学习的一般结构:先产生一组“个体学习器”(individual learner),再用某种策略将它们结合起来.
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/52289f0b4d8ab001d40b97f836ca53ab.png)
个体学习器通常由一个现有的学习算法从训练数据产生。
例如C4.5决策树算法、BP神经网络算法等,此时集成中只包含同种类型的个体学习器,例如“决策树集成”中全是决策树,“神经网络集成”中全是神经网络,这样的集成是“同质”的(homogeneous).同质集成中的个体学习器亦称“基学习器”(base learner),相应的学习算法称为“基学习算法”(base learning algorithm).
集成也可包含不同类型的个体学习器,例如同时包含决策树和神经网络,这样的集成是“异质”的(heterogenous).异质集成中的个体学习器由不同的学习算法生成,这时就不再有基学习算法;相应的,个体学习器一般不称为基学习器,常称为“组件学习器”(component learner)或直接称为个体学习器.
集成学习通过将多个学习器进行结合,常可获得比单一学习器显著优越的泛化性能.这对“弱学习器”(weak learner)尤为明显,因此集成学习的很多理论研究都是针对弱学习器进行的,而基学习器有时也被直接称为弱学习器.但需注意的是,虽然从理论上来说使用弱学习器集成足以获得好的性能,但在实践中出于种种考虑,例如希望使用较少的个体学习器,或是重用关于常见学习器的一些经验等,人们往往会使用比较强的学习器。
弱学习器常指泛化性能略优于随机猜测的学习器;例如在二分类问题上精度略高于50%的分类器.
2.集成学习方法可以分为哪几类,并且分别阐述它们的特点。
- 并行化方法
个体学习器间不存在强依赖关系、可同时生成的并行化方法
Bagging与随机森林是并行化方法的代表。
Bagging这个名字是由Bootstrap AGGregatlNG缩写而来.
Bagging是并行式集成学习方法最著名的代表. 从名字即可看出,它直接基于自助采样法(bootstrap sampling).
自助采样法:
i) 给定包含m个样本的数据集,我们先随机取出一个样本放入采样集中
ii) 再把该样本放回初始数据集,使得下次采样时该样本仍有可能被选中
iii) 这样,经过m次随机采样操作,我们得到含m个样本的采样集,初始训练集中有的样本在采样集里多次出现,有的则从未出现.由下式可知,初始训练集中约有63.2%的样本出现在采样集中.
![]()
1) Bagging的基本流程
i) 可采样出T个含m个训练样本的采样集
ii) 然后基于每个采样集训练出一个基学习器
iii) 再将这些基学习器进行结合.
在对预测输出进行结合时,Bagging通常对分类任务使用简单投票法,对回归任务使用简单平均法.
若分类预测时出现两个类收到同样票数的情形,则最简单的做法是随机选择一个,也可进一步考察学习器投票的置信度来确定最终胜者.
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/4a55f7ecfd469236ab8192d8ea220d12.png)
假定基学习器的计算复杂度为O(m),则Bagging的复杂度大致为T(O(m)+O(s)),考虑到采样与投票/平均过程的复杂度O(s)很小,而T通常是一个不太大的常数,因此,训练一个Bagging集成与直接使用基学习算法训练一个学习器的复杂度同阶.
这说明Bagging是一个很高效的集成学习算法. 另外,与标准AdaBoost只适用于二分类任务不同,Bagging能不经修改地用于多分类、回归等任务.
自助采样过程还给Bagging带来了另一个优点:由于每个基学习器只使用了初始训练集中约63.2%的样本,剩下约36.8%的样本可用作验证集来对泛化性能进行“包外估计”. 事实上,包外样本还有许多其他用途. 例如当基学习器是决策树时,可使用包外样本来辅助剪枝,或用于估计决策树中各结点的后验概率以辅助对零训练样本结点的处理;当基学习器是神经网络时,可使用包外样本来辅助早期停止以减小过拟合风险.
Bagging主要关注降低方差.
2) 随机森林(Random Forest,简称RF)
随机森林是Bagging的一个扩展变体. RF在以决策树为基学习器构建Bagging集成的基础上,进一步在决策树的训练过程中引入了随机属性选择.
传统决策树在选择划分属性时是在当前结点的属性集合(假定有d个属性)中选择一个最优属性;
而在随机森林中,对基决策树的每个结点,先从该结点的属性集合中随机选择一个包含k个属性的子集,然后再从这个子集中选择一个最优属性用于划分. 这里的参数k控制了随机性的引入程度:若令k=d,则基决策树的构建与传统决策树相同;若令k=1,则是随机选择一个属性用于划分;一般情况下,推荐值 k = l o g 2 d k=log_2 d k=log2d.
随机森林简单、容易实现、计算开销小,令人惊奇的是,它在很多现实任务中展现出强大的性能,被誉为“代表集成学习技术水平的方法”.
随机森林对Bagging只做了小改动,但是与Bagging中基学习器的“多样性”仅通过样本扰动(通过对初始训练集采样)而来不同,随机森林中基学习器的多样性不仅来自样本扰动,还来自属性扰动,这就使得最终集成的泛化性能可通过个体学习器之间差异度的增加而进一步提升.
3) 随机森林与Bagging异同
随机森林的收敛性与Bagging相似. 如下图所示,随机森林的起始性能往往相对较差,特别是在集成中只包含一个基学习器时.这很容易理解,因为通过引入属性扰动,随机森林中个体学习器的性能往往有所降低.
然而,随着个体学习器数目的增加,随机森林通常会收敛到更低的泛化误差.
随机森林的训练效率常优于Bagging,因为在个体决策树的构建过程中,Bagging使用的是“确定型”决策树,在选择划分属性时要对结点的所有属性进行考察,而随机森林使用的“随机型”决策树则只需考察一个属性子集.
- 序列化方法
个体学习器间存在强依赖关系、必须串行生成的序列化方法.
Boosting是序列化方法的代表,Boosting是一族可将弱学习器提升为强学习器的算法.
Boosting的工作机制:
i) 先从初始训练集训练出一个基学习器
ii) 再根据基学习器的表现对训练样本分布进行调整,使得先前基学习器做错的训练样本在后续受到更多关注
iii) 然后基于调整后的样本分布来训练下一个基学习器
iiii) 如此重复进行,直至基学习器数目达到事先指定的值T,最终将这T个基学习器进行加权结合.
Boosting算法要求基学习器能对特定的数据分布进行学习,这可通过“重赋权法”(re-weighting)实施,对无法接受带权样本的基学习算法,则可通过“重采样法”(re-sampling)来处理
“重赋权法”(re-weighting)即在训练过程的每一轮中,根据样本分布为每个训练样本重新赋予一个权重.
“重采样法”(re-sampling)即在每一轮学习中,根据样本分布对训练集重新进行采样,再用重采样而得的样本集对基学习器进行训练.
一般而言这两种做法没有显著的优劣差别. 需注意的是,Boosting算法在训练的每一轮都要检查当前生成的基学习器是否满足基本条件(例如上图的第5行,检查当前基分类器是否是比随机猜测好),一旦条件不满足,则当前基学习器即被抛弃,且学习过程停止.
在此种情形下,初始设置的学习轮数T也许还远未达到,可能导致最终集成中只包含很少的基学习器而性能不佳.若采用“重采样法”,则可获得“重启动”机会以避免训练过程过早停止,即在抛弃不满足条件的当前基学习器之后,可根据当前分布重新对训练样本进行采样,再基于新的采样结果重新训练出基学习器,从而使得学习过程可以持续到预设的T轮完成.
从偏差-方差分解的角度看,Boosting 主要关注降低偏差,因此Boosting能基于泛化性能相当弱的学习器构建出很强的集成.
以决策树桩为基学习器,在表4.5的西瓜数据集3.0上运行AdaBoost算法,不同规模(size)的集成及其基学习器所对应的分类边界如下图所示.
3.在集成学习中,阐述针对二分类问题的AdaBoost算法实现过程。思考AdaBoost算法在每一轮如何改变训练数据的权值或概率分布?
AdaBoost算法步骤如下:
1)以相同的初始值来初始化样本的权重ω,并且样本权重之和为1。
2)在m轮boosting操作中,对第j轮做如下操作
3)训练一个加权的弱学习机:
C
(
j
)
=
t
r
a
i
n
(
X
,
y
,
ω
)
C(j)=train(X,y,\omega )
C(j)=train(X,y,ω)
4)预测样本类标
p
r
e
d
_
y
=
p
r
e
d
i
c
t
(
C
(
j
)
,
X
)
pred\_y=predict(C(j),X)
pred_y=predict(C(j),X)
5)计算权重错误率
ε
=
w
∗
(
p
r
e
d
_
y
=
=
y
)
\varepsilon =w*(pred\_y==y)
ε=w∗(pred_y==y)
6)计算相关系数
0.5
∗
l
o
g
1
−
ε
ε
=
α
j
0.5*log\frac{1-\varepsilon }{\varepsilon }=\alpha _{j}
0.5∗logε1−ε=αj
7)更新权重
ω
∗
e
(
−
α
j
∗
y
^
∗
y
)
\omega *e^{(-\alpha _{j}*\widehat{y}*y)}
ω∗e(−αj∗y
∗y)
y
^
\widehat{y}
y
表示预测值,
y
y
y表示真实值
8)归一化权重,并保证权重之和为1
ω
=
ω
∑
i
=
1
n
ω
i
\omega =\frac{\omega }{\sum ^n_{i=1}\omega _{i}}
ω=∑i=1nωiω
9)完成最终预测
y
^
=
(
∑
j
=
1
m
α
j
×
p
r
i
d
i
c
t
(
C
j
,
X
)
>
0
)
\widehat{y}=(\sum ^m_{j=1}\alpha _{j}\times pridict(C_{j},X)>0)
y
=(∑j=1mαj×pridict(Cj,X)>0)
例子
样本编号 | x | y(实际类别) | 权重值 | y ^ \widehat{y} y (预测类别) | 预测是否正确 | 更新后的权重 |
---|---|---|---|---|---|---|
1 | 1.0 | 1 | 0.1 | 1 | 是 | 0.072 |
2 | 2.0 | 1 | 0.1 | 1 | 是 | 0.072 |
3 | 3.0 | 1 | 0.1 | 1 | 是 | 0.072 |
4 | 4.0 | -1 | 0.1 | -1 | 是 | 0.072 |
5 | 5.0 | -1 | 0.1 | -1 | 是 | 0.072 |
6 | 6.0 | -1 | 0.1 | -1 | 是 | 0.072 |
7 | 7.0 | 1 | 0.1 | -1 | 否 | 0.167 |
8 | 8.0 | 1 | 0.1 | -1 | 否 | 0.167 |
9 | 9.0 | 1 | 0.1 | -1 | 否 | 0.167 |
10 | 10.0 | -1 | 0.1 | -1 | 否 | 0.072 |
上表表示的是10个一维样本的数据。第一列表示样本的编号,第二列表示样本特征的值,第三列表示样本所对应的实际类标,第四列表示的是样本的权重值,第五列表示的是预测的样本,第六列表示预测的类标是否准确,第七列表示的是样本权重更新后的值。
根据上面AdaBoost的步骤,详细介绍权重的更新过程. 从第5步开始:
5)、计算权重的错误率,预测类标与实际类标相等用0表示,否则用1表示,所以预测错误的样本有三个
ε = ω ∗ ( y ^ = = y ) = 0.1 ∗ ∑ i = 1 10 ( y ^ i = = y i ) = 0.1 ∗ 3 = 0.3 \varepsilon =\omega *(\widehat{y}==y)=0.1*\sum ^{10}_{i=1}(\widehat{y}_{i}==y_{i})=0.1*3=0.3 ε=ω∗(y ==y)=0.1∗∑i=110(y i==yi)=0.1∗3=0.3
6)、计算相关系数
α j = 0.5 ∗ l o g 1 − ε ε = 0.5 ∗ l o g 1 − 0.3 0.3 = 0.424 \alpha _{j}=0.5*log\frac{1-\varepsilon }{\varepsilon }=0.5*log\frac{1-0.3}{0.3}=0.424 αj=0.5∗logε1−ε=0.5∗log0.31−0.3=0.424
7)、更新权重
ω ∗ e ( − α j ∗ y ^ ∗ y ) = ω \omega *e^{(-\alpha _{j}*\widehat{y}*y)} = \omega ω∗e(−αj∗y ∗y)=ω y ^ \widehat{y} y 表示预测值, y y y表示真实值
其中 ω 、 y ^ 、 y \omega、\widehat{y}、y ω、y 、y都表示一个所有样本的(十维)向量
单个样本预测错误则更新后的权值为:
ω = 0.1 ∗ e ( − 0.424 ∗ ( − 1 ) ∗ 1 = 0.153 \omega=0.1*e^{(-0.424*(-1)*1}=0.153 ω=0.1∗e(−0.424∗(−1)∗1=0.153
单个样本预测正确则更新后的权值为:
ω = 0.1 ∗ e ( − 0.424 ∗ 1 ∗ 1 = 0.066 \omega=0.1*e^{(-0.424*1*1}=0.066 ω=0.1∗e(−0.424∗1∗1=0.066
8)、权重归一化
∑ i = 1 10 ω i = 7 ∗ 0.065 + 3 ∗ 0.153 = 0.914 \sum ^{10}_{i=1}\omega_i=7*0.065+3*0.153=0.914 ∑i=110ωi=7∗0.065+3∗0.153=0.914
预测正确的权重,归一化的权重为
0.066 0.914 = 0.072 \frac{0.066}{0.914}=0.072 0.9140.066=0.072
预测错误的权重,归一化后的权重为
0.153 0.914 = 0.167 \frac{0.153}{0.914}=0.167 0.9140.153=0.167
通过一轮权重的更新可以发现,之前预测正确的权重由0.1变成了0.072,预测错误的权重由0.1变成了0.167。所以说,在权重更新的过程中,会增大预测错误样本的权重,同时也会减少预测正确样本的权重。
4.随机森林与集成学习之间有什么样的关系?
随机森林是一种集成学习方法。随机森林与bagging的异同如上文问题二所示。
随机森林在以决策树为基学习器,构建Bagging的基础上,进一步在决策树的训练过程中引入了随机属性选择。Bagging是集成学习框架下的一个很大的门类,所以随机森林从结构上来讲是属于集成学习的。随机森林就是通过集成学习的思想将多棵树集成的一种算法,它的基本单元是决策树,而它的本质属于集成学习方法。
5. 用python实现基于单层决策树的AdaBoost算法。
我们使用单层决策树构建一个分类器处理如下的分类问题:
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/0b50b0f6ed7833430a8aa2bd380be331.png)
决策树算法主要有ID3,C4.5和CART,其中ID3和C4.5主要用于分类,CART可以解决回归问题。对于单层决策树是无法求解上面这样的问题的。
#coding:UTF-8
from numpy import *
def loadSimpleData():
datMat = mat([[1., 2.1],
[2., 1.1],
[1.3, 1.],
[1., 1.],
[2., 1.]])
classLabels = mat([1.0, 1.0, -1.0, -1.0, 1.0])
return datMat, classLabels
def singleStumpClassipy(dataMat, dim, threshold, thresholdIneq):
classMat = ones((shape(dataMat)[0], 1))
#根据thresholdIneq划分出不同的类,在'-1'和'1'之间切换
if thresholdIneq == 'left':#在threshold左侧的为'-1'
classMat[dataMat[:, dim] <= threshold] = -1.0
else:
classMat[dataMat[:, dim] > threshold] = -1.0
return classMat
def singleStump(dataArr, classLabels, D):
dataMat = mat(dataArr)
labelMat = mat(classLabels).T
m, n = shape(dataMat)
numSteps = 10.0
bestStump = {}
bestClasEst = zeros((m, 1))
minError = inf
for i in xrange(n):#对每一个特征
#取第i列特征的最小值和最大值,以确定步长
rangeMin = dataMat[:, i].min()
rangeMax = dataMat[:, i].max()
stepSize = (rangeMax - rangeMin) / numSteps
for j in xrange(-1, int(numSteps) + 1):
#不确定是哪个属于类'-1',哪个属于类'1',分两种情况
for inequal in ['left', 'right']:
threshold = rangeMin + j * stepSize#得到每个划分的阈值
predictionClass = singleStumpClassipy(dataMat, i, threshold, inequal)
errorMat = ones((m, 1))
errorMat[predictionClass == labelMat] = 0
weightedError = D.T * errorMat#D是每个样本的权重
if weightedError < minError:
minError = weightedError
bestClasEst = predictionClass.copy()
bestStump['dim'] = i
bestStump['threshold'] = threshold
bestStump['inequal'] = inequal
return bestStump, minError, bestClasEst
def adaBoostTrain(dataArr, classLabels, G):
weakClassArr = []
m = shape(dataArr)[0]#样本个数
#初始化D,即每个样本的权重
D = mat(ones((m, 1)) / m)
aggClasEst = mat(zeros((m, 1)))
for i in xrange(G):#G表示的是迭代次数
bestStump, minError, bestClasEst = singleStump(dataArr, classLabels, D)
print 'D:', D.T
#计算分类器的权重
alpha = float(0.5 * log((1.0 - minError) / max(minError, 1e-16)))
bestStump['alpha'] = alpha
weakClassArr.append(bestStump)
print 'bestClasEst:', bestClasEst.T
#重新计算每个样本的权重D
expon = multiply(-1 * alpha * mat(classLabels).T, bestClasEst)
D = multiply(D, exp(expon))
D = D / D.sum()
aggClasEst += alpha * bestClasEst
print 'aggClasEst:', aggClasEst
aggErrors = multiply(sign(aggClasEst) != mat(classLabels).T, ones((m, 1)))
errorRate = aggErrors.sum() / m
print 'total error:', errorRate
if errorRate == 0.0:
break
return weakClassArr
def adaBoostClassify(testData, weakClassify):
dataMat = mat(testData)
m = shape(dataMat)[0]
aggClassEst = mat(zeros((m, 1)))
for i in xrange(len(weakClassify)):#weakClassify是一个列表
classEst = singleStumpClassipy(dataMat, weakClassify[i]['dim'], weakClassify[i]['threshold'], weakClassify[i]['inequal'])
aggClassEst += weakClassify[i]['alpha'] * classEst
print aggClassEst
return sign(aggClassEst)
if __name__ == '__main__':
datMat, classLabels = loadSimpleData()
weakClassArr = adaBoostTrain(datMat, classLabels, 30)
print "weakClassArr:", weakClassArr
#test
result = adaBoostClassify([1, 1], weakClassArr)
print result
reference:
https://blog.csdn.net/google19890102/article/details/46376603