MLiA笔记_adaBoost

#-*-coding:utf-8-*-

from numpy import *

def loadSimpData():
    datMat = 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 datMat, classLabels



# 7.1 单层决策树生成函数
# 函数1stumpClassify(),通过阈值比较对数据进行分类
# 所有在阈值一边的数据会分到类别-1,而在另一边的数据分到类别+1
def stumpClassify(dataMatrix, dimen, threshVal, threshIneq):
    # 数组过滤,首先将返回数组的全部元素设置为1
    retArray = ones((shape(dataMatrix)[0], 1))
    if threshIneq == 'lt':
        retArray[dataMatrix[:, dimen] <= threshVal] = -1.0
    else:
        retArray[dataMatrix[:, dimen] > threshVal] = -1.0
    return  retArray

# 函数2builstump()遍历stumpClassify()函数所有可能的输入值,并找到数据集上最佳的单层决策树
# 权重向量D定义“最佳”
def buildStump(dataArr, classLabels, D):
    dataMatrix = mat(dataArr)
    labelMat = mat(classLabels).T
    m, n = shape(dataMatrix)
    numSteps = 10.0 # 变量numsteps,用于在特征的所有可能值上进行遍历
    bestStump = {} # 构建空字典,存储给定权重向量D时所得最佳单层决策树的相关信息
    bestClasEst = mat(zeros((m, 1)))
    minError = inf # 变量minError,初始为正无穷,用于之后寻找可能的最小错误率
    # 三层嵌套for函数
    # 第一层在数据集的所有特征上遍历,最小值和最大值
    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):
            # 第三层循环,调用stumpClassify()函数,该函数返回分类预测结果
            for inequal in ['lt','gt']:
                threshVal = (rangeMin + float(j)*stepSize)
                predictedVals = stumpClassify(dataMatrix,i,threshVal,inequal)
                errArr = mat(ones((m,1))) # 构建列向量errArr
                errArr[predictedVals == labelMat] = 0 # 如果predictedVals中值不等于labelMat中的真正类别标签值,errArr相应位置为1
                weightedError = D.T * errArr # adaBoost和分类器交互的地方:基于权重向量D评价分类器
                if weightedError < minError:
                    minError = weightedError
                    bestClasEst = predictedVals.copy()
                    bestStump['dim'] = i
                    bestStump['thresh'] = threshVal
                    bestStump['ineq'] = inequal
    return bestStump, minError, bestClasEst


# 7.2 基于单决策树的AdaBoost训练过程
# AdaBoost算法参数包括数据集,类别标签, 迭代次数numIt(numIt是唯一需要用户指定的参数)
# (假定迭代次数为9,如果在第三次迭代之后错误率为0, 那么就会退出迭代过程)
# 函数名称内部DS代表单层决策树(decision stump),它是AdaBoost中最流行的弱分类器
def adaboostTrainDS(dataArr, classLabels,numIt=4.0):
    weakClassArr = []      # 算法会输出一个DS的数组,因此首先需要建立一个新的表来对其进行存储
    m = shape(dataArr)[0]  # 然后,得到一个数据集中的数据点的数目m
    D = mat(ones((m,1))/m) # 建立一个列向量D
                           # 列向量D非常重要,它包含了每个数据点的权重,一开始赋予每个权重等值,在后续迭代中,AdaBoost算法在增加错分数据的权重同时,降低正确分类数据的权重
                           # D是一个概率分布向量,因此其所有的元素之和为1.0,为此所有向量初始化为1/m
    aggClassEst = mat(zeros((m,1))) # 同时建立一个aggClassEst列向量,记录每个数据点的类别估计累计值
    # 算法核心for循环,运行numIt次或者直到错误率为0为止
    for i in range(numIt):
        bestStump, error, classEst = buildStump(dataArr,classLabels,D) # 循环的第一件事就是利用前面的buildStump()函数建立一个单层决策树
                                                                       # 返回的是利用D而得到的具有最小错误率的单层决策树,和最小错误率,以及估计的类别向量
        print("D:", D.T) # 该函数的输入为权重向量D,
        alpha = float(0.5*log((1.0 - error)/max(error, 1e-16))) # 计算alpha值,该值会告诉分类器本次单层决策树输出结果的权重
                                                                # max(error, 1e-16)用于确保在没有错误时不会发生除零溢出
        weakClassArr.append(bestStump) # 而后,alpha值加入到bestStump字典中,该字典又添加到列表中,该字典包含了分类所需要的所有信息
        print("classEst: ", classEst.T)
        # 下三行,用于计算下一次迭代中的新权重向量D
        expon = multiply(-1*alpha*mat(classLabels).T, classEst)
        D = multiply(D, exp(expon))
        D = D/D.sum()
        # aggClassEst变量保持一个运行时的类别估计值,来实现在训练错误率为0时,提前结束for循环
        aggClassEst += alpha*classEst
        print("aggClassEst:" ,aggClassEst.T)
        # 该值只是一个浮点数,调用sign()函数得到一个二值分类结果
        aggErrors = multiply(sign(aggClassEst) != mat(classLabels).T, ones((m,1)))
        errorRate = aggErrors.sum()/m
        print("total error:", errorRate,"\n")
        if errorRate == 0.0:
            break
    return weakClassArr, aggClassEst

# 7.3 AdaBoost分类函数
# adaClassify()函数就是利用训练出的多个弱分类器进行分类的函数
# 输入是由一个或者多个待分类样例datToClass以及多个弱分类器组成的数组classifierArr
def adaClassify(datToClass, classifierArr):
    dataMatrix = mat(datToClass) # 转换成numpyjuzhen
    m = shape(dataMatrix)[0] # 得到待分类样例的个数m
    aggClassEst = mat(zeros((m,1))) # 构建0列向量aggCLassEst(同adaBoostTrain()中的含义)
    # 遍历classifierA中的所有弱分类器,
    for i in range(len(classifierArr)):
        # 基于stumpClassify()对每个分类器得到一个类别的估计值
        # 在这里只是简单了应用了DS:输出的类别估计乘上该DS的alpha权重然后累加到aggClassEst上
        ClassEst = stumpClassify(dataMatrix,classifierArr[i]['dim'],classifierArr[i]['thresh'],classifierArr[i]['ineq'])
        aggClassEst += classifierArr[i]['alpha']*ClassEst
        # 加入print语句,以便了解每次迭代后的变化结果
        print aggClassEst
    # 最后返回aggClassEst的符号,如果大于0返回+1, 如果小于0返回-1
    return sign(aggClassEst)


# 在一个难数据集上应用AdaBoost
# 7.4 自适应数据加载函数
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

# 7.5 ROC曲线的绘制及AUC计算函数
# 两个参数输入
# 第一个是NumPy数组活着一个行向量组成的矩阵,代表的是分类器的预测强度
# 第二个是先前使用过的classLabels
def plotROC(predStrengths, classLabels):
    import matplotlib.pyplot as plt
    cur = (1.0,1.0)  # 构建一个浮点数二元组,并初始化。该元组保留的是绘制光标的位置
    ySum = 0.0       # ySum则用于计算AUC的值
    # 通过数组过滤方式计算正例的数目,并将该值赋给numPosClas
    numPosClas = sum(array(classLabels) == 1.0)
    # 该值先是确定了在y坐标轴上的步进数目
    # 接着在x、y轴上绘点,y轴上步长是1.0/numPosCLas
    yStep = 1/float(numPosClas)
    # 类似得到x轴步长
    xStep = 1/float(len(classLabels) - numPosClas)
    # 得到排序索引,但是这些索引是按照最小到最大的顺序排列,因此需要从点(1.0,1.0)到(0.0)
    sortedIndicies = predStrengths.argsort()
    # 下三行,构建画笔,
    fig = plt.figure()
    fig.clf()
    ax = plt.subplot(111)
    # 在所有排序值上进行循环。
    # 这些值在一个NumPy数组或矩阵中进行排序,需要一个表来进行迭代循环,因此需要调用tolist()方法
    for index in sortedIndicies.tolist()[0]:
        # 只关注1这个类别标签,无所谓1/0或1/-1
        # 每得到一个标签为1.0的类,则要沿着y轴方向下降一个步长,即不断降低真阳率v
        if classLabels[index] == 1.0:
            delX = 0
            delY = yStep
        # 类似对于其他类别标签,则是在x轴上倒退一个步长,即假阴率
        # 为了计算AUC,需要对多个小矩形的面积进行累加,小矩形宽度为xStep,所有高度和随着x轴的每次移动而渐次增加
        else:
            delX = xStep
            delY = 0
            ySum += cur[1]
        #一旦决定了是在x轴还是y轴方向上移动,我们就可以在当前点和新点之间画出一条线段
        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.title('ROC curve for AdaBoost Horse Colic Detection System')
    ax.axis([0,1,0,1])
    plt.show()

猜你喜欢

转载自blog.csdn.net/weixin_42836351/article/details/81393051