整体把握
大多数提升方法是改变训练数据的概率分布(权值分布).
- 提高前一轮被错误分类的样本的权重,降低正确分类样本的权重;对于无法接受带权样本的基学习器,可以采用“重采样”
- 加权多数表决,加大分类误差小的弱分类器权重
算法流程
输入:训练数据集D,
输出:最终分类器G(x)
- 1.初始化训练数据的权值分布:
- 2.对M = 1,2,...,m
(a)使用具有权值分布
的训练数据集学习,得到基本分类器
(b)计算在训练数据集上的分类误差率
,
,只要基学习器的分类效果比随机效果好,就能改善模型效果
(c)计算
的系数:
(d) 更新训练数据集的权值分布;
是规范化因子,让
成为一个概率分布
- 3.构建基本分类器的线性组合
,得到最终分类器
训练误差分析
AdaBoost最基本的性质是基于"加性模型",在学习过程中不断最小化指数损失函数:
由此得到, AdaBoost算法最终分类器的训练误差界为, 其中,对所有m有
这表明在此条件下AdaBoost的训练误差是以指数速率下降的. AdaBoost具有适应性,即它能适应弱分类器各自的训练误差率。这也是它的名称(适应的提升)的由来,Ada是Adaptive的简写
为什么用指数损失函数?
其可微性,可以替代损失函数作为优化目标
AdaBoost算法是模型为加法模型、损失函数为指数函数、学习算法为前向分步算法时的二类分类学习方法。
前向分步算法
加法模型:,
为基函数;
基函数参数,
基函数权重系数
在给定训练数据及损失函数L(Y,f(X))的条件下,学习加法模型f(x)成为经验风险极小化即损失函数极小化问题:
这是复杂的优化问题。前向分步算法求解这一优化问题的想法是:从前向后,每一步只学习一个基函数及其系数,逐步逼近优化目标函数式。每步只需优化如下损失函数:
输入:训练数据集D,
,损失函数
;基函数集
输出:加法模型
- 1.初始化
- 2.对m=1,2,...,M
(a) 极小化损失函数
,得到参数
(b)更新
- 3.得到加法模型
这样,前向分步算法将同时求解从m=1到M所有参数的优化问题简化为逐次求解各个
的优化问题
boosting tree
Boosting实际采用加法模型与前向分步算法。以决策树为基函数的提升方法称为提升树。对分类问题决策树是二叉分类树,对回归问题决策树是二叉回归树。提升树模型可以表示为决策树的加法模型:
其中,表示决策树;
为决策树的参数;M为树的个数
提升树算法采用前向分步算法。
- 首先确定初始提升树
- 第m歩的模型是
其中,
为当前模型,
- 通过经验风险极小化确定下一棵决策树的参数
不同问题的提升树学习算法,其主要区别在于使用的损失函数不同。包括用平方误差损失函数的回归问题,用指数损失函数的分类问题
分类问题的提升树
对于二类分类问题,提升树算法只需将AdaBoost算法中的基本分类器限制为二类分类树即可,可以说这时的提升树算法是AdaBoost算法的特殊情况
"""基于单层决策树构建AdaBoost分类器"""
import numpy as np
from sklearn.datasets import make_hastie_10_2
import matplotlib.pyplot as plt
def stumpClassify(dataMatrix, dimen, threshVal, threshIneq):
"""
通过阈值比较对数据进行分类的,阈值一边分为-1,另一边为+1。通过数组过滤实现
:param dataMatrix:
:param dimen: 特征维度的索引
:param threshVal: 阈值
:param threshIneq: < 或者 其他
:return:
"""
retArray = np.ones((np.shape(dataMatrix)[0], 1)) #[n_samples,1]
if threshIneq == 'lt':
retArray[dataMatrix[:, dimen] <= threshVal] = -1.0
else:
retArray[dataMatrix[:, dimen] > threshVal] = -1.0
return retArray
def buildStump(dataArr, classLabels, D):
"""
遍历stumpClassify函数所有的可能输入值,并找到数据集上最佳的单层决策树,这里"最佳"是基于数据的权重D来定义
:param dataArr:
:param classLabels:
:param D:
:return:
流程:
将最小误差率 minError设为无穷
For every feature in the dataset(第一层循环):
对每个步长(第二层循环):
对每个不等号(第三层循环):
建立一颗单层决策树并利用加权数据集对它进行测试
如果错误率低于minError,则将当前单层决策树设为最佳单层决策树
Return the best stump
"""
dataMatrix = np.mat(dataArr);
labelMat = np.mat(classLabels).T
m, n = np.shape(dataMatrix)
numSteps = 10.0 # 用于在特征的所有可能值上进行遍历
bestStump = {} # 存储给定权重向量D时所得到的最佳单层决策树的weights
bestClasEst = np.mat(np.zeros((m, 1)))
minError = np.inf # 初始化为无穷大,之后寻找最小错误率
for i in range(n): # loop over all dimensions
rangeMin = dataMatrix[:, i].min()
rangeMax = dataMatrix[:, i].max()
stepSize = (rangeMax - rangeMin) / numSteps
for j in range(-1, int(numSteps) + 1): # loop over all range in current dimension
for inequal in ['lt', 'gt']: # go over less than and greater than
threshVal = (rangeMin + float(j) * stepSize)
predictedVals = stumpClassify(dataMatrix, i, threshVal,
inequal) # call stump classify with i, j, lessThan
errArr = np.mat(np.ones((m, 1)))
errArr[predictedVals == labelMat] = 0
weightedError = D.T * errArr # calc total error multiplied by D
# print "split: dim %d, thresh %.2f, thresh ineqal: %s, the weighted error is %.3f" % (i, threshVal, inequal, weightedError)
if weightedError < minError:
minError = weightedError
bestClasEst = predictedVals.copy()
bestStump['dim'] = i
bestStump['thresh'] = threshVal
bestStump['ineq'] = inequal
return bestStump, minError, bestClasEst
def adaBoostTrainDS(dataArr, classLabels, numIt=40):
"""
对每次迭代:
利用buildStump()函数找到最佳的单层决策树
将最佳单层决策树加入到单层决策树数组
计算alpha
计算新的权重向量D
更新累计类别估计值
如果错误率为0。0,则退出循环
:param dataArr:
:param classLabels:
:param numIt:
:return:
"""
weakClassArr = []
m = np.shape(dataArr)[0]
D = np.mat(np.ones((m, 1)) / m) # init D to all equal
aggClassEst = np.mat(np.zeros((m, 1)))
for i in range(numIt):
bestStump, error, classEst = buildStump(dataArr, classLabels, D) # build Stump
# print "D:",D.T
alpha = float(
0.5 * np.log((1.0 - error) / max(error, 1e-16))) # calc alpha, throw in max(error,eps) to account for error=0
bestStump['alpha'] = alpha
weakClassArr.append(bestStump) # store Stump Params in Array
# print "classEst: ",classEst.T
expon = np.multiply(-1 * alpha * np.mat(classLabels).T, classEst) # exponent for D calc, getting messy
D = np.multiply(D, np.exp(expon)) # Calc New D for next iteration
D = D / D.sum()
# calc training error of all classifiers, if this is 0 quit for loop early (use break)
aggClassEst += alpha * classEst
# print "aggClassEst: ",aggClassEst.T
aggErrors = np.multiply(np.sign(aggClassEst) != np.mat(classLabels).T, np.ones((m, 1)))
errorRate = aggErrors.sum() / m
print("total error: ", errorRate)
if errorRate == 0.0: break
return weakClassArr, aggClassEst
def adaClassify(datToClass, classifierArr):
"""
利用训练得到的多个弱分类器进行分类
:param datToClass:
:param classifierArr:
:return:
"""
dataMatrix = np.mat(datToClass) # do stuff similar to last aggClassEst in adaBoostTrainDS
m = np.shape(dataMatrix)[0]
aggClassEst = np.mat(np.zeros((m, 1)))
for i in range(len(classifierArr)):
classEst = stumpClassify(dataMatrix, classifierArr[i]['dim'],
classifierArr[i]['thresh'],
classifierArr[i]['ineq']) # call stump classify
aggClassEst += classifierArr[i]['alpha'] * classEst
print(aggClassEst)
return np.sign(aggClassEst)
if __name__ == "__main__":
X, y = make_hastie_10_2(n_samples=12000, random_state=1)
X_test, y_test = X[2000:], y[2000:]
X_train, y_train = X[:2000], y[:2000]
classifierArray = adaBoostTrainDS(X_train, y_train, 20)
prediction10 = adaClassify(X_test, classifierArray[0])
errArr = np.mat(np.ones((10000, 1)))
print(errArr[prediction10!=np.mat(y_test).T].sum())
回归问题的提升树
如果将输入空间x划分为J个互不相交的区域R1,R2,…,RJ,并且在每个区域上确定输出的常量cj,那么树可表示为
参数表示树的区域划分和各区域上的常数。J是回归树的复杂度即叶结点个数.
当采用平方误差损失函数时
其损失变为
这里,是当前模型拟合数据的残差,即将残差作为下一个回归树的输出目标值。所以,对回归问题的提升树算法来说,只需简单地拟合当前模型的残差。
- 1.初始化
- 2.对m=1,2,...,M
(a) 计算残差
(b)拟合残差
学习一个回归树,得到
(c)更新
- 3.得到回归问题提升树
gradient boosting
提升树利用加法模型与前向分歩算法实现学习的优化过程。当损失函数是平方损失和指数损失函数时,每一步优化是很简单的。但对一般损失函数而言,往往每一步优化并不那么容易。如果我们的模型能够让损失函数持续的下降,则说明我们的模型在不停的改进,而最好的方式就是让损失函数在其梯度方向上下降。针对这一问题, 利用最速下降法的近似方法,其关键是利用损失函数的负梯度在当前模型的值
作为回归问题提升树算法中的残差的近似值,拟合一个回归树.即利用损失函数的负梯度在当前模型的值,作为回归问题中提升树算法的残差的近似值,拟合一个回归树
“输入:训练数据集T;损失函数
;
输出:回归树。
- 1.初始化
,估计使损失函数极小化的常数值,它是只有一个根结点的树
2.对m=1,2,…,M
(a)对i=1,2,…,N,计算
(b)对
拟合一个回归树,得到第m棵树的叶结点区域
,j=1,2,…,J
(c)对j=1,2,…,J,计算,线性搜索估计叶结点区域的值,使损失函数极小化
(d)更新
- 3.得到回归树
总结
优点:泛化错误率低;无参数调整
缺点:对离群点数据敏感