李宏毅《机器学习》飞桨特训营(六)——集成学习[Bagging、Boosting(含案例程序模拟)、Stacking]

视频PPT

集成学习 Ensemble

1. 什么是集成学习?

在这里插入图片描述
集成学习就是通过训练出不同的模型,合作完成某一项任务,从而实现比较理想的预测效果。

下面分别介绍集成学习的三种方法:Bagging、Boosting和Stacking。

2. 集成学习方法:Bagging

⭐注意:Bagging是用来降低过拟合的一种方法,只有当你的模型很复杂导致方差较大时,才采用Bagging方法。

2.1 回顾

在文章 李宏毅《机器学习》飞桨特训营(二)——回归(含作业) 中我们了解到:模型越复杂,方差越大,偏差越小,越容易过拟合。
在这里插入图片描述
因此我们考虑通过训练出多个模型 f 1 f\mathop{{}}\nolimits_{{1}} f1(x), f 2 f\mathop{{}}\nolimits_{{2}} f2(x),
f 3 f\mathop{{}}\nolimits_{{3}} f3(x) ······,取这些模型输出的平均值,从而达到减小方差的目的。

2.2 Bagging步骤

在这里插入图片描述

  1. 通过有放回的取样的方法(重采样)将训练集分为4个不同的训练集。
  2. 用这4个数据集训练出4个不同的模型。在这里插入图片描述
  3. 在上一步训练出4个模型之后,将testing data输入到这4个模型中,得到四个预测值。
  4. 如果是回归问题,那就将这4个结果取平均,得到最终的结果;如果是分类问题,就进行投票,看看那一个类别获得的票数最多,就选这个类别作为最终的分类结果。

2.3 Bagging案例

决策树是一个非常容易过拟合的方法。可以通过无限增加树的深度,使得模型在训练集上拿到100%正确率,但在测试集就很难达到良好的效果。

2.3.1 决策树

一个简单的决策树如下图所示,通过不断地逐层进行条件判断,从而达到分类地目的。
在这里插入图片描述
决策树的关键参数包括以下内容:

  • 每个节点有多少分支
  • 分叉的标准是什么
  • 什么时候停止分叉

2.3.2 决策树分类案例

下面以初音分类为例:
在这里插入图片描述
红色区域是class 1,蓝色区域是class 2 。

下面看不同深度的决策树的分类效果。
在这里插入图片描述
可以看到,随着深度的不断增加,单决策树的分类效果越来越好。实际上当达到一定深度时,单决策树在训练集上分类准确率可以达到100%,但却非常容易过拟合。因此,要采用Bagging的方法减小决策树的过拟合。

2.3.3 随机森林=决策树+Bagging

2.3.3.1 Training

前面我们提到Bagging可以通过重采样的方法构建不同的数据集,但在随机森林中这种方法并不能够完全满足,因为这样训练出来的树还是有比较高的相似度。

在这里插入图片描述

因此我们在每次产生分支时随机限制哪些feature可用哪些feature不可用,避免各棵树长得太像。 将这些树集合起来就是随机森林。

2.3.3.2 Validation(OOB)

之前做验证的时候是把数据切成两份,一份用于训练,一份用于验证。而这里使用bagging的话,就可以用Out-of-bag(OOB),这样就不用把数据切成两份。如下图,f1,f2,f3,f4代表不同的四棵树,x1,x2,x3,x4代表4份数据。做bagging的时候,每棵树只用其中的部分数据(比如f1只用都x1,x2的数据)。

在这里插入图片描述
由上图可知,f2和f4训练时都没有用到数据x1,因此可以将f2+f4 bagging的结果去 test x1,以此类推,然后把所有测试的结果取平均值,得到最终的error值。

在这里插入图片描述
下图是决策树和随机森林的结果对比。

在这里插入图片描述
可以看出采用随机森林(100棵树)并不会使模型更加拟合数据,但是由于把100棵树的结果平均起来,可以让结果更加平滑(即比较有泛化性)。

3. 集成学习方法:Boosting(以Adaboost为例)

⭐注意:Boosting是用来降低欠拟合的一种方法,只有当你的模型不复杂导致偏差较大时,才采用Boosting方法。

3.1 Boosting步骤

在这里插入图片描述

  1. 先训练一个分类器 f 1 f\mathop{{}}\nolimits_{{1}} f1(x)。
  2. 找一个分类器 f 2 f\mathop{{}}\nolimits_{{2}} f2(x)来弥补 f 1 f\mathop{{}}\nolimits_{{1}} f1(x)的不足。要求 f 2 f\mathop{{}}\nolimits_{{2}} f2(x)和 f 1 f\mathop{{}}\nolimits_{{1}} f1(x)不能太像,这样才能起到互补的作用。
  3. 继续找一个分类器 f 3 f\mathop{{}}\nolimits_{{3}} f3(x)和 f 2 f\mathop{{}}\nolimits_{{2}} f2(x)互补。
  4. 循环步骤3

⭐注意:这个获取模型的过程是一个串行操作,即模型 f 1 f\mathop{{}}\nolimits_{{1}} f1(x), f 2 f\mathop{{}}\nolimits_{{2}} f2(x), f 3 f\mathop{{}}\nolimits_{{3}} f3(x) ······是按顺序得到的。

最后将这些分类器合起来,就形成一个很强的分类器。

3.2 如何获取不同的数据集

既然要训练不同的模型,就需要用不同的数据集。

在这里插入图片描述
除了重采样的方法,还可以给你数据集里面的每一笔数据一个权重,每次训练时都修改权重的值,从而得到不同的数据集。

其实重采样也可以视同是改了权重,比如说某一笔数据被 采样了两次,就代表它的权重变成2,。如果用重采样的方法权重 只能是整数,直接调一个权重的话就可以赋值小数了。

在实际中,要实现给数据加权重,只需要改变Loss Function。

3.3 如何训练互补的模型

既然要使 f 1 f\mathop{{}}\nolimits_{{1}} f1(x)和 f 2 f\mathop{{}}\nolimits_{{2}} f2(x)两个分类器互补,那就要找到一个新的data set, 在这个data set上分类效果很差。然后以这个新的data set去训练 ,就能起到两个分类器互补的结果。

在这里插入图片描述
实验步骤如下:

  • 先训练一个分类器 f 1 f\mathop{{}}\nolimits_{{1}} f1(x),使得它的error rate低于0.5。
  • 更新权重获得新的training data,这些新的training data要使分类器 f 1 f\mathop{{}}\nolimits_{{1}} f1(x)的error rate等于0.5 (即 f 1 f\mathop{{}}\nolimits_{{1}} f1(x)在这个数据集的训练效果非常差)。
  • 以这些新的training data去训练和 f 1 f\mathop{{}}\nolimits_{{1}} f1(x)互补的新的分类器 f 2 f\mathop{{}}\nolimits_{{2}} f2(x)。

下面是具体的例子:

在这里插入图片描述

3.4 如何更新权重

在这里插入图片描述

  • 如果 f 1 f\mathop{{}}\nolimits_{{1}} f1(x)分类错误,weight就乘上 d 1 d\mathop{{}}\nolimits_{{1}} d1(x)变大。
  • 如果 f 1 f\mathop{{}}\nolimits_{{1}} f1(x)分类正确,weight就除以 d 1 d\mathop{{}}\nolimits_{{1}} d1(x)变小。

接下来看下如何确定 d 1 d\mathop{{}}\nolimits_{{1}} d1(x)值:

在这里插入图片描述
一顿公式推导(总之就是使得新权重下的error为0.5),得到 d 1 d\mathop{{}}\nolimits_{{1}} d1(x)的计算公式为:
在这里插入图片描述

Adaboost权重更新算法如下所示:

在这里插入图片描述
由于这里的问题是二分类(结果为+1或-1),所以红框中的内容可以简写为:

在这里插入图片描述
上式即为权重更新公式。

3.5 如何整合获得的所有模型

在这里插入图片描述
如图所示,有两种方法:

  • 由于 f t f\mathop{{}}\nolimits_{{t}} ft(x)的结果只有+1和-1,所以可以直接把所有分类器的output累加起来,看大于1的结果多还是小于1的结果多,然后选择结果多的那个作为最后的输出。
  • 根据分类器的分类好坏,给予不同的权重 a t a\mathop{{}}\nolimits_{{t}} at。如果error rate为0.1,则说明分类比较准, a t a\mathop{{}}\nolimits_{{t}} at给得比较大。如果error rate为0.4,则说明分类比较不准, a t a\mathop{{}}\nolimits_{{t}} at给得比较小。

3.6 AdaBoost案例

在这里插入图片描述
这里用的分类器是decision stump,一个很弱的模型,不认识它没事,反正就知道它只是切一刀就行。

  • 如上图,一开始data的weight都是1,然后decision stump就往这个空间切一刀。
  • 此时 e 1 e\mathop{{}}\nolimits_{{1}} e1=0.3, d 1 d\mathop{{}}\nolimits_{{1}} d1=1.53, a 1 a\mathop{{}}\nolimits_{{1}} a1=0.42。
  • 更新权重得到右图所示新的权重(分类正确的权重除以 d 1 d\mathop{{}}\nolimits_{{1}} d1变小,错误的权重乘以 d 1 d\mathop{{}}\nolimits_{{1}} d1变大)

重复上述步骤。

在这里插入图片描述
现在训练出三个模型了,也有它们各自的权重 a t a\mathop{{}}\nolimits_{{t}} at了,来进行最后的分类了。

在这里插入图片描述
由于有三条线,把空间切成6块。以右上角那一块为例子。

  1. f 1 f\mathop{{}}\nolimits_{{1}} f1认为这一块是红色的, f 2 f\mathop{{}}\nolimits_{{2}} f2认为这一块也是红色,而 f 3 f\mathop{{}}\nolimits_{{3}} f3认为这一块是蓝色的。
  2. 意见不合,那算一下权重, f 1 f\mathop{{}}\nolimits_{{1}} f1的weight为0.42, f 2 f\mathop{{}}\nolimits_{{2}} f2的weight为0.66,加起来超过 f 3 f\mathop{{}}\nolimits_{{3}} f3的0.95。
  3. 所以最后判定这一块是属于红色的。

3.7 案例程序模拟(自己写的,仅供参考)

import numpy as np
import matplotlib.pyplot as plt
import random
import math

class1 = np.array([[2,2.1,1],[2.5,4.5,1],[4.3,8.7,1],[6.2,9.3,1],[8.6,7.1,1]])
class2 = np.array([[4.2,1,-1],[4,4.2,-1],[6,4.5,-1],[8.9,3.3,-1],[9.8,8,-1]])

plt.scatter(class1[:,0],class1[:,1])
plt.scatter(class2[:,0],class2[:,1],marker='*')  

all_data = np.concatenate((class1,class2)) #组合两个数组
np.random.shuffle(all_data) #打乱数组

weight = np.ones((all_data.shape[0],1)) #初始化权重

iterations = 5 #循环次数

a_l = []

fx=[] #用来判断每个分类器是纵向切一刀还是横向切一刀,0表示纵向切一刀,1表示横向切一刀
fx_num=[]# 保存每个分类器切的位置
u_l=[] #保存横向切一刀时上方的类别,纵向切一刀时左方的类别
d_r=[] #保存横向切一刀时下方的类别,纵向切一刀时左右方的类别

#建模型
for i in range(iterations):
    
    m = random.randint(0,1) #0表示纵向切一刀,1表示横向切一刀
    fx.append(m)
    
    y = m*random.uniform(1, 10) 
    x = (1-m)*random.uniform(1, 10) 
    fx_num.append(x+y)
    
    #初始化y上面的表示类别1,下面表示类别-1,x左边表示类别1,右边表示类别-1
    u_l.append(1)
    d_r.append(-1)
    
    right=[] #分类正确数据集的索引
    wrong=[] #分类错误的数据集索引
    
    error = 0
    
    #判断是纵向切还是横向切分别处理
    if(m):        
        for j in range(all_data.shape[0]):
            if all_data[j,1]>y:
                if all_data[j,2]==-1:
                    error = error+weight[j]
                    wrong.append(j)
                else:
                    right.append(j)
            elif all_data[j,1]<y: 
                if all_data[j,2]==1:
                    error = error+weight[j]
                    wrong.append(j)
                else:
                    right.append(j)
            else:
                error = error+weight[j]
                wrong.append(j)
    else:    
        for j in range(all_data.shape[0]):
            if all_data[j,0]<x:
                if all_data[j,2]==-1:
                    error = error+weight[j]
                    wrong.append(j)
                else:
                    right.append(j)
            elif all_data[j,0]>x: 
                if all_data[j,2]==1:
                    error = error+weight[j]
                    wrong.append(j)
                else:
                    right.append(j)
            else:
                error = error+weight[j]
                wrong.append(j)
    #计算错误率                
    error_rate = error[0]/sum(weight)[0]     
	
	#如果误差大于0.5,则翻转上下/左右代表的类别,让误差小于0.5
    if error_rate > 0.5:
        u_l[i]=-1
        d_r[i]=1
        error_rate=1-error_rate
                  
    d = math.sqrt((1-error_rate)/error_rate)
        
    a = np.log(d)
    a_l.append(a)
        
    #更新权重   
    weight[right[:]] = weight[right[:]]/d
    weight[wrong[:]] = weight[wrong[:]]*d
    
#画图
for i in range(iterations):
    if fx[i]==0:
        y = np.arange(11)
        plt.plot(fx_num[i]*np.ones(11),y,'r')
    else:
        x = np.arange(11)
        plt.plot(x,fx_num[i]*np.ones(11),'r')
plt.show()

#画图
x = np.arange(0,10,0.1)
y = np.arange(0,10,0.1)
class1_x=[]
class1_y=[]
class2_x=[]
class2_y=[]
for i in range(len(x)):
    for m in range(len(y)):
        result = 0;
        for j in range(iterations):
            if fx[j]==0:
                if x[i]<fx_num[j]:
                    result += u_l[j]*a_l[j]
                else:
                    result += d_r[j]*a_l[j]
            else:
                if y[m]>fx_num[j]:
                    result += u_l[j]*a_l[j]
                else:
                    result += d_r[j]*a_l[j]  
    
        if result > 0:
            class1_x.append(x[i])
            class1_y.append(y[m])
        else:
            class2_x.append(x[i])
            class2_y.append(y[m])

plt.scatter(np.asarray(class1_x),np.asarray(class1_y),color = 'hotpink')
plt.scatter(np.asarray(class2_x),np.asarray(class2_y),color = '#88c999')

自己仿照上面的案例写的程序,也不知道有没有写错,欢迎大神帮忙纠错,运行后结果如下所示(由于是随机切的,每次运行的结果都不太一样,我选了个效果比较好的一次):

在这里插入图片描述

4. 集成学习方法:Stacking

构造一个最终的分类器,它的输入是其他模型的输出。这个分类器可以评估前面这些分类器的好坏从而赋予不同的权重,效果好的权重高,效果差的权重低,从而获得更加准确的结果。

这个最终的 classifier 就不需要太复杂,如果最前面都已经用了好几个 Hidden Layer 的 Neural Network 了,也许 final classifier 就不需要再好几个 Hidden Layer 的 Neural Network,它可以只是 Logistic Regression 就行了。
在这里插入图片描述

那在做这个实验的时候要注意,在做 Stacking 的时候要把 training set 再分成两部分,一部分的 training set 拿来学习这些 classifier,另外一部分的 training data 拿来学习 final classifier。为什么要这么做?因为有的要来做 Stacking 的前面 classifier 它可能只是 fit training data。

所以在 train final classifier 的时候必须要用另外一笔 training data 来 train final classifier,不能跟前面 train 系统的 classifier 一样,这时有 final classifier 就可以给不同的系统不同的权重。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值