机器学习实战(第七章-利用AdaBoost元算法提高分类性能-所有代码与详细注解-python3.7)

本章节是《机器学习实战》第一部分-分类 的最后一个章节,旨在通过AdaBoost方法提升分类器的性能,多次在同一数据集上训练若分类器,将这些多个弱分类器组合成一个强分类器,以到达更好的分类效果

本次代码考虑了两种缺失值填补法,第一种方法可以在一定程度降低错误率

那么,正文就从这里开始啦!(我的代码都是可直接运行的,只要环境正确

1、adaBoost01_base.py

'''
集成分类器方法有:bagging(boosting aggregating,自举汇聚法)、随机森林(random forest)、boosting等
boosting也可细分为很多种,其中比较流行的一种是AdaBoost(adaptive boosting, 自适应boosting)
AdaBoost一般流程为:
1、收集数据
2、准备数据
3、分析数据
4、训练算法:AdaBoost的大部分时间用在训练上,分类器将多次在同一数据集上训练弱分类器
5、测试算法:计算分类的错误率
6、使用算法
以下是利用多个单层决策树和adaboost算法,在小数据上的运用实例
'''
from numpy import *
import matplotlib.pyplot as plt


def loadSimpleData():
    dataMat = matrix([[1.0, 2.1], [2, 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 dataMat, classLabels


# 分析数据,数据可视化
def plot(dataMat, labelMat):
    dataArr = array(dataMat)
    num = shape(dataArr)[0]
    xcord1 = []
    ycord1 = []  # 标签为1的数据点坐标
    xcord0 = []
    ycord0 = []  # 标签为0的数据点坐标
    for i in range(num):
        if int(labelMat[i]) == 1:
            xcord1.append(dataArr[i, 0])
            ycord1.append(dataArr[i, 1])
        else:
            xcord0.append(dataArr[i, 0])
            ycord0.append(dataArr[i, 1])
    fig = plt.figure()
    fig.set_size_inches(18.5, 18.5)
    ax = fig.add_subplot(111)
    ax.scatter(xcord1, ycord1, s=30, c='red', marker='s')
    ax.scatter(xcord0, ycord0, s=30, c='green', marker='o')
    plt.show()


# 通过阈值比较对数据进行分类
def stumpClassifiy(dataMat, dimen, threshVal, threshIneq):
    retArr = ones((shape(dataMat)[0], 1))
    if threshIneq == 'lt':
        retArr[dataMat[:, dimen] <= threshVal] = -1.0
    else:
        retArr[dataMat[:, dimen] > threshVal] = -1.0
    return retArr


# 构建单层决策树(决策树的简化版本),是一种弱分类器算法
def buildStump(dataArr, classLabels, D):
    dataMat = mat(dataArr)
    labelMat = mat(classLabels).T
    m, n = shape(dataMat)
    numSteps = 10.0  # 用于在特征的所有可能值上进行遍历
    bestStump = {}  # 在给定权重向量D的情况下,最佳单层决策树的相关信息
    bestClassEst = mat(zeros((m, 1)))
    minError = inf  # 用于寻找最小错误率
    for i in range(n):  # 第一层循环:在数据集的所有特征上遍历
        rangeMin = dataMat[:, i].min()  # 找到当前特征的最大/小值
        rangeMax = dataMat[:, i].max()
        stepSize = 0
        try:
            stepSize = (rangeMax - rangeMin) / numSteps  # 已知数值范围与步数,求步长
        except BaseException:
            print("error print:", dataMat)
            return 0
        for j in range(-1, int(numSteps) + 1):  # 第二层循环:按一定步长,遍历当前特征的特征值
            for inequal in ['lt', 'gt']:  # 第三层循环:在大于和小于之间切换不等式
                threshVal = (rangeMin + float(j) * stepSize)
                # 根据阈值对数据进行分类,得到预测分类值
                predictedVals = stumpClassifiy(dataMat, i, threshVal, inequal)
                errArr = mat(ones((m, 1)))
                errArr[predictedVals == labelMat] = 0
                # errArr中类别预测错误处标为1,其余为0;此处求加权和作为错误率
                # 此处是AdaBoost与分类器交互的地方,此处基于权重向量D来评价分类器
                weightedError = D.T * errArr
                # print("split: dim", i, ", thresh", threshVal, ", thresh inequal",
                #       inequal, ", the weighted error is", weightedError)
                if weightedError < minError:
                    minError = weightedError
                    bestClassEst = predictedVals.copy()
                    bestStump['dim'] = i
                    bestStump['thresh'] = threshVal
                    bestStump['ineq'] = inequal

    return bestStump, minError, bestClassEst


'''
参数:数据集,类别标签,迭代次数(整个算法中唯一需要用户指定的参数)
DS,即 decision stump,单层决策树
'''
def adaBoostTrainDS(dataArr, classLabels, numIt=40):
    weakClassArr = []
    m = shape(dataArr)[0]
    D = mat(ones((m, 1)) / m)  # 权重向量存储了每一个数据点的权重,初始值相等;且D为概率分布向量,所有值的和为1
    aggClassEst = mat(zeros((m, 1)))  # 记录每个数据点的类别估计累计值
    for i in range(numIt):  # 达到最大循环次数,或者训练错误率为0,则退出
        bestStump, error, classEst = buildStump(dataArr, classLabels, D)
        # print("D:", D.T)
        # 对应分类器的权重
        alpha = float(0.5 * log((1.0 - error) / max(error, 1e-16)))  # 此处分母这么设计是为了避免除0溢出
        bestStump['alpha'] = alpha  # 该字典包含了分类所需要的所有信息
        weakClassArr.append(bestStump)
        # print("classEst:", classEst.T)
        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

    return weakClassArr, aggClassEst


'''
参数:多个待分类样例 dataToClass,多个弱分类器 classifierArr
'''


def adaclassify(dataToClass, classifierArr):
    dataMat = mat(dataToClass)
    m = shape(dataMat)[0]
    aggClassEst = mat(zeros((m, 1)))
    for i in range(len(classifierArr)):
        classEst = stumpClassifiy(dataMat, classifierArr[i]['dim'],
                                  classifierArr[i]['thresh'], classifierArr[i]['ineq'])
        aggClassEst += classifierArr[i]['alpha'] * classEst
        # print(aggClassEst)

    return sign(aggClassEst)


if __name__ == "__main__":
    dataMat, classLabels = loadSimpleData()
    # plot(dataMat, classLabels)

    # D = mat(ones((5, 1)) / 5)  # 权重向量存储了每一个数据点的权重,初始值相等;且D为概率分布向量,所有值的和为1
    # bestStump, minError, bestClassEst = buildStump(dataMat, classLabels, D)
    # # {'dim': 0, 'thresh': 1.3, 'ineq': 'lt'}、[[0.2]]、[[-1.] [1.] [-1.] [-1.] [1.]]
    # print(bestStump, minError, bestClassEst)

    classifierArr = adaBoostTrainDS(dataMat, classLabels, 30)[0]
    # print(classifierArr)
    sign = adaclassify([[1.3, 1.2], [1.0, 1.1]], classifierArr)
    print(sign)


数据可视化:                                                                                  

运行结果:

2、adaBoost02_horseColic.py

'''
以下是利用多个单层决策树和adaboost算法,在难数据集(马疝病数据集)上的运用实例
1、收集数据
2、准备数据,标签为-1,1而非0,1
处理数据集中的缺失值,可选方法有:(此数据集中的属性部分,0为缺失值)
(1) 使用可用特征的均值填补缺失值
(2) 使用特殊值(如-1)填补缺失值
(3) 忽略有缺失值的样本
(4) 使用相似样本的均值填补缺失值
(5) 使用另外的机器学习算法预测缺失值
3、分析数据
4、训练数据:在数据集上训练处一系列的分类器
5、测试算法:并且与logistic回归的分类结果进行对等的比较
6、使用算法
与logistic回归算法作比较:
logistic回归的平均错误率是0.35,而本adaboost一般的运行结果都不超过0.35
'''

from adaBoost01_base import *

def loadDataSet(fileName):
    numFeature = 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(numFeature - 1):
            # 原算法随着若分类器数量的增加,会出现过拟合现象,此处对原数据中的缺失值(即0值)做适当调整,看是否能够提高性能
            # 缺失值补充法1:把缺失值0用任一负值来替换,发现错误率变化比较随机
            # the error rate is 0.23880597014925373\0.208955223880597
            # if float(curLine[i]) - 0.0 < 1e-6:
            #     curLine[i] = "-10.0"
            lineArr.append(float(curLine[i]))
        dataMat.append(lineArr)
        # 书中直接使用处理后的数据,而本文采用原数据,所以此处进行标签转换
        if float(curLine[-1]) - 0.0 < 1e-6:
            curLine[-1] = "-1.000000"
        labelMat.append(float(curLine[-1]))
    return dataMat, labelMat

if __name__ == "__main__":

    # 训练
    trainDataArr, trainLabelArr = loadDataSet("data/horseColicTraining.txt")
    classifierArr = adaBoostTrainDS(trainDataArr, trainLabelArr, 10)[0]

    # 测试
    testDataArr, testLabelArr = loadDataSet("data/horseColicTest.txt")
    prediction = adaclassify(testDataArr, classifierArr)
    # print(prediction)

    errArr = mat(ones((len(testLabelArr), 1)))
    errCount = errArr[prediction != mat(testLabelArr).T].sum()
    print(prediction.T.tolist()[0])
    print(testLabelArr)
    # 因为没有随机性,所以结果是固定的:
    # the number of test data is 67 , among which the error number is 16.0
    # the error rate is 0.23880597014925373
    print("the number of test data is", len(testLabelArr), ", among which the error number is", errCount)
    print("the error rate is", (float(errCount) / len(testLabelArr)))

运行结果:

3、adaBoost02_horseColic_fill_missing_value.py

'''
以下是缺失值的第2种尝试(用样本均值填充),结果是失败的,错误率很高,仅作为提供思路,亦可不看

处理数据集中的缺失值,可选方法有:(此数据集中的属性部分,0为缺失值)
(1) 使用可用特征的均值填补缺失值
(2) 使用特殊值(如-1)填补缺失值
(3) 忽略有缺失值的样本
(4) 使用相似样本的均值填补缺失值
(5) 使用另外的机器学习算法预测缺失值
'''
from adaBoost02_horseColic import *

# 缺失值补充法2:采用标签相同的特征平均值
# 该方法貌似错误更高了,可能原因是,我们可以通过该方法把训练数据的缺失值补齐,却不能补齐测试数据的缺失值
# the error rate is 0.29850746268656714
def loadDataSet02Train(fileName):
    numFeature = len(open(fileName).readlines()[0].split('\t'))
    dataMat = []
    labelMat = []
    fr = open(fileName)
    for line in fr.readlines():
        lineArr = []
        curLine = line.strip().split('\t')
        for i in range(numFeature - 1):
            lineArr.append(float(curLine[i]))
        dataMat.append(lineArr)
        # 书中直接使用处理后的数据,而本文采用原数据,所以此处进行标签转换
        if float(curLine[-1]) - 0.0 < 1e-6:
            curLine[-1] = "-1.000000"
        labelMat.append(float(curLine[-1]))

    # 计算不同类别不同特征平均值
    m, n = shape(dataMat)
    sum = zeros((2, m))
    count = zeros((2, m))
    for i in range(m):
        for j in range(n):
            if dataMat[i][j] - 0.0 >= 1e-6:
                row = int((int(labelMat[i]) + 1) / 2)
                sum[row][j] += dataMat[i][j]
                count[row][j] += 1
    average = sum / count

    # 将缺失值(即0值)一一替换掉
    for i in range(m):
        for j in range(n):
            if dataMat[i][j] - 0.0 < 1e-6:
                row = int((int(labelMat[i]) + 1) / 2)
                dataMat[i][j] = average[row][j]

    return dataMat, labelMat, average

# 缺失值补充法2:采用标签相同的特征平均值
# 结合loadDataSet02Train方法,用所有数据的均值补齐测试数据的缺失值,试试看,结果错误率很高,仅做思路吧
# the error rate is 0.47761194029850745
def loadDataSet03Test(fileName, average):
    numFeature = len(open(fileName).readlines()[0].split('\t'))
    dataMat = []
    labelMat = []
    fr = open(fileName)
    for line in fr.readlines():
        lineArr = []
        curLine = line.strip().split('\t')
        for i in range(numFeature - 1):
            # the error rate is 0.23880597014925373\0.208955223880597
            if float(curLine[i]) - 0.0 < 1e-6:
                curLine[i] = (average[0][i] + average[1][i]) / 2.0
            lineArr.append(float(curLine[i]))
        dataMat.append(lineArr)
        if float(curLine[-1]) - 0.0 < 1e-6:
            curLine[-1] = "-1.000000"
        labelMat.append(float(curLine[-1]))
    return dataMat, labelMat

if __name__ == "__main__":

    # 训练
    trainDataArr, trainLabelArr, average = loadDataSet02Train("data/horseColicTraining.txt")
    classifierArr = adaBoostTrainDS(trainDataArr, trainLabelArr, 10)[0]

    # 测试
    testDataArr, testLabelArr = loadDataSet03Test("data/horseColicTest.txt", average)
    prediction = adaclassify(testDataArr, classifierArr)

    errArr = mat(ones((len(testLabelArr), 1)))
    errCount = errArr[prediction != mat(testLabelArr).T].sum()
    print(prediction.T.tolist()[0])
    print(testLabelArr)
    print("the number of test data is", len(testLabelArr), ", among which the error number is", errCount)
    print("the error rate is", (float(errCount) / len(testLabelArr)))

4、adaBoost03_ROC_AUC.py

from adaBoost02_horseColic import *

'''
参数:分类器的预测强度(即每个特征对应的类别累计估计值),数据标签
'''
def plotROC(predictStrengths, classLabels):
    import matplotlib.pyplot as plt
    cur = (1.0, 1.0)  # 绘制光标的位置
    ySum = 0.0 # 用于计算AUC的值
    numPosClass = sum(array(classLabels) == 1.0)  # 正例的数目
    yStep = 1 / float(numPosClass)  # 纵坐标表示实际正例中被正确识别的概率
    xStep = 1 / float(len(classLabels) - numPosClass)  # 横坐标表示实际反例中被错误识别的概率
    sortedIndicies = predictStrengths.argsort()  # 获取排序索引:由小到大
    fig = plt.figure()
    fig.clf()
    ax = plt.subplot(111)
    for index in sortedIndicies.tolist()[0]:
        if classLabels[index] == 1.0:
            delX = 0    # delX是横坐标变化值,delY是纵坐标变化值
            delY = yStep
        else:
            delX = xStep
            delY = 0
            ySum += cur[1]  # 把每一小段的y值相加,最后乘以xStep就是面积AUC
        ax.plot([cur[0], cur[0] - delX], [cur[1], cur[1] - delY], c='b')  # 从右上到左下画线
        cur = (cur[0] - delX, cur[1] - delY)  # 画完线之后,当前点作为光标起点
    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])  # 设置坐标轴范围
    # plt.savefig('ROC.png')
    plt.show()
    print("the Area Under the Curve is: ", ySum * xStep)

if __name__ == "__main__":

    dataArr, labelArr= loadDataSet("data/horseColicTraining.txt")
    classifierArr, aggClassEst = adaBoostTrainDS(dataArr, labelArr, 10)
    # the Area Under the Curve is:  0.8582969635063604
    plotROC(aggClassEst.T, labelArr)

ROC曲线:

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我有明珠一颗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值