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

1. 元算法介绍

  • 做重要决定时,大家可能会考虑多个权威的意见而不是一个人的意见,机器学习中也是如此,这就是元算法的背后思想。元算法是对其他算法组合的一种方式。
  • 优点:泛化错误低,易编码,可以用在大部分分类器上,无参数调整问题
  • 缺点:对离群点敏感

2. AdaBoost思想 以及 涉及公式

2.1 简单理解

AdaBoost是adaptive boosting(自适应boosting)的缩写,是利用弱分类器和多个实例来构建为一个强分类器,弱强指的是分类效果,AdaBoost算法是与分类器交互并且重点关注分类失败的点来获得新的分类器….(实际就是分类失败的点权重问题)
与SVM有着相似的label , 也就是+1/-1化(sign函数把小于0的赋值-1反之+1)
可以把AdaBoost弱分类器类比SVM核函数
- 首先本文选择的弱分类器为单层决策树(DS),实际可以选择任何弱分类器
- 初始化每个分类器权重alpha
- 初始化每个样本的权重D
- 计算弱分类器的分类错误率,根据错误率更新权重D,权重alpha
- 根据新的权重D进行分类
- 每个分类器乘以权重alpha求和(加权求和)

简易流程图 流程图
(黑色柱状代表权重D大小)

2.1 公式

错误率定义 : ϵ=

alpha权重更新公式 : α=12ln(1ϵϵ) 公式1

D权重更新公式 :

样本正确分类 : D(t+1)i=DtieαSum(D) 公式2

样本错误分类 : D(t+1)i=DtieαSum(D)

可以看到 : 错误率减小,alpha变大,样本错误分类的权重D变大…..

下文代码解释 :
代码 multiply(-1 * alpha * mat(label).T, preLabel)
这么理解 -1 * alpha * (mat(label).T * preLabel)
如果 labeli 是1, preLabeli 是-1 正确即为+1 错误为-1

3. 主要算法伪代码

# 建立单层决策树
初始最小误差值inf
for 每一个特征
    for 每一个待处理的阀值
        for 每一个符号(大/小于)
            设立阀值
            调用函数,产生预测值
            预测/实际分类的误差求和
            误差打擂台
                记录最小的误差
返回 最优DS,最优误差,最优分类

# AdaBoost算法
初始化样本权重D矩阵
建立弱分类器列表
for 0 - numIt 迭代:
    调用建立单层决策树函数
    计算alpha,保存alpha
    弱分类器列表加入上决策树
    更新样本权重D
    累计错误率
返回弱分类器列表

4. 代码 以及 几个小栗子

# coding:utf-8

from numpy import *

def loadSimpleData():
    data = matrix([[1.0, 2.1],
                   [2.0, 1.1],
                   [2.0, 1.0],
                   [1.0, 1.0],
                   [1.3, 1.0]])
    label = [1.0, 1.0, 1.0, -1.0, -1.0]
    return data, label

def loadData(fileName):
    dataMat = []
    labelMat = []
    with open(fileName) as txtFile:
        for line in txtFile.readlines():
            temp = line.split()
            dataMat.append(map(float, temp)[0:-1])
            labelMat.append(map(float, temp)[-1])
    return dataMat, labelMat

def sigTreeClassify(data, index, splitValue, splitSign):
    labelPre = ones((shape(data)[0], 1))
    if splitSign == 'small':
        labelPre[data[:, index] <= splitValue] = -1.0  # 符号无所谓 用1与-1区分类别 与SVM相似
    else:
        labelPre[data[:, index] > splitValue] = -1.0  # 有区别就行
    return labelPre

def buildTree(data, label, D): # D 样本权重
    data = mat(data)
    label = mat(label).T
    m, n = shape(data)
    numSteps = 10.0
    bestTree = {}   # 最优的弱分类器
    bestLabel = mat(zeros((m, 1)))  # 最优预测label
    minError = inf  # 打擂台专属
    for index in range(n):
        rangeMin = data[:, index].min()
        rangeMax = data[:, index].max()
        stepSize = (rangeMax - rangeMin) / numSteps
        for j in range(-1, int(numSteps) + 1):
            for sign in ["small", "large"]:
                value = rangeMin + float(j) * stepSize  # j:-1~numstep+1 value稍微超出范围
                labelPre = sigTreeClassify(data, index, value, sign)
                errArr = ones((m, 1))
                errArr[labelPre[:] == label[:]] = 0  # 预测正确为0  反之为1
                weightError = D.T * errArr  # 可以看得出来01的作用, 方便加权求和
                if minError > weightError:  # 更新最小误差 以及 最优树
                    minError = weightError
                    bestLabel = labelPre
                    bestTree['value'] = value # 字典存取单层决策树的信息
                    bestTree['index'] = index
                    bestTree['sign'] = sign
                    ''' if U have some questions '''
                    # print "inloop: index:%d, value:%f, sign:%s" % (index, value, sign)  # 循环中的变量
                    # print "label :", label.T      # 正确值
                    # print "labelPre :", labelPre.T  # 预测值
                    # print "errArr :", errArr.T    # 预测是否正确
                    # print "D :", D.T  # D
                    # print "weightError :", weightError  # alpha加权误差
                    # print "minError :", minError, '\n----------------------------------------------'
    # print "bestTree :", bestTree
    # print "minError :", minError
    # print "bestLabel :", bestLabel.T, "\n++++++++++++++++++++++++++++"
    ''' have a look '''
    return bestTree, minError, bestLabel

def AdaBoostTrainDS(data, label, numIt=40): # numIt实际就是生成单层决策树的个数
    weakClassArr = []  # 弱分类器列表
    m = shape(data)[0]
    D = mat(ones((m, 1)) / m)  # 样本权重和为1 每个样本有一个权重
    aggClassEst = mat(zeros((m, 1)))
    for i in range(numIt):
        bestTree, error, preLabel = buildTree(data, label, D) # 生成一个单层决策树
        alpha = float(0.5 * log((1.0 - error) / max(error, 1e-16)))  # max()防止分母0,公式1:更新alpha
        bestTree['alpha'] = alpha
        weakClassArr.append(bestTree)    # 弱分类器加入带有alpha的最优树
        expon = multiply(-1 * alpha * mat(label).T, preLabel)  # 公式2:样本权重更新 见公式2详解
        D = multiply(D, exp(expon)) / D.sum()
        aggClassEst += alpha * preLabel  # 运行时的类别估计值 sign函数处理之前
        aggErrors = multiply(sign(aggClassEst) != mat(label).T, ones((m, 1)))  # 预测label的误差和
        errorRate = aggErrors.sum() / m  # 预测正确为0 不正确为1 再乘以one矩阵得到矩阵
        ''' if U have some questions '''
        # print "loop number is :", i
        # print "label :", label
        # print "preLabel :", preLabel.T
        # print "expon :", expon.T
        # print "alpha :", alpha
        # print "D :", D.T
        # print "aggClassEst :", aggClassEst.T
        # print "sign(aggClassEst) :",sign(aggClassEst)
        # print "aggClassEst != label :",sign(aggClassEst) != mat(label).T
        # print "aggErrors :", aggErrors.T
        # print "errorRate :", errorRate, "\n----------------------------------------------"
        ''' have a look above '''
        if errorRate == 0.0: break
    # print "numIt is :", numIt,"\ntrainError rate is :",errorRate
    return weakClassArr

def AdaBoostClassify(data, classifierArr): # classifierArr 就是所有单层决策树的列表
    data = mat(data)
    m = shape(data)[0]
    aggClassEst = mat(zeros((m, 1)))
    # print "len",len(classifierArr)
    for i in range(len(classifierArr)):
        labelPre = sigTreeClassify(data, classifierArr[i]['index'], classifierArr[i]['value'], classifierArr[i]['sign'])
        aggClassEst += classifierArr[i]['alpha'] * labelPre
        ''' have a look '''
        # print "loopNum is :",i
        # print "labelPre :", labelPre.T
        # print "aggClassEst :",aggClassEst.T
        # print "sign(aggClassEst) :",sign(aggClassEst).T
        ''' ok '''
    return sign(aggClassEst)  # -1 ~ 1

if __name__ == '__main__':
    data, label = loadSimpleData()  # 加载简单数据集
    ''' 建单层决策树验证 '''
    D = mat(ones((shape(data)[0], 1)) / shape(data)[0])
    bestTree, minError, bestLabel = buildTree(data, label, D)
    print "bestTree :", bestTree, '\n+++++++++++++++++++++++++++++++++++++++++++++++++++++++'
    ''' 简单数据训练+预测 '''
    classifierArr = AdaBoostTrainDS(data, label, 10)  # 训练模型
    data = matrix([
        [1.0, 2.1],
        [2.0, 1.1],
        [2.0, 1.0],
        [1.0, 1.0],
        [1.3, 1.0],
        [0.8, 1.4],
        [1.0, 0.5]
    ])
    label = [1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0]
    labelPre = AdaBoostClassify(data, classifierArr)  # 进行预测
    print "label    :", mat(label)
    print "prelabel :", labelPre.T, '\n+++++++++++++++++++++++++++++++++++++++++++++++++++++++'
    ''' 复杂数据训练+预测 '''
    trainData, trainLabel = loadData("horseTrain.txt")
    testData, testLabel = loadData("horseTest.txt")
    m = shape(testData)[0]
    for numIt in [1, 10, 50, 100, 500, 1000, 10000]:
        classifierArr = AdaBoostTrainDS(trainData, trainLabel, numIt) # 训练模型
        labelPre = AdaBoostClassify(testData, classifierArr)          # 进行预测
        errArr = mat(ones((m, 1)))
        errorSum = errArr[labelPre != mat(testLabel).T].sum()
        print "testError rate is :", 1.0 * errorSum / 67, '\n----------------------------------------'

5. 结果分析 以及 辅助理解图像

# 输出结果
# 如详细理解过程可以在代码中撤销注释获得打印输出
'''
bestTree : {'index': 0, 'value': 1.3, 'sign': 'small'} 
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
label    : [[ 1.  1.  1. -1. -1.  1. -1.]]
prelabel : [[ 1.  1.  1. -1. -1. -1. -1.]] 
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
numIt is : 1 
trainError rate is : 0.284280936455
testError rate is : 0.268656716418 
----------------------------------------
numIt is : 10 
trainError rate is : 0.234113712375
testError rate is : 0.238805970149 
----------------------------------------
numIt is : 50 
trainError rate is : 0.207357859532
testError rate is : 0.208955223881 
----------------------------------------
numIt is : 100 
trainError rate is : 0.1872909699
testError rate is : 0.238805970149 
----------------------------------------
numIt is : 500 
trainError rate is : 0.157190635452
testError rate is : 0.253731343284 
----------------------------------------
numIt is : 1000 
trainError rate is : 0.133779264214
testError rate is : 0.268656716418 
----------------------------------------
numIt is : 10000 
trainError rate is : 0.110367892977
testError rate is : 0.328358208955 
----------------------------------------
'''

# 分析
'''
第一个栗子中 打印了最优DS的分类特征,分类阀值,分类符号小于号
第二个栗子中 很显然最后一个预测失误了 
第三个栗子中 numIt从1~10000过程中,对于训练数据拟合越来越好,而相对于测试数据在出现最优错误率之后又下降了,很显然的过拟合....(相关文献证明AdaBoost错误率回达到一个稳定值,不会随着分类器的个数增加而变优)
'''

简单数据集的坐标图像
简单数据集坐标图

6. 附 数据集

数据集 请点击这里

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值