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

7.1 基于数据集多重抽样的分类器

我们可以将不同的分类器组合起来,而这种组合结果则被称为集成方法或者元算法。使用集成方法时会有多种形式:可以是不同算法的集成,也可以是统一算法在不同设置下的集成,还可以是数据集不同部分分配给不同分类器之后的集成。

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

7.1.1 bagging:基于数据随机重抽样的分类器构建方法

自举汇聚法,也称为bagging方法,是在从原始数据集选择S次后得到S个新数据集的一种技术。新数据集和原数据集的大小相等。每个数据集都是通过在原始数据集中随机选择一个样本来进行替换而得到的。替换意味着可以多次地选择同一样本。这一性质就允许新数据集中可以有重复的值,而原始数据集的某些值在新集合中则不再出现。

在S个数据集建好之后,将某个学习算法分别作用于每个数据集就得到了S个分类器。当我们要对新数据进行分类时,就可以应用这S个分类器进行分类。与此同时,选择分类器投票结果中最多的类别作为最后的分类结果。

7.1.2 boosting

boosting是一种与bagging类似的技术。不论是在boosting还是bagging当中,所使用的的多个分类器的类型都是一致的。但是在前者当中,不同的分类器是通过串行训练而获得的,每个新分类器都根据已训练出的分类器的性能来进行训练。boosting是通过集中关注被已有分类器错分的那些数据来获得新的分类器。

由于boosting分类的结果是基于所有分类器的加权求和结果的,因此boosting与bagging不太一样。boosting中的分类器权重是相等的,而boosting中的分类器权重并不相等,每个权重代表的是其对应分类器在上一轮迭代中的成功度。

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

能否使用弱分类器和多个实例来构建一个强分类器?“弱”意味着分类器的性能比随机猜测要略好,但不会好太多。也就是说,在二分类情况下弱分类器的错误率会高于50%,而“强”分类器的错误率将会低很多。AdaBoost算法即脱胎于上述理论问题。
Adaboost是adaptive boosting(自适应boosting)的缩写,其运行过程如下:训练数据中的每个样本,并赋予其一个权重,这些权重构成了向量D。一开始,这些权重都初始化成相等值。首先在训练数据上训练出一个弱分类器并计算该分类器的错误率,然后在同一数据集上再次训练弱分类器。在分离器的第二次训练当中,将会重新调整每个样本的权重,其中第一次分对的样本的权重将会降低,而第一次分错的样本的权重将会提高。为了从所有弱分类器中得到最终的分类结果,AdaBoost为每个分类器都分配了一个权重值alpha,这些alpha值是基于每个弱分类器的错误率进行计算的。错误率\varepsilon的定义为:

 
alpha的计算公式如下:
\alpha=\frac{1}{2} \ln \left(\frac{1-\varepsilon}{\varepsilon}\right)
AdaBoost算法流程如下:

计算出alpha值后,可以对权重向量D进行更新,以使得那些正确分类的样本权重降低而错分样本的权重升高。
如果某个样本被正确分类,那么该样本的权重更改为:
D_{i}^{(t+1)}=\frac{D_{i}^{(t)} \mathrm{e}^{-\alpha}}{\operatorname{Sum}(D)}
如果某个样本被错分,那么该样本的权重更改为:
D_{i}^{(t+1)}=\frac{D_{i}^{(t)} \mathrm{e}^{\alpha}}{\operatorname{Sum}(D)}
计算出D后,AdaBoost将开始下一轮迭代。AdaBoost算法会不断地重复训练和调整权重的过程,直到训练错误率为0或者弱分类器的数目达到用户的指定值为止。

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

单层决策树是一种简单的决策树。下面将构建一个单层决策树,它仅基于单个特征来做决策。由于这棵树只有一次分裂过程,因此它实际上就是一个树桩。

我们首先通过一个简单数据集来确保在算法实现上一切就绪。创建adaboost.py文件并加入代码:

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

上述代码给出了一个数据集,示意图如下。如果想要试着从某个坐标轴上选择一个值(即选择一条与坐标轴平行的直线)来将所有圆形点和方形点分开是不可能的。通过使用多棵单层决策树,我们就可以构建出一个能够对该数据集完全正确分类的分类器。

通过如下代码可以实现数据集合类标签的导入:

import adaboost
datmat,classLable = adaboost.loadSimpData()

下面构建多个函数来建立单层决策树。第一个函数用于测试是否有某个值小于或大于我们正在测试的阈值。第二个函数会在一个加权数据集中循环,并找到具有最低错误率的单层决策树。伪代码如下:

def stumpClassify(dataMatrix, dimen, threshVal, threshIneq):  # just classify the data
    retArray = ones((shape(dataMatrix)[0], 1))
    if threshIneq == 'lt':
        retArray[dataMatrix[:, dimen] <= threshVal] = -1.0
    else:
        retArray[dataMatrix[:, dimen] > threshVal] = -1.0
    return retArray

def buildStump(dataArr, classLabels, D):
    dataMatrix = mat(dataArr);
    labelMat = mat(classLabels).T
    m, n = shape(dataMatrix)
    numSteps = 10.0;
    bestStump = {};
    bestClasEst = mat(zeros((m, 1)))
    minError = inf  # init error sum, to +infinity
    for i in range(n):  # loop over all dimensions
        rangeMin = dataMatrix[:, i].min();
        rangeMax = dataMatrix[:, i].max();
        stepSize = (rangeMax - rangeMin) / numSteps
        for j in range(-1, int(numSteps) + 1):  # loop over all range in current dimension
            for inequal in ['lt', 'gt']:  # go over less than and greater than
                threshVal = (rangeMin + float(j) * stepSize)
                predictedVals = stumpClassify(dataMatrix, i, threshVal,
                                              inequal)  # call stump classify with i, j, lessThan
                errArr = mat(ones((m, 1)))
                errArr[predictedVals == labelMat] = 0
                weightedError = D.T * errArr  # calc total error multiplied by D
                # print "split: dim %d, thresh %.2f, thresh ineqal: %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

第一个函数 stumpClassify() 是通过阈值比较对数据进行分类的。所有在阈值一边的数据会分到类别-1,而在另一边的数据分到类别+1。该函数可以通过数组过滤来实现,首先将返回数组的全部元素设置为1,然后将所有不满足不等式要求的元素设置为-1。可以基于数据集中的任一元素进行比较,同时也可以将不等号在大于、小于之间切换。
第二个函数 buildStump() 将会遍历 stumpClassify() 函数所有的可能输入值,并找到数据集上最佳的单层决策树。“最佳”是基于数据的权重向量来定义的。在确保输入数据符合矩阵格式之后,整个函数就开始执行。然后,函数将构建一个称为bestStump的空字典,这个字典用于存储给定权重向量时所得到的最佳单层决策树相关信息。变量numSteps用于在特征的所有可能值上进行遍历。而遍历minError则在一开始就初始化成无穷大,之后用于寻找可能的最小错误率。
三层嵌套的for循环是程序最主要的部分。第一层for循环在数据集的所有特征上遍历。考虑到数值型的特征,我们就可以通过计算最小值和最大值来了解应该需要多大的步长。然后,第二层for循环再在这些值上遍历。甚至将阈值设置为整个取值范围之外也是可以的。因此,在取值范围之外还应该有两个额外的步骤。最后一个for循环则是在大于和小于之间切换不等式。
在嵌套的三层for循环之内,我们在数据集及三个循环变量上调用stumpclassify(〉函数。基于这些循环变量,该函数将会返回分类预测结果。接下来构建一个列向量errArr,如果predictedvals中的值不等于1abelMat中的真正类别标签值,那么errArr的相应位置为1。将错误向量errArr和权重向量D的相应元素相乘并求和,就得到了数值weightedError①。这就是AdaBoost和分类器交互的地方。这里,我们是基于权重向量D而不是其他错误计算指标来评价分类器的。如果需要使用其他分类器的话,就需要考虑D上最佳分类器所定义的计算过程。
程序接下来输出所有的值。虽然这一行后面可以注释掉,但是它对理解函数的运行还是很有帮助的。最后,将当前的错误率与已有的最小错误率进行对比,如果当前的值较小,那么就在词典beststump中保存该单层决策树。字典、错误率和类别估计值都会返回给AdaBoost算法。
通过下面的代码了解实际运行过程:

import adaboost
from numpy import *
datmat,classLables = adaboost.loadSimpData()
D = mat(ones((5,1))/5)
print(adaboost.buildStump(datmat,classLables,D))

buildStump在所有可能的值上遍历的同时,也能观察到输出结果和最后返回的字典。

7.4 完整AdaBoost算法的实现

将下面的代码加入adaboost.py:

def adaBoostTrainDS(dataArr,classLabels,numIt=40):
    weakClassArr = []
    m = shape(dataArr)[0]
    D = mat(ones((m,1))/m)   #init D to all equal
    aggClassEst = mat(zeros((m,1)))
    for i in range(numIt):
        bestStump,error,classEst = buildStump(dataArr,classLabels,D)#build Stump
        #print "D:",D.T
        alpha = float(0.5*log((1.0-error)/max(error,1e-16)))#calc alpha, throw in max(error,eps) to account for error=0
        bestStump['alpha'] = alpha
        weakClassArr.append(bestStump)                  #store Stump Params in Array
        #print "classEst: ",classEst.T
        expon = multiply(-1*alpha*mat(classLabels).T,classEst) #exponent for D calc, getting messy
        D = multiply(D,exp(expon))                              #Calc New D for next iteration
        D = D/D.sum()
        #calc training error of all classifiers, if this is 0 quit for loop early (use break)
        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
import adaboost
from numpy import *
datmat,classLables = adaboost.loadSimpData()
D = mat(ones((5,1))/5)
classifierArray = adaboost.adaBoostTrainDS(datmat,classLables,9)

AdaBoost算法的输人参数包括数据集、类别标签以及迭代次数numIt,其中numIt是在整个AdaBoost算法中唯一需要用户指定的参数。
假定迭代次数为9,如果算法在第三次迭代之后错误率为0,那么就会退出迭代过程,因此,此时就不需要执行所有的9次迭代过程。
函数名称尾部的DS代表的就是单层决策树(decision stump),它是AdaBoost中最流行的弱分类器,当然并非唯一可用的弱分类器。上述函数确实是建立于单层决策树之上的,但是我们也可以很容易对此进行修改以引人其他基分类器。实际上,任意分类器都可以作为基分类器。上述算法会输出一个单层决策树的数组,因此首先需要建立一个新的Python表来对其进行存储。然后,得到数据集中的数据点的数目m,并建立一个列向量D。
向量D非常重要,它包含了每个数据点的权重。一开始,这些权重都赋予了相等的值。在后续的迭代中,AdaBoost算法会在增加错分数据的权重的同时,降低正确分类数据的权重。D是一个概率分布向量,因此其所有的元素之和为1.0。为了满足此要求,一开始的所有元素都会被初始化成1/m。同时,程序还会建立另一个列向量aggclassEst,记录每个数据点的类别估计累计值。
AdaBoost算法的核心在于for循环,该循环运行numIt次或者直到训练错误率为0为止。循环中的第一件事就是利用前面介绍的buildstump()函数建立一个单层决策树。该函数的输入为权重向量D,返回的则是利用D而得到的具有最小错误率的单层决策树,同时返回的还有最小的错误率以及估计的类别向量。
接下来,需要计算的则是alpha值。该值会告诉总分类器本次单层决策树输出结果的权重。其中的语句max(error,1e-16)用于确保在没有错误时不会发生除零溢出。而后,alpha值加人到bestStump字典中,该字典又添加到列表中。该字典包括了分类所需要的所有信息。
接下来的三行则用于计算下一次迭代中的新权重向量D。在训练错误率为0时,就要提前结束for循环。此时程序是通过aggclassEst变量保持一个运行时的类别估计值来实现的。该值只是一个浮点数,为了得到二值分类结果还需要调用 sign() 函数。如果总错误率为0,则由break语句中止for循环。
接下来我们观察一下中间的运行结果。数据的类别标签为[1.0,1.0,-1.0,-1.0,1.0]。在第一轮迭代中,D中的所有值都相等。于是,只有第一个数据点被错分了。因此在第二轮迭代中,D向量给第一个数据点0.5的权重。这就可以通过遍历aggClassEst的符号来了解总的类别。第二次迭代之后,我们就会发现第一个数据点已经正确分类了,但此时最后一个数据点却是错分了。D向量中的最后一个元素变成0.5,而D向量中的其他值都变得非常小。最后,第三次迭代之后aggClassEst所有值的符号和真实类别标签都完全吻合,那么训练错误率为0,程序退出。

import adaboost
from numpy import *
datmat,classLables = adaboost.loadSimpData()
D = mat(ones((5,1))/5)
classifierArray = adaboost.adaBoostTrainDS(datmat,classLables,9)
print(classifierArray)

该数组包含三部词典,其中包含了分类所需要的所有信息。

7.5 测试算法:基于AdaBoost的分类

接下来需要将弱分类器的训练过程从程序中抽出来,然后应用到某个具体的实例上去。每个弱分类器的结果以其对应的alpha值为权重,所有这些弱分类器的结果加权求和就得到了最后的结果。将下面的代码添加到adaboost.py中,就可以利用它基于 adaboTrainDS()  中的弱分类器对数据进行分类。

def adaClassify(datToClass,classifierArr):
    dataMatrix = mat(datToClass)#do stuff similar to last aggClassEst in adaBoostTrainDS
    m = shape(dataMatrix)[0]
    aggClassEst = mat(zeros((m,1)))
    for i in range(len(classifierArr)):
        classEst = stumpClassify(dataMatrix,classifierArr[i]['dim'],\
                                 classifierArr[i]['thresh'],\
                                 classifierArr[i]['ineq'])#call stump classify
        aggClassEst += classifierArr[i]['alpha']*classEst
        print(aggClassEst)
    return sign(aggClassEst)

上述函数是利用训练出的多个弱分类器进行分类的函数。该函数的输入是由一个或多个待分类样例datToClass以及多个弱分类器组成的数组classifierArr。函数 adaClassify() 首先将datToClass转换成一个NumPy矩阵,并且得到datToClass中的待分类样例的个数m。然后构建一个0列向量aggClassEst,这个列向量与 adaBoostTrainDS() 中的含义一样。
接下来,遍历classifierArr中的所有弱分类器,并基于 stumpClassify() 对每个分类器得到一个类别的估计值。在此我们只是简单地应用了单层决策树。输出的类别估计值乘上该单层决策树的alpha权重然后累加到aggClassEst上就完成了这一过程。最后程序返回aggClassEst的符号,即如果aggClassEst大于0则返回+1,小于0则返回-1。

import adaboost
datArr,labelArr = adaboost.loadSimpData()
classifierArr = adaboost.adaBoostTrainDS(datArr,labelArr,30)
adaboost.adaClassify([0,0],classifierArr)

可以看到,随着迭代的进行,数据点[0,0]的分类结果越来越强。尝试对其他点进行分类:

import adaboost
from numpy import *
datArr,labelArr = adaboost.loadSimpData()
classifierArr = adaboost.adaBoostTrainDS(datArr,labelArr,30)
adaboost.adaClassify([[5,5],[0,0]],classifierArr)

两个点的分类结果同样随着迭代的进行越来越强。

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

下面的函数在向文件中加载数据时,能够自动检测出特征的数目。同时,该函数也假定最后一个特征是类别标签。将代码加入到 adaboost.py 中:

def loadDataSet(fileName):      #general function to parse tab -delimited floats
    numFeat = len(open(fileName).readline().split('\t')) #get number of fields 
    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
import adaboost
from numpy import *
datArr,labelArr = adaboost.loadDataSet('horseColicTraining2.txt')
classifierArray = adaboost.adaBoostTrainDS(datArr,labelArr,10)
testArr,testLabelArr = adaboost.loadDataSet('horseColicTest2.txt')
prediction10 = adaboost.adaClassify(testArr,classifierArray)
errArr = mat(ones((67,1)))
print(errArr[prediction10 != mat(testLabelArr).T].sum())

将错分样例个数除以67得到错误率为16%。
将弱分类器的数目设定为1到10000之间的几个不同数字,并运行上述过程。这时得到结果如下表所示。 可以看到,仅仅使用了50个弱分类器就达到了较高的性能。

 

测试错误率在达到一个最小值之后又开始上升,这种现象称为过拟合(过学习)。有文献声称,对于表现好的数据集,AdaBoost的测试错误率就会达到一个稳定值,并不会随着分类器的增多而上升。或许在本例子中的数据集的“表现”并不好 

7.7 非均衡分类问题

7.7.1 其他分类性能度量指标:正确率、召回率及ROC曲线

之前对于错误率的度量,掩盖了样例如何被分错的事实。在机器学习中,有一个普遍适用的称为混淆矩阵的工具,它可以帮助人们更好地了解分类中的错误。一个示例如下:

利用混淆矩阵就可以更好地理解分类中的错误。如果矩阵中的非对角元素均为0,就会得到一个完美的分类器。
接下来我们考虑另外一个混淆矩阵,这次的矩阵只针对一个简单的二类问题、。在下表中,如果一个正例判为正例,就可以认为产生了一个真正例(真阳);如果对一个反例正确地判为了反例,则认为陈胜了一个真反例(真阴)。相应地,另外两种情况则分别称为伪反例(假阴)和伪正例(假阳)。

我们可以根据上述定义定义出多个新指标。

  1. 正确率,它等于TP/(TP+FP),给出的是预测为正例的样本中的真正正例的比例。
  2. 召回率,它等于TP/(TP+FN),给出的是预测为正例的真实正例占所有真实正例的比例。

另一个用于度量分类中的非均衡性的工具是ROC曲线,ROC代表接收者操作特征。下图给出了一条ROC曲线的例子。

图中横轴是伪正例的比例(假阳率=FP/(FP+TN)),而纵轴是真正例的比例(真阳率=TP(TP+FN))。ROC曲线给出的是当阈值变化时假阳率和真阳率的变化情况。左下角的点所对应的是所有样例判为反例的情况,而右上角的点对应的是将所有样例判为正例的情况。虚线给出的是随机猜测的结果曲线。
ROC曲线不但可以用于比较分类器,还可以基于成本效益分析来做出决策。由于在不同的阈值下,不同的分类器表现可能各不相同,因此某种方式将它们组合起来或许更有意义。
在理想的情况下,最佳的分类器应该尽可能地处于左上角,这意味着分类器在假阳率很低的同时获得了很高的真阳率。
对不同的ROC曲线进行比较的一个指标是曲线下的面积。AUC给出的是分类器的平均性能值 ,当然它并不能完全替代对整条曲线的观察,一个完美分类器的AUC为1.0,而随机猜测的AUC为0.5。
为了画出ROC曲线,分类器必须提供每个样例被判为阳性或者阴性的可信程度值。大多数分类器都能做到,但通常这些值会在最后输出离散分类标签之前被清除。朴素贝叶斯能够提供一个可能性,而在Logistic回归中输入到Sigmoid函数中的是一个数值。在AdaBoost和SVM中,都会计算出一个数值然后输入到sign()函数中。所有这些值都可以用于衡量给定分类器的预测强度。为了创建ROC曲线,首先要将分类样例按照其预测强度排序。先从排名最低的样例开始,所有排名更低的样例都被判为反例,而所有排名更高的样例都被判为正例。该情况的对应点为<1.0,1.0>。然后,将其移到排名次低的样例中去,如果该样例属于正例,那么对真阳率进行修改;如果该样例属于反例,那么对假阴率进行修改。
为adaboost.py添加下列代码:

def plotROC(predStrengths, classLabels):
    cur = (1.0,1.0) #cursor
    ySum = 0.0 #variable to calculate AUC
    numPosClas = sum(array(classLabels)==1.0)
    yStep = 1/float(numPosClas); xStep = 1/float(len(classLabels)-numPosClas)
    sortedIndicies = predStrengths.argsort()#get sorted index, it's reverse
    fig = plt.figure()
    fig.clf()
    ax = plt.subplot(111)
    #loop through all the values, drawing a line segment at each point
    for index in sortedIndicies.tolist()[0]:
        if classLabels[index] == 1.0:
            delX = 0; delY = yStep;
        else:
            delX = xStep; delY = 0;
            ySum += cur[1]
        #draw line from cur to (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)
    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.show()
    print("the Area Under the Curve is: ",ySum*xStep)

上述程序中的函数有两个输入参数,第一个参数是一个NumPy数组或者一个行向量组成的矩阵。该参数代表的则是分类器的预测强度。在分类器和训练函数将这些数值应用到sign()函数之前,它们就已经产生了。尽管很快就可以看到该函数的实际执行效果,但是我们还是要先讨论一下这段代码。函数的第二个输入参数是先前使用过的classLabels。我们首先导人pyplot,然后构建一个浮点数二元组,并将它初始化为(1,0,1.0)。该元组保留的是绘制光标的位置,变量ySum则用于计算AUC的值。接下来,通过数组过滤方式计算正例的数目,并将该值赋给numPosClas。该值先是确定了在y坐标轴上的步进数目,接着我们在x轴和y轴的0.0到1.0区间上绘点,因此y轴上的步长是1.0/numPosClas。类似地,就可以得到x轴的步长了。
接下来,我们得到了排序索引,但是这些索引是按照最小到最大的顺序排列的,因此我们需要从点<1.0,1.0>开始绘,一直到<0,0>。跟着的三行代码则是用于构建画笔,并在所有排序值上进行循环。这些值在一个NumPy数组或者矩阵中进行排序,Python则需要一个表来进行迭代循环,因此我们需要调用tolist()方法。当遍历表时,每得到一个标签为1.0的类,则要沿着y轴的方向下降一个步长,即不断降低真阳率。类似地,对于每个其他类别的标签,则是在x轴方向上倒退了一个步长(假阴率方向)。
为了计算AUC,我们需要对多个小矩形的面积进行累加。这些小矩形的宽度输xStep,因此可以先对所有矩形的高度进行累加,最后再乘以xStep得到其总面积。所有高度的和(ySum)随着x轴的每次移动而渐次增加。一旦决定了是在x轴还是y轴方向上进行移动的,我们就可以在当前点和新点之间画出一条线段。然后,当前点cur更新了。最后,我们就会得到一个像样的绘图并将其AUC打印到终端输出。

将adaboostTrainDS()最后一行代码替换成:

return weakClassArr,aggClassEst
import adaboost
datArr,labelArr = adaboost.loadDataSet('horseColicTraining2.txt')
classifierArray,aggClassEst = adaboost.adaBoostTrainDS(datArr,labelArr,10)
print(adaboost.plotROC(aggClassEst.T,labelArr))

 

AUC取值范围在0.5-1.0之间,且越接近1.0真实性越高。该样例检测出为0.858...... ,说明还有较大提高空间。

7.7.2 基于代价函数的分类器决策控制

代价敏感学习也是用于处理非均匀分类代价问题的方法。考虑下图中的代价矩阵,第一张表给出的是到目前为止分类器的代价矩阵(代价不是0就是1)。我们可以基于该代价矩阵计算其总代价:TP*0+FN*1+FP*1+TN*0。第二张表基于该代价矩阵的分类代价的计算公式为:TP*(-5)+FN*1+FP*50+TN*0。采用第二张表作为代价矩阵时,两种分类错误的代价是不一样的。类似地,这两种正确分类所得到的收益也不一样。如果在构建分类器时,知道了这些代价值,那么就可以选择付出最小代价的分类器。
在分类算法中,我们有很多方法可以用来引入代价信息。在AdaBoost中,可以基于代价函数来调整错误权重向量D。在朴素贝叶斯中,可以选择具有最小期望代价而不是最大概率的类别作为最后的结果。在SVM中,可以在代价函数中对于不同的类别选择不同的参数c。上述做法就会给较小类更多的权重,即在训练时,小类当中只允许更少的错误。

7.7.3 处理非均衡问题的数据抽样方法

另外一种针对非均衡问题调节分类器的方法,就是对分类器的训练数据进行改造。这可以通过欠抽样或者过抽样来实现。过抽样意味着复制样例,而欠抽样意味着删除样例。不管采用哪种方式,数据都会从原始形式改造为新形式。抽样过程则可以通过随机方式或者某个预定方式来实现。
通常也会存在某个罕见的类别需要我们来识别,比如在信用卡欺诈当中。如前所述,正例类别属于罕见类别。我们希望对于这种罕见类别能尽可能保留更多的信息,因此,我们应该保留正例类别中的所有样例,而对反例类别进行欠抽样或者样例删除处理。这种方法的一个缺点就在于要确定哪些样例需要进行剔除。但是,在选择剔除的样例中可能携带了剩余样例中并不包含的有价值信息。
上述问题的一种解决办法,就是选择那些离决策边界较远的样例进行删除。假定我们有一个数据集,其中有50例信用卡欺诈交易和5000例合法交易。如果我们想要对合法交易样例进行欠抽样处理,使得这两类数据比较均衡的话,那么我们就需要去掉4950个样例,而这些样例中可能包含很多有价值的信息。这看上去有些极端,因此有一种替代的策略就是使用反例类别的欠抽样和正例类别的过抽样相混合的方法。
要对正例类别进行过抽样,我们可以复制已有样例或者加入与已有样例相似的点。一种方法是加入已有数据点的插值点,但是这种做法可能会导致过拟合的问题。
 

 

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
第5主要是针对公司财务预警建模的问题,提出了基于bp_adaboost的强分类器设计方案。在现代经济社会中,公司财务状况对其经营和发展至关重要。因此,建立有效的财务预警模型对于未雨绸缪、防范风险具有重要意义。 在这一节中,我们首先对bp_adaboost算法进行了介绍,并说明了其在财务预警建模中的应用价值。bp_adaboost算法是一种基于弱分类器集成的机器学习算法,通过多轮迭代训练,不断调整分类器的权重和分布,从而得到一个强分类器。这种算法在财务预警建模中具有很好的适用性,能够更准确地捕捉财务指标之间的复杂关联,提高模型的预测精度。 其次,我们提出了基于bp_adaboost的强分类器设计方案。通过对财务数据进行特征提取和处理,构建bp_adaboost模型,并使用交叉验证等方法对模型进行评估和调优,最终得到了一个适用于公司财务预警的强分类器。 最后,我们展示了实验结果和分析。通过对真实公司财务数据的实证分析,我们验证了基于bp_adaboost的强分类器设计在财务预警建模中的有效性和优越性。该模型能够有效地识别出潜在的财务风险,帮助企业及时采取措施,维护财务稳健和风险防范。 总之,第5基于bp_adaboost的强分类器设计为公司财务预警建模提供了一种有效的解决方案,具有良好的应用前景和实际推广的价值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值