Boosting方法在集成学习中的应用及实现

释放双眼,带上耳机,听听看~!
了解Boosting方法在集成学习中的应用及实现,包括AdaBoostClassifier的训练过程和弱分类器的使用,帮助理解集成学习中的Boosting算法原理和实现细节。

集成学习是一大类模型融合策略和方法的统称,其中包含多种集成学习的思想。

Boosting

Boosting方法训练基分类器时采用串行的方式,各个基分类器之间有依赖。

它的基本思路是将基分类器层层叠加,每一层在训练的时候,对前一层基分类器分错的样本,给予更高的权重。测试时,根据各层分类器的结果的加权得到最终结果。

Boosting的过程很类似于人类学习的过程 (如图) ,我们学习新知识的过程往往是迭代式的,第一遍学习的时候,我们会记住一部分知识,但往往也会犯一些错误,对于这些错误,我们的印象会很深。第二遍学习的时候,就会针对犯过错误的知识加强学习,以减少类似的错误发生。不断循环往复,直到犯错误的次数减少到很低的程度。

Boosting方法在集成学习中的应用及实现

Boosting算法的实现

import util
import numpy as np
import sys
import random

PRINT = True


random.seed(42)
np.random.seed(42)

def small_classify(y):
    classifier, data = y
    return classifier.classify(data)

class AdaBoostClassifier:
    """
    AdaBoost classifier.
    Note that the variable 'datum' in this code refers to a counter of features
    (not to a raw samples.Datum).
    
    """

    def __init__( self, legalLabels, max_iterations, weak_classifier, boosting_iterations):
        self.legalLabels = legalLabels
        self.boosting_iterations = boosting_iterations
        self.classifiers = [weak_classifier(legalLabels, max_iterations) for _ in range(self.boosting_iterations)]
        self.alphas = [0]*self.boosting_iterations

    def train( self, trainingData, trainingLabels):
        """
        The training loop trains weak learners with weights sequentially. 
        The self.classifiers are updated in each iteration and also the self.alphas 
        """
        
        self.features = trainingData[0].keys()
        
        size_sampled_data = int(len(trainingData))
        sampled_weights = [(1.0/size_sampled_data)] * size_sampled_data
        

        for i in range(self.boosting_iterations):
            self.classifiers[i].train(trainingData, trainingLabels, sampled_weights)
            error = 0
            for j in range(len(trainingData)):
                if self.classifiers[i].classify([trainingData[j]])[0] != trainingLabels[j]:
                    error = error + sampled_weights[j]

            for j in range(len(trainingData)):
                if self.classifiers[i].classify([trainingData[j]])[0] == trainingLabels[j]:
                    sampled_weights[j]  = sampled_weights[j] * (error/ (1 - error))

            # Normalize the sampled_weight list
            sum_of_weights = sum(sampled_weights)
            for p in range(len(sampled_weights)):
                sampled_weights[p]  = sampled_weights[p] / sum_of_weights

            self.alphas[i] = np.log((1 - error) / error)
            #print "training loop: ", i





    def classify( self, data):
        """
        Classifies each datum as the label that most closely matches the prototype vector
        for that label. This is done by taking a polling over the weak classifiers already trained.
        See the assignment description for details.
        Recall that a datum is a util.counter.
        The function should return a list of labels where each label should be one of legaLabels.
        """

        
        prediction = []
        for i in range(len(data)):
            guesses = []
            for j in range(self.boosting_iterations):
                guesses.append(self.classifiers[j].classify([data[i]])[0] * self.alphas[j])

            prediction.append(int(np.sign(sum(guesses))))

        return prediction

Bagging

Bagging与Boosting的串行训练方式不同,Bagging方法在训练过程中,各基分类器之间无强依赖,可以进行并行训练。其中很著名的算法之一是基于决策树基分类器的随机森林 (Random Forest) 。为了让基分类器之间互相独立,将训练集分为若干子集 (当训练样本数量较少时,子集之间可能有交叠)。Bagging方法更像是一个集体决策的过程,每个个体都进行单独学习,学习的内容可以相同,也可以不同,也可以部分重叠。但由于个体之间存在差异性,最终做出的判断不会完全一致。在最终做决策时,每个个体单独作出判断,再通过投票的方式做出最后的集体决策 (如图)。

Boosting方法在集成学习中的应用及实现

我们再从消除基分类器的偏差和方差的角度来理解Boosting和Bagging方法的差异。基分类器,有时又被称为弱分类器,因为基分类器的错误率要大于集成分类器。基分类器的错误,是偏差和方差两种错误之和。偏差主要是由于分类器的表达能力有限导致的系统性错误,表现在训练误差不收敛。方差是由于分类器对于样本分布过于敏感,导致在训练样本数较少时,产生过拟合

Boosting方法是通过逐步聚焦于基分类器分错的样本,减小集成分类器的偏差。Bagging方法则是采取分而治之的策略,通过对训练样本多次采样,并分别训练出多个不同模型,然后做综合,来减小集成分类器的方差。假设所有基分类器出错的概率是独立的,在某个测试样本上,用简单多数投票方法来集成结果,超过半数基分类器出错的概率会随着基分类器的数量增加而下降。

下图是Bagging算法的示意图,Model 1、Model 2、Model 3都是用训练集的个子集训练出来的,单独来看,它们的决策边界都很曲折,有过拟合的倾向。集成之后的模型(红线所示) 的决策边界就比各个独立的模型平滑了,这是由于集成的加权投票方法,减小了方差

Boosting方法在集成学习中的应用及实现

Bagging算法的实现

import util
import numpy as np
import sys
import random

PRINT = True

random.seed(42)
np.random.seed(42)

class BaggingClassifier:
    """
    Bagging classifier.
    Note that the variable 'datum' in this code refers to a counter of features
    (not to a raw samples.Datum).
    
    """

    def __init__( self, legalLabels, max_iterations, weak_classifier, ratio, num_classifiers):

        self.ratio = ratio
        self.num_classifiers = num_classifiers
        self.classifiers = [weak_classifier(legalLabels, max_iterations) for _ in range(self.num_classifiers)]

    def train( self, trainingData, trainingLabels):
        """
        The training loop samples from the data "num_classifiers" time. Size of each sample is
        specified by "ratio". So len(sample)/len(trainingData) should equal ratio. 
        """

        self.features = trainingData[0].keys()
        
        sample_size = int(self.ratio * len(trainingData))
        for i in range(self.num_classifiers):
            random_integers = np.random.randint(len(trainingData), size = sample_size)
            sampled_data = []
            sampled_labels = []
            for choosen_element in random_integers:
                sampled_data.append(trainingData[choosen_element])
                sampled_labels.append(trainingLabels[choosen_element])

            self.classifiers[i].train(sampled_data, sampled_labels, None)






    def classify( self, data):
        """
        Classifies each datum as the label that most closely matches the prototype vector
        for that label. This is done by taking a polling over the weak classifiers already trained.
        See the assignment description for details.
        Recall that a datum is a util.counter.
        The function should return a list of labels where each label should be one of legaLabels.
        """

       
        prediction = []
        for i in range(len(data)):
            guesses = []
            for j in range(self.num_classifiers):
                guesses.append(self.classifiers[j].classify([data[i]])[0])

            prediction.append(int(np.sign(sum(guesses))))

        return prediction

集成学习的基本步骤

集成学习一般可分为以下3个步骤。

(1)找到误差互相独立的基分类器。

(2)训练基分类器。

(3)合并基分类器的结果。

合并基分类器的方法有voting和stacking两种。前者是用投票的方式,将获得最多选票的结果作为最终的结果。后者是用串行的方式,把前一个基分类器的结果输出到下一个分类器,将所有基分类器的输出结果相加(或者用更复杂的算法
融合,比如把各基分类器的输出作为特征,使用逻辑回归作为融合模型进行最后的结果预测)作为最终的输出。

以Adaboost为例,其基分类器的训练和合并的基本步骤如下。

(1)确定基分类器:这里可以选取ID3决策树作为基分类器。事实上,任何分类模型都可以作为基分类器,但树形模型由于结构简单且较易产生随机性所以比较常用。

(2)训练基分类器:假设训练集为{xi,yi},i=1,…,N,{x_i,y_i},i=1,…,N,其中yi∈{−1,1}y_iboldsymbol{in}{−1,1},并且有T个基分类器,则可以按照如下过程来训练基分类器。

1.初始化采分布

Dl(i)=1/ND_{mathrm{l}}(i)={1/N}

2.令t=1,2,…,Tt=1,2,…,T循环:

  • 从训练集中,按照DtD_t分布,采样出子集St={xi,yi},i=1,…,Nt;S_{t}={x_{i},y_{i}},i=1,ldots,N_{t};
  • StS_{t}训练出基分类器hth_t
  • 计算hth_t的错误率:

Et=∑i=1NtI[ht(xi)≠yi]Dt(xi)Ntmathcal{E}_t=frac{sum_{i=1}^{N_t}I[h_t(x_i)neq y_i]D_t(x_i)}{N_t}

其中I[]I[]为判别函数;

  • 计算基分类器hth_t的权重:

at=log⁡(1−Et)Eta_{t}=log{frac{(1-{mathcal{E}}_{t})}{{mathcal{E}}_{t}}}

  • 设置下一次采样

Di+1={Di(i)或者Di(i)(1−εi)εi,hi(xi)≠yi;Di(i)εi(1−εi),pi(xi)=yi.D_{i+1}=begin{cases}D_i(i)text{或者}dfrac{D_i(i)(1-varepsilon_i)}{varepsilon_i},h_i(x_i)neq y_i;\ \ dfrac{D_i(i)varepsilon_i}{(1-varepsilon_i)},p_i(x_i)=y_i.end{cases}

并将它归一化为一个概率分布函数。

(3)合并基分类器:给定一个未知样本zz,输出分类结果为加权投票的结果Sign(∑t=1Tht(z)at)Sign(sumlimits_{t=1}^T h_t(z)a_t)

从Adaboost的例子中我们可以明显地看到Boosting的思想,对分类正确的样本降低了权重,对分类错误的样本升高或者保持权重不变。 在最后进行模型融合的过程中,也根据错误率对基分类器进行加权融合。错误率低的分类器拥有更大的“话语权”。

另一个非常流行的模型是梯度提升决策树,其核心思想是,每一棵树学的是之前所有树结论和的残差,这个残差就是一个加预测值后能得真实值的累加量。

我们以一个视频网站的用户画像为例,为了将广告定向投放给指定年龄的用户,视频网站需要对每个用户的年龄做出预测。在这个问题中,每个样本是一个已知性别/年龄的用户,而特征则包括这个人访问的时长、时段、观看的视频的类型等。

例如用户A的真实年龄是25岁,但第一棵决策树的预测年龄是22岁,差了3
岁,即残差为3。那么在第二棵树里我们把A的年龄设为3岁去学习,如果第二棵树
能把A分到3岁的叶子节点,那两棵树的结果相加就可以得到A的真实年龄;如果第二棵树的结论是5岁,则A仍然存在−2岁的残差,第三棵树里A的年龄就变成−2岁,继续学。这里使用残差继续学习,就是GBDT中Gradient Boosted所表达的意思。

基分类器

基分类器的选择是集成学习主要步骤中的第一步,也是非常重要的一步。

最常用的基分类器是决策树,主要有以下3个方面的原因。

(1)决策树可以较为方便地将样本的权重整合到训练过程中,而不需要使用过采样的方法来调整样本权重。

(2)决策树的表达能力和泛化能力,可以通过调节树的层数来做折中。

(3)数据样本的扰动对于决策树的影响较大,因此不同子样本集合生成的决策树基分类器随机性较大,这样的“不稳定学习器”更适合作为基分类器。此外,在决策树节点分裂的时候,随机地选择一个特征子集,从中找出最优分裂属性,很好地引入了随机性。除了决策树外,神经网络模型也适合作为基分类器,主要由于神经网络模型也比较“不稳定”,而且还可以通过调整神经元数量、连接方式、网络层数、初始权值等方式引入随机性。

随机森林中的基分类器即为决策树。

Boosting方法在集成学习中的应用及实现

这是随机森林属于Bagging类的集成学习。Bagging的主要好处是集成后的分类器的方差,比基分类器的方差小。Bagging所采用的基分类器,最好是本身对样本分布较为敏感的(即所谓不稳定的分类器),这样Bagging才能有用武之地。线性分类器或者K-近邻都是较为稳定的分类器,本身方差就不大,所以以它们为基分类器使用Bagging并不能在原有基分类器的基础上获得更好的表现,甚至可能因为Bagging的采样,而导致他们在训练中更难收敛,从而增大了集成分类器的偏差。

“本文正在参加 人工智能创作者扶持计划 ”

本网站的内容主要来自互联网上的各种资源,仅供参考和信息分享之用,不代表本网站拥有相关版权或知识产权。如您认为内容侵犯您的权益,请联系我们,我们将尽快采取行动,包括删除或更正。
AI教程

昇腾AI新技能,还能预防猪生病?

2023-12-5 19:22:14

AI教程

Hugging Face扩散模型课程第一单元:扩散模型简介

2023-12-5 19:30:14

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索