Bootstrap

Boosting 算法

前言:

       Boosting 是一种集成学习方法,它由多个弱分类器组成(准确率>50%)。预测时,用若分类器分别进行预测,然后投票得到结果。 相对随机森林是样本随机抽样构造的训练集,Boosting 更关注被前面弱分类器错分的样本。

      AdaBoost 由Freund等人提出,是Boosting算法的一种实现版本。

     最后给出乳腺癌分类的例子

目录

  1.   算法简介
  2.   算法流程
  3.  误差分析
  4.  例子

一 算法简介:

     AdaBoost 算法全称自适应Boosting 算法(Adaptive Boosting)是一种二分类问题的算法。

通过弱分类线性组合来构造强分类器。 强分类器计算公式为:

        F(x)=\sum_{t=1}^{T}\alpha_t f_t(x)

        每个弱分类器输出值为: +1,-1

         \alpha_t: 代表对应的权重

         T: 弱分类器个数

         f_t(x): 弱分类器,要去>50%

        相对投票算法KNN, 多了一个权重系数

   


二 算法流程:

     输入:

     训练数据集   

     \begin{Bmatrix} (x_1,y_1) & (x_2,y_2) , & ...., & (x_N,y_N) \end{Bmatrix}

     y=\begin{Bmatrix} -1, & +1 \end{Bmatrix}

     弱学习算法 G_t(x)

     N:  样本个数

     T: 总的学习器个数

    1  初始化训练数据集权值分布:

         D_1=(w_{11},...w_{1N})

          其中 W_{1i}=\frac{1}{N}

 

    2:  t =1,2,...T

           a  G_t(x): X-> \begin{Bmatrix} -1, & +1 \end{Bmatrix}

           b: 计算G_t(x)在训练数据集上的分类误差:

                   e_t=\sum_{i=1}^{N} w_{it}I(G_t(x_i) \neq y_i)

           c:  计算G_t(x)的系数

                \alpha_t =\frac{1}{2}log \frac{1-e_t}{e_t}

                对数是自然函数

           d: 更新训练数据集的权值分布

                 D_{t+1}=(w_{1,t+1},w_{2,t+1},....w_{N,t+1})

                 w_{i,t+1}=\frac{w_{i,t}}{Z_t}e^{-\alpha_ty_iG_t(x_i)}

                其中Zm 是归一化因子

                Z_t=\sum_{i=1}^{N}w_{ti}e^{-\alpha_ty_iG_t(x_i)}

 

       3: 构建资本分类器的线性组合

            f(x)=\sum_{i=1}^{N}\alpha_tG_t(x)

          得到最终分类器

            G(x)=sign(f(x))


  三  训练误差分析

      定理1: AdaBoost 算法最终分类器的训练误差界为:

                \frac{1}{N}\sum_{i=1}^{N}I(G(x_i) \neq y_i)\leq \frac{1}{N}\sum_i e^{-y_if(x_i)} =\prod_m Z_m

               

                  预设条件:

                      w_{1i}=\frac{1}{N}

                        W_{t+1,i}=\frac{W_{t,i}}{Z_t}e^{-\alpha_t y_i G_t(x_i)}

                 证明:

                         当G(x_i) \neq y_i ,y_if(x_i)<0 时候,

                         所以e^{-y_if(x_i)} \geq 1

 

                      \frac{1}{N}\sum_i e^{-y_if(x_i)}

                   =\frac{1}{N}e^{-\sum_{t=1}^{T}\alpha_ty_iG_t(x_i)}

                 =      \sum_i w_{1,i} \frac{Z_1}{Z_1}\prod_{t=1}^{T} e^{-\alpha_ty_iG_t(x_i)}

                  =Z_1\sum_{i} \frac{w_{1,i}e^{-\alpha_1y_iG_1(x_i)}}{Z_1} \prod_{t=2}^{T}e^{-\alpha_t y_iG_t(x_i)}

                   =Z_1\sum_{i} W_{2,i}\prod_{t=2}^{T}e^{-\alpha_t y_iG_t(x_i)}

                  =\prod_{t=1}^{T}Z_t

     在每轮选择合适的Gt可以使得Zt最小,从而下降也最快

 

   定理2  (二分类问题  AdaBoost 训练误差界)

      \prod_{t=1}^{T}Z_t=\prod_{t=1}^T2 \sqrt{e_t(1-e_t)}=\prod_{t=1}^{T}(1-4r_t^2) \leq e^{-2 \sum_{t=1}^{M}r_t^2}

     其中

      r_t= 0.5-e_t

   \alpha_t= \frac{1}{2}log\frac{1-e_t}{e_t}

     证明:

     Z_t= \sum_{i=1}^{N}W_{ti}e^{-\alpha_ty_iG_t(x_i)}

     =\sum_{y_i =G_t(x_i)}W_{ti}e^{-\alpha_t}+\sum_{y_i \neq G_t(x_i)}W_{ti}e^{\alpha_t}

   =(1-e_t)e^{-\alpha_t}+e_t e^{\alpha_t}

   =2\sqrt{e_t(1-e_t)}

         =\sqrt{1-4r_t^2}    

       说明每个弱分类器,误差越小越好

 

    其中

    \prod_{t=1}^{T}\sqrt{1-4r_t^2} \leq e^{-2\sum_{t=1}^{T}r_t^2}

    可由 e^x 和 \sqrt{1-x} 在点x = 0 的泰勒公式展开推出不等式

   \sqrt{(1-4r_t^2)} \leq e^{-2r_t^2}  

  证明

   令  x=r_t^2

 即要证明

      1-4x \leq (e^{-2x})^2

      即要证明:e^{-4x}+4x-1 \geq 0 ,st :x\geq 0

      令   f(x)=e^{-4x}+4x-1

              f^{'}=-4e^{-4x}+4=0

             f^{"}=16e^{-4x}>0  有极小值

              极小值就是驻点

              e^{-4x}=1

               x=0 点也满足约束条件

               所以f(x)=1+0-1>=0

                     

       上面不等式也可以由e^x ,\sqrt{1-x} 在泰勒公式 x=0 点展开得到

         

  推论: 如果存在r>0,对所有的T 有 r_t \geq r 则

             \frac{1}{N}\sum_{i=1}^{N} I(G(x_i) \neq y_i) \leq e^{-2Mr^2}             

 

 


四  例子 (乳腺癌分类)

       数据集

       

        训练结果

       

      错误率随着弱分类器个数增加而减小

      

# -*- coding: utf-8 -*-
"""
Created on Mon Nov 25 14:42:22 2019

@author: chengxf2
"""

import numpy as  np
import matplotlib.pyplot  as plt
from sklearn.datasets import load_breast_cancer

class AdaBoost:
    
    def DrawScatter(self, dataMat, labelMat):
        
        m,n = np.shape(dataMat)
        
        for i in range(m):
            
            x1 = dataMat[i,0]
            x2 = dataMat[i,1]
            y = labelMat[i,0]
            
            if np.sign(y)>0:
                plt.scatter(x1,x2,c='r')
            else:
                plt.scatter(x1,x2,c='g')
        plt.show()
    
    """
    计算alpha:
    Args
       e: 未分类的样本数目/所有样本
       
    """
    def CalcAlpha(self, e):
        
        epsilon = np.log((1-e)/e)/2.0
        
        return epsilon
        
        
    """
    加载数据集
    Args
      None
    return 
      None
    """
    def LoadSimpData(self):
        
        cancer = load_breast_cancer()
        
        self.dataMat = np.mat(cancer.data) #(569,30)
        
     
        
        self.classLabels =   np.mat(cancer.target).T #(569,1)

        self.m, self.n = np.shape(self.dataMat)
        for i in range(self.m):
            if self.classLabels[i,0]<1:
                self.classLabels[i,0]=-1
        self.W = np.ones((self.m,1))/self.m
        #print("\n LoadSimData:  \t",self.classLabels)
        self.DrawScatter(self.dataMat, self.classLabels)
        
    
    """
    单个分类器
    Args
       dataMat: 数据
       dimen: 维度
       thresh: 阀值
       Less: True else >
    return 
       retMat
    """
    def stumpClassify(self, dataMat, dimen, thresh, less):
        
        m,n = np.shape(dataMat)
        retArray = np.ones((m,1))
        
        if less:
            retArray[dataMat[:,dimen]<=thresh]=-1.0
        else:
            retArray[dataMat[:,dimen]>thresh]=-1.0
        
        #print("\n retArray ",retArray)
        return retArray
        
    def __init__(self):
        
        self.m = 0  ##样本个数
        self.n = 0  ##样本维度
        self.T = 5  ##基分类器个数
        self.W = None  ##样本分布权值
        self.LoadSimpData()
        self.minError = 0.02
        
    
    """
    创建基分类器
    Arg
      None
    return
      弱分类器
    """
    def BuildStump(self,W):
        
        numSteps = 20.0
        bestStump ={}
        minErr = np.inf
        
        
        for i in range(self.n):
            rangeMin = min(self.dataMat[:,i])
            rangeMax = max(self.dataMat[:,i])
            stepSize =(rangeMax - rangeMin)/numSteps
            #print("\n ***************\n ","\t rangeMin: ",float(rangeMin),"\t max: ",float(rangeMax))
            
            for j in range(int(rangeMin), int(numSteps)+1):
                
                for ls in [True, False]:
                 thresh = float(rangeMin+ j*stepSize)
                 #print("\t j: ",thresh)
                 retLabel = self.stumpClassify(self.dataMat, i, thresh, ls)
                 
                 errArr = np.mat(np.ones((self.m, 1)))##错误
                 errArr[retLabel == self.classLabels] =0   #相等就不计数
                 
                 weightErr = np.dot(W.T,errArr)
                 
                 #print("\n weightErr : ",weightErr)
                 
                 if float(weightErr)<minErr:
                     bestStump['dim'] =i
                     bestStump['thresh']=thresh
                     bestStump['ls'] = ls
                     minErr = float(weightErr)
                     bestClass = retLabel.copy()
        #print("\n minErr", minErr, "\n bestSump ",bestStump)
        return bestStump, minErr,bestClass
    
    
    """
    集成学习
    Args
       numIt: 弱分类器个数
    return 
       weakClass: 弱分类器
    """
    def adaBoostTrain(self, numIt):
        
        W = np.mat(np.ones((self.m, 1)))/self.m
        boostClass =np.mat(np.zeros((self.m , 1)))
        weakClass =[]
        one = np.ones((self.m, 1))  ##统计错误个数得矩阵
        
        ErrList =[]
        
        for t in range(numIt):
            
            
            bestStump, error, classEst= self.BuildStump(W)
            #print("\n *********t**********:\n \t t:  ",t,"\t:error ",error)
            
            if error>0.5:
                break
            
            
            alpha = self.CalcAlpha(error)
            bestStump['alpha']= alpha
            
            weakClass.append(bestStump)
            yg = np.multiply(-1*alpha*self.classLabels, classEst)
            W = np.multiply(W, np.exp(yg))
            
            W = W/W.sum()
           # print("W, ",W)
            
           ##########错误累加计算#########3
            boostClass += alpha*classEst
          
            boostError = np.multiply(np.sign(boostClass)!= self.classLabels, one)
            
            
            errorRate = boostError.sum()/self.m
            print("\n t: ",t, "\t errorRate ",errorRate)
            #print("\n boostError ",boostError)
            ErrList.append(errorRate)
          
            if errorRate<self.minError:
              break
        print("\n errorRate", errorRate,"\n ******weakClass******* \n",weakClass, " 弱分类器个数 ",t)
        
        x = np.arange(0, len(ErrList))
        plt.plot(x, ErrList, c='r')
        plt.xlabel("t")
        plt.ylabel("Error")
        return weakClass
        
        
                 
                 
            
        
ada = AdaBoost()  
ada.adaBoostTrain(200)     

参考文档

  《统计学习方法》

    《机器学习与应用》

;