提升方法(boosting)

原理:

  • 通过改变训练样本的权重,学习多个分类器,并将这些分类器进行线性组合,提高分类的性能;
  • bagging是通过随机抽样的替换方式,得到与原数据集规模一样的数据;
  • boosting在bagging的思路上更进一步,在数据集上顺序应用了多个不同的分类器。

优点:

  • 泛化错误率低,易编码,可以用在大部分分类器上,无参数调整。

缺点:

  • 对离群点敏感

适用数据类型:

  • 数值型和标称型数据
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
def loadSimpData():
    dataMat = np.mat([[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
dataMat,classLabels=loadSimpData()

#可视化
x = pd.DataFrame(dataMat)
y= pd.Series(classLabels)
pos = x[y==1].copy()
neg = x[y==-1].copy()
plt.figure()
plt.scatter(pos.loc[:,0],pos.loc[:,1],c='b',label='positive',marker='s')
plt.scatter(neg.loc[:,0],neg.loc[:,1],c='r',label='negative')
plt.show()
output_1_0.png

分类规则

def stumpClassify(dataMatrix,dimen,threshVal,threshIneq):
    retArray = np.ones((np.shape(dataMatrix)[0],1))
    if threshIneq == 'lt':
        retArray[dataMatrix[:,dimen] <= threshVal] =-1.0  #大小判断进行分类
    else: #threshIneq == 'gt'
        retArray[dataMatrix[:,dimen] > threshVal] =-1.0
    return retArray

遍历所有特征找到最小误差对应的特征、阈值和分类规则

def buildStump(dataArr,classLabels,D):
    dataMatrix = np.mat(dataArr);labelMat = np.mat(classLabels).T
    m,n=np.shape(dataMatrix)
    numSteps = 10.0; bestStump = {}; bestClassEst = np.mat(np.zeros((m,1)))
    minError=np.inf
    for i in range(n):
        rangeMin = dataMatrix[:,i].min() #取第i个特征的最小值
        rangeMax = dataMatrix[:,i].max() #取第i个特征的最大值
        stepSize = (rangeMax - rangeMin)/numSteps #设置步长
        for j in range(-1,int(numSteps)+1): #j:-1到11,用于遍历不同的阈值
            for inequal in ['lt','gt']: #lt:less than; gt: great than
                threshVal = rangeMin + float(j)*stepSize #设置阈值,按一个步长一个步长往上增加
                predictedVals = stumpClassify(dataMatrix,i,threshVal,inequal) #分类结果
                errArr = np.mat(np.ones((m,1))) #初始化误差矩阵
                errArr[predictedVals == labelMat] = 0 #正确分类的误差为0
                weightedError = D.T*errArr #计算分类误差率,参考李航P139
#                 print("split: dim %d, thresh %.2f, thresh ineqal: %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

D = np.mat(np.ones((5,1))/5)
print(D)
buildStump(dataMat,classLabels,D)
[[0.2]
 [0.2]
 [0.2]
 [0.2]
 [0.2]]





({'dim': 0, 'thresh': 1.3, 'ineq': 'lt'}, matrix([[0.2]]), array([[-1.],
        [ 1.],
        [-1.],
        [-1.],
        [ 1.]]))

完整版AdaBoost算法实现

from math import *
def adaBoostTrainDS(dataArr,classLabels,numIt=40):
    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)#classEst:array
#         print('D:\n',D.T)
        
        #更新alpha,weight
        alpha = float(0.5*log((1.0-error)/max(error,1e-16)))
        bestStump['alpha'] = alpha 
        weakClassArr.append(bestStump)
#         print('classEst:\n',classEst.T)
        expon = np.multiply(-1*alpha*np.mat(classLabels).T,classEst) #matrix
        D = np.multiply(D,np.exp(expon))
        D = D/D.sum()
        
        #记录每个样布的评估结果,可以看到每个样本的评估都在往一个正确的方向变化
        aggClassEst += alpha*classEst
#         print('aggClassEst:\n',aggClassEst)
        
        #统计错误率
        aggErrors = np.multiply(np.sign(aggClassEst) != np.mat(classLabels).T,np.ones((m,1))) #统计错误个数
        errorRate = aggErrors.sum() / m#计算错误率
        print('total error:\n',errorRate)
        
        #若错误率为0,则停止迭代
        if errorRate == 0.0: break
    return weakClassArr,aggClassEst

classifierArray,aggClassEst = adaBoostTrainDS(dataMat,classLabels,9)
classifierArray
total error:
 0.2
total error:
 0.2
total error:
 0.0





[{'dim': 0, 'thresh': 1.3, 'ineq': 'lt', 'alpha': 0.6931471805599453},
 {'dim': 1, 'thresh': 1.0, 'ineq': 'lt', 'alpha': 0.9729550745276565},
 {'dim': 0, 'thresh': 0.9, 'ineq': 'lt', 'alpha': 0.8958797346140273}]

测试算法

def adaClassify(datToclass,classifierArr):
    dataMatrix = np.mat(datToclass)
    m = np.shape(dataMatrix)[0]
    aggClassEst = np.mat(np.zeros((m,1)))
    for i in range(len(classifierArray)): #遍历全部弱分类器
        classEst = stumpClassify(dataMatrix,classifierArr[i]['dim'],classifierArray[i]['thresh'],classifierArray[i]['ineq']) #分类结果:1,-1
        aggClassEst += classifierArray[i]['alpha']*classEst
#         print(aggClassEst)
    res =np.sign(aggClassEst)
    return res
#测试
adaClassify([0,0],classifierArray)
matrix([[-1.]])

在马疝病数据集应用AdaBoost分类器

def loadDataSet(fileName):
    fr = open(fileName)
    numFeature = len(fr.readline().strip().split('\t'))
    dataMat =[];labelMat =[]
    for line in fr.readlines():
        lineList =[]
        curLine = line.strip().split('\t')
        for i in range(numFeature-1):
            lineList.append(float(curLine[i]))
        dataMat.append(lineList)
        labelMat.append(float(curLine[-1]))
    return dataMat,labelMat
traindataMat,trainlabelMat = loadDataSet('../../Reference Code/Ch07/horseColicTraining2.txt')
testdataMat,testlabelMat = loadDataSet('../../Reference Code/Ch07/horseColicTest2.txt')
classifierArray,aggClassEst = adaBoostTrainDS(traindataMat,trainlabelMat,10)
classifierArray
total error:
 0.28523489932885904
total error:
 0.28523489932885904
total error:
 0.2483221476510067
total error:
 0.2483221476510067
total error:
 0.2483221476510067
total error:
 0.24161073825503357
total error:
 0.24161073825503357
total error:
 0.2214765100671141
total error:
 0.2483221476510067
total error:
 0.2214765100671141





[{'dim': 9, 'thresh': 3.0, 'ineq': 'gt', 'alpha': 0.4593204546095544},
 {'dim': 17, 'thresh': 52.5, 'ineq': 'gt', 'alpha': 0.31654488263333286},
 {'dim': 3,
  'thresh': 55.199999999999996,
  'ineq': 'gt',
  'alpha': 0.28402835050611847},
 {'dim': 18,
  'thresh': 62.300000000000004,
  'ineq': 'lt',
  'alpha': 0.23222873860913737},
 {'dim': 10, 'thresh': 0.0, 'ineq': 'lt', 'alpha': 0.19836267426245105},
 {'dim': 5, 'thresh': 2.0, 'ineq': 'gt', 'alpha': 0.18642416210017293},
 {'dim': 12, 'thresh': 1.2, 'ineq': 'lt', 'alpha': 0.1496988869138094},
 {'dim': 7, 'thresh': 1.2, 'ineq': 'gt', 'alpha': 0.15848275395378547},
 {'dim': 5, 'thresh': 0.0, 'ineq': 'lt', 'alpha': 0.1370746524177519},
 {'dim': 0, 'thresh': 1.0, 'ineq': 'lt', 'alpha': 0.12365372615766472}]
prediction = adaClassify(testdataMat,classifierArray)
errArr = np.ones((len(prediction),1))
errNum = errArr[prediction!=np.mat(testlabelMat).T].sum()
errRate= errNum/len(prediction)
print('错误个数:%d'%errNum)
print('错误率:%.2f'%errRate)
错误个数:15
错误率:0.23

ROC曲线

def plotROC(predStrengths,classLabels):
    import matplotlib.pyplot as plt
    cur = (0.,0.)
    ySum =0.
    numPosClass = sum(np.array(classLabels)==1.0) #统计正例的数目
    yStep = 1/ float(numPosClass) #正阳率的步长
    xStep = 1/float(len(classLabels)-numPosClass) #假阳率的步长
    sortedIndicies = predStrengths.argsort() #从小到大排序,返回index
    fig = plt.figure()
#     fig.clf()
    ax = plt.subplot(111)
    #[::-1],反转,从大到小排序,即所有样例判定为反例
    for index in sortedIndicies.tolist()[0][::-1]: #tolist(),matrix 变成list,才能遍历里面的元素
        if classLabels[index] == 1.0: #若当前为正例,则正阳率增加一个步长,假阳率不变
            delX=0;delY=yStep
        else:
            delX = xStep;delY=0
            ySum+=cur[1]
        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')
    ax.axis([0,1,0,1])
    plt.show()
    print('the Area Under the Curve is :',ySum*xStep)
traindataMat,trainlabelMat = loadDataSet('../../Reference Code/Ch07/horseColicTraining2.txt')
classifierArray,aggClassEst = adaBoostTrainDS(traindataMat,trainlabelMat,10)

total error:
 0.28523489932885904
total error:
 0.28523489932885904
total error:
 0.2483221476510067
total error:
 0.2483221476510067
total error:
 0.2483221476510067
total error:
 0.24161073825503357
total error:
 0.24161073825503357
total error:
 0.2214765100671141
total error:
 0.2483221476510067
total error:
 0.2214765100671141
plotROC(aggClassEst.T,trainlabelMat) #aggClassEst.T变成一行
output_21_0.png
the Area Under the Curve is : 0.8538389513108627
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容