Adaboost的学习内容整理自菊安酱小姐姐的直播视频,原视频链接:【菊安酱的机器学习】第6期 Adaboost算法,小姐姐还有完整的一系列机器学习视频课程,感兴趣的可以去看看。
Adaboost也属于集成学习,随机森林那里已经提到集成学习,但只写了Bagging算法和Boosting算法,这里补充上集成学习的理解以及弱分类器的结合策略。
一、集成学习(Ensemble learning):将若干个弱分类器通过一定的策略组合之后产生一个强分类器。弱分类器(基分类器)指的是那些分类准确率只比随机猜测略好一点的分类器,而强分类器的分类准确率高很多。
三种结合策略:
1、平均法:对于数值类的回归预测问题,通常使用的结合策略是平均法,对于若干个弱学习器的输出进行平均得到最终的预测输出。假设最终得到的n个弱分类器为{
h
1
,
h
2
,
.
.
.
,
h
n
h_{1},h_{2},...,h_{n}
h1,h2,...,hn},最简单的平均是算术平均,最终预测是
H
(
x
)
=
1
n
∑
1
n
h
i
(
x
)
H\left ( x \right )=\frac{1}{n}\sum_{1}^{n}h_{i}\left ( x \right )
H(x)=n11∑nhi(x)
如果每个弱分类器有一个权重w,则最终预测是
H
(
x
)
=
1
n
∑
1
n
w
i
h
i
(
x
)
(
w
i
≥
0
,
∑
1
n
w
i
=
1
)
H\left ( x \right )=\frac{1}{n}\sum_{1}^{n}w_{i}h_{i}\left ( x \right )\left ( w_{i} \geq 0,\sum_{1}^{n}w_{i}=1\right )
H(x)=n11∑nwihi(x)(wi≥0,1∑nwi=1)
2、投票法:对于分类问题的预测,通常使用的是投票法。假设预测类别是{
c
1
,
c
2
,
.
.
.
,
c
K
c_{1},c_{2},...,c_{K}
c1,c2,...,cK},对于任意一个预测样本x,n个弱学习器的预测结果分别是{
h
1
(
x
)
,
h
2
(
x
)
,
.
.
.
,
h
n
(
x
)
h_{1}\left ( x \right ),h_{2}\left ( x \right ),...,h_{n}\left ( x \right )
h1(x),h2(x),...,hn(x)}。最简单的投票法是相对多数投票法(少数服从多数),n个弱学习器对样本x的预测结果中,数量最多的类别
c
i
c_{i}
ci为最终的分类类别。如果不止一个类别获得最高票,则随机选择一个做最终类别。
稍微复杂的投票法是绝对多数投票法,在相对多数投票法的基础上,不光要求获得最高票,还要求票过半数,否则会拒绝预测。
更加复杂的是加权投票法,和加权平均法一样,每个弱学习器的分类票数要乘以一个权重,最终将各个类别的加权票数求和,最大的值对应的类别为最终类别。
3、学习法:代表方法是stacking,不是对弱学习器的结果做简单的逻辑处理,而是再加上一层学习器,弱学习器的结果作为该学习器的输入,得到最终结果。此时弱学习器称为初级学习器,用于结合的学习器称为次级学习器。
二、Adaboost是adaptive boosting(自适应boosting)的缩写,算法步骤如下:
1、计算样本权重
赋予训练集中每个样本一个权重,构成权重向量D,将权重向量D初始化相等值。假设有n个样本的训练集{(
x
1
,
y
1
x_{1},y_{1}
x1,y1),(
x
2
,
y
2
x_{2},y_{2}
x2,y2),…,(
x
n
,
y
n
x_{n},y_{n}
xn,yn)},设定每个样本的权重都相等,则权重为
1
n
\frac{1}{n}
n1。
2、计算错误率
在训练集上训练出一个弱分类器,并计算分类器的错误率:
ϵ
=
分
错
的
数
量
样
本
总
数
\epsilon =\frac{分错的数量}{样本总数}
ϵ=样本总数分错的数量
3、计算弱分类器权重
为当前分类器赋予权重值
α
\alpha
α,计算公式为:
α
=
1
2
l
n
(
1
−
ϵ
ϵ
)
\alpha=\frac{1}{2}ln\left ( \frac{1-\epsilon }{\epsilon } \right )
α=21ln(ϵ1−ϵ)
4、调整权重值
根据上一次训练结果,调整权重值(上一次分对的权重降低,分错的权重增加)。如果第i个样本被正确分类,则该样本权重更改为:
D
i
(
t
+
1
)
=
D
i
(
t
)
e
−
α
S
u
m
(
D
)
D_{i}^{\left ( t+1 \right )}=\frac{D_{i}^{\left ( t \right )}e^{-\alpha }}{Sum\left ( D \right )}
Di(t+1)=Sum(D)Di(t)e−α
如果第i个样本被分错,则该样本权重更改为:
D
i
(
t
+
1
)
=
D
i
(
t
)
e
α
S
u
m
(
D
)
D_{i}^{\left ( t+1 \right )}=\frac{D_{i}^{\left ( t \right )}e^{\alpha }}{Sum\left ( D \right )}
Di(t+1)=Sum(D)Di(t)eα
之后,在同一数据集上再一次训练弱分类器,然后循环上述过程,直到训练错误率为0,或者弱分类器的数目达到指定值。
AdaBoost算法特点:
优点: 泛化错误率低,易编码,可以应用在大部分分类器上。
缺点: 对离群点敏感。
三、以马疝病数据集练习实践,实现Adaboost算法并将其应用于给定一系列马的特征的情况下,判断马是否患病。原始数据分为训练集和测试集,其中一个样本如下,有21个特征,类别标签为1和-1。
导入外部数据源的相关代码如下:
import numpy as np
#加载外部数据源
def load_data_set(filename):
num_features = len(open(filename).readline().split('\t')) #获得字段数量
data_mat = []
label_mat = []
fr = open(filename)
for line in fr.readlines(): #逐行读取文件
line_arr = []
cur_line = line.strip().split('\t') #分离各个字段
for i in range(num_features - 1):
line_arr.append(float(cur_line[i])) #存储每一行的特征
data_mat.append(line_arr) #存储所有样本特征
label_mat.append(float(cur_line[-1])) #存储样本类别
return data_mat, label_mat
构建单层决策树弱分类器的代码如下:
#根据thresh_ineq标志进行分类,标志为lt,将小于阈值的设为-1,标志为gt,将大于阈值的设为-1
def stump_classify(data_matrix, dimen, thresh_val, thresh_ineq): #dimen表示第dimen列,即第几个特征,分类依据特征;thresh_val是阈值
ret_array = np.ones((np.shape(data_matrix)[0], 1)) #初始化结果集为1
if thresh_ineq == 'lt':
ret_array[data_matrix[:, dimen] <= thresh_val] = -1.0 #小于阈值,赋值为-1
else:
ret_array[data_matrix[:, dimen] > thresh_val] = -1.0 #大于阈值,赋值为-1
return ret_array
#找到最佳单层决策树
def build_stump(data_arr, class_labels, d):
data_matrix = np.mat(data_arr) #转化为矩阵形式,便于计算
label_mat = np.mat(class_labels).T #转置类别集
m, n = np.shape(data_matrix) #获得特征集行、列数量
num_steps = 10.0 #值用来划分步长
best_stump = {} #用来存储最佳分类器
best_clas_est = np.mat(np.zeros((m, 1))) #存储分类结果
min_error = np.inf #最小误差初始化为无穷大
for i in range(n): #遍历所有特征
range_min = data_matrix[:, i].min() #获取特征中的最小值
range_max = data_matrix[:, i].max() #获取特征中的最大值
step_size = (range_max - range_min) / num_steps #设定步长
for j in range(-1, int(num_steps) + 1): #循环增加步长数量改变阈值
for inequal in ['lt', 'gt']: #大于阈值归为1,还是小于阈值归为1的循环
thresh_val = range_min + float(j) * step_size #计算阈值
predicted_vals = stump_classify(data_matrix, i, thresh_val,inequal) #计算分类结果
err_arr = np.mat(np.ones((m, 1))) #初始化误差矩阵全为一
err_arr[predicted_vals == label_mat] = 0 #分类正确的,赋值为0
weighted_error = d.T * err_arr #计算分类误差
if weighted_error < min_error: #找到误差最小的分类方式
min_error = weighted_error
best_clas_est = predicted_vals.copy()
best_stump['dim'] = i #分类特征
best_stump['thresh'] = thresh_val #分类阈值
best_stump['ineq'] = inequal #分类方式
return best_stump, min_error, best_clas_est #输出最好的分类方式、最小误差、分类结果
Adaboost实现代码如下:
#Adaboost训练过程
def adaboost_trainDS(data_arr, class_labels, num_iter): #num_iter迭代次数
weak_class_arr = [] #存储每次迭代最佳分类器
m = np.shape(data_arr)[0] #获得样本数量
d = np.mat(np.ones((m, 1)) / m) #初始化权重都相等
agg_class_est = np.mat(np.zeros((m, 1)))
for i in range(num_iter):
best_stump, error, class_est = build_stump(data_arr, class_labels, d) #找到具有最小错误率的单层决策树
alpha = float(0.5 * np.log((1.0 - error) / max(error, 1e-16))) #计算弱分类器权重alpha,1e-16是避免除零溢出,分母不能为0
best_stump['alpha'] = alpha #记录权重
weak_class_arr.append(best_stump) #记录分类器
expon = np.multiply(-1 * alpha * np.mat(class_labels).T, class_est) #计算e的指数项,分类正确的为-α,分类错误的为α
d = np.multiply(d, np.exp(expon))
d = d / d.sum() #更新样本权重
agg_class_est += alpha * class_est #记录每个数据点的类别估计累计值
agg_errors = np.multiply(np.sign(agg_class_est) != np.mat(class_labels).T, np.ones((m, 1))) #计算误差
error_rate = agg_errors.sum() / m #错误率
if error_rate == 0.0: #错误率为0时退出循环
break
return weak_class_arr, agg_class_est #输出每次迭代最佳分类器,最终每个数据点的类别估计累计值
#基于Adaboost的分类
def ada_classify(dat2class, classifier_arr): #classifier_arr弱分类器集合
data_matrix = np.mat(dat2class)
m = np.shape(data_matrix)[0] #获得样本数量
agg_class_est = np.mat(np.zeros((m, 1)))
for i in range(len(classifier_arr)): #遍历所有弱分类器
class_est = stump_classify(data_matrix, classifier_arr[i]['dim'],classifier_arr[i]['thresh'],
classifier_arr[i]['ineq']) #获得每个分类器的结果
agg_class_est += classifier_arr[i]['alpha'] * class_est #每个数据点的类别估计累计值
return np.sign(agg_class_est) #输出类别
对数据进行训练并预测的代码如下:
def test_horse():
data_arr,label_arr=load_data_set('horseColicTraining.txt') #加载训练数据
classifier_arr,agg_class_est=adaboost_trainDS(data_arr,label_arr,500) #训练模型
train_prediction=np.sign(agg_class_est) #获得预测结果
train_err_arr=np.mat(np.ones((len(data_arr),1)))
train_err_num=train_err_arr[train_prediction!=np.mat(label_arr).T].sum() #预测错误的数量
print("train accuracy:%.2f"%(1-train_err_num/float(len(data_arr)))) #训练集预测准确率
test_arr,test_label_arr=load_data_set('horseColicTest.txt') #加载测试数据
test_prediction=ada_classify(test_arr,classifier_arr) #获得预测结果
test_err_arr=np.mat(np.ones((len(test_arr),1)))
test_err_num=test_err_arr[test_prediction!=np.mat(test_label_arr).T].sum() #预测错误的数量
print("test accuracy:%.2f"%(1-test_err_num/float(len(test_arr)))) #测试集预测准确率
test_horse()
使用不同数量的弱分类器进行训练,即改变Adaboost训练过程num_iter参数的值,测试结果如下:
弱分类器数量 | 训练集准确率 | 测试集准确率 |
---|---|---|
1 | 0.72 | 0.73 |
10 | 0.77 | 0.76 |
50 | 0.81 | 0.79 |
100 | 0.81 | 0.78 |
500 | 0.84 | 0.75 |
可以看到,当弱分类器数量为50时,预测结果最好。随着弱分类器数量的增加,训练集准确率虽然提高了,但是测试集准确率在下降,模型存在过拟合问题。