AdaBoost算法实例详解
AdaBoost算法其实很精炼,算法流程也好理解,但是看了算法的解释版本之后,什么前向分布算法,什么指数损失函数之后有点迷糊了。抛开这些理论性的推导不谈(其实是因为能力有限),通过例子直观的了解AdaBoost算法的计算过程。简要叙述一下AdaBoost算法的主要过程:AdaBoost为每个数据样本分配权重,权重符合概率分布,初始权重符合均匀分布,串行训练M个模型,依据每轮训练的模型的错误率(被误
提升树Boosting Tree算法实例详解_程大海的博客-CSDN博客
从提升树Boosting Tree过度到梯度提升Gradient Boosting_程大海的博客-CSDN博客
GBDT梯度提升之回归算法个人理解_程大海的博客-CSDN博客_梯度回归算法
GBDT梯度提升之二分类算法个人理解_程大海的博客-CSDN博客_gbdt二分类
GBDT梯度提升之多分类算法个人理解_程大海的博客-CSDN博客_gbdt可以多分类吗
XGBoost算法个人理解_程大海的博客-CSDN博客_xgboost 叶子节点权重
交叉熵损失与极大似然估计_程大海的博客-CSDN博客_极大似然估计和交叉熵
使用泰勒展开解释梯度下降方法参数更新过程_程大海的博客-CSDN博客
AdaBoost算法实例详解_程大海的博客-CSDN博客_adaboost算法实例
AdaBoost算法其实很精炼,算法流程也好理解,但是看了算法的解释版本之后,什么前向分布算法,什么指数损失函数之后有点迷糊了。抛开这些理论性的推导不谈(其实是因为能力有限),通过例子直观的了解AdaBoost算法的计算过程。
简要叙述一下AdaBoost算法的主要过程:
AdaBoost为每个数据样本分配权重,权重符合概率分布,初始权重符合均匀分布,串行训练M个模型,依据每轮训练的模型的错误率(被误分类样本的权重之和)确定当前模型在最终模型中的权重,以及更新训练样本的权重,误分类样本权重升高,分类正确的样本权重降低。
下图的算法流程来自于《统计学习方法》。
下面通过具体的实例来理解AdaBoost算法的流程,例子来自于《统计学习方法》。
第一轮迭代:
此时得到的组合模型中只有一个 ,此时 的分类结果就是最终模型的分类结果。第一轮迭代中6,7,8(6,7,8指的是x的值,不是指的序号)被误分类。此时得到的组合模型在训练数样本上的预测结果如下:
X | y | 分类结果 | |||
0 | 1 | 0.4236 | 0.4236 | 1 | 正确 |
1 | 1 | 0.4236 | 0.4236 | 1 | 正确 |
2 | 1 | 0.4236 | 0.4236 | 1 | 正确 |
3 | -1 | -0.4236 | -0.4236 | -1 | 正确 |
4 | -1 | -0.4236 | -0.4236 | -1 | 正确 |
5 | -1 | -0.4236 | -0.4236 | -1 | 正确 |
6 | 1 | -0.4236 | -0.4236 | -1 | 错误 |
7 | 1 | -0.4236 | -0.4236 | -1 | 错误 |
8 | 1 | -0.4236 | -0.4236 | -1 | 错误 |
9 | -1 | -0.4236 | -0.4236 | -1 | 正确 |
其中sign符号函数如下:
第二轮迭代:
第二轮迭代中3,4,5被误分类,此时得到的最终模型是前两轮模型的线性组合。那么在当前的组合条件下 的分类结果是怎样的?
X | y | 分类结果 | ||||
0 | 1 | 0.4236 | 0.6496 | 1.0732 | 1 | 正确 |
1 | 1 | 0.4236 | 0.6496 | 1.0732 | 1 | 正确 |
2 | 1 | 0.4236 | 0.6496 | 1.0732 | 1 | 正确 |
3 | -1 | -0.4236 | 0.6496 | 0.226 | 1 | 错误 |
4 | -1 | -0.4236 | 0.6496 | 0.226 | 1 | 错误 |
5 | -1 | -0.4236 | 0.6496 | 0.226 | 1 | 错误 |
6 | 1 | -0.4236 | 0.6496 | 0.226 | 1 | 正确 |
7 | 1 | -0.4236 | 0.6496 | 0.226 | 1 | 正确 |
8 | 1 | -0.4236 | 0.6496 | 0.226 | 1 | 正确 |
9 | -1 | -0.4236 | -0.6496 | -1.0732 | -1 | 正确 |
第三轮迭代:
第三轮迭代中0,1,2,9被误分类,此时得到的最终模型是前三轮模型的线性组合。那么在当前的组合条件下 的分类结果是怎样的?
X | y | 分类结果 | |||||
0 | 1 | 0.4236 | 0.6496 | -0.7514 | 0.3218 | 1 | 正确 |
1 | 1 | 0.4236 | 0.6496 | -0.7514 | 0.3218 | 1 | 正确 |
2 | 1 | 0.4236 | 0.6496 | -0.7514 | 0.3218 | 1 | 正确 |
3 | -1 | -0.4236 | 0.6496 | -0.7514 | -0.5254 | -1 | 正确 |
4 | -1 | -0.4236 | 0.6496 | -0.7514 | -0.5254 | -1 | 正确 |
5 | -1 | -0.4236 | 0.6496 | -0.7514 | -0.5254 | -1 | 正确 |
6 | 1 | -0.4236 | 0.6496 | 0.7514 | 0.9774 | 1 | 正确 |
7 | 1 | -0.4236 | 0.6496 | 0.7514 | 0.9774 | 1 | 正确 |
8 | 1 | -0.4236 | 0.6496 | 0.7514 | 0.9774 | 1 | 正确 |
9 | -1 | -0.4236 | -0.6496 | 0.7514 | -0.3218 | -1 | 正确 |
经过三轮迭代之后,在训练集上的错误率为0。
AdaBoost是如何侧重于误分类的样本的?
经过上面的算法流程介绍,下面我们就来通过一个简图看一下AdaBoost是如何通过调整训练样本的权重来是模型逐步关注分类错误的样本的。
1、初始化权重。首先在第1轮的时候,初始化一个权重分布𝐷1,然后后续计算错误率𝜀1就是在权重分布𝐷1上计算的。
2、训练弱分类器𝑓1(𝑥)。在训练集上训练一个弱分类器𝑓1(𝑥),使得错误率𝜀1最小。
3、怎么计算错误率𝜀1?错误率是计算弱分类器𝑓1(𝑥)分类错误的样本对应的权重分布𝐷1的和。
所以我们可以看到,权重分布D用来参与计算模型的错误率,所以说,如果一个样本在上一轮被分类错了,它的权重升高了,如果在这一轮还把它分类错,那么在错误率上带来的代价是很大的,所以在这一轮就要重点关注那些权重大的样本,只有把它们分类正确了,才能争取把这一轮的错误率降到最低。
python实现的AdaBoost算法如下,代码来源于《机器学习实战》。
# coding:utf-8
import numpy as np
def loadSimpData():
dataMat = [
[1.0, 2.1],
[2.0, 1.1],
[1.3, 1.0],
[1.0, 1.0],
[2.0, 1.0]
]
classLabels = [1.0, 1.0, -1.0, -1.0, 1.0]
return dataMat, classLabels
def stumpClassify(dataMatrix, dimen, threshval, threshIneq):
"""
单层决策树分类决策函数,根据指定特征,指定特征的划分阈值,指定特征的划分条件(大于或者小于)构建决策树
:param dataMatrix: 数据矩阵
:param dimen: 分支特征索引编号,按照每个特征进行节点分支
:param threshval: 分支阈值
:param threshIneq: 分支类别(大于或者小于)
:return: 返回分类结果
"""
retArray = np.ones(shape=(dataMatrix.shape[0], 1)) # 初始化所有样本的分类结果为+1
if threshIneq == 'lt':
retArray[dataMatrix[:, dimen] <= threshval] = -1.0 # 将第dimen个特征小于threshval的样本标记为-1
else:
retArray[dataMatrix[:, dimen] > threshval] = -1.0 # 将第dimen个特征大于threshval的样本标记为+1
return retArray
def buildStump(dataArr, classLabels, D):
"""
遍历决策树的所有特征,所有特征的划分阈值,所有特征的划分条件,寻找最佳单层决策树桩(只有一层的决策树)
:param dataArr: 数据样本
:param classLabels: 数据样本标签
:param D: 数据样本权重
:return: 最佳单层决策树分类信息、最低错误率、最优分类结果
"""
dataMatrix = np.mat(dataArr)
labelMat = np.mat(classLabels).T
m, n = dataMatrix.shape
numSteps = 10.0 # 为了寻找最优的划分点,总共尝试的次数
bestStump = {}
bestClasEst = np.mat(np.zeros(shape=(m, 1)))
minError = np.inf
for i in range(n): # 遍历所有的特征
rangeMin = dataMatrix[:, i].min() # 计算所有样本特征的最小值
rangeMax = dataMatrix[:, i].max() # 计算所有样本特征的最大值
stepSize = (rangeMax - rangeMin) / numSteps # 计算寻找最优划分每次移动的步长,以固定步长线性搜索最佳分支阈值(仅适用于数值型特征)
for j in range(-1, int(numSteps) + 1): # 遍历当前特征下的所有分支阈值
for inequal in ['lt', 'gt']: # 遍历所有可能的分支条件,大于或者小于
threshVal = (rangeMin + float(j) * stepSize) # 计算决策树的分支阈值,当j=-1或j=numSteps + 1时,是单分支决策树
predictedVals = stumpClassify(dataMatrix, i, threshVal, inequal) # 计算在当前分支阈值条件下,决策树的分类结果
errArr = np.mat(np.ones(shape=(m, 1))) # errArr矩阵用于保存决策树的预测结果
errArr[predictedVals == labelMat] = 0 # 将errArr矩阵中被当前决策树分类正确的样本对应位置的值置为0
weightedError = D.T * errArr # 计算分类错误率(按位相乘并求和),错误率=所有分类错误样本的权重求和
# print("split: dimension %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 adaBoostingTrainDS(dataArr, classLabels, numIter=40):
"""
训练AdaBoost集成模型
:param dataArr: 输入样本数据
:param classLabels: 样本数据标签
:param numIter: 训练迭代次数
:return: 每轮迭代的最佳弱决策树信息(特征索引编号、分类阈值、分类条件、分类器权重alpha)
"""
dataArr = np.mat(dataArr)
weakClassArr = [] # 用于记录各个弱分类器的信息
m = dataArr.shape[0] # 训练样本数量
D = np.mat(np.ones(shape=(m, 1)) / m) # 向量D用来保存样本权重,初始权重相等
aggClassEst = np.mat(np.zeros(shape=(m, 1))) # 最终得到的分类函数
for i in range(numIter):
bestStump, error, classEst = buildStump(dataArr, classLabels, D) # 最佳决策树信息(特征编号,阈值,分支条件)、错误率、分类结果
# print("D: ", D.T)
alpha = float(0.5 * np.log((1 - error) / max(error, 1e-16))) # 基于当前弱分类器的分类错误率计算该分类器的最终决策权重
bestStump['alpha'] = alpha
weakClassArr.append(bestStump)
# print("classEst: ", classEst.T)
# 对应统计学习方法中公式8.3、8.4、8.5
expon = np.multiply(-1 * alpha * np.mat(classLabels).T, classEst) # 更新样本权重,分类错误的样本权重增加,分类正确的样本权重减少
D = np.multiply(D, np.exp(expon))
D = D / D.sum() # 将D归一化为一个概率分布,D中元素的和为1
aggClassEst += alpha * classEst # 根据权重整合弱分类器,就是将每个样本对应弱分类器的分类结果乘以对应的alpha然后求和,然后使用sign进行符号化
# print("aggClassEst: ", aggClassEst.T)
# print("np.sign -> ", np.sign(aggClassEst) != np.mat(classLabels).T)
aggErrors = np.multiply(np.sign(aggClassEst) != np.mat(classLabels).T, np.ones(shape=(m, 1))) # 统计集成后的模型预测错误的样本数量
errorRate = aggErrors.sum() / m # 计算错误率
print("total error: ", errorRate)
if errorRate == 0.0: # 直到aggClassEst对全部样本预测正确为止
break
return weakClassArr, aggClassEst
def adaBoostingClassify(dataToClassify, classifierArr):
"""
使用训练好的AdaBoost集成模型进行预测
:param dataToClassify: 数据样本
:param classifierArr: 训练得到的AdaBoost弱模型以及模型对应的权重alpha
:return: 返回数据集的分类结果
"""
dataMatrix = np.mat(dataToClassify) # 待预测的输入数据
m = dataMatrix.shape[0]
aggClassEst = np.mat(np.zeros(shape=(m, 1))) # 最终分类器的分类结果
for i in range(len(classifierArr)): # 遍历所有的弱分类器
classEst = stumpClassify(dataMatrix, classifierArr[i]["dim"], classifierArr[i]["thresh"],
classifierArr[i]["ineq"]) # 获取每个弱分类器的预测结果
aggClassEst += classifierArr[i]["alpha"] * classEst # 线性累加所有弱分类器的结果
# print("aggClassEst: ", aggClassEst)
return np.sign(aggClassEst) # 输出最终分类结果,累加结果符号化
if __name__ == '__main__':
dataMat, labelMat = loadSimpData()
D = np.mat(np.ones(shape=(5, 1)) / 5)
# print(D)
# 计算每轮迭代的最佳决策树分类方法
print("#" * 80)
bestStump, minError, bestClasEst = buildStump(dataMat, labelMat, D)
print(bestStump)
print(minError)
print(bestClasEst)
# 训练得到的一些列弱分类器信息
print("#" * 80)
classifyArr, aggClassEst = adaBoostingTrainDS(dataMat, labelMat, numIter=9)
print(classifyArr)
print(aggClassEst)
# 测试adaBoosting算法
print("#" * 80)
testSet = np.array(
[
[0.5, 1.0],
[1.5, 2.0],
[0.5, 1.1],
[0.0, 1.0],
[1.0, 2.0],
[0.0, 0.0]
]
)
classifierResult = adaBoostingClassify(testSet, classifyArr)
print(classifierResult)
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)