上节课我们介绍了AdaBoost这一算法,其基本思想就是通过集中关注被已有分类器分类错误的那些数据来训练新的分类器。
在上一节中估计每个分类器误差:
ϵt=P(ht(x)≠f(x))=分类错误的样本数目所有样本数目
然后根据这一误差来计算每个分类器的权重
α=12ln(1−ϵϵ)
计算alpha值后,可以对权重向量进行跟新,以使得错分类样本权重加大,正确分类的样本权重降低。 权重更新公示: Dt(i)←Dt(i)exp(−αth(xi)yi)
如果分类正确h(xi)==yi,那该样本权重改为: Dt+1i=Dtie−αSum(D)
如果分类正确h(xi)≠yi,那该样本权重改为: Dt+1i=DtieαSum(D)
上述两个公式合并为:Dt+1i=Dtie−αyiht(xi)Sum(D)

接下来我们一个完整的AdaBoost算法的Python代码,用到的弱分类器为单层决策树
1 首先生成一些训练数据和标签:

from numpy import * 
def loadData():  #  生成训练样本和标签
    datMat = matrix([[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 datMat, classLabels

这里 我们可以将该数据可视化,利用 matplotlib

import matplotlib
import matplotlib.pyplot as plt # 相当于画笔
def DrawData(data, labels):
    x0 = []
    y0 = []
    x1 = []
    y1 = []
    for i in range(len(labels)):
        if (labels[i] == 0):
            x0.append(data[i,0]) # 点的 x y坐标
            y0.append(data[i,1])
        else:
            x1.append(data[i,0]) # 点的 x y坐标
            y1.append(data[i,1])
    fig = plt.figure() # 创建画笔
    ax = fig.add_subplot(111)  # 创建画布
    ax.scatter(x0, y0, marker='s', s=90)  # 画散点图, 参数: x,y坐标,以及形状 颜色 大小等
    ax.scatter(x1, y1, marker='o', s=90, c='red')
    plt.title('Original Data') # 标题
    plt.show()  # 显示

python可以打开dta格式吗 python dta_集成学习

2 接下来就可以构建单层决策树:
伪代码:

将最小错误率minerror设为inf 
对数据中每个特征:
    对每个步长:
        对每个不等号(分类错误的样本):
            建立一个单层决策树并利用加权数据集对它测试
            如果错误率低于minerror,则将当前单层决策树设为最佳单层决策树
返回最佳的单层决策树

其中有三层循环,后面将会分析每层循环的作用。

Python code

def stumpClassify(data, dimen, threshval, threshIneq): 
    # 通过比较阈值对数据进行分类,以阈值为界 分为{+1 -1}两类 相当于上一节课的 剪枝分类器。
    # 输入参数 dimen 是哪个特征, threshIneq:有两种模式,在大于和小于之间切换不等式 
    retArray = ones((shape(data)[0], 1))
    if (threshIneq== 1):
        retArray[data[:,dimen] <= threshval] = -1.0
    else:
        retArray[data[:,dimen] > threshval] = -1.0
    return retArray

def buildStump(dataArray, classLabels, D):
    # 输入 样本数组 标签 D为样本初始权重向量
    dataMat = mat(dataArray)
    labelMat = mat(classLabels).T  # 转置
    m, n = shape(dataMat) # 矩阵行列
    bestStump = {} # 用字典来存放最后的单层决策树
    bestclassEst = mat(zeros((m,1)))  # 类别估计值(预测标签)
    minerror = inf # 最小错误率初始值设置为 无限大
    for i in range(n):  #对数据中的每一个特征
        minVal = dataMat[:,i].min() # 得到某一个特征中最小最大值
        maxVal = dataMat[:,i].max() 
        stepsize = (maxVal-minVal)/10   #步长
        for j in range(-1, 11):  # 从-1开始 到 10
            for inequal in [1,2]:  # 在大于和小于之间切换不等式???
                threshVal = (minVal+float(j)*stepsize)  #  不同的阈值  ???
                predictedVals = stumpClassify(dataMat, i, threshVal, inequal) # 预测的标签值。
                errArr = mat(ones((m,1))) #  ones((m,n))
                errArr[predictedVals == labelMat] = 0 # 预测正确的为0 预测错误的为1
                weightedError = D.T * errArr  # 得到加权分类误差
                if weightedError < minerror:
                    minerror = weightedError
                    bestclassEst = predictedVals.copy()
                    bestStump['dim'] = i # 字典赋值
                    bestStump['thresh'] = threshVal
                    bestStump['ineq'] = inequal
    return bestStump, minerror, bestclassEst 
      # 返回 字典、分类器估计错误率、类别估计值(这事实上就是训练出来的弱分类器)

分析:
(1)第一个函数stumpClassify是将输入数据(矩阵)分为两类,首先将返回数组全部设置为1,然后将满足不等式要求的元素设置为-1,dimen保证可以基于数据集中任一元素进行比较,最后一个元素threshIneq:将不等号在大于和小于之间切换。
(2)第二个函数,将会遍历所有的可能输入值,并找到数据集上最佳的单层决策树。
该函数有三层循环:
第一层循环: 在数据集所有特征上进行遍历,
第二层循环: 在某特征的所有值上进行遍历(不断改变阈值,阈值的设置尽量大于特征值的范围。)
第三层循环: 在大于和小于之间切换不等式。
在这三层循环内,调用第一个函数,返回分类预测结果,接下来构建一个列向量errArr,如果预测错误,那么errArr相应位置设为1,将错误向量和权重向量D相应元素相乘并求和,就得到了数值weightedError,这就是AdaBoost和分类器交互的地方,这里我们是基于权重向量D而不是其他错误计算指标来评价分类器的。最后将当前错误率和已有的最小错误率比较,如果当前值较小,那就在词典bestStump中保存该单层决策树。

上述代码输出::

python可以打开dta格式吗 python dta_Python_02

3 完整AdaBoost算法的实现
伪代码:

对每次迭代:
    利用buildStump函数找到最佳的单层决策树
    奖最佳单层决策树加入到单层决策树组
    计算alpha
    计算新的权重向量D
    更新累计估计值
    如果错误率等于0.0,则退出循环。
def adaboostTrainDs(dataArr, classLabels, numIt=40):
    # 输入: 数据向量, 标签list 迭代次数
    weakClassArr = [] #  存储每次迭代产生的最佳单层决策树和alpha值
    m = shape(dataArr)[0] # 样本个数
    D = mat(ones((m, 1))/m) # 初始的样本权重设置为一样的(1/m)
    aggClassEst = mat(zeros((m, 1))) # 记录每个数据点的类别估计 累计值 
    for i in range(numIt):  # 开始迭代
        bestStump, error, classEst = buildStump(dataArr, classLabels, D)
        # 调用函数 返回最佳单层决策树, 分类器估计错误率和预测标签值
        #print('D: ', D.T) # 输出权重向量D  (中间变量测试语句)
        #print('classEst: ', classEst)
        alpha = float(0.5*log((1.0-error)/max(error,1e-16))) # 分类器的权重  分母max保证如果没有错误时防止分母为0
        bestStump['alpha'] = alpha  # 将alpha 放入到最佳决策树的字典中
        weakClassArr.append(bestStump)
        ## 开始更新D
        expon = multiply(-1*alpha*mat(classLabels).T, classEst) #  classLabels 和classEst 都是m*1的,multiply函数对应元素相乘返回还是m*1的
        D = multiply(D, exp(expon))
        D = D/D.sum()
        aggClassEst += alpha*classEst  #相当于更新强分类器 aggClassEst
        #print('aggClassEst', aggClassEst)
        ##### 错误率的累加
        aggError = multiply(sign(aggClassEst) != mat(classLabels).T, ones((m, 1)))   # 判段预测值与实际值是否一致,不一致的赋为1,预测一致为0 可得预测错误的个数
        errorRate = aggError.sum()/m  #求得误差率
        #print('total error: ', errorRate)
        if errorRate == 0.0:
            break
    return weakClassArr

分析

向量D非常重要, 它包含了每个数据点的权重,一开始都给相同的值,在后续的迭代中,根据分类的结果,给不同的样本赋值上不同的权重,然后再次训练弱分类器。最后得到好几个不同的弱分类器。

最后返回的数组是一个包含多个弱分类器的字典。

python可以打开dta格式吗 python dta_决策树_03

==========
上述代码就是AdaBoost的算法实现代码,接着我们要进行测试。

def adaTest(dataTest, classfier):
    #输入 待分类的样例  弱分类器
    dataMat = mat(dataTest)
    m = shape(dataMat)[0]
    aggClassEst = mat(zeros((m, 1)))
    for i in range(len(classfier)):
        classEst = stumpClassify(dataMat, classfier[i]['dim'], classfier[i]['thresh'], classfier[i]['ineq'])
        aggClassEst += classfier[i]['alpha'] * classEst  # 预测值
        #print(aggClassEst)
    return sign(aggClassEst)  # 输出标签值

分析:
测试函数利用训练出来的多个弱分类器进行分类。for循环中遍历几个弱分类器,基于stumpClassify函数对每个分类器得到一个类别的估计值。输出的类别值乘上该单层决策树的alpha权重然后累加到aggClassEst上,就完成了分类过程。
代码: 链接:http://pan.baidu.com/s/1kVntHzh 密码:ss1x