不断努力,突破自己的Adaboost
引言
在前面已经讲过了一个集成算法random forest,但是因为random forest是一种并形式的集成算法,所以导致了他的每个弱分类器都不是太强,最后叠加起来虽然可能得到比较好的结果但是,如果是一个比较困难的问题,那么他的性能天花板就可能没有那么高的,所以为了解决这个问题,这儿为大家讲解一个新的算法(Adaboost)来打破这个困境。
[
1
\color{red}{1}
1]图片来源于bilibili截图(5分钟机器学习)
这两张图片就形象地展示了random forest的不足之处,虽然你们三个都有各自的优势,但是本身却是比较差的,所以三个人加起来也不一定能达到预期的目标,虽然都说三个臭皮匠赛过诸葛亮,但是在现实生活中你3个才及格的同学加在一起难道就比一个科科都接近满分的学霸能比了吗?
Adaboost(自适应增强算法),他是通过不断地增强自身以此来得到性能天花板更高的结果,
B
o
o
s
t
i
n
g
算
法
核
心
思
想
:
这
个
过
程
就
很
像
一
个
学
渣
逆
袭
成
为
学
霸
的
经
历
\color{red}{Boosting算法核心思想:这个过程就很像一个学渣逆袭成为学霸的经历}
Boosting算法核心思想:这个过程就很像一个学渣逆袭成为学霸的经历。他的形象展示如下:
[
2
\color{red}{2}
2]图片来源于bilibili截图(5分钟机器学习)
其实Adaboost算法的训练方法其实和我们人类学习有异曲同工之妙,就是对之前做错了的事情高度注意,然后对以前做对了的事情减少注意力,然后最后将所以学习过的模型按照一定的权重加权在一起就形成了一个性能天花板很高的模型。
1.Adaboost的原理
1.1Adaboost算法简介
Boosting是一族可以将弱学习器提升成为强学习器的算法,它的工作机制可以分为以下4步:
- 先从初始训练集中训练出一个基学习器。让他们的权重为1/N,N为样本数量
- 再根据基学习器的表现结果对每个训练样本的权重进行调整,如果该样本分类正确,那么它的权值就会降低,所获得关注就会减少;反之如果该样本分类错误,那么它的权值就会提高,在之后的训练中就会被特别关注。
- 然后根据调整好的权值分布来训练下一个学习器。
- 将各个训练得到的弱分类器组合成强分类器。各个弱分类器的训练过程结束后,加大分类误差率小的弱分类器的权重,使其在最终的分类函数中起着较大的决定作用,而降低分类误差率大的弱分类器的权重,使其在最终的分类函数中起着较小的决定作用。换言之,误差率低的弱分类器在最终分类器中占的权重较大,否则较小。
1.2Adaboost算法流程
假如有一个训练集D1=(x11,x12,x13,…,x1N)
步骤1.先赋予每个样本相同的权重1/N
W1=(w11,w12,w13,…,w1N),w1i=
1
N
\frac{1}{N}
N1,i=1,2,3…,N
然后进行不断迭代(i=1,2,3,…,n)轮
1. 根据当前权值Wi的训练数据进行训练,得到基分类器(选取让误差率最低的阈值来设计基本分类器)
Gi(x)={+1;-1},让低于阈值的标签全部为+1,让大于阈值的标签全部为-1。
'''
定义符号函数 参数:x为样本,y为标签,threshold为阈值
'''
def sign(x,y,threshold):
y1 = y.copy()
for i in range(len(x)):
if x[i] < threshold:
y1[i] = 1
else:
y1[i] = -1
return np.array(y1)
步骤2.计算当前的分类误差率
em= ∑ i n w i ∗ I ( G i ( x i ) ≠ y i ) \sum_{i}^{n}w_i*I(G_{i}({x_i})\ne y_i) ∑inwi∗I(Gi(xi)=yi), G i ( x ) G_i(x) Gi(x)在训练数据集上的 误差率em就是被 G i ( x ) G_i(x) Gi(x)误分类样本的权值之和,需要保证分类误差率都在0.5以下,也就是要保证一半及以上的样本分类正确。
'''
计算错误率,参数:y为原始的标签,m为经过符号函数转化后的标签,weight为每个标签的权重
'''
def error(y,m,weight):
error_ratio = np.sum((y!=m)*weight)
if error_ratio<=0.5:
return error_ratio
else:
for i in range(len(m)):
if m[i] == 1:
m[i] = -1
else:
m[i] = 1
return error(y,m,weight)
步骤3.计算权重系数am
am表示在最后计算中每个分类器所占有的权重,这儿采用
1
2
∗
ln
1
−
e
m
e
m
\frac{1}{2}*\ln\frac{1-e_m}{em}
21∗lnem1−em。
这儿先解释下为什么要用这个公式来计算
a
m
a_m
am。将
e
m
e_m
em在取值为0-1上的图像绘制出来:
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] #指定默认字体
plt.rcParams['axes.unicode_minus'] = False #解决保存图像是符号'-'显示方块的问题
x = np.linspace(0.001,0.999,100)
y = 1/2*np.log((1-x)/x)
plt.figure()
plt.grid(True)
plt.plot(x,y,'r')
plt.xlabel('分类误差率')
plt.ylabel('am权重系数')
plt.title('分类误差率与权重系数的关系')
plt.show()
'''
计算权重系数am,参数:error_ratio为错误率
'''
def weight_coef(error_ratio):
am = 1/2*np.log((1-error_ratio)/error_ratio)
return am
从这儿可以看出当分类误差率在0-0.5逐渐变大的时候权重系数快速下降,也就意味着当分类误差率越大时该分类器所占有的权重是很小的。
步骤4.计算归一化因子Zm
计算归一化因子使得更新好的权重W呈现一个概率型的分布。
Z
m
=
∑
i
=
1
n
w
m
i
∗
e
−
a
m
∗
y
i
∗
G
i
(
x
)
Z_m=\sum_{i=1}^{n}w_{mi}*e^{-am*y_{i}*G_{i}(x)}
Zm=i=1∑nwmi∗e−am∗yi∗Gi(x).
'''
计算归一化因子Zm,参数:y原始标签,y1符号函数计算出来的标签,am权重系数,weight标签权重
'''
def Zm(y,y1,am,weight):
zm = np.sum(weight*np.exp(-am*y*y1))
return zm
步骤5.更新权重
W m + 1 , i = w m i Z m ∗ e − a m ∗ y i G i ( x ) W_{m+1,i} = \frac{w_{mi}}{Z_m}*e^{-am*y_{i}G_{i}(x)} Wm+1,i=Zmwmi∗e−am∗yiGi(x).
'''
更新权重,参数:zm归一化因子,y原始标签,y1符号函数计算出来的标签,am权重系数,weight标签权重
'''
def weight_new(zm,y,y1,am,weight):
w_new = weight/zm*np.exp(-am*y*y1)
return w_new
2.Adaboost计算例子
先传入一个数据集
x | y |
---|---|
0 | 1 |
1 | 1 |
2 | 1 |
3 | -1 |
4 | -1 |
5 | -1 |
6 | 1 |
7 | 1 |
8 | 1 |
9 | -1 |
首先给与每个样本一个权重 w m i w_{mi} wmi=[0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1],然后通过寻找能使分类错误率最少的阈值进行划分。通过上面讲原理的代码大家可以自己调用计算
阈值 | 分类错误率 |
---|---|
2.5 | 0.3 |
5.5 | 0.4 |
8.5 | 0.3 |
因此我们2.5或者8.5都可以进行选择,那么我们先用2.5进行划分把:
x | y | y 1 y_1 y1 | w |
---|---|---|---|
0 | 1 | 1 | 0.1 |
1 | 1 | 1 | 0.1 |
2 | 1 | 1 | 0.1 |
3 | -1 | -1 | 0.1 |
4 | -1 | -1 | 0.1 |
5 | -1 | -1 | 0.1 |
6 | 1 | -1 | 0.1 |
7 | 1 | -1 | 0.1 |
8 | 1 | -1 | 0.1 |
9 | -1 | -1 | 0.1 |
- 计算错误分类率:
通过比较发现样本(0,1,2,3,4,5,9)分类正确,因此分类错误率是0.1+0.1+0.1=0.3; - 计算am权重系数
a m = 1 2 ∗ log 1 − 0.3 0.3 = 0.4236 a_m = \frac{1}{2}*\log^{\frac{1-0.3}{0.3}}=0.4236 am=21∗log0.31−0.3=0.4236 - 计算归一化因子
Z
m
Z_m
Zm
Z m = ∑ i = 0 9 w i ∗ e − a m ∗ y ∗ y 1 Z_m=\sum_{i=0}^{9}w_i*e^{-am*y*y_1} Zm=i=0∑9wi∗e−am∗y∗y1
0.1 ∗ e − 0.4236 ∗ 1 ∗ 1 + 0.1 ∗ e − 0.4236 ∗ 1 ∗ 1 + . . . + 0.1 ∗ e 0.4236 ∗ ( − 1 ) ∗ ( − 1 ) = 0.9165 0.1*e^{-0.4236*1*1}+0.1*e^{-0.4236*1*1}+...+0.1*e^{0.4236*(-1)*(-1)}=0.9165 0.1∗e−0.4236∗1∗1+0.1∗e−0.4236∗1∗1+...+0.1∗e0.4236∗(−1)∗(−1)=0.9165 - 更新权重:
x | y | y 1 y_1 y1 | w | w1_new |
---|---|---|---|---|
0 | 1 | 1 | 0.1 | 0.07 |
1 | 1 | 1 | 0.1 | 0.07 |
2 | 1 | 1 | 0.1 | 0.07 |
3 | -1 | -1 | 0.1 | 0.17 |
4 | -1 | -1 | 0.1 | 0.17 |
5 | -1 | -1 | 0.1 | 0.17 |
6 | 1 | -1 | 0.1 | 0.07 |
7 | 1 | -1 | 0.1 | 0.07 |
8 | 1 | -1 | 0.1 | 0.07 |
9 | -1 | -1 | 0.1 | 0.07 |
- 通过迭代三次然后得到三次的错误分类率和权重系数还有更新的权值因为计算占篇幅太大只介绍第一轮的如果想要看完整的计算可以参考详细计算过程
第一次阈值取得是2.5,第二次是8.5, 第三次是5.5
weight_coef1 | weight_coef2 | weight_coef3 |
---|---|---|
0.4236 | 0.6496 | 0.7520 |
error_ratio1 | error_ratio2 | error_ratio3 |
---|---|---|
0.3 | 0.2143 | 0.1818 |
x | y | y 1 y_1 y1 | y 2 y_2 y2 | y 3 y_3 y3 | w | w1_new | w2_new | w3_new |
---|---|---|---|---|---|---|---|---|
0 | 1 | 1 | 1 | 1 | 0.1 | 0.07 | 0.045 | 0.125 |
1 | 1 | 1 | 1 | 1 | 0.1 | 0.07 | 0.045 | 0.125 |
2 | 1 | 1 | 1 | 1 | 0.1 | 0.07 | 0.045 | 0.125 |
3 | -1 | -1 | 1 | 1 | 0.1 | 0.17 | 0.167 | 0.1019 |
4 | -1 | -1 | 1 | 1 | 0.1 | 0.17 | 0.167 | 0.1019 |
5 | -1 | -1 | 1 | 1 | 0.1 | 0.17 | 0.167 | 0.1019 |
6 | 1 | -1 | -1 | 1 | 0.1 | 0.07 | 0.106 | 0.065 |
7 | 1 | -1 | -1 | 1 | 0.1 | 0.07 | 0.106 | 0.065 |
8 | 1 | -1 | -1 | 1 | 0.1 | 0.07 | 0.106 | 0.065 |
9 | -1 | -1 | - 1 | -1 | 0.1 | 0.07 | 0.045 | 0.125 |
因此最后的分类计算就是
f
=
0.3
∗
y
1
+
0.2143
∗
y
2
+
0.1818
∗
y
3
f = 0.3*y1+0.2143*y2+0.1818*y3
f=0.3∗y1+0.2143∗y2+0.1818∗y3
因为迭代次数不足因此结果不是很好这儿只是介绍方法,如果大家要多迭代几次或者重新选择阈值进行计算可能会得到一个很好的结果。
3.Adaboost案例
参数 | 用法 |
---|---|
base_estimator | 默认是决策树,在该分类器上进行boosting,理论上可以是任意一个分类器,如果是其他分类器请指明样本权重 |
n_estimators | 基分类器boosting次数,默认50,值过大容易过拟合,值过小,容易欠拟合 |
learning_rate | 学习率,梯度收敛速度,过大容易错过最优解,过小会收敛很慢,需要和n_extimators配合使用 |
algorithm | boosting算法,也就是模型提升准则,有两种方式SAMME和SAMME.R,默认是SAMME.R,两者主要的区别是弱学习器权重的度量,前者是对样本集预测错误的概率进行划分,后者是对样本集的预测错误的比例进行划分,默认是SAMME.R |
对象 | 用法 |
---|---|
.estimators_ | 以列表的形式返回所有分类器 |
.classes_ | 类别标签 |
.estimator_weight | 每个分类器的权重 |
.estimator_errors_ | 每个分类器的错分率,与分类器权重相对应 |
.feature_importances_ | 特征重要性,前提是基分类器有这个对象 |
方法 | 用法 |
---|---|
decision_function(X) | 返回决策函数值(比如svm中的决策距离) |
fit(X,Y) | 在数据集(X,Y)上训练模型。 |
get_parms() | 获取模型参数 |
predict(X) | 预测数据集X的结果。 |
predict_log_proba(X) | 预测数据集X的对数概率。 |
predict_proba(X) | 预测数据集X的概率值。 |
score(X,Y) | 输出数据集(X,Y)在模型上的准确率。 |
staged_decision_function(X) | 返回每个基分类器的决策函数值 |
staged_predict(X) | 返回每个基分类器的预测数据集X的结果。 |
staged_predict_proba(X) | 返回每个基分类器的预测数据集X的概率结果。 |
staged_score(X, Y) | 返回每个基分类器的预测准确率。 |
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.ensemble import AdaBoostClassifier
from sklearn.model_selection import cross_validate
iris = load_iris()
x = iris.data
y = iris.target
xtrain,xtest,ytrain,ytest = train_test_split(x,y,test_size=0.3)
abc = AdaBoostClassifier()
model = abc.fit(xtrain,ytrain)
print('精确度:',abc.score(xtest,ytest))
print('交叉验证:',cross_validate(abc,x,y,cv=5)['test_score'].mean())