机器学习:AdaBoost算法及其实现

楔子

前面提到boosting算法为一类算法,这一类算法框架分为3步:1、训练一个弱分类;2、根据分类结果调整样本权重;3、最后决策时,根据弱模型的表现来决定其话语权。
那么这里面就有2个关键点:
1、如何调整样本权重;
2、如何根据弱模型的表现来决定其话语权。

算法描述:

adaboost里的样本权重和话语权

事实上,能够将这两个问题的解决方案有机地糅合在一起、正是 AdaBoost 的巧妙之处之一。

如何调整样本权重,一个弱分类器得到一个错误分类率e > 0.5, 我们调整样本权重是分类错误率等于0.5,用这个样本权重用于下一个分类器,这个这两个分类器就不会强相关,这个就是调整样本权重的主要思想。

分类错误样本权重乘以d倍,分类正确样本权重处理d,得到e*d = (1-e)/d =====> d^2 = (1-e)/e

我们定义该分类器的话语权为:alpha = ln(d) = 0.5*ln (1-e)/e,话语权是随着错误率的增大而减小的。

事实上,能够将这两个问题的解决方案有机地糅合在一起、正是 AdaBoost 的巧妙之处之一。

实际的样本权重是归一化处理过的:
在这里插入图片描述

算法描述

在这里插入图片描述

AdaBoost的实现:

实现比较简单,首先你的分类器需要支持样本权重,然后按照序列调用你的分类器就行了,每次把计算好的样本权重传进去。

class AdaBoost:
    # 弱分类器字典,如果想要测试新的弱分类器的话、只需将其加入该字典即可
    _weak_clf = {
        "SKMNB": SKMultinomialNB,
        "SKGNB": SKGaussianNB,
        "SKTree": SKTree,
        "MNB": MultinomialNB,
        "GNB": GaussianNB,
        "ID3": ID3Tree,
        "C45": C45Tree,
        "Cart": CartTree
    }
    """
        AdaBoost框架的朴素实现
        使用的弱分类器需要有如下两个方法:
            1) 'fit'      方法,它需要支持输入样本权重
            2) 'predict'  方法, 它用于返回预测的类别向量

    """
    def __init__(self):
        # 初始化结构
        # self._clf:记录弱分类器名称的变量
        # self._clfs:记录弱分类器的列表
        # self._clfs_weights:记录弱分类器“话语权”的列表
        self._clf, self._clfs, self._clfs_weights = "", [], []
        
    def fit(self, x, y, sample_weight=None, clf=None, epoch=10, eps=1e-12, **kwargs):
        # 默认使用10个CART决策树桩作为弱分类器
        if clf is None or AdaBoost._weak_clf[clf] is None:
            clf = "Cart"
            kwargs = {"max_depth": 1}
        # 当前使用的分类器
        self._clf = clf
        # 样本权重,注意是归一化后的样本权重,默认样本权重为[1/N,1/N,...]
        if sample_weight is None:
            sample_weight = np.ones(len(y)) / len(y)
        else:
            sample_weight = np.array(sample_weight)
            
        # AdaBoost算法的主循环,epoch为迭代次数
        for _ in range(epoch):
            # 根据样本权重训练弱分类器
            tmp_clf = AdaBoost._weak_clf[clf](**kwargs)
            tmp_clf.fit(x, y, sample_weight)
            # 调用弱分类器的predict方法进行预测
            y_pred = tmp_clf.predict(x)
            # 计算加权错误率;考虑到数值稳定性,在边值情况加了一个小的常数
            # 点乘是行*列
            em = min(max((y_pred != y).dot(self._sample_weight[:, None])[0], eps), 1 - eps)
            # 计算该弱分类器的“话语权”,化简了一下
            am = 0.5 * log(1 / em - 1)
            # 更新样本权重并利用deepcopy将该弱分类器记录在列表中
            sample_weight *= np.exp(-am * y * y_pred)
            sample_weight /= np.sum(sample_weight)
            # 记录分类器和话语权
            self._clfs.append(deepcopy(tmp_clf))
            self._clfs_weights.append(am)
            
    def predict(self, x):
        x = np.atleast_2d(x)
        # 保存结果
        rs = np.zeros(len(x))
        # 根据各个弱分类器的“话语权”进行决策
        for clf, am in zip(self._clfs, self._clfs_weights):
            rs += am * clf.predict(x)
        # 将预测值大于0的判为类别1,小于0的判为类别-1
        return np.sign(rs)

数学基础(了解)

AdaBoost 算法是前向分步算法的特例,AdaBoost 模型等价于损失函数为指数函数的加法模型。
在这里插入图片描述
所谓的前向分步算法,就是从前向后、一步一步地学习加法模型中的每一个基函数及其权重而非将f(x)作为一个整体来训练,这也正是 AdaBoost 的思想。

猜你喜欢

转载自blog.csdn.net/weixin_40759186/article/details/85532954