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)