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

前言

  • AdaBoost 属于集成学习
  • 集成学习是指,以前用一个模型干的事,现在我用多个模型一块干(人多力量大)。
    简单理解为:一道选择题,以前呢,都是一个人(模型)来做,正确性完全依赖这一个人(模型);现在呢,应用集成学习,还是这道选择题,我让 10 人(模型)来做,如果8人(模型)个选 A,1人(模型)选 B,1人(模型)选 C。毫无疑问,我们认为 A 更有可能为正确答案。这就是集成学习!
  • 集成学习分两大类(BaggingBoosting)。
    • Bagging 比如分类,以前我训练一颗决策树,就用这棵树进行分类了,准确性仅仅依靠这棵树。现在呢,集成学习出现了,我们同时训练多颗树,最后输入待测试数据,每棵树都会给出一个分类结果,我们最终投票选举即可。实例: 随机森林
    • Boosting 具有顺序性,每一个分类器都是在前一个分类器的基础之上进行操作的。Boosting 系列算法里最著名算法主要有 AdaBoost 算法和提升树(boosting tree)系列算法。提升树系列算法里面应用最广泛的是梯度提升树(Gradient Boosting Tree)

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

import numpy as np
import matplotlib.pyplot as plt
  • 加载数据
def loadSimpData():
    datMat = np.mat([[1. , 2.1],
                     [1.5, 1.6],
                     [1.3, 1. ],
                     [1. , 1. ],
                     [2. , 1. ]])
    classLabels = [1.0, 1.0, -1.0, -1.0, 1.0]
    return datMat, classLabels
  • 读进数据集之后,现在让我们来以图像化显示一下数据集
def plotFigure():
    x, y = loadSimpData()
    xarr = np.array(x)
    n = np.shape(x)[0]
    x1 = []; y1 = []
    x2 = []; y2 = []
    for i in np.arange(n):
        if int(y[i]) == 1:
            x1.append(xarr[i,0]); y1.append(xarr[i,1])
        else:
            x2.append(xarr[i,0]); y2.append(xarr[i,1])
    
    # scatter 画出散点图
    plt.scatter(x1, y1, s = 30, c = 'r', marker = 's')
    plt.scatter(x2, y2, s = 30, c = 'g')
    plt.show()

# 画图
plotFigure()
  • 单层决策树生成函数
    第一个函数 stumpClassify() 是通过阈值比较对数据进行分类的。所有在阈值一边的数据会分到类别 -1, 而在另外一边的数据分到类别 +1。该函数可以通过数组过滤来实现, 首先将返回数组的全部元素设置为 1, 然后将所有不满足不等式要求的元素设置为 -1。可以基于数据集中的任一元素进行比较,同时也可以将不等号在大于、小于之间切换。
# 用于测试是否有某个值小于或者大于我们正在测试的阈值
def stumpClassify(dataMatrix, dimen, threshVal, threshIneq):
    retArray = np.ones((np.shape(dataMatrix)[0], 1))
    if threshIneq == 'lt':
        retArray[dataMatrix[:, dimen] <= threshVal] = -1.0
    else:
        retArray[dataMatrix[:, dimen] > threshVal] = -1.0
    return retArray

第二个函数 buildStump() 将会遍历 stumpClassify() 函数所有可能输入值,并找到数据集上最佳的单层决策树。

# buildStump() 伪代码如下
将最小错误率 minError 设为正无穷
对数据集中的每一个特征(第一层循环):
   对每个步长(第二层循环) :
      对每个不等号(第三层循环):
        建立一棵单层决策树并利用加权数据集对它进行测试
        如果错误率低于 minError,则将当前单层决策树设为最佳单层决策树
返回最佳单层决策树
# 在一个加权数据集中循环,并找到具有最低错误率的单层决策树
def buildStump(dataArr, classLabels, D):
    dataMatrix = np.mat(dataArr)
    labelMat = np.mat(classLabels).T
    m, n = np.shape(dataMatrix)
    numSteps = 10.0
    bestStump = {}
    bestClasEst = np.mat(np.zeros((m, 1)))
    minError = float('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 = np.mat(np.ones((m, 1)))
                errArr[predictedVals == labelMat] = 0
                # D是不变的, errArr 向量里面只有0、1
                weightedError = D.T * errArr
                #print(D.T, errArr, '\n')
                #print('split: dim %d, thresh %.2f, thresh inequal: %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
  • 测试一下
D = np.mat(np.ones((5, 1)) / 5)
datMat, classLabels = loadSimpData()
a, b, c = buildStump(datMat, classLabels, D)
print(a)
print(b)
print(c)

完整 AdaBoost 算法的实现

# 伪代码如下:
对每次迭代:
    利用 buildStump() 函数找到最佳的单层决策树
    将最佳单层决策树加入到单层决策树数组
    计算 alpha
    计算新的权重向量 D
    更新累计类别估计值
    如果错误率等于 0,则退出循环
# 基于单层决策树的 AdaBoost 训练过程
def adaBoostTrainDS(dataArr, classLabels, numIt = 40):
    weakClassArr = []
    m = np.shape(dataArr)[0]
    D = np.mat(np.ones((m, 1)) / m)
    aggClassEst = np.mat(np.zeros((m, 1)))
    
    for i in range(numIt):
        bestStump, error, classEst = buildStump(dataArr, classLabels, D)
        print('D: ', D.T)
        alpha = float(0.5 * np.log((1.0 - error) / max(error, 1e-16)))
        bestStump['alpha'] = alpha
        weakClassArr.append(bestStump)
        print('classEst: ', classEst.T)
        expon = np.multiply(-1 * alpha * np.mat(classLabels).T, classEst) 
        D = np.multiply(D, np.exp(expon))
        D = D / D.sum()
        aggClassEst += alpha * classEst
        print('aggClassEst: ', aggClassEst.T)
        aggErrors = np.multiply(np.sign(aggClassEst) != np.mat(classLabels).T, np.ones((m, 1)))
        errorRate = aggErrors.sum() / m
        print('total error: ', errorRate, '\n')
        if errorRate == 0.0:
            break
            
    return weakClassArr
  • 测试一下
datMat, classLabels = loadSimpData()
classifierArray = adaBoostTrainDS(datMat, classLabels, 9)
print(classifierArray)

测试算法: 基于 AdaBoost 的分类

# AdaBoost 分类函数
def adaClassify(datToClass, classifierArr):
    dataMatrix = np.mat(datToClass)
    m = np.shape(dataMatrix)[0]
    aggClassEst = np.mat(np.zeros((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)
    return np.sign(aggClassEst)
  • 测试一下
# 加载数据
atArr, labelArr = loadSimpData()
# 训练模型
classifierArray = adaBoostTrainDS(datArr, labelArr, 30)
print(adaClassify([0, 0], classifierArray)) # [[ -1 ]]
print(adaClassify([[5, 5], [0, 0]], classifierArray)) 
# [[ 1.]
#  [-1.]]

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

  • 加载数据
def loadDataSet(fileName):
    numFeat = len(open(fileName).readline().split('\t'))
    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
  • 训练模型
datArr, labelArr = loadDataSet('horseColicTraining2.txt')
classifierArray = adaBoostTrainDS(datArr, labelArr, 10)
# 迭代输出每一棵单层决策树(用for循环,这样形式美观)
for i in classifierArray:
    print(i)
  • 测试模型
testArr, testlabelArr = loadDataSet('horseColicTest2.txt')
prediction10 = adaClassify(testArr, classifierArray)
print(prediction10.shape) #(67, 1)

errArr = np.mat(np.ones((67, 1)))
errArr[prediction10 != np.mat(testlabelArr).T].sum() # 16

可以看到一共 67 个样本,测试结果为出错 16 个,错误率为 16/67 = 23.88

参考

  • 集成学习原理小结

你可能感兴趣的:(第7章 利用 AdaBoost 元算法提高分类性能)