《机器学习实战第7章:利用AdaBoost元算法提高分类性能》

Posted hongjianchen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《机器学习实战第7章:利用AdaBoost元算法提高分类性能》相关的知识,希望对你有一定的参考价值。

import numpy as np
import matplotlib.pyplot as plt
def loadSimpData():
    dataMat = np.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 dataMat, classLabels

def showDataSet(dataMat, label):
    """
    数据可视化
    Parameters:
        dataMat - 数据矩阵
        label - 数据标签
    Returns:
        无
    """
    
    # 方法一 via CHJ
    data = np.array(dataMat)
    #print(data)
    for i in range(len(data)):
        if label[i] == 1.0:
            #用plt.scatter画散点图也行
            plt.plot(data[i][0], data[i][1], marker = ‘o‘, color = ‘red‘)
        else:
            plt.plot(data[i][0], data[i][1],  marker = ‘s‘,color = ‘blue‘)

    plt.show()

    # # 方法二 
    # data_plus = [] #正样本
    # data_mins = [] #负样本
    # for i in range(len(dataMat)):
    #   if label[i] > 0:
    #       data_plus.append(dataMat[i])
    #   else:
    #       data_mins.append(dataMat[i])

    # data_plus_np = np.array(data_plus)  #转换成numpy矩阵  #[[[1.  2.1]], [[2.  1.1]], [[2.  1. ]]]
    # data_mins_np = np.array(data_mins)  #转换成numpy矩阵  #[[[1.3 1. ]], [[1.  1. ]]]
    # #需要转置,否则绘图不正确
    # x = np.transpose(data_plus_np)  # [[[1.  2.  2. ]], [[2.1 1.1 1. ]]]
    # y = np.transpose(data_mins_np)  # [[[1.3 1. ]], [[1.  1. ]]]
    # plt.scatter(x[0], x[1])  # 正样本散点图
    # plt.scatter(y[0], y[1])  # 负样本散点图
    # plt.show()


print("------------7.4节:基于单层决策树构建分类器-----------------------")

def stumpClassify(dataMatrix, dimen, threshVal, threshIneq):
    """
    (将数据集,按照feature列的value进行 二分法切分比较来赋值分类)
    单层决策树分类函数
    Parameters:
        dataMatrix - 数据矩阵
        dimen - 第dimen列,也就是第几列特征
        threshVal - 阈值(特征列要比较的值)
        threshIneq - 阈值不等式(这里有两个:lt和gt)
    Returns:
        retArray - 分类结果(np.array类型)
    """
    retArray = np.ones((np.shape(dataMatrix)[0], 1))  # 生成m 行1列的单位矩阵
    # thresh_ineq == ‘lt‘表示阈值不等式取lt(less than)
    if threshIneq == ‘lt‘:
        # data_mat[:, dimen] 表示数据集中第dimen列的所有值
        retArray[dataMatrix[:, dimen] <= threshVal] = -1.0   # 如果小于等于阈值,则赋值为-1


    else:  # 表示阈值不等式取gt(great than)
        retArray[dataMatrix[:, dimen] > threshVal] = -1.0    # 如果大于阈值,则赋值为-1

    return retArray

def buildStump(dataArr, classLabels, D):

    """
    找到数据集上的最佳单层决策树 -- 单层决策树是指只考虑其中的一个特征,在该特征的基础上进行分类,
    寻找分类错误率最低的阈值即可, 非常简单
    例如本文例子中,如果以第一列特征(dimen = 1)为基础,阈值v选择1.3,并且设置lt:<=1.3的为-1,< 1.3的为+1; gt: <1.3的为+1,<= 1.3的为-1, 
    这样就构造出了一个二分类器
    Parameters:
        dataArr - 数据矩阵
        classLabels - 数据标签
        D - 样本权重
    Returns:
        bestStump - 最佳单层决策树信息
        minError - 最小误差
        bestClassEst - 最佳的分类结果   
    """

    dataMatrix = np.mat(dataArr)
    labelMat = np.mat(classLabels).T
    m, n = np.shape(dataMatrix)   # m行n列
    
    
     # numSteps用于在特征的所有可能值上进行遍历
    numSteps = 10.0
    bestStump = {}
    bestClassEst = np.mat(np.zeros((m, 1)))  
    minError = float(‘inf‘)   # np.inf    # 最小误差初始化为正无穷大
    # 第一层循环:对n列特征进行遍历(如本例中,n =2 )
    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):
            ‘‘‘
            lt(less than)是指在该阈值下,如果<阈值,则分类为-1
            gt(greater than)是指在该阈值下,如果>阈值,则分类为-1
            就这个题目来说,两者加起来误差肯定为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  # 分类正确的,赋值为0
                # 基于权重向量D而不是其他错误指标来评价分类器的,不同的分类器计算方法不一样
                weightedError = D.T * errArr # 计算误差--这里没有采用常规方法来评价这个分类器的分类准确率,而是乘上了权重
                # print(‘split: dim %d, thresh %.2f, thresh inequal: %s, the weighted error is %.3f‘                 #   %(i, threshVal, inequal, weightedError))
                if weightedError < minError:
                    minError = weightedError
                    bestClassEst = predictedVals.copy()
                    bestStump[‘dim‘] = i
                    bestStump[‘thresh‘] = threshVal
                    bestStump[‘ineq‘] = inequal


    return bestStump, minError,bestClassEst


print("----------7.4:完整AdaBoost算法的实现----------------------")
def adaBoostTrainDS(dataArr, classLabels, numIt= 40):
    """
    使用AdaBoost算法提升分类器性能
    Parameters:
        dataArr - 数据矩阵
        classLabels - 数据标签
        numIt - 最大迭代次数
    Returns:
        weakClassArr - 训练好的分类器
        aggClassEst - 类别估计累计值
    """
    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,使error不等于0,因为分母不能为0
        # alpha 目的主要是计算每一个分类器实例的权重(加和就是分类结果)
        # 计算每个分类器的 alpha 权重值
        alpha = float(0.5 * np.log((1.0 - error) / max(error, 1e-16)))

        bestStump[‘alpha‘] = alpha  # 存储若学习算法的权重  # store Stump Params in Array
        
        weakClassArr.append(bestStump)  # 存储单层决策树
        #print("classEst = ", classEst.T)
        expon = np.multiply(-1 * alpha * np.mat(classLabels).T, classEst)
        # print("expon = ", expon.T)
        D = np.multiply(D, np.exp(expon)) 
        # print("权重分布D = ", D.T)  
        D = D / D.sum() # 根据样本权重公式,更新样本权重
        # print("更新后权重分布D = ", D.T)  # 使D成为一个概率分布

        #计算AdaBoost的误差,当误差errorRate为0时,退出循环
        # 计算所有类别估计累计值--注意 这里包括了目前已经训练好的每一个弱分类器
        aggClassEst = aggClassEst + alpha * classEst
        #print("aggClassEst: ", aggClassEst.T)

        #计算分类器集成后的错误矩阵,错误设置为1,便于后续计算
        aggErrors = np.multiply(np.sign(aggClassEst) != np.mat(classLabels).T, np.ones((m, 1)))  #目前的集成分类器,分类错误
        # print("aggErrors = 
", aggErrors)
        errorRate = aggErrors.sum() / m  
        # errorRate:集成分类器分类错误率(平均),如果错误率为0,则整个集成算法停止,训练完成
        #print("total error: ", errorRate)
        print("集成第%s个弱分类器后的错误率ε = %.3f" %(i, errorRate))
        # 如错误率为0,此时分类器以达最佳,循环终止
        if errorRate == 0.0:
            break

    return weakClassArr, aggClassEst

def adaClassify(datToClass, classifierArr):
    """
    Parameters:
        datToClass - 待分类样例
        classifierArr - 训练好的分类器
    Returns:
        分类结果
    """

    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)


##########################7.6 示例: 在一个难数据集上应用AdaBoost##########################
def loadDataSet(fileName):
    numFeat = len(open(fileName).readline().split(‘	‘))  #获得特征(包括类别标签列)的个数
    # print("特征的个数: ", numFeat)  # 输出22个(包括类别标签列)
    dataMat = [] ; labelMat = []
    fr = open(fileName)
    for line in fr.readlines():   # 遍历行数(样本行)
        lineArr = []
        curLine = line.strip().split(‘	‘)  # curLine该行(当前行)的所有特征的数据
        # print("curLine = ", curLine)
        for i in range(numFeat - 1):  # 遍历所有特征(除去类别标签列的)
            lineArr.append(float(curLine[i]))  # 获得该行(当前行)除最后一列的其他所有特征的数据(格式化为浮点型)
        dataMat.append(lineArr)
        # i = 0列时,dataMat =  [[2.0, 1.0, 38.5, 66.0, 28.0, 3.0, 3.0, 0.0, 2.0, 5.0, 4.0, 4.0,         #                        0.0, 0.0, 0.0, 3.0, 5.0, 45.0, 8.4, 0.0, 0.0]]
        # print("dataMat = ", dataMat)
        labelMat.append(float(curLine[-1]))
    return dataMat, labelMat

#################7.7节:非均衡分类问题--TPR和FPR和ROC曲线绘制-###########################


def plotRoc(predStrengths, classLabels):
    """
    Parameters:
        predStrengths--分类器的预测强度
        classLabels--类别
    Returns:
    无
    """
    import matplotlib.pyplot as plt
    cur = (1.0, 1.0)  # 绘制光标的位置
    ySum = 0.0         # 用于计算AUC面积
    numPosClas = np.sum(np.array(classLabels) == 1.0) # 统计正类的数量
    yStep = 1 / float(numPosClas)   # y轴步长
    xStep = 1 / float(len(classLabels) - numPosClas)  # x轴步长

    sortedIndicies = predStrengths.argsort()  # 预测强度排序,从低到高
    # print("sortedIndicies = 
", sortedIndicies)
    fig = plt.figure()
    fig.clf()
    ax = plt.subplot(111)
    # print("sortedIndicies.tolist()[0] = 
", sortedIndicies.tolist()[0])
    for index in sortedIndicies.tolist()[0]:
        if classLabels[index] == 1.0:
            delX = 0 ; delY = yStep
        else:
            delX = xStep; delY = 0
            # 高度累加(xStep是固定的,最后ySum * xStep即可求AUC面积)
            ySum = ySum + cur[1]  # 注意每次cur[1]都可能变化了
        # 绘制ROC(两点一线) 每次点(cur[0],cur[1])和点(cur[0] - delX, cur[1] - delY)连成一条直线
        ax.plot([cur[0], cur[0] - delX], [cur[1], cur[1] - delY], c = ‘b‘) 
        cur = (cur[0] - delX, cur[1] - delY)    # 更新绘制光标的位置
        #print("cur ====== 
",cur)

    ax.plot([0,1], [0,1], ‘b--‘)  # 画虚线(即随机猜测的结果曲线)
    plt.xlabel(‘False Positive Rate‘)
    plt.ylabel(‘True Positive Rate‘)
    plt.title(‘ROC curve for AdaBoost Horse Colic Detection System‘)
    ax.axis([0,1,0,1])
    print("AUC面积为:", ySum * xStep)
    plt.show()




if __name__ == ‘__main__‘:
    
    dataMat, classLabels = loadSimpData()
    # print(dataMat, classLabels)
    # showDataSet(dataMat, classLabels)
    D = np.mat(np.ones((5,1))/5)
    # print(buildStump(dataMat, classLabels, D))
    weakClassArr, aggClassEst = adaBoostTrainDS(dataMat, classLabels, 9)
    # print("weakClassArr = ", weakClassArr) # 打印出所有弱分类器
    # print("aggClassEst = ", aggClassEst)
    # print(‘-------分割线-------‘)
    # print(adaClassify([0,0], weakClassArr))
    # print(‘=======分割线=======‘)
    # print(adaClassify([[5,5], [0,0]], weakClassArr))
    print()

    print("-----第7.6节 示例:在一个难数据集上应用AdaBoost-----")
    print()
    dataArr, LabelArr = loadDataSet(‘horseColicTraining2.txt‘)
    weakClassArr, aggClassEst = adaBoostTrainDS(dataArr, LabelArr)
    print("----------用在测试集试试-----------------------")
    testArr, testLabelArr = loadDataSet(‘horseColicTest2.txt‘)
    # print("weakClassArr = ", weakClassArr)
    predictions = adaClassify(dataArr,weakClassArr)
    errArr = np.mat(np.ones((len(dataArr), 1)))
    print("训练集上的错误数:%s" %(errArr[predictions != np.mat(LabelArr).T].sum()))
    print(‘训练集上的错误率: %.3f%%‘ %float(errArr[predictions != np.mat(LabelArr).T].sum() / len(dataArr) * 100))

    predictions = adaClassify(testArr, weakClassArr)
    errArr = np.mat(np.ones((len(testArr), 1)))
    print("测试集上的错误数:%s" %(errArr[predictions != np.mat(testLabelArr).T].sum()))
    print(‘测试集上的错误率: %.3f%%‘ %float(errArr[predictions != np.mat(testLabelArr).T].sum() / len(testArr) * 100))
    print("-------------7.7节:非均衡分类问题--TPR和FPR和ROC曲线绘制--------------------")
    print()
    plotRoc(aggClassEst.T, LabelArr)


    

以上是关于《机器学习实战第7章:利用AdaBoost元算法提高分类性能》的主要内容,如果未能解决你的问题,请参考以下文章

机器学习实战笔记-利用AdaBoost元算法提高分类性能

机器学习实战之 第七章 集成方法(随机森林和 AdaBoost)

机器学习(利用adaboost元算法提高分类性能)

python AdaBoost的示例代码。参考:机器学习在行动第7章。

机器学习——AdaBoost元算法

机器学习算法-Adaboost