集成学习算法:AdaBoost原理详解以及基于adaboost的图像二分类代码实现

本文尽量从一个机器学习小白或是只对机器学习算法有一个大体浅显的视角入手,尽量通俗易懂的介绍清楚AdaBoost算法!

一、AdaBoost简介

  AdaBoost,是英文"Adaptive Boosting"(自适应增强)的缩写,由Yoav Freund和Robert Schapire在1995年提出。AdaBoost(Adaptive Boosting)是一种集成学习算法

1.1:什么是集成学习算法?

 集成学习算法其实不能理解为一种具体算法(像knn、kmeans这样十分具体的算法),它其实是一种算法策略、算法框架。它的核心是在于, 将多个弱学习器(模型)结合(集成),从而得到一个更强大的模型。提高整体的性能和稳定性。

💡说的好像很轻松,“结合”,那么具体如何结合呢?如何结合才能提到性能呢?确实,我也有这个疑问,我们继续接着往下看。

 其实举个例子🌰就好理解了:其实集成学习的思想在监督学习中最为常见,比如我们知道的随机森林算法就是多个决策树的集成。

在这里插入图片描述

💡是不是有点get到了这种意味?结合多个模型,共同来实现目的,OK,我们再继续往下看

1.2:什么是AdaBoost算法?

AdaBoost算法,简单来说,就是把很多个不是很强大的模型组合起来,形成一个非常强大的模型。在这个过程中,AdaBoost特别关注那些之前被错误分类的数据点,确保这些点在后续的训练中得到更多的注意,从而提高整体的学习效果。

💡这里其实还不是很懂,说的文绉绉的,“得到更多的注意”,那么是如何实现的呢?还有,为什么得到注意了就可以提高整体的效果勒?

我们先来看 AdaBoost的算法流程!!! ⭐⭐⭐⭐⭐

  1. 首先,是初始化训练数据的权值分布D1。假设有N个训练样本数据,则每一个训练样本最开始时,都被赋予相同的权值:w1=1/N
  2. 然后,训练弱分类器hi。具体训练过程中是:如果某个训练样本点,被弱分类器hi准确地分类,那么在构造下一个训练集中,它对应的权值要减小;相反,如果某个训练样本点被错误分类,那么它的权值就应该增大。权值更新过的样本集被用于训练下一个分类器,整个训练过程如此迭代地进行下去。
  3. 最后,将各个训练得到的弱分类器组合成一个强分类器。各个弱分类器的训练过程结束后,加大分类误差率小的弱分类器的权重,使其在最终的分类函数中起着较大的决定作用,而降低分类误差率大的弱分类器的权重,使其在最终的分类函数中起着较小的决定作用。
    换而言之,误差率低的弱分类器在最终分类器中占的权重较大,否则较小。

在这里插入图片描述

这里有几个点其实说的很抽象

  • “权重”:其实就是这个数据点的系数,权重越高,那么在下次迭代时会“得到更多关注”
  • “得到更多关注”,其实本质是影响的是损失函数,权重越高,在损失函数中所占比重越大,那么模型就会更加关注这个数据点(一旦被错误分类,那么会导致损失函数值加大很多,那么相应的模型也需要更多的调整以适应这个数据,根据损失函数调整的过程中其实就是对这个数据的关注)。

🛁其实到前面,就差不多能理解这个算法的主要思想了,如果这个数据越是难以被分类,那么模型对它的关注随着被分类错误而逐渐提升(自适应),一直到被分类正确。

二、 Adaboost算法流程

给定一个训练数据集T={(x1,y1), (x2,y2)…(xN,yN)},其中实例 x ∈ χ x\in \chi xχ,而实例空间 χ ∈ R n \chi \in R^{n} χRn,yi属于标记集合{-1,+1},Adaboost的目的就是从训练数据中学习一系列弱分类器或基本分类器,然后将这些弱分类器组合成一个强分类器。

Adaboost的算法流程如下:

相关符号定义:

  • t = 1 , 2 , . . . , T t = 1,2, ..., T t=1,2,...,T表示迭代的第多少轮
  • N N N:样本个数
  • D t ( i ) D_{t}(i) Dt(i):训练样本集的权值分布
  • w i w_{i} wi:每个训练样本的权值大小
  • h h h:弱分类器
  • H H H:基本分类器
  • H f i n a l H_{final} Hfinal:最终由弱分类器组合的强分类器
  • e e e:误差率 ( H t ( x i ) H_{t}(x_{i}) Ht(xi)在训练数据集上的 误差率 e t e_{t} et就是被 H t ( x i ) H_{t}(x_{i}) Ht(xi)误分类样本的权值之和)
  • α t \alpha _{t} αt:弱分类器的权重
  • Step1.:首先,初始化训练数据的权值分布。每一个训练样本最开始时都被赋予相同的权值: w i = 1 N w_{i} = \frac{1}{N} wi=N1, 这样得到训练样本集的初始权值分布。
    D 1 ( i ) = ( w 1 , w 2 , . . . w N ) = ( 1 N , . . . . . . 1 N ) D_{1}(i) = (w_{1},w_{2},...w_{N}) = (\frac{1}{N},......\frac{1}{N}) D1(i)=(w1,w2,...wN)=(N1,......N1)

  • Step2:进行多轮迭代,用 t = 1,2, ..., T 表示迭代的第多少轮
    1. 选取一个当前误差率最低的弱分类器h作为第t个基本分类器Ht,并计算该弱分类器在分布Dt上的误差率e为:

    e t = P ( H t ( x i ≠ y i ) = ∑ i = 1 N w t i I ( H t ( x i ) ≠ y i ) e_{t} = P(H_{t}(x_{i}\ne y_{i}) = \sum_{i=1}^{N}w_{ti}I(H_{t}(x_{i}) \ne y_{i}) et=P(Ht(xi=yi)=i=1NwtiI(Ht(xi)=yi)

    由上述式子可知, H t ( x ) H_{t}(x) Ht(x)在训练数据集上的 误差率 e t e_{t} et就是被 H t ( x ) H_{t}(x_) Ht(x)误分类样本的权值之和。

    2. 计算本次迭代的基本分类器 H t ( x ) H_{t}(x) Ht(x)的系数,即该弱分类器在最终分类器所占权重:

    a t = 1 2 ln ⁡ ( 1 − e t e t ) a_{t} = \frac{1}{2}\ln_{}{\left ( \frac{1-e_{t}}{e_{t}} \right ) } at=21ln(et1et)
    由上述式子可知,分类误差率越小的基本分类器在最终分类器中的作用越大。

    3. 更新训练数据集的权值分布(目的:得到样本的新的权值分布),用于下一轮迭代

    D t + 1 = D t ( i ) e x p ( − a t y i H t ( x i ) ) Z t D_{t+1} = \frac{D_{t}(i)exp(-a_{t}y_{i}H_{t}(x_{i}))}{Z_t} Dt+1=ZtDt(i)exp(atyiHt(xi))
    其中 Z t Z_t Zt为归一化常数: Z t = 2 e t ( 1 − e t ) Z_t = 2\sqrt{e_t(1-e_t)} Zt=2et(1et)

  • Step3:组合各个弱分类器Gm(m∈1~M)(可以看到其实就是将每轮得到的弱分类器乘以其权重系数叠加)
    f ( x ) = ∑ t = 1 T a t H t ( x ) f(x) = \sum_{t=1}^{T}a_tH_t(x) f(x)=t=1TatHt(x)
    通过符号函数sign的作用,从而得到最终分类器,如下:
    H f i n a l = s i g n ( f ( x ) ) = s i g n ( ∑ t = 1 T a t H t ( x ) ) H_{final} = sign(f(x)) = sign(\sum_{t=1}^{T}a_tH_t(x)) Hfinal=sign(f(x))=sign(t=1TatHt(x))


其实看完上面流程,我是有以下疑惑: adaboost算法的基本分类器的个数和具体形式如何确定呢?

  1. 基本分类器的个数的确定:
    基本分类器的个数,也就是迭代次数,通常是一个超参数,需要通过交叉验证或其他模型选择技术来确定。在训练过程中,随着基本分类器数量的增加,整体模型的性能通常会提高,但也可能出现过拟合的情况。因此,需要找到一个平衡点,使得模型在验证集上有最佳的性能。过多的基本分类器不仅会导致过拟合,还会增加模型的计算复杂度。
  2. 基本分类器的具体形式的确定:
    AdaBoost算法可以与多种类型的弱学习器结合使用,但最常用的是决策树,尤其是一层的决策树(也称为决策树桩)。选择哪种类型的弱学习器通常取决于问题本身的特性。例如,对于文本数据,可能选择朴素贝叶斯分类器作为基本分类器;而对于有大量数值型特征的数据,使用决策树桩可能更合适。基本分类器的选择也是一个超参数,需要基于实验结果来选择。在实际应用中,会通过试验不同的基本分类器类型,然后选择在验证集上性能最好的一种。

弱分类器(单层决策树)
Adaboost一般使用单层决策树作为其弱分类器。单层决策树是决策树的最简化版本,只有一个决策点,也就是说,如果训练数据有多维特征,单层决策树也只能选择其中一维特征来做决策,并且还有一个关键点,决策的阈值也需要考虑。
在这里插入图片描述


2024.05.22补充,在和csdn的好友交流到能不能使用贝叶斯模型作为弱学习器时,其实这是完全可以的,只要是一个可以分类(但是单一使用性能可能不太好的,都可以作为弱学习器)
在 AdaBoost 算法中,弱学习器通常被选取为容易训练且性能稍差的分类器,它们通常会随着训练过程进行改变。这是因为每次训练弱学习器时,都会根据上一轮迭代中错误的样本调整数据集的权重,使得新的弱学习器更专注于之前分类错误的样本。常见的弱学习器选择有:

  1. 决策树桩(Decision Stumps):一种非常简单的一棵决策树,通常只包含一个特征的判断。

  2. 感知机(Perceptrons):线性分类器,适用于简单的线性可分问题。

  3. 核机(Nueral Networks):简单的神经网络结构,例如单层感知器。

  4. 朴素贝叶斯分类器(Naive Bayes Classifiers):基于概率的简单分类器,适用于文本分类等任务。

  5. K-近邻分类器(K-Nearest Neighbors):一种非参数方法,根据数据点之间的距离来分类。

  6. 逻辑回归(Logistic Regression):虽然是常规的分类器,但其模型的正则化和限制可以在某些情况下被视作弱学习器。

  7. 支持向量机(Support Vector Machines):尽管本身是一个强分类器,但是如果适当设置参数(如不使用核技巧或降低正则化),可以降低其性能。

弱学习器通常不会在训练过程中改变其内部结构,但是它们是在不同的数据集上重新训练的,因为每次迭代都会基于之前的错误样本重新调整权重。AdaBoost 通过这种方式在多次迭代中不断优化每个弱学习器,以改进整个集成模型的性能。

重要的是要记住,所谓的“弱学习器”并不是说这些模型在单个任务上表现不佳,而是相对于单独任务的最优模型,它们性能稍逊一筹。在集成学习中,组合多个弱学习器能显著提高整体性能,从而构建出一个强学习器。

三、Adaboost的一个例子

例:给定如图所示的训练样本,弱分类器采用平行于坐标轴的直线,用Adaboost算法的实现强分类过程。

在这里插入图片描述

数据分析:
将这10个样本作为训练数据,根据 X 和Y 的对应关系,可把这10个数据分为两类,图中用“+”表示类别1,用“O”表示类别-1。本例使用水平或者垂直的直线作为分类器,图中已经给出了三个弱分类器,即:

在这里插入图片描述

  • Step1.:首先,初始化训练数据的权值分布。每一个训练样本最开始时都被赋予相同的权值:1/N。
     令每个权值w1i = 1/N = 0.1,其中,N = 10,i = 1,2, …, 10,然后分别对于t= 1,2,3, …等值进行迭代(t表示迭代次数,表示第t轮)
     下表已经给出训练样本的权值分布情况:
    在这里插入图片描述

  • Step2:进行多轮迭代,用t = 1,2, …, T表示迭代的第多少轮
    1. 选取一个当前误差率最低的弱分类器h作为第t个基本分类器Ht,并计算该弱分类器在分布Dt上的误差率e为:

    初试的权值分布D1为1/N(10个数据,每个数据的权值皆初始化为0.1),

    D1=[0.1, 0.1, 0.1, 0.1, 0.1, 0.1,0.1, 0.1, 0.1, 0.1]

    在权值分布D1的情况下,取已知的三个弱分类器h1、h2和h3中误差率最小的分类器作为第1个基本分类器H1(x)(三个弱分类器的误差率都是0.3,那就取第1个吧)
    H 1 = { 1 , X 1 < 2.5 − 1 , x 1 > 2.5 H_1 = \begin{cases}1, X_1<2.5 \\-1, x1>2.5 \end{cases} H1={1,X1<2.51,x1>2.5

    ps:某个分类器的误差率为该分类器的被错分样本的权重和

    2. 计算本次迭代的基本分类器 H t ( x ) H_{t}(x) Ht(x)的系数,即该弱分类器在最终分类器所占权重:

    分类器H1(x)=h1情况下,样本点“5 7 8”被错分,因此基本分类器H1(x)的误差率为:
    e 1 = ( 0.1 + 0.1 + 0.1 ) = 0.3 e_1 = (0.1+0.1+0.1) = 0.3 e1=(0.1+0.1+0.1)=0.3
    根据该分类器的误差率计算其在最终分类器中的权重:
    α 1 = 1 2 ln ⁡ 1 − e 1 e 1 = 1 2 ln ⁡ 1 − 0.3 0.3 = 0.4236 \alpha _1 = \frac{1}{2}\ln_{}{\frac{1-e_1}{e_1} } = \frac{1}{2}\ln_{}{\frac{1-0.3}{0.3} }= 0.4236 α1=21lne11e1=21ln0.310.3=0.4236

    ps:这个 α 1 \alpha_1 α1代表 H 1 ( x ) H_1(x) H1(x)在最终的分类函数中所占的权重为:0.4236

    可见,被误分类样本的权值之和影响误差率e,误差率e影响基本分类器在最终分类器中所占的权重α。
    在这里插入图片描述

    3. 更新训练数据集的权值分布(目的:得到样本的新的权值分布),用于下一轮迭代

    对于正确分类的训练样本“1 2 3 4 6 9 10”(共7个)的权值更新为:
    D 2 = D 1 2 ( 1 − e 1 ) = 1 10 × 1 2 × ( 1 − 0.3 ) = 1 14 D_2 = \frac{D_1}{2(1-e_1)} = \frac{1}{10}\times \frac{1}{2\times (1-0.3)}=\frac{1}{14} D2=2(1e1)D1=101×2×(10.3)1=141

    ps:可见,正确分类的样本权值减小

    而对于所有被错误分类的样本,权值变为:
    D 2 = D 1 2 e 1 = 1 10 × 1 2 × 0.3 = 1 6 D_2 = \frac{D_1}{2e_1} = \frac{1}{10}\times \frac{1}{2\times 0.3}=\frac{1}{6} D2=2e1D1=101×2×0.31=61

    ps:可见,被错误分类的样本权值增大
    这样,第1轮迭代后,最后得到各个样本数据新的权值分布:

    D2=[1/14,1/14,1/14,1/14,1/6,1/14,1/6,1/6,1/14,1/14]

    由于样本数据“5 7 8”被H1(x)分错了,所以它们的权值由之前的0.1增大到1/6;反之,其它数据皆被分正确,所以它们的权值皆由之前的0.1减小到1/14,下表给出了权值分布的变换情况:
    在这里插入图片描述

💡思考一下,这样有什么好处?这样的好处是,增大了错误样本的权重,导致下次选基本分类器时,就会尽量选错误分类样本少Or不是被经常分错的的那几个样本的分类器,这样的好处是能够自适应调整,从而提高性能。

可得分类函数:f1(x)= α1H1(x) = 0.4236H1(x)。此时,组合一个基本分类器sign(f1(x))作为强分类器在训练数据集上有3个误分类点(即5 7 8),此时强分类器的训练错误为:0.3

  • Step3:组合各个弱分类器Gm(m∈1~M)(可以看到其实就是将每轮得到的弱分类器乘以其权重系数叠加)
    通过Step2的迭代3次后,可以得到3个分类器:
    在这里插入图片描述
    可得分类函数:f3(x)=0.4236H1(x) + 0.6496H2(x)+0.9229H3(x)。此时,组合三个基本分类器sign(f3(x))作为强分类器,在训练数据集上有0个误分类点。至此,整个训练过程结束。
    整合所有分类器,可得最终的强分类器为:
    H f i n a l = s i g n ( ∑ t = 1 T a t H t ( x ) ) = s i g n ( 0.4236 H 1 ( x ) + 0.6496 H 2 ( x ) + 0.9229 H 3 ( x ) ) H_{final} = sign(\sum_{t=1}^{T}a_t H_t(x)) = sign(0.4236H_1(x)+ 0.6496H_2(x) + 0.9229H_3(x)) Hfinal=sign(t=1TatHt(x))=sign(0.4236H1(x)+0.6496H2(x)+0.9229H3(x))
    这个强分类器Hfinal对训练样本的错误率为0!

2024.05.22日反思,AdaBoost算法,训练的(改变的)到底是什么???

  1. 弱学习器在最终学习器的权重
  2. 弱学习器的内部结构(不一定);比如在上面这个例子中,弱学习器其实是没有改变的,但并不代表它不可以改变,而且你也可以想象,弱学习器当然是如果单个分类的比较好,当然也可以更好呀!!能训练(改变)为什么不训练呢?况且错误样本的权重也被增大,就更能很好的训练把错误样本尽量少分错。

所以,可以看到,与随机森林这种集成学习方法不同,Adaboost算法并不是(并不完全都是)给固定的几个分类器分配权重,而是在分配权重的这个训练过程中,也会对学习器根据其训练样本进行训练(弱学习器自身的选择和结构也会随着迭代过程而改变)。

四、AdaBoost的优点和缺点

🌸优点:

  1. 提高准确性: AdaBoost通过组合多个简单的模型(弱学习器)来构建一个强大的模型。这种方法可以有效提高分类的准确性。
  2. 易于实现: 相比于一些复杂的算法,AdaBoost相对容易实现。特别是当使用决策树桩作为基本分类器时,整个模型构建过程简单直观。
  3. 自动处理特征选择: 在构建决策树时,AdaBoost算法会自动选择对分类最有用的特征,因此不需要手动进行特征选择。
  4. 不太容易过拟合: 虽然AdaBoost涉及多个学习器的组合,但实践中表现出来的是,当基本分类器设置得当时,AdaBoost不太容易过拟合。

🌲缺点:

  1. 对噪声和异常值敏感:AdaBoost算法在增加错误分类数据点的权重时,如果数据集中存在噪声或异常值,它们的权重也会被增加,这可能会导致模型性能下降。
  2. 计算强度:尽管AdaBoost算法相对容易实现,但是随着基本分类器数量的增加,模型的训练和预测过程可能会变得计算密集,尤其是在数据集很大的情况下。
  3. 需要调整的参数:虽然AdaBoost的参数比较少(如基本分类器的数量),但是这些参数的选择会对最终模型的性能有显著影响,需要通过交叉验证等方法来仔细选择。

五、代码实现

5.1:Python的scikit-learn库简单实现:

下面是一个简单的AdaBoost算法实现,使用Python的scikit-learn库。这个例子中,我们将使用决策树桩(DecisionTreeClassifier的max_depth=1)作为弱学习器。

from sklearn.ensemble import AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

# 创造一些模拟的二分类数据集
X, y = make_classification(n_samples=1000, n_features=20, n_informative=2, n_redundant=0,
                           random_state=42, n_clusters_per_class=1)

# 将数据集分割成训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 创建AdaBoost模型实例
# 使用决策树桩作为基本分类器
ada_clf = AdaBoostClassifier(
    DecisionTreeClassifier(max_depth=1), n_estimators=200,
    algorithm="SAMME.R", learning_rate=0.5, random_state=42)

# 训练AdaBoost模型
ada_clf.fit(X_train, y_train)

# 进行预测
y_pred = ada_clf.predict(X_test)

# 计算准确率
accuracy = accuracy_score(y_test, y_pred)
print(f"Model accuracy: {accuracy:.2f}")

代码详细注释:

  • 首先,我们导入必要的库。AdaBoostClassifier是scikit-learn中的AdaBoost模型,DecisionTreeClassifier是我们选择的基本分类器。
  • 接着,我们使用make_classification函数生成一个模拟的二分类数据集,其中包含1000个样本,每个样本有20个特征。
  • 然后,我们使用train_test_split函数将数据集分割成训练集和测试集,测试集大小占20%。
  • 创建一个AdaBoostClassifier实例,设置基本分类器为DecisionTreeClassifier,其中max_depth=1意味着每个基本分类器是一个决策树桩。n_estimators=200指定了200个弱学习器的数量,algorithm="SAMME.R"是AdaBoost的一种实现,learning_rate=0.5是学习率,random_state=42用于确保结果的可重复性。
  • 使用fit方法在训练数据上训练AdaBoost模型。
  • 使用训练好的模型对测试集数据进行预测。
  • 计算并打印出模型在测试集上的准确率。

5.2:手写adaBoost算法

import numpy as np

def stumpClassify(dataMatrix, dimen, threshVal, threshIneq):
    """
    对数据进行分类的单层决策树分类函数。
    Args:
      dataMatrix: 数据矩阵
      dimen: 需要考虑的特征的维度
      threshVal: 阈值
      threshIneq: 不等式,可以是'lt'(小于)或'gt'(大于)

    Returns:
      retArray: 分类结果
    """
    retArray = np.ones((np.shape(dataMatrix)[0], 1))  # 默认所有样本分类结果为1
    # 根据阈值和不等式标记分类结果
    if threshIneq == 'lt':
        retArray[dataMatrix[:, dimen] <= threshVal] = -1.0
    else:
        retArray[dataMatrix[:, dimen] > threshVal] = -1.0
    return retArray

def buildStump(dataArr, classLabels, D):
    """
    在加权数据集中找到最佳的单层决策树。
    Args:
      dataArr: 数据集
      classLabels: 类别标签
      D: 数据点的权重

    Returns:
      bestStump: 最佳的单层决策树信息
      minError: 最小的错误率
      bestClasEst: 最佳的分类结果
    """
    dataMatrix = np.mat(dataArr)
    labelMat = np.mat(classLabels).T
    m, n = np.shape(dataMatrix)
    numSteps = 10.0  # 在特征的所有可能值上进行遍历的步数
    bestStump = {}  # 存储最佳单层决策树的信息
    bestClasEst = np.mat(np.zeros((m, 1)))
    minError = np.inf  # 初始错误率设为无穷大
    # 遍历所有特征
    for i in range(n):
        rangeMin = dataMatrix[:, i].min()
        rangeMax = dataMatrix[:, i].max()
        stepSize = (rangeMax - rangeMin) / numSteps
        # 遍历所有步长
        for j in range(-1, int(numSteps) + 1):
            # 对每个步长,都尝试大于和小于的不等式
            for inequal in ['lt', 'gt']:
                threshVal = (rangeMin + float(j) * stepSize)
                predictedVals = stumpClassify(dataMatrix, i, threshVal, inequal)
                errArr = np.mat(np.ones((m, 1)))
                errArr[predictedVals == labelMat] = 0
                weightedError = D.T * errArr  # 计算加权错误率
                # 如果当前的错误率小于最小错误率,则保存当前的单层决策树
                if weightedError < minError:
                    minError = weightedError
                    bestClasEst = predictedVals.copy()
                    bestStump['dim'] = i
                    bestStump['thresh'] = threshVal
                    bestStump['ineq'] = inequal
    return bestStump, minError, bestClasEst

def adaBoostTrainDS(dataArr, classLabels, numIt=40):
    """
    使用AdaBoost算法训练模型。
    Args:
      dataArr: 数据集
      classLabels: 类别标签
      numIt: 迭代次数

    Returns:
      weakClassArr: 训练好的弱分类器数组
    """
    weakClassArr = []
    m = np.shape(dataArr)[0]
    D = np.mat(np.ones((m, 1)) / m)  # 初始化权重
    aggClassEst = np.mat(np.zeros((m, 1)))
    # 进行numIt次迭代
    for i in range(numIt):
        # 构建单层决策树
        bestStump, error, classEst = buildStump(dataArr, classLabels, D)
        # 计算分类器权重alpha,用于更新样本权重D和最终的分类结果
        alpha = float(0.5 * np.log((1.0 - error) / max(error, 1e-16)))
        bestStump['alpha'] = alpha
        weakClassArr.append(bestStump)
        # 更新样本权重D
        expon = np.multiply(-1 * alpha * np.mat(classLabels).T, classEst)
        D = np.multiply(D, np.exp(expon))
        D = D / D.sum()
        # 更新累计的类别估计值
        aggClassEst += alpha * classEst
        aggErrors = np.multiply(np.sign(aggClassEst) != np.mat(classLabels).T, np.ones((m, 1)))
        errorRate = aggErrors.sum() / m
        if errorRate == 0.0: break  # 如果错误率为0,则提前中止循环
    return weakClassArr

def adaClassify(datToClass, classifierArr):
    """
    使用AdaBoost算法对数据进行分类。
    Args:
      datToClass: 待分类的数据
      classifierArr: 训练好的弱分类器数组

    Returns:
      分类结果
    """
    dataMatrix = np.mat(datToClass)
    m = np.shape(dataMatrix)[0]
    aggClassEst = np.mat(np.zeros((m, 1)))
    # 遍历所有弱分类器,进行分类估计并累加
    for i in range(len(classifierArr)):
        classEst = stumpClassify(dataMatrix, classifierArr[i]['dim'],
                                 classifierArr[i]['thresh'],
                                 classifierArr[i]['ineq'])
        aggClassEst += classifierArr[i]['alpha'] * classEst
    # 返回最终的分类结果
    return np.sign(aggClassEst)


# 示例数据集,这里只有两个特征和五个样本点
dataMat = np.array([
    [1.0, 2.1],
    [2.0, 1.1],
    [1.3, 1.0],
    [1.0, 1.0],
    [2.0, 1.0]
])

# 类别标签,与数据集中的样本相对应
classLabels = [1.0, 1.0, -1.0, -1.0, 1.0]

# 训练AdaBoost模型,这里假设迭代次数为9
# 这将返回一个包含若干弱分类器信息的列表
classifierArray = adaBoostTrainDS(dataMat, classLabels, 9)

# 使用训练好的AdaBoost模型进行分类
# 这里我们用训练数据作为测试,实际中应使用独立的测试集
prediction = adaClassify(dataMat, classifierArray)

# 打印出预测结果
print("Predictions:", prediction)

# 打印出实际的类别标签,以便比较
print("Actual labels:", classLabels)

5.3:基于adaboost的图像二分类

代码功能:读取文件夹分别为defectgood两个文件夹代表的两类的图片,进行训练集和测试集的划分,然后将预测得到的图片保存进根目录的defectgood文件夹

import os
import shutil

import numpy as np
from skimage.io import imread, imsave
from skimage.color import rgb2gray
from skimage.transform import resize
from skimage.feature import hog
from sklearn.ensemble import AdaBoostClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, accuracy_score
from sklearn.tree import DecisionTreeClassifier




# 图像加载和预处理函数
def load_images_from_folder(folder, label):
    images = []
    labels = []
    filenames = []  # 新增:保存文件名
    for filename in os.listdir(folder):
        img = imread(os.path.join(folder, filename))
        if img is not None:
            img_gray = rgb2gray(img)
            img_resized = resize(img_gray, (128, 128), anti_aliasing=True)
            images.append(img_resized)
            labels.append(label)
            filenames.append(filename)  # 保存文件名
    return images, labels, filenames  # 返回文件名

# 特征提取函数
def extract_features(images):
    features = []
    for image in images:
        hog_feature = hog(image, pixels_per_cell=(16, 16), cells_per_block=(1, 1), visualize=False)
        features.append(hog_feature)
    return features

# 加载数据集
good_images, good_labels, good_filenames = load_images_from_folder('D:/CollegeStudy/AI/gaoTie/hangingString/predictCropImgs/good', 0)
defect_images, defect_labels, defect_filenames = load_images_from_folder('D:/CollegeStudy/AI/gaoTie/hangingString/predictCropImgs/defect', 1)

# 合并数据集
all_images = good_images + defect_images
all_labels = good_labels + defect_labels
all_filenames = good_filenames + defect_filenames  # 保存所有文件名

# 特征提取
all_features = extract_features(all_images)

# 划分数据集
# 注意:这里我们需要保留测试集中图像的索引,以便后续保存预测的图像
X_train, X_test, y_train, y_test, idx_train, idx_test = train_test_split(all_features, all_labels, range(len(all_labels)), test_size=0.2, random_state=42)
# 打印训练集和测试集的个数
print(f'训练集个数: {len(X_train)}')
print(f'测试集个数: {len(X_test)}')
# 创建一个决策树分类器实例作为基础估计器
# 创建AdaBoost分类器实例,使用决策树作为基础估计器
ada_clf = AdaBoostClassifier(DecisionTreeClassifier(max_depth = 5),
                            n_estimators = 300)

# 训练模型
ada_clf.fit(X_train, y_train)

# 进行预测
y_pred = ada_clf.predict(X_test)

# 评估模型
print(classification_report(y_test, y_pred))
print(f'Accuracy: {accuracy_score(y_test, y_pred)}')

# 保存预测结果的图像
# 保存预测结果的图像
def save_predicted_images(filenames, predictions, base_path='predicted_images'):
    for filename, prediction in zip(filenames, predictions):
        # 检查文件是否存在于原始文件夹中
        original_folder = 'good' if prediction == 0 else 'defect'
        original_path = os.path.join('D:/CollegeStudy/AI/gaoTie/hangingString/predictCropImgs', original_folder, filename)
        if not os.path.isfile(original_path):
            print(f'文件不存在,跳过:{original_path}')
            continue

        # 创建目标文件夹路径
        target_folder = os.path.join(base_path, 'good' if prediction == 0 else 'defect')
        if not os.path.exists(target_folder):
            os.makedirs(target_folder)

        # 构建目标文件完整路径
        target_path = os.path.join(target_folder, filename)

        # 复制文件到新位置
        shutil.copy(original_path, target_path)
# 获取测试集中的原始文件名
test_filenames = [all_filenames[i] for i in idx_test]

# 调用函数保存预测的图像
save_predicted_images(test_filenames, y_pred)

5.4:使用朴素贝叶斯模型作为弱分类器——谈Adaboost的弱分类器

上面三个代码都是使用决策树桩作为弱学习器,这也是在Adaboost算法中最常见的一种弱分类器。

  • 传统AdaBoost中的弱学习器和集成的基本概念:

    • 弱学习器:单独一个学习器在数据集上进行训练得到的结果不会太好(这可能与这个弱学习器结构简单等因素有关)
    • 强学习器:单一个学习器在数据集上进行几轮训练得到的结果就已经够好了——这个时候其实就没有必要使用Adaboost了,相较于弱学习使用Adaboost的提升,强学习器使用Adaboost算法提升的其实不大。
  • Adaboost与弱学习器的关联——为什么使用弱学习器作为基础算法

    1. 易于集成:弱学习器,一般都是结构简单嘛(像上面代码里面的决策树桩都是一层深度),那么在多个学习器同时训练的时候速度就会更快,易于集成。朴素贝叶斯虽然计算起来也算简便,但它可能不像决策树桩那样简单并且容易适应性调整。
    2. 容易训练(改正错误)可塑性和多样性:提供更多的错误空间来一步一步利用更新错误样本的权重,来改善学习器及其最终权重,这就是Adaboost的理念!
    3. AdaBoost的多样性:简单的,不同的决策树桩,它们的分类逻辑都不太一样,可能单独表现不太好,但是训练后集成在一起就会表现好(这种多样性的存在,虽然简单,但是每个简单模型的关注点可能不太一样);而具有相似性能的朴素贝叶斯模型,我们就很难从集成中获得多样性。

说是这么说,可不可以用呢?当然也可以,貌似结果也还不错
这里给出代码:

from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import MultinomialNB
from sklearn.ensemble import AdaBoostClassifier
from sklearn.datasets import fetch_20newsgroups
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.preprocessing import LabelEncoder

# 获取数据集
data = fetch_20newsgroups(subset='all', categories=['sci.space', 'comp.graphics', 'rec.sport.baseball'], shuffle=True)

# 初始数据特征和标签
X = data.data
y = data.target

# 文本数据转换为TF-IDF特征值
vectorizer = TfidfVectorizer()
X = vectorizer.fit_transform(X)

# 标签编码
encoder = LabelEncoder()
y = encoder.fit_transform(y)

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=42)

# 初始化朴素贝叶斯分类器实例
base_classifier = MultinomialNB()

# 初始化AdaBoostClassifier,这里不需要base_classifier参数
ada_boost = AdaBoostClassifier(base_classifier, n_estimators=100, random_state=0)

# 训练模型
ada_boost.fit(X_train, y_train)

# 模型性能评估
print("Training accuracy:", ada_boost.score(X_train, y_train))
print("Test accuracy:", ada_boost.score(X_test, y_test))

在这里插入图片描述

  • 25
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

是瑶瑶子啦

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

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

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

打赏作者

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

抵扣说明:

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

余额充值