干货 | 集成学习综述与代码实现

点击上方,选择星标置顶,每天给你送干货

阅读大概需要14分钟

跟随小博主,每天进步一丢丢

转载自 AI部落联盟

1. 集成学习

  • 定义

顾名思义,就是将多个单一模型进行组合,最后形成一个更好的模型的过程。之所以组合多个单一学习器,是因为很多时候单一学习器的效果不够理想,多个模型组合可以互帮互助,各取所长,从而能够更好的完成任务。集成学习一般的结构是先学习单一的学习器,之后通过某种策略将其组合在一起。

  • 条件

(a)首先应该保证分类器之间的差异性,如果分类器都相同,那么组合的出来的结果是不会有变化的。

(b)每个个体分类器的精度必须大于0.5,如果个体分类器的精度低于0.5,那集成之后的精度低于规模的增大而降低。但如果精度是大于0.5的,最后的分类结果会趋于1。

  • 分类

根据个体学习器的生成方式,目前的集成学习方法大致可以分成两类:第一类是单个学习器之间有着很强的依赖关系,需要以串行的序列化的方式生成,代表方法:Boosting。Boosting方法中也有很多分类:Adaboost、GBDT等等。第二类就是个体学习器之间不存在很强的依赖关系,学习器可以并行生成,代表方法:Bagging 和Random Forest。

2. Bagging和Boosting

2.1 Bagging

Bagging又称为套袋法:

每次从样本集合中有放回地抽取n个值,一共抽取k轮,形成k个训练集合,训练集合之间彼此独立。对于分类问题,k个训练器可以训练出k个模型,从而产生k个结果,对于回归问题,取均值作为预测结果。

2.2 Boosting

每次从样本集合中有放回地抽取n个值,一共抽取k轮,形成k个训练集合,训练集合之间彼此独立。对于分类问题,k个训练器可以训练出k个模型,从而产生k个结果,对于回归问题,取均值作为预测结果。

梯度提升的Boosting方法则是使用代价函数对上一轮训练的损失函数f的偏导数来拟合残差。

2.3 Bagging和Boosting的区别

  • 训练数据集

Bagging是每次从样本集中有放回地抽取n个样本,抽取k次,形成k个样本集合,样本集合之间相互独立;

Boosting则是训练集合保持不变,每次变化的是训练集中样例的权重。

  • 样例权重

Bagging使用的是均匀采样,每个样例权重相等;

Boosting根据上一轮结果调整权重,错误率越大权重越大。

  • 预测函数

Bagging所有的预测函数权重相同;

Boosting预测函数权重不同,预测误差小的权重越大。

  • 并行计算

Bagging各个预测函数可以并行生成;

Boosting只能顺序生成,因为后一个预测要用到上一个结果。

2.4 样本权值和模型权值

Boosting会减小在上一轮正确样本的权重,增大错误样本的权重(因为正确样本残差小,错误样本残差大。组合模型的时候会将错误率高的模型赋予低的权重,将错误率低的模型赋予高的权重,有助于增加组合后模型准确率。

3. 组合模型方式

3.1 平均法

(a)简单平均

         将所有的个体学习器的数值型输出求平均。

(b)加权平均

         各个学习器有自身的权重,根据权重来进行加权平均,代表算法:Adaboost。

3.2 学习法

当训练数据很多时,更加强大的结合策略就是学习法,也就是通过另一个学习器来进行组合。典型代表是Stacking。把个体学习器称之为初级学习器,用于结合的学习器称为次级学习器或者元学习器。

Stacking先从初始训练集训练出初级学习器,然后生成一个新的数据集来训练次级学习器。新的数据集是以初级学习器的输出作为样例,初始样本的标记被当做样例标记。

4. AdaBoost算法

4.1 核心思想

  • 权值的更新取决于上一轮训练结果,增大误分类的样本的权值,减小正确分类的样本权值,在下一轮训练的时候误分类样本会得到更多的关注,正确分类的样本得到的关注变少。

  • 因为每次挑选的是误差最小的分类器,增大误分类的样本的权重相当于增大当前分类器的误差,错误率高的分类器对应小权重,错误率低的分类器对应大权重。

4.2 前向分步算法

简单来说就是从前向后,每次只学习一个基函数,最后通过基函数的线性组合,去逼近优化目标函数。

4.3 算法步骤

AdaBoost的Python实现(iris数据集)

import os
import numpy as np
import pandas as pd
from sklearn.cross_validation import train_test_split
from sklearn.datasets import load_iris

def load_data():
    iris = load_iris()#导入数据
    #转换为dataframe
    df = pd.DataFrame(iris.data,columns=iris.feature_names)
    df['label'] = iris.target#导入标签
    #把0和1类拿出来
    df.columns = ['sepal length','sepal width','petal length','petal width','label']#重命名列名
    data = np.array(df.iloc[:100,[0,1,-1]])#前100行为分类为0和1两类
    #把为0的变成-1
    for i in range(len(data)):
        if data[i,-1]==0:#把0类的转换为-1类
            data[i,-1] = -1
    return data[:,0:2],data[:,-1]

X,y = load_data()#导入数据
train_x,test_x,train_x,train_y = train_test_split(X,y,test_size=0.2,random_state=2333)#划分训练集和测试集
#定义Adaboost类
class Adaboost(object):
    def __init__(self,n_estimators=1,learning_rate = 0):
    '''
    n_estimators 分类器个数
    learning_rate 步长
    '''
        self.n_estimate = n_estimators
        self.learning_rate = learning_rate

    def init_data(self,X,y):
    '''
    X:属性
    y:标签
    '''
        self.M,self.N = X.shape#属性一共有M行N列
        self.X = X#训练属性
        self.y = y#标签数据
        self.w = [1.0/self.M]*self.M#初始化权重为1/M
        self.alpha = []
        self.clf_set = []#模型参数集合

    def _alpha(self,error):#误差越大 alpha值越小
        return 0.5*np.log((1-error)/error)

    def _Z(self, weights, a, clf):
    '''
    a:各个分类器的权重
    weights:权重
    clf:compare_array
    '''
        return sum([weights[i]*np.exp(-1*a*self.y[i]*clf[i]) for i in range(self.M)])

    def _w(self, a, clf, Z):#相当于给权重w做了一层softmax 归一化
    '''
    a:各个分类器的权重
    weights:权重
    clf:compare_array
    误分类self.y[i]*clf[i]=1权重增大 正确分类self.y[i]*clf[i]=-1权重变小
    '''
        for i in range(self.M):
            self.w[i] = self.w[i]*np.exp(-1*a*self.y[i]*clf[i])/ Z

    #具体算法
    def G(self,feature,label,weights):#每次更新的是w
        #求出属性列中的最大值和最小值
        f_min = min(feature)
        f_max = max(feature)
        #在属性列上迭代的次数
        n_step = (f_max-f_min+self.learning_rate)//self.learning_rate
        error = 10000.0#定义为无穷大
        best_v = 0.0#表示属性中最好分类点的值
        direct,compare_array = None,None
        #direct表示优化方向
        #compare_array表示该方向对应的c_o_positive or c_o_negative数组
        for i in range(1,int(n_step)):
            v = f_min+self.learning_rate*i#每次增加的步长为learning_rate
            if v not in feature:#如果v不存在属性列中
                c_o_positive = np.array([1 if feature[k] > v else -1 for k in range(len(feature))])#大于v的label置为1 小于v的置为-1
                #print([weights[k] for k in range(len(feature)) if c_o_positive[k]!=label[k]])
                error_positive = sum([weights[k] for k in range(len(feature)) if c_o_positive[k]!=label[k]])#记录不等于label的weight并求和
                c_o_negative = np.array([-1 if feature[k] > v else 1 for k in range(len(feature))])#同上 但反向
                error_negative = sum([weights[k] for k in range(len(feature)) if c_o_negative[k]!=label[k]])
                if error_positive < error_negative:#如果正向误差小于反向误差
                    weight_error = error_positive#记录误差
                    _compare_array = c_o_positive
                    direct = 'positive'
                else:
                    weight_error = error_negative
                    _compare_array = c_o_negative
                    direct = 'negative'
                if weight_error < error:
                    error = weight_error#记录误差
                    best_v = v#记录最佳分类值
                    compare_array = _compare_array#记录label
        return best_v,error,compare_array,direct

    def _G(self,x,v,direct):
        if direct=='positive':
            return 1 if x > v else -1
        else:
            return -1 if x>x else -1

    def fit(self,X,y):
        self.init_data(X,y)#初始化类内参数
        for i in range(self.n_estimate):#迭代次数
            best_clf_error,best_v,clf_result = 100000,None,None
            #单个属性
            for j in range(self.N):#在属性列上迭代
                feature = self.X[:,j]#
                v,error,compare,direct = self.G(feature,self.y,self.w)
                if error<best_clf_error:
                    best_clf_error = error
                    best_v = v
                    final_direct = direct
                    clf_result = compare
                    axis = j
                if best_clf_error == 0:
                    break
            a = self._alpha(best_clf_error)#计算alpha 0.5*np.log((1-error)/error)
            self.alpha.append(a)#记录alpha
            z = self._Z(self.w,a,clf_result)
            self.clf_set.append((axis,best_v,final_direct))#clf_set集合
            self._w(a,clf_result,z)#修改self.w

    def predict(self,feature):#预测函数(比较简单)
        result = 0.0
        for i in range(len(self.clf_set)):
            axis,best_v,final_direct = self.clf_set[i]
            f = feature[axis]
            result += self.alpha[i] * self._G(f,best_v,final_direct)
        return 1 if result >0 else -1

    def score(self,X_test,y_test):#打分函数(比较简单)
        count = 0
        for i in range(len(X_test)):
            feature = X_test[i]
            if self.predict(feature) == y_test[i]:
                count += 1
        return count/len(X_test)

推荐阅读:

一大批历史精彩文章啦

详解文本分类之多通道CNN的理论与实践

详解文本分类之DeepCNN的理论与实践

介绍4个大神常用而你不常用的python函数

资源 | 邓力、刘洋等合著的这本NLP经典书籍之情感分析中文版

资源 | 有没有必要把机器学习算法自己实现一遍?

谈谈我在自然语言处理进阶上的一些个人拙见

从Word Embedding到Bert模型——自然语言处理预训练技术发展史


好文!必须点赞

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值