机器学习实战——AdaBoost

集成学习

Ensemble learning集成学习:将不同的分类器组合成一个元分类器 → \rightarrow 更好的泛化性能

构建并结合多个学习器来完成学习任务.只包含同种类型的个体学习器,这样的集成是“同质”的;包含不同类型的个体学习器,这样的集成是“异质”的.

集成学习通过将多个学习器进行结合,常可获得比单一学习器显著优越的泛化性能

  • 根据个体学习器的生成方式,目前的集成学习
    方法大致可分为两大类,
    • 个体学习器间不存在强依赖关系、可同时生成的并行化方法;代表是Bagging和“随机森林”.
    • 个体学习器间存在强依赖关系、必须串行生成的序列化方法,代表是Boosting

Bagging

  • Bagging(套袋法):基于数据随机重抽样的分类器构建方法

    • 从原始数据集选择s次后得到s个新数据集的一种技术
    • 新数据集和原数据集的大小相等
    • 每个数据集都是通过在原始数据集上先后随机选择一个样本来进行替换得到的新的数据集(即先随机选择一个样本,然后随机选择另外一个样本替换之前的样本),并且这里的替换可以多次选择同一样本,也就是说某些样本可能多次出现,而另外有一些样本在新集合中不再出现。
    • s个数据集(相互独立)准备好之后,将某个学习算法分别作用于每个数据集(并行)就得到s个分类器。当要对新的数据进行分类时,就应用这s个分类器进行分类,最后根据多数表决的原则确定出最后的分类结果
  • 一些更先进的bagging方法:随机森林

  • 随机森林是除了样本集是有放回的采样外,属性集合也引入了随机属性选择.

    • 传统决策树在选择划分属性时是在当前结点的属性集合中选择一个最优属性
    • 而在RF中,对基决策树的每个结点,先从该结点的属性集合中随机选择一个包含k个属性的子集,然后再从这个子集中选择一个最优属性用于划分.
  • 随机森林简单、容易实现、计算开销小.效果能使得最终集成的泛化性能可通过个体学习器之间差异度的增加而进一步提升.

  • Sklearn中已经实现BaggingClassifer的相关算法,可以从ensemble子模块中导入使用

from sklearn.ensemble import RandomForestClassifier
  • 结果可见未经过剪枝的决策树准确的预测了训练数据所有类标,但测试数据上准确率较低,表明模型过拟合,bagging分类器在测试数据上的泛化性能稍有胜出
  • 单颗决策树容易过拟合,bagging算法具有优势,可以降低模型方差(过拟合)

Boost

  • boosting方法:分类算法,其与上面提到的bagging很类似,都是采用同一种基分类器的组合方法。而与bagging不同的是,boosting是集中关注分类器错分的那些数据来获得新的分类器(降低偏差bias)

    • 说明:bagging中分类器权重相等,而boosting中分类器的权值并不相等,分类器的错误率越低,那么其对应的权重也就越大,越容易对预测结果产生影响
    • 每次使用的是全部的样本,每轮训练改变样本的权重。下一轮训练的目标是找到一个函数f 来拟合上一轮的残差。当残差足够小或者达到设置的最大迭代次数则停止。Boosting会减小在上一轮训练正确的样本的权重,增大错误样本的权重。(对的残差小,错的残差大)
    • boosting有许多版本,比较流行的AdaBoost
  • AdaBoost:一种元算法(meta-algorithm)或者集成方法(ensemble method),是对其它算法进行组合的一种方式

  • 有人认为AdaBoost是最好的监督学习的方法

  • 使用集成算法时,可是不同算法的集成,也可是同一算法在不同设置下的集成,还可是数据集不同部分分配不同分类器之后的集成

    • 优点:泛化错误率低,易编码,可应用在大部分分类器上,无参数调整。
    • 缺点:对离群点敏感。适用数据类型:数值型和标称型数据

Bagging vs Boosting

  • 样本选择上: Bagging采取Bootstraping的是随机有放回的取样,Boosting的每一轮训练的样本是固定的,改变的是每个样本的权重。
  • 样本权重上:Bagging采取的是均匀取样,且每个样本的权重相同,Boosting根据错误率调整样本权重,错误率越大的样本权重会变大
  • 预测函数上:Bagging预测函数权值相同,Boosting中误差越小的预测函数其权值越大。
  • 并行计算: Bagging 的各个预测函数可以并行生成;Boosting的各个预测函数必须按照顺序迭代生成.

训练算法:基于错误提升分类器的性能

  • 基分类器(或者说弱分类器)Each week classifier:意味着分类器的性能不会太好,可能要比随机猜测要好一些,一般而言,在二类分类情况下,弱分类器的分类错误率达到甚至超过50%,显然也只是比随机猜测略好。但是,强分类器的分类错误率相对而言就要小很多
  • adaBoost算法就是基于这些弱分类器的组合来完成分类预测的。Use multiple instances of weakclassifier to create a “ strong ” classifier.

adaBoost的运行过程:

  • 训练数据的每一个样本,并赋予其一个权重,这些权值构成权重向量D,维度等于数据集样本个数。

  • 开始时,这些权重都是相等的

  • 首先在训练数据集上训练出一个弱分类器并计算该分类器的错误率

  • 然后在同一数据集上再次训练弱分类器,但是在第二次训练时,将会根据分类器的错误率,对数据集中样本的各个权重进行调整,分类正确的样本的权重降低,而分类错的样本权重则上升,但这些权重的总和保持不变为1.

  • 最终的分类器会基于这些训练的弱分类器的分类错误率,分配不同的决定系数alpha,错误率低的分类器获得更高的决定系数,从而在对数据进行预测时起关键作用

  • AdaBoost为每个分类器都分配一个权重值alpha,这些alpha是
    基于每个弱分类器的错误率计算的,其中,错误率的定义:
    ϵ = 未正确分类的样本数目 所有的样本数目 \epsilon = \frac {未正确分类的样本数目}{所有的样本数目} ϵ=所有的样本数目未正确分类的样本数目

  • alpha的计算公式如下: α = 1 2 ln ⁡ ( 1 − ϵ ϵ ) \alpha = \frac 12\ln(\frac{1-\epsilon}{\epsilon}) α=21ln(ϵ1ϵ)

  • 得到alpha值后,对权重向量D进行更新。若某个样本被正确分类,则该样本的权重更改为: D i t + 1 = D i t e − α S u m ( D ) D_i^{t+1} = \frac {D_i^te^{-\alpha}}{Sum(D)} Dit+1=Sum(D)Diteα

  • 若样本被错分,则该样本的权重更改为: D i ( t + 1 ) = D i t e α S u m ( D ) D_i^(t+1) = \frac {D_i^t e^{\alpha}}{Sum(D)} Di(t+1)=Sum(D)Diteα

  • 计算出D后,AdaBoost开始下一轮迭代,AdaBoost会不断地重复训练和调整权重,直到训练错误率为0,或者弱分类器的数目达到了用户指定值为止。

  • AdaBoost的做法是:

    • 一是对于前一轮分类器中分类错误的样本,提高其权值,引起后一轮分类器的更大关注
    • 二是加大分类误差率小的分类器权值,使其在表决时起更大作用
    • 基本分类器之间的整合方式,一般有简单多数投票、权重投票,贝叶斯投票,基于D-S证据理论的整合,基于不同的特征子集的整合。

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

  • 弱分类器:采用是单层分类器,又叫树桩分类器,是决策树最简单的一种
  • 单层决策树生成函数
    • 遍历每个属性,以一定步长,枚举大于和小于:找一条错误率最小的与垂直坐标轴的直线分开样本点;例如 i n s = ( a , b , c ) ins= (a,b,c) ins=(a,b,c),找到的弱分类器是a= 1 or b = 2 or c =3 这样的垂直坐标轴的直线

def stumpClassify(dataMatrix, dimen, threshVal, threshIneq):
    """
    单层决策树分类函数
    :param dataMatrix:数据矩阵
    :param dimen:第dimen列,也就是第几个特征
    :param threshVal:阈值
    :param threshIneq:标志
    :return:分类结果
    """
    retArray = np.ones((dataMatrix.shape[0], 1))
    if threshIneq == 'lt':
        retArray[dataMatrix[:, dimen] <= threshVal] = -1.0
    else:
        retArray[dataMatrix[:, dimen] > threshVal] = -1.0
    return retArray

通过阈值比较对数据进行分类,类别标记为:+1,-1;可以基于任一元素比较、并在大于、小于之间切换

def buildStump(dataArr, classLabels, D):
    """
    找到数据集上最佳的单层决策树
    :param dataArr:数据矩阵
    :param classLabels:数据标签
    :param D:样本权重
    :return:
     bestStump : 最佳单层决策树信息
     minError : 最小误差
     bestClasEst : 最佳的分类结果
    """
    dataMatrix = np.mat(dataArr)
    labelMat = np.mat(classLabels).T
    row, col = dataMatrix.shape
    numSteps = 10.0
    bestStump = {
    
    }
    bestClassEst = np.mat(np.zeros((row, 1)))
    minError = np.inf
    for i in range(col):
        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 = (rangeMax + float(j) * stepSize)
                predictedVals = stumpClassify(dataMatrix, i, threshVal, inEqual)
                errArr = np.mat(np.ones((row, 1)))
                errArr[predictedVals == labelMat] = 0
                weightError = D.T * errArr
                print("split:dim %d,thresh %.2f,thresh inEqual: %s,the weighted error is %.3f" % (
                    i, threshVal, inEqual, weightError))
                if weightError < minError:
                    minError = weightError
                    bestClassEst = predictedVals.copy()
                    bestStump['dim'] = i
                    bestStump['thresh'] = threshVal
                    bestStump['ineq'] = inEqual
    return bestStump, minError, bestClassEst

基于单层决策树的AdaBoost训练过程

def adaBoostTrainDS(dataArr, classLabels, numIt=40):
    '''
    基于单层决策树的AdaBoost训练过程
    :param dataArr:数据矩阵
    :param classLabels:数据标签
    :param numIt: 迭代次数,弱分类器数目
    :return:
        weakClassArr弱分类器的组合列表
        aggClassEst单层决策树的加权预测值
    '''
    weakClassArr = []  # 弱分类器相关信息列表
    row = np.shape(dataArr)[0]  # 获取数据数量
    D = np.mat(np.ones((row, 1)) / row)  # 权重向量,初始1/m,之和为1
    aggClassEst = np.mat(np.zeros((row, 1)))  # 各数据样本类别集成的估计值
    for i in range(numIt):
        # 构建最佳单层决策树
        bestStump, error, classEst = buildStump(dataArr, classLabels, D)
        alpha = float(0.5 * np.log((1.0 - error) / max(error, 1e-6)))  # alpha的计算,防止没有错误时0的溢出
        bestStump['alpha'] = alpha  # 存储决策树的系数alpha到字典
        weakClassArr.append(bestStump)  # 报存当前弱分类器
        print("classEst:", classEst.T)
        # 预测正确为exp(-alpha),预测错误为exp(alpha)
        # 即增大分类错误样本的权重,减少分类正确的数据点权重
        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((row, 1)))
        # 计算错误率
        errorRate = aggErrors.sum() / row
        print("total error:", errorRate, "\n")
        # 错误率为0.0退出循环
        if errorRate == 0.0:
            break
    # 返回弱分类器的组合列表,单层决策树的加权预测值
    return weakClassArr, aggClassEst

AdaBoost分类函数

def adaClassify(dataToClass, classifierArr):
    """
    AdaBoost分类函数
    :param dataToClass:
    :param classifierArr:
    :return:
    """
    # 构建数据向量或矩阵
    dataMatrix = np.mat(dataToClass)
    # 获取矩阵行数
    m = dataMatrix.shape[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
    # 通过sign函数根据结果大于或小于0预测出+1或-1
    return np.sign(aggClassEst)

应用实例:预测马疝病是否死亡

def loadDataSet(filename):
    """
    自适应加载数据
    :param filename: 文件名
    :return: 数据矩阵,特征标签
    """
    # 创建数据集矩阵,标签向量
    dataMat = []
    labelMat = []
    # 获取特征数目(包括最后一类标签)
    numFeat = len(open(filename).readline().split('\t'))
    # 打开文件
    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


def classify():
    # 利用训练集训练分类器
    dataArr, labelArr = loadDataSet('horseColicTraining2.txt')
    # 得到训练好的分类器
    classifierArray, aggClassEst = adaBoostTrainDS(dataArr, labelArr)
    # 利用测试集测试分类器的分类效果
    testArr, testLabelArr = loadDataSet('horseColicTest2.txt')
    prediction = adaClassify(testArr, classifierArray)
    # 输出错误率
    num = np.shape(np.mat(testLabelArr))[1]
    errArr = np.mat(np.ones((num, 1)))
    error = errArr[prediction != np.mat(testLabelArr).T].sum()
    errorRate = float(error) / float(num)
    print("the errorRate is :%.2f" % errorRate)
    plotROC(aggClassEst.T, labelArr)

绘制ROC曲线

def plotROC(predStrengths, classLabels):
    """
    绘制ROC曲线
    :param predStrengths: 单层决策树的加权预测值
    :param classLabels: 类别标签
    :return: 
    """
    cur = (1.0, 1.0)
    ySum = 0.0
    numPosClass = np.sum(np.array(classLabels) == 1.0)
    yStep = 1 / float(numPosClass)
    xStep = 1 / float(len(classLabels) - numPosClass)
    sortedIndicies = predStrengths.argsort()
    fig = plt.figure()
    fig.clf()
    ax = plt.subplot(111)
    for index in sortedIndicies.tolist()[0]:
        if classLabels[index] == 1.0:
            delX = 0
            delY = yStep
        else:
            delX = xStep
            delY = 0
        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 Postive Rate')
    plt.ylabel('True Postive Rate')
    plt.title('ROC curve for AdaBoost Horse Colic Detection System')
    ax.axis([0, 1, 0, 1])
    plt.show()

猜你喜欢

转载自blog.csdn.net/m0_55818687/article/details/127540165