AdaBoost算法详解与实战

【原创】Liu_LongPo 转载请注明出处

【CSDN】http://blog.csdn.net/llp1992

AdaBoost算法是基于单层决策树等弱分类算法的强学习分类算法。单层决策树算法也是一种分类算法,但是其分类效果较差,只根据一个特征进行数据划分,因此单层决策树算法被称为弱分类算法;而AdaBoost算法通过将多个弱分类算法串行训练而成,属于强分类算法。

AdaBoost算法是boosting算法的一种,它所串联的弱分类器一般都是一致的,而且它训练是的关注点在于被之前分类器分错的数据,通过调整每个样本数据的权重 D 以及每个分类器的权重 a 来进行强分类器的训练。

优缺点

优点:泛化错误率低,可应用在大部分分类器上,无需调整参数

缺点:对离群点敏感

算法原理

AdaBoost是adaptive boosting的缩写,运行过程如下:

1.训练数据中的每个样本刚开始被赋予一个相等的权重,这些权重构成向量 D

2.在训练集上训练出一个弱分类器并计算该分类器的错误率 ?

3.根据分类器的错误率可以计算出该分类器的权重 a=12ln(1???)

4.根据上一次分类器的权重 a 调整每个训练样本的权重,分对的样本权重降低,分错的样本权重升高,然后在同一训练集上再次训练弱分类器

5.训练直到错误率为0或者弱分类器的数目达到用户指定值为止

上述过程中错误率 ? 的计算方法如下:

?=未正确分类的样本数目所有样本数目

分类器的权重的计算方法如下:

a=12ln(1???)

正确分类样本权重更新方法如下:

D(t+1)i=D(t)ie?aSum(D)

错误分类样本权重更新方法如下:

D(t+1)i=D(t)ieaSum(D)

算法流程图

如上图:左边第一列是样本数据,直方图的不同长度代表不同样本的权重D,三角形中的数字就是每个分类器的权重a。

当我们训练得到模型后,直接将数据乘以权重代入分类器,然后再将分类器加权求和进行分类,就得到我们的预测结果

关于样本权重以及分类器权重:

上面我们说到,AdaBoost算法关注点在于被已有分类器分错的那些数据,而数据的权重的更新方法是:分对的数据的权重降低,分错的数据权重升高。这是因为我们算法主要就是找到 误差最小的分类器,分错的数据显然是比较难区分的点,加大权重进行训练有利于我们找到性能更好的分类器对其进行划分,从而减少总的误差。

而分类效果好的分类器,由上面的权重a的更新公式可以看出, a 跟误差 ? 是负相关关系,也就是说,分类误差 ? 越小的分类器权重越高,这也很容易理解,就是为了让所有分类器求和之后得到的分类效果更好。

实例详解

该实例的图片来自百度某大牛,链接好像失效了。文字描述是自己对算法的理解。

如上图,假如我们现在有数据集,+号和-号分别代表两种不同的数据集,总共10个数据点。样本的权重 D1 此时是相等的,为 0.1 ,也就是 1/10 。运用AdaBoost算法时,我们首先使用水平或者垂直的直线作为分类器(默认使用决策树算法作为弱分类器)来对数据进行划分。

第一次迭代,也就是第一个分类器:

显然,有3个样本被分错了,根据上面的误差公式,我们可以得到误差 ?=310=0.3

同样的,根据分类器的权重公式可以得到 a1=0.5×ln(1?0.30.3)=0.42

同样的,此时每个样本的权重 D也可以根据公式计算,由于数据比较多,这里就不计算啦。样本权重更新之后如下:

图中被分错的3个数据点被标大了,表示它们的权重被加大了。

第二次迭代,也就是第二个分类器:

由上图可以知道,有3个点分错了,同样的,样本权重?2和分类器权重a2可以由公式计算得到。这次迭代之后的样本权重更新如下:

第三次迭代,也就是第三个分类器:

每一次迭代之后,分类器的预测结果都是由前面所有的分类器的分类结果与分类器的权重 a 相乘累加得到,如果累加得到的分类结果误差为0,或者是分类器的数目达到用户指定的值,那么循环退出。

这里假设我们3个分类器就可以达到分类误差为0的效果。整合所有的分类器,也就是加权求和,最终得到的结果如下:

由图可以知道,此时的分类效果较好。

Python代码实现

接下来用Python代码实现AdaBoost算法并进行测试。

首先加载数据集,如下:

def loadSimpData():
    dataMat = array([[1.,2.1],[1.5,1.6],[1.3,1.],[1.,1.],[2.,1.],[1.2,1.1],    [1.1,0.4],[0.9,1.3],[0.86,1.2],[1.8,1.8],[1.7,1.5],[1.9,1.8]])
    classLabels = array([1.0,1.0,-1.0,-1.0,1.0,-1.0,-1.0,-1.0,    -1.0,1.0,1.0,1.0])
    return dataMat,classLabels

如图:

我们要做的就是将这两个不同的数据集分开。

首先我们建立单层决策树分类器,代码如下:

from numpy import *
# 该函数根据某个最好的特征的最好划分点对数据进行分类
# demen就是特征,threshVal就是划分点,retArray就是返回的分类结果
def stumpClassify(dataMatrix,dimen,threshVal,threshIneq):
    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是列,也就是每个特征
    m,n = shape(dataMatrix)
    # 步数的设置,也就是在每个特征的最大值和最小值中分几次设置阈值进行数据划分
    # 也就是获取这个特征的最佳划分点,步数越高,特征点寻找得越精细,但耗时更多
    # 用字典来存储bestStump的数据
    numSteps = 10.0;bestStump = {};bestClassEst = mat(zeros((m,1)))
    minError = inf
    # 对每个特征
    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):
            for inequal in [‘lt‘,‘gt‘]:
                threshVal = (rangeMin + float(j) * stepSize)
                # 预测值
                predictedVals = stumpClassify(dataMatrix,i,threshVal,inequal)
                errArr = mat(ones((m,1)))
                errArr[predictedVals == labelMat] = 0
                # 样本权重乘以样本误差
                weightedError = D.T * errArr
                print "split:dim %d,thresh %.2f, thresh ineqal: %s,the weighted eror 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

然后再编写AdaBoost训练函数,该函数返回值为弱分类器的组合

def adaBoostTrainDS(dataArr,classLabels,numIt = 40):
    weakClassArr = []
    m = shape(dataArr)[0]
    D = mat(ones((m,1))/m)
    # 创建矩阵 mat
    aggClassEst = mat(zeros((m,1)))
    for i in range(numIt):
        bestStump,error,classEst = buildStump(dataArr,classLabels,D)
        print ‘D:‘,D.T
        #  a = 0.5*ln((1-e)/e)
        alpha = float(0.5*log((1.0-error)/max(error,1e-16)))
        bestStump[‘alpha‘] = alpha
        # 将当前的最好的树桩添加到弱分类其数组中
        weakClassArr.append(bestStump)
        print ‘classEst:‘,classEst.T
        # D权重的更新公式,利用原本的类别classLabels与划分的类别classEst做乘积
        # 用来同时计算正确划分和错误划分的公式,也就是自动确定正负号
        expon = multiply(-1*alpha*mat(classLabels).T,classEst)
        # 更新权重D
        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,"\n"
        if errorRate == 0.0:break;
    return weakClassArr

最后一个函数是用训练得到的多个分类器进行级联数据分类

    # 利用学习得到的多个级联弱分类器进行数据分类
def adaClassify(datToClass,classifierArr):
    dataMatrix = mat(datToClass)
    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‘])
        aggClassEst += classifierArr[i][‘alpha‘] * classEst
        print aggClassEst
    return sign(aggClassEst)

测试代码

# -*- coding: utf-8 -*-
"""
Created on Thu Jun 11 12:57:27 2015
AdaBoost
@author: LiuLongpo
"""

import adaboost
import matplotlib
import matplotlib.pyplot as plt
from numpy import *
dataMat,classLabels = adaboost.loadSimpData()

# D是样本的权重矩阵
D = mat(ones((5,1))/5)
#adaboost.buildStump(dataMat,classLabels,D)
print ‘data train...‘
classifierArr = adaboost.adaBoostTrainDS(dataMat,classLabels,30)
print ‘getClassifier:‘,classifierArr
print ‘data predict...‘
# 学习得到3个分类器,predict时,每一个分类器级联分类得到的预测累加值
# aggClassEst越来越远离0,也就是正越大或负越大,也就是分类结果越来越强
adaboost.adaClassify([[1,0.8],[1.8,2]],classifierArr)
# 0,lt,1.3   1,lt,1.0   0,lt,0.9
plt.figure()
I = nonzero(classLabels>0)[0]
plt.scatter(dataMat[I,0],dataMat[I,1],s=60,c=u‘r‘,marker=u‘o‘)
I = nonzero(classLabels<0)[0]
plt.scatter(dataMat[I,0],dataMat[I,1],s=60,c=u‘b‘,marker=u‘o‘)

plt.plot([1.32,1.32],[0.5,2.5])
plt.plot([0.5,2.5],[1.42,1.42])
plt.plot([0.97,0.97],[0.5,2.5])

训练完数据后,我们得到的分类器总数为3,如下:

[{‘dim’: 0, ‘ineq’: ‘lt’, ‘thresh’: 1.3160000000000001, ‘alpha’: 1.1989476363991853}, {‘dim’: 1, ‘ineq’: ‘lt’, ‘thresh’: 1.4199999999999999, ‘alpha’: 1.5222612188617115}, {‘dim’: 0, ‘ineq’: ‘lt’, ‘thresh’: 0.97399999999999998, ‘alpha’: 1.1256458993032477}]

第一个分类器是对0维,也就是x轴的特征进行划分,划分的点为:1.3160,分类器的阈值 a 为:1.1989,不等号是:lt,小于

第二个分类器是对1维,也就是y轴的特征进行划分,划分的点为:1.4199,分类器的阈值 a 为:1.5222,不等号是:lt,小于

第三个分类器是对0维,也就是x轴的特征进行划分,划分的点为:0.9739,分类器的阈值 a 为:1.1256,不等号是:lt,小于

画出来如下图:

这三个分类器串联起来训练时误差为0,接下来我们来分析上图,看看它是怎么个分类法。

回到这个函数 stumpClassify(). 中间代码如下:

 retArray = ones((shape(dataMatrix)[0],1))
 if threshIneq == ‘lt‘:
     retArray[dataMatrix[:,dimen]<=threshVal] = -1.0
 else:
     retArray[dataMatrix[:,dimen]>threshVal] = -1.0

以及这个函数:buildStump(),中间有代码如下:

          for inequal in [‘lt‘,‘gt‘]:
                threshVal = (rangeMin + float(j) * stepSize)
                # 预测值
                predictedVals = stumpClassify(dataMatrix,i,threshVal,inequal)
                errArr = mat(ones((m,1)))
                errArr[predictedVals == labelMat] = 0
                # 样本权重乘以样本误差
                weightedError = D.T * errArr
                print "split:dim %d,thresh %.2f, thresh ineqal: %s,the weighted eror is %.3f" %                (i,threshVal,inequal,weightedError)
                if weightedError < minError:
                    minError = weightedError
                    # 最好的分类结果
                    bestClassEst = predictedVals.copy()
                    bestStump[‘dim‘] = i
                    bestStump[‘thresh‘] = threshVal
                    bestStump[‘ineq‘] = inequal

我们在代码中对于有不等号 lt 和 gt ,这两个的作用就是用来对划分的阈值后进行分类的,我们初始化数据时是分为+1和-1两类,所以在stumpClassify()函数中,首先让返回矩阵初始化为全1矩阵,如果当前是lt,也就是小于号,则让所有当前维的特征中小于等于阈值的数据样本全部划分为-1的类,如果是gt,也就是大于号,则让当前维的特征中大于阈值的所有数据点全部划分为-1的类。这就完成了根据某个特征的某一个划分点对数据进行分类的功能。

这个函数buildStump()中就是不断在每个特征的每个划分点的大于号和小于号中寻找分类误差最小的划分方法。

由上面知道,我们的3个分类器都是lt,我们看第一个分类器,也就四对应图上的蓝色直线(数据集中红色点为+1类,蓝色点为-1类),lt表示小于,也就是说,蓝色线左边的点被划分-1类,我们看到,有一个红色点被分错了,该分类器的阈值为:1.1989。可以看到,此时如果单独由这个分类器来划分数据是有误差的。

接着看第二个分类器,是对y轴进行划分,也就是绿色线,也是lt,也就是说,绿色线下方的就是被划分为-1类,我们看到,也是有1个红色点被分错了,该分类器的阈值为:1.5222,可以看到,如果结合第一和第二两个分类器来划分数据,也是有一个点划分错误,为右下角的红点。

接着看第三个分类器,是对x轴进行划分,也就是红色线,也是lt,也就是说,红色线左方的被划分为-1类,也就是蓝色类,咋一看,好像有好几个点分错啊,怎么可能!我们要知道,AdaBoost算法是多个弱分类器算法进行串联划分数据的,所以允许单个弱分类器出现划分错误,但是如果我们结合这三个分类器来看的话,数据集刚好被无误差划分好,如下:

红色圆圈中的区域就是红色线右边,绿色线上边和蓝色线右边三者的合集,完美划分了数据。

数据测试

我们使用学习到的分类器来对数据进行划分,假如有两个点:[1,0.8],[1.8,2],我们对其进行划分,代码上面有,过程如下:

data predict…

[[-1.19894764]

[ 1.19894764]]

[[-2.72120886]

[ 2.72120886]]

[[-1.59556296]

[ 3.84685475]]

可以看到,第一个点被划分为-1类,第二个点被划分为+1类,而且从第一个分类器到第三个分类器,第一个点的分类结果越来越小于0,第二个点的分类结果越来越大于0,也就是分类结果越来越强。

最后一个问题,对于简单,量比较小的数据集,AdaBoost算法是可以让训练错误率为0的。但是真实情况并不是这样,很多真实数据一般都是比较大,比较难划分的。

这时候,AdaBoost算法的弱分类器的个数就是一个要考虑的问题。分类器多,算法学习得比较充分。但跟其他算法一样,并不是学习得越充分越好,太多的弱分类器会导致学习过拟合。在难数据集上,AdaBoost算法在某个数目弱分类器上会达到一个稳定的测试错误率,也就是最小的错误率,如果分类器的数目多于这个值或者是小于这个值,分类器的分类性能都会降低。

写得比较多,为的是尽可能容易看懂。

时间: 2024-08-04 21:08:21

AdaBoost算法详解与实战的相关文章

Adaboost算法详解(haar人脸检测)

Adaboost是一种迭代算法,其核心思想是针对同一个训练集训练不同的分类器(弱分类器),然后把这些弱分类器集合起来,构成一个更强的最终分类器(强分类器).Adaboost算法本身是通过改变数据分布来实现的,它根据每次训练集之中每个样本的分类是否正确,以及上次的总体分类的准确率,来确定每个样本的权值.将修改过权值的新数据集送给下层分类器进行训练,最后将每次得到的分类器最后融合起来,作为最后的决策分类器. 算法概述 1.先通过对N个训练样本的学习得到第一个弱分类器: 2.将分错的样本和其他的新数据

图像算法研究---Adaboost算法详解

本篇文章先介绍了提升放法和AdaBoost算法.已经了解的可以直接跳过.后面给出了AdaBoost算法的两个例子,附有详细计算过程. 1.提升方法(来源于统计学习方法) 提升方法是一种常用的统计学习方法,应用十分广泛且有效.在分类问题中,它通过改变训练样本的权重,学习多个分类器,并将这些分类器进行线性组合,提高分类的性能.提升算法基于这样一种思路:对于一个复杂任务来说,将多个专家的判断进行适当的综合所得出的判断,要比其中任何一个专家单独的判断好.实际上,就是"三个臭皮匠顶个诸葛亮"的道

我的新书:《工业机器学习算法详解与实战》

图书简介 实用性是本书的第一个基本出发点,书中介绍了近年来在工业界被广泛应 用的机器学习算法,这些算法经受了时间的考验,不但效果好而且使用方便.此 外作者也十分注意理论的深度和完整性,组织各章节的内容时力求由浅入深.推 理完整.先后连贯.自成体系,先讲统计学.矩阵.优化方法这些基础知识,再 由简到繁地介绍线性模型.概率图模型.文本向量化算法.树模型和神经网络, 神经网络主要指的是各种深层网络.跟绝大多数机器学习类的图书不同,本书 还会介绍算法周边的一些工程架构及实现原理,比如如何实时地收集训练样

机器学习经典算法详解及Python实现---朴素贝叶斯分类及其在文本分类、垃圾邮件检测中的应用

摘要: 朴素贝叶斯分类是贝叶斯分类器的一种,贝叶斯分类算法是统计学的一种分类方法,利用概率统计知识进行分类,其分类原理就是利用贝叶斯公式根据某对象的先验概率计算出其后验概率(即该对象属于某一类的概率),然后选择具有最大后验概率的类作为该对象所属的类.总的来说:当样本特征个数较多或者特征之间相关性较大时,朴素贝叶斯分类效率比不上决策树模型:当各特征相关性较小时,朴素贝叶斯分类性能最为良好.另外朴素贝叶斯的计算过程类条件概率等计算彼此是独立的,因此特别适于分布式计算.本文详述了朴素贝叶斯分类的统计学

机器学习经典算法详解及Python实现--线性回归(Linear Regression)算法

(一)认识回归 回归是统计学中最有力的工具之一.机器学习监督学习算法分为分类算法和回归算法两种,其实就是根据类别标签分布类型为离散型.连续性而定义的.顾名思义,分类算法用于离散型分布预测,如前面讲过的KNN.决策树.朴素贝叶斯.adaboost.SVM.Logistic回归都是分类算法:回归算法用于连续型分布预测,针对的是数值型的样本,使用回归,可以在给定输入的时候预测出一个数值,这是对分类方法的提升,因为这样可以预测连续型数据而不仅仅是离散的类别标签. 回归的目的就是建立一个回归方程用来预测目

EM算法(3):EM算法详解

目录 EM算法(1):K-means 算法 EM算法(2):GMM训练算法 EM算法(3):EM算法详解

[转] KMP算法详解

转载自:http://www.matrix67.com/blog/archives/115 KMP算法详解 如果机房马上要关门了,或者你急着要和MM约会,请直接跳到第六个自然段.    我们这里说的KMP不是拿来放电影的(虽然我很喜欢这个软件),而是一种算法.KMP算法是拿来处理字符串匹配的.换句话说,给你两个字符串,你需要回答,B串是否是A串的子串(A串是否包含B串).比如,字符串A="I'm matrix67",字符串B="matrix",我们就说B是A的子串.

[搜索]波特词干(Porter Streamming)提取算法详解(3)

 接上 [搜索]波特词干(Porter Streamming)提取算法详解(2) 下面分为5大步骤来使用前面提到的替换条件来进行词干提取. 左边是规则,右边是提取成功或者失败的例子(用小写字母表示). 步骤1 SSES -> SS                   caresses  ->  caress IES  -> I                          ponies    ->  poni ties      ->  ti SS   -> S

KMP算法详解(图示+代码)

算法过程非常绕,不要企图一次就能看明白,多尝试就会明白一些.下面试图用比较直观的方法解释这个算法,对KMP算法的解释如下: 1. 首先,字符串"BBC ABCDAB ABCDABCDABDE"的第一个字符与搜索词"ABCDABD"的第一个字符,进行比较.因为B与A不匹配,所以搜索词后移一位. 2. 因为B与A不匹配,搜索词再往后移. 3. 就这样,直到字符串有一个字符,与搜索词的第一个字符相同为止. 4. 接着比较字符串和搜索词的下一个字符,还是相同. 5. 直到字