《机器学习实战Machine_Learning_in_Action》 CH07-AdaBoost元算法

第七章 利用AdaBoost元算法提高分类性能

AdaBoost算法的优缺点

  • 优点: 泛化错误率低,易编码,可以应用再大部分分类器上,无参数调整。
  • 缺点: 对离群点敏感。
  • 使用数据类型: 数值型和标称型数据。

bagging: 基于数据随机重抽样的分类器构造方法

  • 在原始数据选择S次后得到S个数据集的一种技术。新数据集和原数据集的大小相等。每个数据集通过原始样本中随机替换得到的。

boosting

  • 收集数据:可以使用任意方法。
  • 准备数据:依赖于所使用的弱分类器类型,本章使用的是单层决策树,这种分类器可以处理任何数据类型。当然也可以使用任意分类器作为弱分类器,第2章到第6章中的任一分类器都可以充当弱分类器。作为弱分类器,简单分类器的效果更好。
  • 分析数据:可以使用任意方法。
  • 训练算法: Adaboost的大部分时间都用在训练上,分类器将多次在同一数据集上训练弱分类器。
  • 测试算法:计算分类的错误率。
  • 使用算法:同SVM一样, Adaboost预测两个类别中的一个。如果想把它应用到多个类次后别的场合,那么就要像多类SVM中的做法一样对。

训练算法: 基于错误提升分类器的性能

  • AbaBoost算法会不断得重复训练和调整权重的过程,直到悬链错误率为0或者弱分类器的数目达到用户的制定值为止。

基于单层决策树构建弱分类器

伪代码

  • 将minError设置为无穷大
  • 对数据集中的每一个属性
  • 对每个步长(第二层循环):
  • 对每个不等号:
  • 建立一棵单层决策树并利用加权数据集对其进行测试
  • 如果错误率低于minError,则将当前的决策树设为最佳单层决策树
  • 返回最佳单层决策树

根据公式构建Adaboost

伪代码

  • 对每次迭代:
  • 利用buildStump找到最佳的单层决策树
  • 将最佳单层决策树加入数组
  • 计算分类器系数alpha
  • 计算新的权重D
  • 更新累计类别估计值
  • 如果错误率为0.0,跳出循环

例子: 在一个难数据集上应用AdaBoost

  • 收集数据:提供的文本文件。
  • 准备数据:确保类别标签是+1和-1而非1和0。
  • 分析数据:手工检查数据。
  • 训练算法:在数据上,利用adaboosttrainds()画教训练出一系列的分类器。
  • 测试算法:我们拥有两个数据集。在不采用随机抽样的方法下,我们就会对 Adaboost和 Logistic回归的结果进行完全对等的比较。
  • 使用算法:观察该例子上的错误率。不过,也可以构建一个Web网站,让驯马师输入马的症状然后预测马是否会死去。

Example: AdaBoost on a difficult dataset

import pandas as pd
import numpy as np
%matplotlib inline
%matplotlib notebook
import matplotlib.pyplot as plt
from numpy import *
import adaboost

#导入训练数据
datArr,labelArr = adaboost.loadDataSet('horseColicTraining2.txt')

#建立分类器
classifierArray,_ = adaboost.adaBoostTrainDS(datArr,labelArr,10)
#total error:  0.2842809364548495
#total error:  0.2842809364548495
#total error:  0.24749163879598662
#total error:  0.24749163879598662
#total error:  0.25418060200668896
#total error:  0.2408026755852843
#total error:  0.2408026755852843
#total error:  0.22073578595317725
#total error:  0.24749163879598662
#total error:  0.23076923076923078

#检查输出1
classifierArray
[{
    
    'dim': 9, 'thresh': 3.0, 'ineq': 'gt', 'alpha': 0.4616623792657674},
 {
    
    'dim': 17, 'thresh': 52.5, 'ineq': 'gt', 'alpha': 0.31248245042467104},
 {
    
    'dim': 3,  'thresh': 55.199999999999996,  'ineq': 'gt',  'alpha': 0.28680973201695786},
 {
    
    'dim': 18,  'thresh': 62.300000000000004,  'ineq': 'lt',  'alpha': 0.23297004638939492},
 {
    
    'dim': 10, 'thresh': 0.0, 'ineq': 'lt', 'alpha': 0.19803846151213736},
 {
    
    'dim': 5, 'thresh': 2.0, 'ineq': 'gt', 'alpha': 0.18847887349020628},
 {
    
    'dim': 12, 'thresh': 1.2, 'ineq': 'lt', 'alpha': 0.1522736899747682},
 {
    
    'dim': 7, 'thresh': 1.2, 'ineq': 'gt', 'alpha': 0.15510870821690512},
 {
    
    'dim': 5, 'thresh': 0.0, 'ineq': 'lt', 'alpha': 0.1353619735335938},
 {
    
    'dim': 4,  'thresh': 28.799999999999997,  'ineq': 'lt',  'alpha': 0.12521587326132094}

#检查输出2
len(classifierArray)
#10

#导入测试数据
testArr,testLabelArr = adaboost.loadDataSet('horseColicTest2.txt')

#进行分类
prediction10 = adaboost.adaClassify(testArr,classifierArray)

#错误统计
errArr=mat(ones((67,1)))
errnum = errArr[prediction10!=mat(testLabelArr).T].sum()
errnum
#16.0

#错误率
errnum/len(errArr)
#0.23880597014925373

adaboost源码

#coding=utf-8

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 datMat,classLabels

#对数据进行分类
def stumpClassify(dataMatrix,dimen,threshVal,threshIneq):#just classify the data
    retArray = ones((shape(dataMatrix)[0],1))
    if threshIneq == 'lt':
        retArray[dataMatrix[:,dimen] <= threshVal] = -1.0
    else:
        retArray[dataMatrix[:,dimen] > threshVal] = -1.0
    return retArray

#找到最佳决策树
def buildStump(dataArr,classLabels,D):
    dataMatrix = mat(dataArr); labelMat = mat(classLabels).T
    m,n = shape(dataMatrix)
    numSteps = 10.0; bestStump = {
    
    }; bestClasEst = mat(zeros((m,1)))
    minError = 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) #设置阈值
                predictedVals = stumpClassify(dataMatrix, i, threshVal,inequal) #分类预测
                errArr = mat(ones((m, 1)))#初始化全部为1(初始化为全部不相等)
                errArr[predictedVals == labelMat] = 0#预测与label相等则为0,否则为1
                # 分类器与adaBoost交互
                # 权重向量×错误向量=计算权重误差(加权错误率)
                weightedError = D.T * errArr
                if weightedError < minError:
                    minError = weightedError #保存当前最小的错误率
                    bestClasEst = predictedVals.copy() #预测类别
                    #保存该单层决策树
                    bestStump['dim'] = i
                    bestStump['thresh'] = threshVal
                    bestStump['ineq'] = inequal
    return bestStump, minError, bestClasEst #返回字典,错误率和类别估计

#完整adaboost算法
def adaBoostTrainDS(dataArr,classLabels,numIt=40): #numIt 用户设置的迭代次数
    weakClassArr = []
    m = shape(dataArr)[0]#m表示数组行数
    D = mat(ones((m,1))/m)   #初始化每个数据点的权重为1/m
    aggClassEst = mat(zeros((m,1)))#记录每个数据点的类别估计累计值
    for i in range(numIt):
        # 建立一个单层决策树,输入初始权重D
        bestStump,error,classEst = buildStump(dataArr,classLabels,D)
        print ("D:",D.T)
        # alpha表示本次输出结果权重
        alpha = float(0.5*log((1.0-error)/max(error,1e-16)))#1e-16防止零溢出
        bestStump['alpha'] = alpha  #alpha加入字典
        weakClassArr.append(bestStump)     #字典加入列表
        print ("classEst: ",classEst.T)
        # 计算下次迭代的新权重D
        expon = multiply(-1*alpha*mat(classLabels).T,classEst)
        D = multiply(D,exp(expon))
        D = D/D.sum()
        # 计算累加错误率
        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#错误率为0时 停止迭代
    return weakClassArr,aggClassEst

#测试adaboost
def adaClassify(datToClass,classifierArr):
    dataMatrix = mat(datToClass)#待分类样例 转换成numpy矩阵
    m = shape(dataMatrix)[0]
    aggClassEst = mat(zeros((m,1)))
    for i in range(len(classifierArr)):#遍历所有弱分类器
        classEst = stumpClassify(dataMatrix,\
                                 classifierArr[i]['dim'],\
                                 classifierAr[i]['thresh'],\
                                 classifierArr[i]['ineq'])
        aggClassEst += classifierArr[i]['alpha']*classEst
        print (aggClassEst) #输出每次迭代侯变化的结果
    return sign(aggClassEst) #返回符号,大于0返回1,小于0返回-1

#在难数据集上应用
#自适应数据加载函数
def loadDataSet(fileName):
    numFeat = len(open(fileName).readline().split('\t')) #get number of fields
    dataMat = []; labelMat = []
    fr = open(fileName)
    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 dataMat,labelMat

小结

  • 多个分类器组合可能会进一步凸显单个分类器的不足,如过拟合问题。若多个分类器间差别显著,可能会缓解这一问题。差别可以是算法本身或者应用于算法上的数据的不同。
  • 针对错误的调节能力是AdaBoost的长处,AdaBoost函数可以应用于任意能够处理加权数据的分类器。AdaBoost算法十分强大,它能够快速处理其他分类器难以处理的数据集。

猜你喜欢

转载自blog.csdn.net/m0_46629123/article/details/111180896