Adaboost

概述

Adaboost的思想是将关注点放在被错误分类的样本上,减小上一轮被正确分类的样本权值
Adaboost采用加权投票的方法,分类误差小的弱分类器的权重大,而分类误差大的弱分类器的权重小

算法流程

1.\(假设输入训练数据为\)

\[T=\{(x_1,y_1),(x_2,y_2),...,(x_n,y_n)\} \]

\(其中xi\in X\supset R^n,y_i\in Y=[-1,1],迭代次数即弱分类器个数为M\)
2.\(初始化训练样本的权值分布为\)

\[D_1=(w_{11},w_{12},...,w_{1n}),w_{1i}=\frac{1}{N},i=1,2,...,N,下标第一个数指轮次 \]

3.\(对于m=1,2,...,M\)

  • a.\(使用具有权重分布D_m的训练数据集进行学习,得到弱分类器G_m(x)\)
  • b.\(计算G_m(x)在训练数据集上的分类误差率,I是示性函数\)

\[e_m=\sum\limits_{i=1}^{N}w_{mj}I(Gm(x_i)\ne y_i) \]

  • c.\(计算G_m(x)在强分类器中所占的权重\)

\[\alpha_m=\frac{1}{2}\log\frac{1-e_m}{e_m} \]

这里隐含两个点

  • 1.\(弱分类器必须正确率>0.5,这个好办,哪怕<0.5,做个1-分类器,就可以得到>0.5的\)
  • 2.\(分类器学习的是带有权值分布的数据,也就是说分类器必须支持带权重的分类\)

案例

第二次的迭代中的划分如下

理解

从这个案例里面我们也可以看到,这里用的基分类器还是CART树,只不过
1.这里只有一维数据
2.计算切分点,并没有使用基尼系数,而是直接采用了Adaboost的误差函数

\[e_m=\sum\limits_{i=1}^{N}w_{mj}I(Gm(x_i)\ne y_i) \]

3.扩展一下的话,我们仍然可以使用CART树,对所有特征(多维),所有切分点,依次计算带权重的损失函数(而不是基尼指数)
4.CART替换损失函数有一定的局限性,貌似无法扩展到平方损失函数和交叉熵损失函数(都是连续函数无法转为示性函数I),至少网上我没找到相关资料
5.网上找了些资料,还找到一个CART支持带权重的算法,如下

另外还找到了一位网友的代码,其实现方式的确也是照着这个算法实现的

 p_i = 1.0 * value * np.mean(weight_counter.get(key)) / x_num

这句是照着图片里面的p公式写的,的确一模一样,感觉这个算法好像更靠谱些

点击查看代码

"""
定义计算gini系数相关的函数,代码封装到ml_models.utils
"""
import numpy as np
def gini(x, sample_weight=None):
    """
    计算基尼系数 Gini(D)
    :param x:
    :param sample_weight:
    :return:
    """
    x_num = len(x)
    # 如果sample_weight为None设均设置一样
    if sample_weight is None:
        sample_weight = np.asarray([1.0] * x_num)
    x_counter = {}
    weight_counter = {}
    # 统计各x取值出现的次数以及其对应的sample_weight列表
    for index in range(0, x_num):
        x_value = x[index]
        if x_counter.get(x_value) is None:
            x_counter[x_value] = 0
            weight_counter[x_value] = []
        x_counter[x_value] += 1
        weight_counter[x_value].append(sample_weight[index])

    # 计算gini系数
    gini_value = 1.0
    for key, value in x_counter.items():
        p_i = 1.0 * value * np.mean(weight_counter.get(key)) / x_num
        gini_value -= p_i * p_i
    return gini_value


def cond_gini(x, y, sample_weight=None):
    """
    计算条件gini系数:Gini(y,x)
    """
    x = np.asarray(x)
    y = np.asarray(y)
    # x中元素个数
    x_num = len(x)
    # 如果sample_weight为None设均设置一样
    if sample_weight is None:
        sample_weight = np.asarray([1.0] * x_num)
    # 计算
    gini_value = .0
    for x_value in set(x):
        x_index = np.where(x == x_value)
        new_x = x[x_index]
        new_y = y[x_index]
        new_sample_weight = sample_weight[x_index]
        p_i = 1.0 * len(new_x) / x_num
        gini_value += p_i * gini(new_y, new_sample_weight)
    return gini_value


def gini_gain(x, y, sample_weight=None):
    """
    gini值的增益
    """
    x_num = len(x)
    if sample_weight is None:
        sample_weight = np.asarray([1.0] * x_num)
    return gini(y, sample_weight) - cond_gini(x, y, sample_weight)

总结

个人理解,如果用网上的这个基尼指数算法,做上面那道题也是一样的结果

另外,这个算法非常要注意的就是Adaboost的误差函数和基分类器的误差函数之间的关系,可以理解是两个完全不同的误差函数,先进行分类器的误差函数优化得到分类器,再进行一次Adaboost的误差函数优化

硬要将两个合并在一起是不是也不是不可以,但是本身是算法外面再套一层算法,合并的难度很大,像上面李航老师的这道题,应该只适合于CART分类器,其他分类器不合适,因为无法转换为示性函数\(I\),只能采用贪心搜索的CART,不能用逻辑回归等分类器

当然最后注意分类要支持权重

posted @ 2022-05-03 16:21  筷点雪糕侠  阅读(158)  评论(0编辑  收藏  举报