Boosting之Adaboost简单实现

网友投稿 650 2022-09-02

Boosting之Adaboost简单实现

Boosting之Adaboost简单实现

《机器学习实战》第七章,Adaboost的实现。有两个问题: 1. 基分类器是用的是单决策树桩,选择最好的分类器是遍历所有可能,挑选加权误差最小的那个单决策树桩。实际应用应该选择普通CART树做基分类器,那么权重对基分类器的影响总不能遍历所有可能存在的决策树情况。这里的逻辑需要再确认 2. 如果基分类器的加权误差大于0.5,这里比较方式有大于和小于,所以不存在小于0.5的情况,如果存在小于0.5的,那么改变预测结果,就可以提高预测进度了

真正应用级的源码似乎找不到,这些先存疑,以后有机会查查资料。 最后会调用封装好的函数,求解李航《统计学习方法》中Adaboost的习题。

生成测试数据函数

from numpy import *def loadSimpData(): datMat = matrix([[ 1. , 2.1], [ 2. , 1.1], [ 1.3, 1. ], [ 1. , 1. ], [ 2. , 1. ]]) classLabels = [1.0, 1.0, -1.0, -1.0, 1.0] return

导入外部数据函数

## 传入的参数:带有路径信息的文件名## 解析Tab分隔的文本数据 ## 返回特征列表,label列表,注意特征列表的子元素是某一个样本的所有特征,其本身为一个列表。## 后期会把这些列表转成matrixdef loadDataSet(fileName): #general function to parse tab -delimited floats ## 读取第一行数据,输出该行有几个数据 numFeat = len(open(fileName).readline().split('\t')) #get number of fields ## 定义两个列表 dataMat = []; labelMat = [] fr = open(fileName) ## 对每一行执行同样操作: #### 1.按照Tab拆分数据,前numFeat-1个数据储存在dataMat中,最后一个数据储存在labelMat中 #### 2.所有行数据处理完之后,输出两个矩阵 for line in fr.readlines(): lineArr =[] curLine = line.strip().split('\t') for i in range(numFeat-1): lineArr.append(float(curLine[i])) dataMat.append(lineArr) labelMat.append(float(curLine[-1])) return

基分类器—决策树

阈值划分函数

## 参数:dataMatrix-特征矩阵;dimen-特征位置;threshVal-阈值;threshIneq-lt<=,否则>## 要求特征为矩阵格式,特征维度、阈值、比较符在后面加权损失函数最小化中会通通遍历## 脚本逻辑:根据输入的相关参数,输出样本的预测labeldef stumpClassify(dataMatrix,dimen,threshVal,threshIneq):#just classify the data ## 生成5行1列的矩阵,矩阵元素都是1 ## 也可以理解为原始样本都标记为1 retArray = ones((shape(dataMatrix)[0],1)) ## 比较的逻辑 #### 如果比较符号是<=,那么小于阈值的样本被判为-1 if threshIneq == 'lt': retArray[dataMatrix[:,dimen] <= threshVal] = -1.0 else: retArray[dataMatrix[:,dimen] > threshVal] = -1.0 return

最佳单层决策树生成函数

## 参数:特征矩阵,label矩阵,样本权重-矩阵## 输出:bestStump,minError,bestClasEst## 脚本逻辑#### 按照特征、阈值、比较方式(大于小于)遍历#### 最底层循环把特征维度、阈值、比较方式传入stumpClassify函数,给到预测结果#### 根据预测结果,计算加权误差,如果比minError小替换#### 最后输出最好的单层分类器def buildStump(dataArr,classLabels,D): ## 转换成矩阵格式,classLabels同时做了转置 dataMatrix = mat(dataArr); labelMat = mat(classLabels).T ## m,n分别是行数列数 m,n = shape(dataMatrix) ## numSteps浮点型;阈值变动次数,后面通过极差/numSteps确定步长 ## bestStump字典,储存单层决策树的结构信息:特征维度,阈值,比较符号 ## bestClasEst矩阵,最好的预测结果 numSteps = 10.0; bestStump = {}; bestClasEst = mat(zeros((m,1))) ## 最小误差,后面进行比较,保留最好的分类器 minError = inf #init error sum, to +infinity 正无穷,后期进行比较 ## 第一层循环遍历所有特征 for i in range(n):#loop over all dimensions ## 计算该特征的最小值最大值 rangeMin = dataMatrix[:,i].min(); rangeMax = dataMatrix[:,i].max(); ## 步长为10等分极值的大小 stepSize = (rangeMax-rangeMin)/numSteps ## 第二层循环,按照stepSize大小,依次增加,循环10次 for j in range(-1,int(numSteps)+1):#loop over all range in current dimension ## 第三层循环,按照less than和greater than分别计算 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 ## error矩阵,先定义为m行1列,元素均为1的矩阵 errArr = mat(ones((m,1))) ## 预测结果和真实label一致,则为0 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) ## 第一次判断总能替换掉,后面相当于只保留error最小的信息 if weightedError < minError: ## 如果本次循环的error更小 ## minError重新设置, minError = weightedError ## copy的用法很关键,直接赋值,predictedVals改变,bestClasEst也改变,小心 bestClasEst = predictedVals.copy()## bestClasEst重新复制,为什么用copy呢? ## bestStump为字典,把决策树的信息储存在字典里 bestStump['dim'] = i bestStump['thresh'] = threshVal bestStump['ineq'] = inequal return

Adaboost训练过程

## 输入参数:特征矩阵,标签矩阵,迭代次数## 输出:每一轮迭代的树结构、最后的预测结果,即投票之后的结果,没有sign## 脚本逻辑#### 循环numIt次,即最多有numIt个基分类器#### 每一循环buildStump返回最好的分类器,计算分类器权重,更新样本权重#### 判断分类器的加权误差是否为0,为0则跳出循环def adaBoostTrainDS(dataArr,classLabels,numIt=40): weakClassArr = [] ## 特征矩阵行数,即样本个数 m = shape(dataArr)[0] ## 样本权重,初始值为1/m D = mat(ones((m,1))/m) #init D to all equal ## m行1列元素均为0的矩阵 aggClassEst = mat(zeros((m,1))) ## 迭代过程 for i in range(numIt): ## 返回基分类器的结果 #### 决策树结构、加权误差、预测结果 bestStump,error,classEst = buildStump(dataArr,classLabels,D)#build Stump #print "D:",D.T ## 下面进行分类器权重和样本权重的计算 #### 分类器权重计算,一个trick,error为0的时候计算会出问题,用1e-16 alpha = float(0.5*log((1.0-error)/max(error,1e-16)))#calc alpha, throw in max(error,eps) to account for error=0 #### 更新alpha bestStump['alpha'] = alpha #### 储存分类器权重,放在列表中 weakClassArr.append(bestStump) #store Stump Params in Array #print "classEst: ",classEst.T ## 计算每个样本的权重 ## 可以理解为分成三个步骤 #### expon指数里面部分,矩阵乘法multiply,是对应位置的元素相乘,点乘是dot函数 expon = multiply(-1*alpha*mat(classLabels).T,classEst) #exponent for D calc, getting messy #### 乘以原来权重 D = multiply(D,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,为什么年-- ## 明白了,我们最终考察的是adaboost,所以需要累加错误率,aggClassEst相当于最后的投票加权 ## 所以最后判断投票之后结果是不是完全正确,正确就break出来 aggClassEst += alpha*classEst #print "aggClassEst: ",aggClassEst.T aggErrors = multiply(sign(aggClassEst) != mat(classLabels).T,ones((m,1))) errorRate = aggErrors.sum()/m print("total error: ",errorRate) if errorRate == 0.0: break return

Adaboost预测函数

## 输入:预测的数据特征矩阵,模型结构#### 模型结构为训练集训练的得到的Adaboost算法## 输出:预测的结果## 脚本逻辑#### classifierArr为列表,列表元素为字典,所以列表长度就是基分类器个数#### 遍历所有的基分类器,把模型结构输入stumpClassify,返回预测结果#### 预测结果乘权重,累加#### 最后做signdef adaClassify(datToClass,classifierArr): ## 转成矩阵形式 dataMatrix = mat(datToClass)#do stuff similar to last aggClassEst in adaBoostTrainDS ## 矩阵的行数,即预测样本的个数 m = shape(dataMatrix)[0] aggClassEst = mat(zeros((m,1))) ## 遍历每一个分类器 for i in range(len(classifierArr)): ## stumpClassify返回该基分类器的预测结果 classEst = stumpClassify(dataMatrix,classifierArr[i]['dim'],\ classifierArr[i]['thresh'],\ classifierArr[i]['ineq'])#call stump classify ## 加权求和,相当于线性组合 aggClassEst += classifierArr[i]['alpha']*classEst print(aggClassEst) return

李航《统计学习方法》习题

## 载入数据dataMat,labelMat = loadDataSet("./Adaboost Exercise.txt")## 训练模型weakClassArr,aggClassEst = adaBoostTrainDS(dataArr=mat(dataMat),classLabels=mat(labelMat),numIt=40)

total error: 0.3total error: 0.3total error: 0.0

## 打印基分类器for i in range(3):print(weakClassArr[i])

{'dim': 0, 'thresh': 2.7000000000000002, 'ineq': 'gt', 'alpha': 0.4236489301936017}{'dim': 0, 'thresh': 8.0999999999999996, 'ineq': 'gt', 'alpha': 0.6496414920651304}{'dim': 0, 'thresh': 5.4000000000000004, 'ineq': 'lt', 'alpha': 0.752038698388137}

## 打印分类器最后的预测结果,加权和

[[ 0.32125172] [ 0.32125172] [ 0.32125172] [-0.52604614] [-0.52604614] [-0.52604614] [ 0.97803126] [ 0.97803126] [ 0.97803126] [-0.32125172]]

## 打印分类器最后的预测结果,label

[[ 1.] [ 1.] [ 1.] [-1.] [-1.] [-1.] [ 1.] [ 1.] [ 1.] [-1.]]

训练误差最后达到0,模型结构如上所述。 主要是学习下py的语法,Adaboost的算法逻辑,几乎所有的程序逻辑都已经添加上去了。备查。                   2018-04-26 于杭州

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Shiny07---用Shiny完成分箱调参工作
下一篇:PHP非递归遍历目录下所有文件,可以试一下!(php递归创建目录)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~