机器学习(9): Adaboost算法 小结及实验

在这里插入图片描述

注:转载请标明原文出处链接:https://xiongyiming.blog.csdn.net/article/details/97163076


1 Adaboost算法简介

Adaboost (adaptive boosting) 是一种迭代算法,其核心思想是针对同一个训练集训练不同的分类器(弱分类器),然后把这些弱分类器集合起来,构成一个更强的最终分类器(强分类器)。
Adaboost算法已被证明是一种有效而实用的Boosting算法。该算法是Freund和Schapire于1995年对Boosting算法的改进得到的,其算法原理是通过调整样本权重和弱分类器权值,从训练出的弱分类器中筛选出权值系数最小的弱分类器组合成一个最终强分类器。基于训练集训练弱分类器,每次下一个弱分类器都是在样本的不同权值集上训练获得的。每个样本被分类的难易度决定权重,而分类的难易度是经过前面步骤中的分类器的输出估计得到的。
Adaboost算法在样本训练集使用过程中,对其中的关键分类特征集进行多次挑选,逐步训练分量弱分类器,用适当的阈值选择最佳弱分类器,最后将每次迭代训练选出的最佳弱分类器构建为强分类器。其中,级联分类器的设计模式为在尽量保证感兴趣图像输出率的同时,减少非感兴趣图像的输出率,随着迭代次数不断增加,所有的非感兴趣图像样本都不能通过,而感兴趣样本始终保持尽可能通过为止。
Aadboost 算法系统具有较高的检测速率,且不易出现过适应现象。但是该算法在实现过程中为取得更高的检测精度则需要较大的训练样本集,在每次迭代过程中,训练一个弱分类器则对应该样本集中的每一个样本,每个样本具有很多特征,因此从庞大的特征中训练得到最优弱分类器的计算量增大。
(以上均来自百度百科)



2 集成学习

2.1 集成学习概念

k-近邻算法、决策树、朴素贝叶斯、逻辑回归和支持向量机是一些经典的分类算法,他们各自有自己的优缺点,我们将这些方法可以称之为 弱分类器
弱分类器间存在一定的差异性,这会导致分类的边界不同,也就是说可能存在错误。那么将多个若分类器合并后,就可以得到更加合理的边界,减少整体的错误率,实现更好的效果。
集成学习 (Ensemble learning) 就是将若干个弱分类器通过一定的策略组合之后产生一个强分类器,如下图所示。弱分类器(Weak Classifier)指的就是那些分类准确率只比随机猜测略好一点的分类器,而强分类器 (Strong Classifier) 的分类准确率会高很多。这里的"强"和"弱"是相对的。有些书中也会把弱分类器称为“基分类器”。

在这里插入图片描述

目前集成学习算法的类型主要有 bagging boosting 两种。


2.2 bagging

bagging (bootstrap aggregating)自主汇聚,是通过结合几个模型降低泛化误差的技术。主要想法是分别训练几个不同的模型,然后让所有模型表决测试样例的输出。
bagging是一种根据均匀概率分布从数据集中重复抽样(有放回的)的技术。每个新数据集和原始数据集的大小相等。由于新数据集中的每个样本都是从原始数据集中有放回的随机抽样出来的,所以新数据集中可能有重复的值,而原始数据集中的某些样本可能根本就没出现在新数据集中。
bagging结构框架如下图所示,首先从训练样本集( m m m个样本)中重复采样(有放回的)得到 n n n个采样集,每个采样集中的样本数 m m m个,然后每个采样集用不同的分类器独立训练得到 n n n个弱分类器,将这些弱分类器以某种策略进行组合得到强分类器。
bagging方法的代表算法是随机森林

在这里插入图片描述



2.3 boosting

boosting 是一种框架算法,主要是通过对样本集的操作获得样本子集,然后用弱分类算法在样本子集上训练生成一系列的基分类器。
boosting方法是一种用来提高弱分类算法准确度的方法,这种方法通过构造一个预测函数系列,然后以一定的方式将他们组合成一个预测函数。boosting是一种提高任意给定学习算法准确度的方法。它的思想起源于 Valiant提出的 PAC (Probably Approxi mately Correct)学习模型。
boosting方式每次使用的是全部的样本,每轮训练改变样本的权重。下一轮训练的目标是找到一个函数来拟合上一轮的残差。当残差足够小或者达到设置的最大迭代次数则停止。Boosting会减小在上一轮训练正确的样本的权重,增大错误样本的权重。(正确分类的残差小,错误分类的残差大)
boosting结构框架如下图所示,首先从赋予权重给训练样本集( m m m个样本),然后第一个含有样本权重的训练街进行训练得到弱分类器1,根据弱分类器1的训练误差更新权重系数并更新第二个样本权重,然后继续训练,以此类推。boosting的每个权重代表着对应分类器在上一个迭代的成功度。 n n n个弱分类器训练好后,在根据策略进行组合得到强分类器。

在这里插入图片描述

boosting方法的代表算法有AdaboostGBDTXGBoost算法。我们这里主要介绍最流行的Adaboost 算法



2.4 bagging与boosting的区别

从图中可以看出,bagging与boosting还是有很大不同的,下面是对二者区别的总结。参考博客 https://blog.csdn.net/u013709270/article/details/72553282

(1) 样本选择
bagging:训练集是在原始集中有放回选取的,从原始集中选出的各轮训练集之间是独立的;
boosting:每一轮的训练集不变,只是训练集中每个样本在分类器中的权重发生变化。而权值是根据上一轮的分类结果进行调整。

(2) 样本权重
bagging:使用均匀取样,每个样例的权重相等;
boosting:根据错误率不断调整样本的权值,错误率越大则权重越大。

(3) 预测函数
bagging:所有预测函数的权重相等;
Boosting:每个弱分类器都有相应的权重,对于分类误差小的分类器会有更大的权重。

(4) 并行计算
bagging:各个预测函数可以并行生成;
boosting:各个预测函数只能顺序生成,因为后一个模型参数需要前一轮模型的结果。



2.5 组合策略

(1) 平均法

对于数值类的回归预测问题,通常使用的结合策略是平均法,也就是说,对于若干个弱学习器的输出进行平均得到最终的预测输出。
假设我们最终得到的n个弱分类器为 { h 1 , h 2 , … , h n } \left\{ {{h_1},{h_2}, \ldots ,{h_n}} \right\} {h1,h2,,hn}
最简单的平均是算术平均,也就是说最终预测是
H ( x ) = 1 n ∑ i = 1 n h i ( x ) H(x) = {1 \over n}\sum\limits_{i = 1}^n {{h_i}(x)} H(x)=n1i=1nhi(x) 如果每个弱分类器有一个权重 w w w且所有权重和为1,则最终预测是
H ( x ) = 1 n ∑ i = 1 n w i h i ( x )  s.t  w i ≥ 0 , ∑ i = 1 n w i = 1 \begin{array}{c}{H(x)=\frac{1}{n} \sum_{i=1}^{n} w_{i} h_{i}(x)} \\ {\text { s.t } w_{i} \geq 0, \sum_{i=1}^{n} w_{i}=1}\end{array} H(x)=n1i=1nwihi(x) s.t wi0,i=1nwi=1


(2) 投票法

最简单的投票法是相对多数投票法,也就是我们常说的少数服从多数,也就是n个弱学习器的对样本的预测结果中,数量最多的类别 为最终的分类类别。如果不止一个类别获得最高票,则随机选择一个做最终类别。
我们常说的要票过半数。在相对多数投票法的基础上,不光要求获得最高票,还要求票过半数。否则会拒绝预测。当前投票法还有更复杂的方法。这里不再赘述


(3) 学习法

平均法和投票法前都是对弱学习器的结果做一些简单的处理,但是可能在有些情况下学习误差较大,于是就有了学习法这种方法,对于学习法,代表方法是stacking,当使用stacking的结合策略时, 我们不是对弱学习器的结果做简单的逻辑处理,而是再加上一层学习器,也就是说,我们将训练集弱学习器的学习结果作为输入,将训练集的输出作为输出,重新训练一个学习器来得到最终结果。
在这种情况下,我们将弱学习器称为初级学习器,将用于结合的学习器称为次级学习器。对于测试集,我们首先用初级学习器预测一次,得到次级学习器的输入样本,再用次级学习器预测一次,得到最终的预测结果。



3 Adaboost 算法

Adaboost (adaptive boosting)算法流程如下

Adaboost算法流程

  1. 计算样本权重
    赋予训练集中每个样本一个权重,构成权重向量 D D D,将权重向量 D D D初始化相等值。
    假设我们有n个样本的训练集 { ( x 1 , y 1 ) , ( x 2 , y 2 ) , … , ( x n , y n ) } \left\{ {\left( {{x_1},{y_1}} \right),\left( {{x_2},{y_2}} \right), \ldots ,\left( {{x_n},{y_n}} \right)} \right\} {(x1,y1),(x2,y2),,(xn,yn)},则设定每个样本的权重都相等,则权重为 1 n {1 \over n} n1
  2. 训练并计算错误率
    在训练集上训练出一个弱分类器,并计算分类器的错误率
    ε = N e r r o r N A l l \varepsilon = {{{N_{{\rm{error}}}}} \over {{N_{A{\rm{ll}}}}}} ε=NAllNerror
  3. 计算弱分类器权重
    为当前分类器赋予权重值 α \alpha α,则 α \alpha α计算公式为
    α = 1 2 l n ( 1 − ε ε ) \alpha = {1 \over 2}{\rm{ln}}\left( {{{1 - \varepsilon } \over \varepsilon }} \right) α=21ln(ε1ε)
  4. 调整权重值
    根据上一次训练结果,调整权重向量 D D D(上一次正确分类的权重降低,错误分类的权重增加)
    如果第 i i i个样本被正确分类,则该样本权重更改为
    D i ( t + 1 ) = D i ( t ) e − α s u m ( D ) D_i^{\left( {t + 1} \right)} = {{D_i^{\left( t \right)}{e^{ - \alpha }}} \over {{\rm{sum}}\left( D \right)}} Di(t+1)=sum(D)Di(t)eα如果第 i i i个样本被错误分类,则该样本权重更改为
    D i ( t + 1 ) = D i ( t ) e α s u m ( D ) D_i^{\left( {t + 1} \right)} = {{D_i^{\left( t \right)}{e^\alpha }} \over {{\rm{sum}}\left( D \right)}} Di(t+1)=sum(D)Di(t)eα
    计算出权重向量 D D D之后,进入下一次迭代,直到训练错误率或者迭代次数达到用户指定的时停止。
    这个算法流程也可以参见上面提到的boosting的结构框图



4 实验

实验1 基于单层决策树构建弱分类器

单层决策树(decision stump)也称决策树桩,是一种简单的决策树。接下来我构建一个单层决策树,它仅仅基于单个特征来做决策。由于这棵树只有一次分裂过程,因此它实际上就是一个树桩。

(1) 构建简单的数据集
读取 simpdata.txt 文件 ,文件数据图下图所示,含有5个样本,最后一列是标签.

在这里插入图片描述
我们可以可视化数据,如下图所示。

在这里插入图片描述


读取数据并可视化数据代码如下

import pandas as pd
import numpy as np
#获得特征矩阵和标签矩阵
def get_Mat(path):
    dataSet = pd.read_table(path,header = None)
    xMat = np.mat(dataSet.iloc[:,:-1].values)
    yMat = np.mat(dataSet.iloc[:,-1].values).T
    return xMat,yMat
xMat,yMat = get_Mat('simpdata.txt')

import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['simhei']
#%matplotlib inline

#数据集可视化函数
def showPlot(xMat,yMat):
    x=np.array(xMat[:,0])
    y=np.array(xMat[:,1])
    label = np.array(yMat)
    plt.scatter(x,y,c=label)
    plt.title('单层决策树测试数据')
    plt.show()
showPlot(xMat,yMat)

(2) 构建单层决策树
我们会建立两个函数来实现我们的单层决策树:
第一个函数用来测试是否有某个值小于或者大于我们正在测试的阈值。
第二个函数稍微复杂一些,会在一个加权数据集中循环,并找到具有最低错误率的单层决策树.
伪代码如下

伪代码
将最小错误率minE设为+∞
对数据集中每一个特征(第1层循环):
     对每个步长(第2层循环):
          对每个不等号(第3层循环):
               建立一颗单层决策树并利用加权数据集对它进行预测
               如果错误率低于minE,则将当前单层决策树设为最佳单层决策树
返回最佳单层决策树


代码如下

# 1 构建简单的数据集

import pandas as pd
import numpy as np
#获得特征矩阵和标签矩阵
def get_Mat(path):
    dataSet = pd.read_table(path,header = None)
    xMat = np.mat(dataSet.iloc[:,:-1].values)
    yMat = np.mat(dataSet.iloc[:,-1].values).T
    return xMat,yMat
xMat,yMat = get_Mat('simpdata.txt')

# import matplotlib.pyplot as plt
# plt.rcParams['font.sans-serif']=['simhei']
# #%matplotlib inline
# #数据集可视化函数
# def showPlot(xMat,yMat):
#     x=np.array(xMat[:,0])
#     y=np.array(xMat[:,1])
#     label = np.array(yMat)
#     plt.scatter(x,y,c=label)
#     plt.title('单层决策树测试数据')
#     plt.show()
# showPlot(xMat,yMat)


# 构建单层决策树
"""
函数功能:单层决策树分类函数
参数说明:
xMat: 数据矩阵
i: 第i列,也就是第几个特征
Q: 阈值
S: 标志
返回:
re: 分类结果
"""
def Classify0(xMat,i,Q,S):
    re = np.ones((xMat.shape[0],1)) #初始化re为1
    if S == 'lt':
        re[xMat[:,i] <= Q] = -1 #如果小于阈值,则赋值为-1
    else:
        re[xMat[:,i] > Q] = -1 #如果大于阈值,则赋值为-1
    return re


"""
函数功能:找到数据集上最佳的单层决策树
参数说明:
xMat:特征矩阵
yMat:标签矩阵
D:样本权重
返回:
bestStump:最佳单层决策树信息
minE:最小误差
bestClas:最佳的分类结果
"""
def get_Stump(xMat,yMat,D):
        m,n = xMat.shape #m为样本个数,n为特征数
        Steps = 10 #初始化一个步数
        bestStump = {} #用字典形式来储存树桩信息
        bestClas = np.mat(np.zeros((m,1))) #初始化分类结果为1
        minE = np.inf #最小误差初始化为正无穷大
        for i in range(n): #遍历所有特征
            Min = xMat[:,i].min() #找到特征中最小值
            Max = xMat[:,i].max() #找到特征中最大值
            stepSize = (Max - Min) / Steps #计算步长
            for j in range(-1, int(Steps)+1):
                for S in ['lt', 'gt']: #大于和小于的情况,均遍历。lt:less than,gt:greater than
                    Q = (Min + j * stepSize) #计算阈值
                    re = Classify0(xMat, i, Q, S) #计算分类结果
                    err = np.mat(np.ones((m,1))) #初始化误差矩阵
                    err[re == yMat] = 0 #分类正确的,赋值为0
                    eca = D.T * err #计算误差
                #print(f'切分特征: {i}, 阈值:{np.round(Q,2)}, 标志:{S}, 权重误差: {np.round(eca,3)}')
                    if eca < minE: #找到误差最小的分类方式
                        minE = eca
                        bestClas = re.copy()
                        bestStump['特征列'] = i
                        bestStump['阈值'] = Q
                        bestStump['标志'] = S
        return bestStump,minE,bestClas


# 测试函数并运行查看结果
m = xMat.shape[0]
D = np.mat(np.ones((m, 1)) / m) #初始化样本权重(每个样本权重相等)
bestStump,minE,bestClas= get_Stump(xMat,yMat,D)

print("bestStump=",bestStump)
print("minE=",minE)
print("bestClas=",bestClas)

运行结果

在这里插入图片描述



实验2 AdaBoost算法的训练与测试

AdaBoost算法的训练伪代码如下所示,数据还是上面的简单数据集。

AdaBoost算法伪代码
对每一次迭代:
          利用bestStump()函数找到最佳的单层决策树
          将单层决策树加入到单层决策树数组
          计算分类器权重alpha 更新样本权重向量D
          更新累积类别估计值 如果错误率等于0,则退出循环


代码示例

import pandas as pd
from numpy import *
import numpy as np

# 1 构建简单的数据集

import pandas as pd
import numpy as np
#获得特征矩阵和标签矩阵
def get_Mat(path):
    dataSet = pd.read_table(path,header = None)
    xMat = np.mat(dataSet.iloc[:,:-1].values)
    yMat = np.mat(dataSet.iloc[:,-1].values).T
    return xMat,yMat


# import matplotlib.pyplot as plt
# plt.rcParams['font.sans-serif']=['simhei']
# #%matplotlib inline
# #数据集可视化函数
# def showPlot(xMat,yMat):
#     x=np.array(xMat[:,0])
#     y=np.array(xMat[:,1])
#     label = np.array(yMat)
#     plt.scatter(x,y,c=label)
#     plt.title('单层决策树测试数据')
#     plt.show()
# showPlot(xMat,yMat)


# 构建单层决策树
"""
函数功能:单层决策树分类函数
参数说明:
xMat: 数据矩阵
i: 第i列,也就是第几个特征
Q: 阈值
S: 标志
返回:
re: 分类结果
"""
def Classify0(xMat,i,Q,S):
    re = np.ones((xMat.shape[0],1)) #初始化re为1
    if S == 'lt':
        re[xMat[:,i] <= Q] = -1 #如果小于阈值,则赋值为-1
    else:
        re[xMat[:,i] > Q] = -1 #如果大于阈值,则赋值为-1
    return re


"""
函数功能:找到数据集上最佳的单层决策树
参数说明:
xMat:特征矩阵
yMat:标签矩阵
D:样本权重
返回:
bestStump:最佳单层决策树信息
minE:最小误差
bestClas:最佳的分类结果
"""
def get_Stump(xMat,yMat,D):
        m,n = xMat.shape #m为样本个数,n为特征数
        Steps = 10 #初始化一个步数
        bestStump = {} #用字典形式来储存树桩信息
        bestClas = np.mat(np.zeros((m,1))) #初始化分类结果为1
        minE = np.inf #最小误差初始化为正无穷大
        for i in range(n): #遍历所有特征
            Min = xMat[:,i].min() #找到特征中最小值
            Max = xMat[:,i].max() #找到特征中最大值
            stepSize = (Max - Min) / Steps #计算步长
            for j in range(-1, int(Steps)+1):
                for S in ['lt', 'gt']: #大于和小于的情况,均遍历。lt:less than,gt:greater than
                    Q = (Min + j * stepSize) #计算阈值
                    re = Classify0(xMat, i, Q, S) #计算分类结果
                    err = np.mat(np.ones((m,1))) #初始化误差矩阵
                    err[re == yMat] = 0 #分类正确的,赋值为0
                    eca = D.T * err #计算误差
                #print(f'切分特征: {i}, 阈值:{np.round(Q,2)}, 标志:{S}, 权重误差: {np.round(eca,3)}')
                    if eca < minE: #找到误差最小的分类方式
                        minE = eca
                        bestClas = re.copy()
                        bestStump['特征列'] = i
                        bestStump['阈值'] = Q
                        bestStump['标志'] = S
        return bestStump,minE,bestClas



# AdaBoost训练

"""
函数功能:基于单层决策树的AdaBoost训练过程
参数说明:
    xMat:特征矩阵
    yMat:标签矩阵
    maxC:最大迭代次数
返回:
    weakClass:弱分类器信息
    aggClass:类别估计值(其实就是更改了标签的估计值)
"""
def Ada_train(xMat, yMat, maxC = 40):
    weakClass = []
    m = xMat.shape[0]
    D = np.mat(np.ones((m, 1)) / m) #初始化权重
    aggClass = np.mat(np.zeros((m,1)))
    for i in range(maxC):
        Stump, error, bestClas = get_Stump(xMat, yMat,D) #构建单层决策树
        #print(f"D:{D.T}")
        alpha=float(0.5 * np.log((1 - error) / max(error, 1e-16))) #计算弱分类器权重alpha
        Stump['alpha'] = np.round(alpha,2) #存储弱学习算法权重,保留两位小数
        weakClass.append(Stump) #存储单层决策树
        #print("bestClas: ", bestClas.T)
        expon = np.multiply(-1 * alpha *yMat, bestClas) #计算e的指数项
        D = np.multiply(D, np.exp(expon))
        D = D / D.sum() #根据样本权重公式,更新样本权重
        aggClass += alpha * bestClas #更新累计类别估计值
        #print(f"aggClass: {aggClass.T}" )
        aggErr = np.multiply(np.sign(aggClass) != yMat, np.ones((m,1)))#计算误差
        errRate = aggErr.sum() / m
        #print(f"分类错误率: {errRate}")
        if errRate == 0: break #误差为0,退出循环
    return weakClass, aggClass



# AdaBoost实现分类(测试)

"""
函数功能:AdaBoost分类函数
参数说明:
data: 待分类样例(测试样本)
classifys:训练好的分类器
返回:
分类结果
"""
def AdaClassify(data,weakClass):
    dataMat = np.mat(data)
    m = dataMat.shape[0]
    aggClass = np.mat(np.zeros((m,1)))
    for i in range(len(weakClass)): #遍历所有分类器,进行分类
        classEst = Classify0(dataMat,
        weakClass[i]['特征列'],
        weakClass[i]['阈值'],
        weakClass[i]['标志'])
        aggClass += weakClass[i]['alpha'] * classEst
        # print("aggClass=",aggClass)
    return np.sign(aggClass) #根据制的大小进行判别


#### 读取数据 训练 测试
xMat,yMat = get_Mat('simpdata.txt') # 读取数据
weakClass, aggClass =Ada_train(xMat, yMat, maxC = 40) # AdaBoost算法训练

# AdaBoost算法训练后,对测试样本进行分类
result=AdaClassify([0,0],weakClass)
print("分类结果=",result)

运行结果

在这里插入图片描述



实验3 马疝病数据集上应用AdaBoost算法

逻辑回归算法 小结及实验 中用到了马疝病数据集,我们利用逻辑回归算法来预测患有疝病的马是否能存活。而在这个实验中,我们想要知道利用多个单层决策树(弱分类器)和AdaBoost算法的预测性能如何。

代码示例
(使用20个弱分类器)

import pandas as pd
from numpy import *
import numpy as np

# 1 构建简单的数据集

import pandas as pd
import numpy as np
#获得特征矩阵和标签矩阵
def get_Mat(path):
    dataSet = pd.read_table(path,header = None)
    xMat = np.mat(dataSet.iloc[:,:-1].values)
    yMat = np.mat(dataSet.iloc[:,-1].values).T
    return xMat,yMat


# import matplotlib.pyplot as plt
# plt.rcParams['font.sans-serif']=['simhei']
# #%matplotlib inline
# #数据集可视化函数
# def showPlot(xMat,yMat):
#     x=np.array(xMat[:,0])
#     y=np.array(xMat[:,1])
#     label = np.array(yMat)
#     plt.scatter(x,y,c=label)
#     plt.title('单层决策树测试数据')
#     plt.show()
# showPlot(xMat,yMat)


# 构建单层决策树
"""
函数功能:单层决策树分类函数
参数说明:
xMat: 数据矩阵
i: 第i列,也就是第几个特征
Q: 阈值
S: 标志
返回:
re: 分类结果
"""
def Classify0(xMat,i,Q,S):
    re = np.ones((xMat.shape[0],1)) #初始化re为1
    if S == 'lt':
        re[xMat[:,i] <= Q] = -1 #如果小于阈值,则赋值为-1
    else:
        re[xMat[:,i] > Q] = -1 #如果大于阈值,则赋值为-1
    return re


"""
函数功能:找到数据集上最佳的单层决策树
参数说明:
xMat:特征矩阵
yMat:标签矩阵
D:样本权重
返回:
bestStump:最佳单层决策树信息
minE:最小误差
bestClas:最佳的分类结果
"""
def get_Stump(xMat,yMat,D):
        m,n = xMat.shape #m为样本个数,n为特征数
        Steps = 10 #初始化一个步数
        bestStump = {} #用字典形式来储存树桩信息
        bestClas = np.mat(np.zeros((m,1))) #初始化分类结果为1
        minE = np.inf #最小误差初始化为正无穷大
        for i in range(n): #遍历所有特征
            Min = xMat[:,i].min() #找到特征中最小值
            Max = xMat[:,i].max() #找到特征中最大值
            stepSize = (Max - Min) / Steps #计算步长
            for j in range(-1, int(Steps)+1):
                for S in ['lt', 'gt']: #大于和小于的情况,均遍历。lt:less than,gt:greater than
                    Q = (Min + j * stepSize) #计算阈值
                    re = Classify0(xMat, i, Q, S) #计算分类结果
                    err = np.mat(np.ones((m,1))) #初始化误差矩阵
                    err[re == yMat] = 0 #分类正确的,赋值为0
                    eca = D.T * err #计算误差
                #print(f'切分特征: {i}, 阈值:{np.round(Q,2)}, 标志:{S}, 权重误差: {np.round(eca,3)}')
                    if eca < minE: #找到误差最小的分类方式
                        minE = eca
                        bestClas = re.copy()
                        bestStump['特征列'] = i
                        bestStump['阈值'] = Q
                        bestStump['标志'] = S
        return bestStump,minE,bestClas



# AdaBoost训练

"""
函数功能:基于单层决策树的AdaBoost训练过程
参数说明:
    xMat:特征矩阵
    yMat:标签矩阵
    maxC:最大迭代次数
返回:
    weakClass:弱分类器信息
    aggClass:类别估计值(其实就是更改了标签的估计值)
"""
def Ada_train(xMat, yMat, maxC = 40):
    weakClass = []
    m = xMat.shape[0]
    D = np.mat(np.ones((m, 1)) / m) #初始化权重
    aggClass = np.mat(np.zeros((m,1)))
    for i in range(maxC):
        Stump, error, bestClas = get_Stump(xMat, yMat,D) #构建单层决策树
        #print(f"D:{D.T}")
        alpha=float(0.5 * np.log((1 - error) / max(error, 1e-16))) #计算弱分类器权重alpha
        Stump['alpha'] = np.round(alpha,2) #存储弱学习算法权重,保留两位小数
        weakClass.append(Stump) #存储单层决策树
        #print("bestClas: ", bestClas.T)
        expon = np.multiply(-1 * alpha *yMat, bestClas) #计算e的指数项
        D = np.multiply(D, np.exp(expon))
        D = D / D.sum() #根据样本权重公式,更新样本权重
        aggClass += alpha * bestClas #更新累计类别估计值
        #print(f"aggClass: {aggClass.T}" )
        aggErr = np.multiply(np.sign(aggClass) != yMat, np.ones((m,1)))#计算误差
        errRate = aggErr.sum() / m
        #print(f"分类错误率: {errRate}")
        if errRate == 0: break #误差为0,退出循环
    return weakClass, aggClass



# AdaBoost实现分类(测试)

"""
函数功能:AdaBoost分类函数
参数说明:
data: 待分类样例(测试样本)
classifys:训练好的分类器
返回:
分类结果
"""
def AdaClassify(data,weakClass):
    dataMat = np.mat(data)
    m = dataMat.shape[0]
    aggClass = np.mat(np.zeros((m,1)))
    for i in range(len(weakClass)): #遍历所有分类器,进行分类
        classEst = Classify0(dataMat,
        weakClass[i]['特征列'],
        weakClass[i]['阈值'],
        weakClass[i]['标志'])
        aggClass += weakClass[i]['alpha'] * classEst
        # print("aggClass=",aggClass)
    return np.sign(aggClass) #根据制的大小进行判别


# #### 读取数据 训练 测试
# xMat,yMat = get_Mat('simpdata.txt') # 读取数据
# weakClass, aggClass =Ada_train(xMat, yMat, maxC = 40) # AdaBoost算法训练
#
# # AdaBoost算法训练后,对测试样本进行分类
# result=AdaClassify([0,0],weakClass)
# print("分类结果=",result)

# train = pd.read_table('horseColicTraining2.txt',header=None)
# test = pd.read_table('horseColicTest2.txt',header=None)

# 封装分类函数(读取数据集 训练 分类)
# maxC 表示分类器数量
def calAcc(maxC = 40):
    train_xMat,train_yMat = get_Mat('horseColicTraining2.txt')
    m=train_xMat.shape[0]
    weakClass, aggClass =Ada_train(train_xMat, train_yMat, maxC)
    yhat = AdaClassify(train_xMat,weakClass)
    train_re=0
    for i in range(m):
        if yhat[i]==train_yMat[i]:
            train_re+=1
    train_acc= train_re/m
    print(f'训练集准确率:{train_acc}')
    test_re=0
    test_xMat,test_yMat=get_Mat('horseColicTest2.txt')
    n=test_xMat.shape[0]
    yhat = AdaClassify(test_xMat,weakClass)
    for i in range(n):
        if yhat[i]==test_yMat[i]:
            test_re+=1
    test_acc=test_re/n
    print(f'测试集准确率:{test_acc}')
    return train_acc,test_acc


# 使用20个分类器
calAcc(maxC = 20) # 调用封装好的分类函数

运行结果

使用20个分类器训练及测试结果如下图所示
在这里插入图片描述



下面使用不同数量的弱分类器,看一下AdaBoost算法的性能会怎样?
我们试着分别使用1,10,50,100,500,1000,10000个弱分类器

主函数代码如下

Cycles=[1,10,50,100,500,1000,10000] #弱分类器数量
train_acc=[]
test_acc=[]
for maxC in Cycles:
    a,b=calAcc(maxC)
    train_acc.append(round(a*100,2))
    test_acc.append(round(b*100,2))
df=pd.DataFrame({'分类器数目':Cycles,
                '训练集准确率':train_acc,
                '测试集准确率':test_acc})
print(df)

运行结果

在这里插入图片描述
从上述结果中可以看出,当弱分类器数量达到50个的时候,训练集和测试集的预测准确率均达到了一个比较高的值,但是如果继续增加弱分类器数量的话,测试集的准确率反而开始下降了,这就是所谓的过拟合(overfitting)。

过拟合(overfitting)的判断
一个假设在训练数据上能够获得比其他假设更好的拟合, 但是在训练数据外的数据集上却不能很好地拟合数据,此时认为这个假设出现了过拟合的现象。


过拟合常见的原因
(1) 建模样本选取有误,如样本数量太少,选样方法错误,样本标签错误等,导致选取的样本数据不足以代表预定的分类规则;

(2) 样本噪音干扰过大,使得机器将部分噪音认为是特征从而扰乱了预设的分类规则;

(3) 假设的模型无法合理存在,或者说是假设成立的条件实际并不成立;
(4) 参数太多,模型复杂度过高;

(5) 对于决策树模型,如果我们对于其生长没有合理的限制,其自由生长有可能使节点只包含单纯的事件数据(event)或非事件数据(no event),使其虽然可以完美匹配(拟合)训练数据,但是无法适应其他数据集。

(6) 对于神经网络模型:
     a) 对样本数据可能存在分类决策面不唯一,随着学习的进行,BP算法使权值可能收敛过于复杂的决策面;
     b) 权值学习迭代次数足够多(Overtraining),拟合了训练数据中的噪声和训练样例中没有代表性的特征。




参考资料

[1] 机器学习实战. 人民邮电出版社.
[2] https://www.jianshu.com/p/157ad5f1f38c
[3] https://blog.csdn.net/u013709270/article/details/72553282

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TechArtisan6

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值