机器学习笔记 第八章集成学习

8.1  个体与集成

         集成学习就是通过构建并结合多个学习器来完成学习任务。如图所示,集成学习一般先产生一组“个体学习器”,再通过某种方法将它们结合起来。通过判断集成是否只包含同种类型的个体学习器可分为同质集成和异质集成。

        \circ同质集成:集成中只包含同种类型的个体学习器。同质集成中的个体学习器称为“基学习器”,相应的算法为“基学习算法”;

        \circ异质集成:集成中包含不同类型的个体学习器。

        优点:集成学习通过把多个个体学习器结合在一起,通常比单一学习器的泛化性能更有显著优势。

        通常情况下,如果把效果好坏不等的个体学习器掺杂在一起,那么得到结果通常会介于最好和最坏的个体学习器之间,那么,集成学习如何才能获得比最好的个体学习器更好的性能呢?结论是个体学习器要“好而不同”,即个体学习器要有一定的准确性,且每个个体学习器之间具有差异。接下来通过一个例子说明该结论,如下表所示,其中√表示分类正确,×表示分类错误,集成学习采用投票法,即“少数服从多数”,那么(a)中,每个分类器的精度为66.6%,但集成学习的精度达到100%;在(b)中,集成学习与每个分类器的精度无差别;在(c)中,每个分类器的精度为33.3%,而集成学习的精度却为0。以上例子恰好说明个体学习器应“好而不同”。

          (a)集成提升性能                           (b)集成不起作用                   (c)集成起负作用
样例1样例2样例3样例1样例2样例3样例1样例2样例3
h_{1}×h_{1}×h_{1}××
h_{2}×h_{2}×h_{2}××
h_{3}×h_{3}×h_{3}××
集成集成×集成×××

        事实上,个体学习器的“准确性”和“多样性”本身存在一定的冲突,在确保准确性之后,则不得不牺牲其多样性;而在增加多样性的同时,有不得不牺牲其准确性。进而,如何产生并且结合准确度和多样性的个体学习器,恰好是集成学习的研究核心。

        集成学习大致可分为以下两大类:

        \square序列化方法:个体学习器之间存在强依赖关系、必须串行生成的方法,代表方法是Boosting;

        \square并行化方法:个体学习器之间不存在强依赖关系、可同时生成的方法,代表方法是Bagging和“随机森林”。

8.2  Boosting

        Boosting是一族可以将弱学习器提升为强学习器的算法。

        算法思想:从初始的训练集中训练出一个基学习器,再从基学习器的表现对训练集进行合理的调整,即先前基学习器做错的训练样本进行重点关注,然后用调整后的样本来训练下一个基学习器;重复进行前面步骤,直到基学习器数目达到事先指定的值T,最终对这T个基学习器进行加权结合。

        Boosting族算法的代表是AdaBoost,如图8.2所示,其中y_{i}\in \left \{ -1,+1 \right \}f是真是函数。采用基学习器的线性组合

H(x)=\sum_{t=1}^{T}\alpha _{t}h_{t}(x)

来最小化指数损失函数

\iota _{exp}(H|D)=E_{x\sim D}\left [ e^{-f(x)H(x)} \right ]

若指数损失函数能最小化,则可以对H(x)求偏导

\frac{\partial \iota _{exp}(H|D)}{\partial H(x)}=-e^{H(x)}P(f(x)=1|x)+e^{H(x)}P(f(x)=-1|x)

令上式等于零,可解得

H(x)=\frac{1}{2}ln\frac{P(f(x)=1|x)}{P(f(x)=-1|x)}

故有

                                sign(H(x))=sign(\frac{1}{2}ln\frac{P(f(x)=1|x)}{P(f(x)=-1|x)})

                                                      =\begin{cases} 1 & \text{ if } P(f(x)=1|x)>P(f(x)=-1|x) \\ -1& \text{ if } P(f(x)=1|x)<P(f(x)=-1|x) \end{cases}

                                                ​​​​​​​      =argmax_{y\in \left \{ -1,1 \right \}}P(f(x)=y|x)

        此时,sign(H(x))达到了贝叶斯最优错误解,也就是指数损失函数最小化,那么分类错误率也最小化。

图8.2  AdaBoost算法

        Boosting的特点是主要关注降低偏差,因此Boosting可以将泛化能力相当弱的学习器构建出很强的集成。

8.3  Bagging

        Bagging是并行式集成学习方法最著名的代表。给定包含个样本的数据集,我们先随机取出一个样本放入采样集中,再把该样本放回初始数据集,使得下次采样时该样本仍有可能被选中,这样,经过次随机采样操作,我们得到含个样本的采样集,初始训练集中有的样本在采样集里多次出现,有的则从未出现。我们可采样出个含个训练样本的采样集,然后基于每个采样集训练出一个基学习器,再将这些基学习器进行结合,这就是 Bagging 的基本流程。Bagging的算法描述如图8.5所示。

         D_{t}表示h_{t}实际使用的训练样本集,令H^{oob}(x)表示对样本的包外预测,即仅考虑那些未使用x训练的基学习器在x上的预测,有

H^{oob}(x)=argmax_{y\in \gamma }\sum_{t=1}^{T}\prod (h_{t}(x)=y)\cdot \prod (x\notin D_{t}),

则Bagging泛化误差的包外估计为

\epsilon^{oob}=\frac{1}{|D|}\sum_{(x,y)\in D}^{}\prod (H^{oob}(x)\neq y).

         从偏差方差分解的角度看,Bagging主要关注降低方差,因此它在不剪枝决策树、神经网络等易受样本扰动的学习器上效用更为明显。​​​​​​​      

8.4  多样性度量

        顾名思义,多样性度量就是估算个体学习器的多样化程度,做法就是考虑个体分类器两两是否相似。

        给定数据集D=\left \{ (x_{1},y_{1}),(x_{2},y_{2}),...,(x_{m},y_{m}) \right \}y_{i}\in \left \{ -1,+1 \right \},分类器h_{i}h_{j}的预测结果列联表为

h_{i}=+1h_{i}=-1
h_{j}=+1ac
h_{j}=-1bd

        其中,a表示h_{i}h_{j}均预测为正类的样本数目,b、c、d同理可得,a+b+c+d=m。基于上面的列联表,可以给出常见的多样性度量。

        \bullet不合度量

dis_{ij}=\frac{b+c}{m}

        \bullet相关系数

\rho _{ij}=\frac{ad-bc}{\sqrt{(a+b)(a+c)(c+d)(b +d)}}

        \rho _{ij}的值域为\left [ -1,1 \right ],若h_{i}h_{j}无关,则值为0;若h_{i}h_{j}正相关则值为正,否则为负。

        \bulletQ-统计量

Q_{ij}=\frac{ad-bc}{ad+bc}

        Q_{ij}与相关系数\rho _{ij}符号相同,且|Q_{ij}|\leqslant |\rho _{ij}|

        \bullet\kappa -统计量

\kappa =\frac{p_{1}-p_{2}}{1-p_{2}}

p_{1}=\frac{a+d}{m}

p_{2}=\frac{(a+b)(a+c)+(c+d)(b+d)}{m^{2}}

        若分类器h_{i}h_{j}在D上完全一致,则\kappa =1;若它们仅是偶然达成一致,则\kappa =0

8.5   实验

      下面是用Python实现AdaBoost算法的示例代码,使用了numpy和sklearn库。我们先手动实现一个简单的AdaBoost算法,然后使用sklearn中的实现进行比较。

import numpy as np


class WeakClassifier:
    """简单的弱分类器"""

    def __init__(self):
        self.feature_index = None
        self.threshold = None
        self.polarity = None

    def fit(self, X, y, weights):
        m, n = X.shape
        self.feature_index = 0
        self.threshold = 0
        self.polarity = 1
        min_error = float('inf')

        for feature in range(n):
            thresholds = np.unique(X[:, feature])
            for threshold in thresholds:
                for polarity in [-1, 1]:
                    predictions = np.ones(m)
                    predictions[X[:, feature] < threshold] = -1
                    predictions *= polarity
                    error = sum(weights[y != predictions])

                    if error < min_error:
                        min_error = error
                        self.threshold = threshold
                        self.feature_index = feature
                        self.polarity = polarity

    def predict(self, X):
        m = X.shape[0]
        predictions = np.ones(m)
        predictions[X[:, self.feature_index] < self.threshold] = -1
        return predictions * self.polarity


class AdaBoost:
    def __init__(self, n_classifiers=50):
        self.n_classifiers = n_classifiers
        self.classifiers = []
        self.alphas = []

    def fit(self, X, y):
        m = X.shape[0]
        weights = np.ones(m) / m

        for _ in range(self.n_classifiers):
            classifier = WeakClassifier()
            classifier.fit(X, y, weights)
            predictions = classifier.predict(X)

            # Calculate the error
            error = sum(weights[y != predictions])
            # Calculate the alpha value (weight of the classifier)
            alpha = 0.5 * np.log((1 - error) / (error + 1e-10))

            # Update weights
            weights *= np.exp(-alpha * y * predictions)
            weights /= sum(weights)  # Normalize

            # Store the classifier and its weight
            self.classifiers.append(classifier)
            self.alphas.append(alpha)

    def predict(self, X):
        m = X.shape[0]
        final_predictions = np.zeros(m)

        for alpha, classifier in zip(self.alphas, self.classifiers):
            final_predictions += alpha * classifier.predict(X)

        return np.sign(final_predictions)

    # 示例使用


if __name__ == "__main__":
    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=100, n_features=20, n_informative=10, n_redundant=2, random_state=42)
    y = np.where(y == 0, -1, 1)  # 将类标签转换为-1和1

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

    # 训练和预测
    model = AdaBoost(n_classifiers=50)
    model.fit(X_train, y_train)
    y_pred = model.predict(X_test)

    # 输出准确率
    print("Accuracy:", accuracy_score(y_test, y_pred))  

参考文献:《机器学习》周志华

  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值