文章目录
概述
元算法(meta-algorithm
)或集成方法(ensemble mothed
)的思路是对多个不同的算法进行组合,从而使答案尽量达到最优的一种方法。Adaboost 是一种非常优秀的监督学习方法。集成方式可以分为很多种,可以是不同算法的集成,可以是同一算法在不同设置下的集成,可以是数据集不同部分分类给不同分类器之后的集成。
bagging 自举汇聚法
自举汇聚法(booststrap aggregating
),即为 bagging
方法。它每次在原数据集上选择S次后得到S个新数据集,并将新的S个数据集应用在不同的算法上。选择的方法是随机抽取某个样本,将这个样本用原数据集中的一个替换。整个算法过程是抽取不放回,并且是串行进行的。
Adaboost 元算法
Adaboost 全称为adaptive boosting
(自适应boost
ing),和 bagging 类似,都是使用多个弱分类器。弱分类器的意义在于分类准确率比50大,但也不会好太多。Adaboost中每个分类器的权重不一样,每次迭代更新,权重的大小对应分类器在上一轮迭代的正确率。
下面使用的弱分类器是单层决策树。
- AdaBoost
- 优点:泛化错误率低,易编码,可以应用在大部分分类器上,无参数调整。
- 缺点:对离群点敏感。
- 适用数据类型:数值型和标称型数据。
AdaBoost的一般流程
(1) 收集数据:可以使用任意方法。
(2) 准备数据:依赖于所使用的弱分类器类型,本章使用的是单层决策树,这种分类器可
以处理任何数据类型。当然也可以使用任意分类器作为弱分类器,第2章到第6章中的
任一分类器都可以充当弱分类器。作为弱分类器,简单分类器的效果更好。
(3) 分析数据:可以使用任意方法。
(4) 训练算法:AdaBoost的大部分时间都用在训练上,分类器将多次在同一数据集上训练
弱分类器。
(5) 测试算法:计算分类的错误率。
(6) 使用算法:同SVM一样,AdaBoost预测两个类别中的一个。如果想把它应用到多个类
别的场合,那么就要像多类SVM中的做法一样对AdaBoost进行修改。
Adaboost 对训练数据中的每个样本,赋予一个权重,所有样本构成向量 D
,每次训练完分类器后,计算它的错误率。对于错分的样本,它的权重将会提高,便于后续继续优化;对于分对的样本,对应的权重将会降低。
对于每一个分类器也有一个权重值 alpha
,alpha
根据错误率
ε
\varepsilon
ε 改变。
ε
=
未
正
确
分
类
的
样
本
数
目
所
有
样
本
数
目
\varepsilon = \frac{未正确分类的样本数目}{所有样本数目}
ε=所有样本数目未正确分类的样本数目
α
=
1
2
l
n
(
1
−
ε
ε
)
\alpha =\frac{1}{2}ln(\frac{1-\varepsilon}{\varepsilon})
α=21ln(ε1−ε)
图片来源:《机器学习实战》
AdaBoost算法的示意图。左边是数据集,其中直方图的不同宽度表示每个样例上的不同权重。在经过一个分类器之后,加权的预测结果会通过三角形中的alpha值进行加权。每个三角形中输出的加权结果在圆形中求和,从而得到最终的输出结果。
更新规则:
正确分类(减小):
D
i
(
t
+
1
)
=
D
i
(
t
)
e
−
α
S
u
m
(
D
)
D_i^{(t+1)}=\frac{D_i^{(t)}e^{-\alpha}}{Sum(D)}
Di(t+1)=Sum(D)Di(t)e−α
错误分类(增大)
D
i
(
t
+
1
)
=
D
i
(
t
)
e
α
S
u
m
(
D
)
D_i^{(t+1)}=\frac{D_i^{(t)}e^{\alpha}}{Sum(D)}
Di(t+1)=Sum(D)Di(t)eα
单层决策树decision stump
单层决策树(decision stump
)被称为树桩(stump
)的原因是因为只有一次划分,没有分支。
考虑一个二维的数据集:
单层决策树的划分即按照坐标轴方向,将数据划分为两侧。
伪代码:
将最小错误率 minError 设为+∞
对数据集中的每一个特征(第一层循环):
对每个步长(第二层循环):
对每个不等号(第三层循环):
建立一棵单层决策树并利用加权数据集对它进行测试
如果错误率低于 minError ,则将当前单层决策树设为最佳单层决策树
返回最佳单层决策树
第二层循环是为了划出对应的平行于坐标轴的分类边界,也即阈值。
第三层循环很好理解,在不知道label如何分配情况下遍历两种情况。
'''
thresh_val 阈值
D 样本的权重
ineq 不等号方向
'''
def stump_classfy(data_matrix, dimen, thresh_val, thresh_ineq): # 阈值threshold 不等号Inequality/Value
ret_arr = ones((shape(data_matrix)[0], 1))
if thresh_ineq == 'lt':
ret_arr[data_matrix[:, dimen] <= thresh_val] = -1.0
else:
ret_arr[data_matrix[:, dimen] > thresh_val] = 1.0
return ret_arr
def build_stump(data_arr, class_labels, D):
data_mat = mat(data_arr)
label_mat = mat(class_labels).T
m, n = shape(data_mat)
num_steps = 10.0
best_stump = {}
best_class_est = mat(zeros((m, 1)))
min_error = inf
for i in range(n): # 遍历特征
range_min = data_mat[:, i].min()
range_max = data_mat[:, i].max()
step_size = (range_max - range_min) / num_steps
for j in range(-1, int(num_steps) + 1): # j for calc thresh_val 计算阈值
for inequl in ['lt', 'gt']:
thresh_val = (range_min + step_size * j)
predict_labels = stump_classfy(data_mat, i, thresh_val, inequl) # 返回此时对应的预测值
err_arr = mat(ones((m, 1)))
err_arr[predict_labels == label_mat] = 0
weighted_err = D.T * err_arr # calc total error multiplied by D
if weighted_err < min_error:
min_error = weighted_err
best_class_est = predict_labels.copy()
best_stump['dim'] = i
best_stump['thresh'] = thresh_val
best_stump['ineq'] = inequl
return best_stump, min_error, best_class_est
基于 DS 建立 Adaboost
def ada_boost_train_decision_stump(data_arr, class_labels, max_iter=40):
weak_class_arr = [] # 弱分类器数组 保存对应的阈值,不等号方向,alpha,dim 等
m = shape(data_arr)[0]
D = mat(ones((m, 1)) /m)
agg_class_est = mat(zeros((m, 1))) # Aggregate class estimation 累计类别估计值
for i in range(max_iter):
best_stump, min_error, best_class_est = build_stump(data_arr, class_labels, D)
alpha = float(0.5 * log((1.0 - min_error) / max(min_error, 1e-16))) # 计算 alpha
best_stump['alpha'] = alpha
weak_class_arr.append(best_stump) # 加入弱分类器数组
# 更新 D
expon = multiply(-1.0 *alpha* mat(class_labels).T, best_class_est) # 确定符号
D = multiply(D, exp(expon)) # 因为expon不同 更新方向不同
D = D / D.sum()
agg_class_est += alpha * best_class_est # 累计类别估计值
agg_errors = multiply(sign(agg_class_est) != mat(class_labels).T, ones((m, 1)))
error_rate = agg_class_est.sum() / m
print('total error rate is ' + str(error_rate))
if error_rate == 0.0:
break
#print('D is =================')
#print(D)
return weak_class_arr
完整代码,预测患有疝病的马是否能够存活
from numpy import *
def load_simp_data():
data_mat = matrix([
[1, 2.1],
[2., 1.1],
[1.3, 1.],
[1., 1.],
[2., 1.]])
class_labels = [1.0, 1.0, -1.0, -1.0, 1.0]
return data_mat, class_labels
def load_data_set(file_name):
num_feat = len(open(file_name).readline().split('\t'))
data_mat = []
label_mat = []
fr = open(file_name, encoding='utf-8')
for line in fr.readlines():
line_arr = []
cur_line = line.strip().split('\t')
for i in range(num_feat-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
def stump_classfy(data_matrix, dimen, thresh_val, thresh_ineq): # 阈值threshold 不等号Inequality/Value
ret_arr = ones((shape(data_matrix)[0], 1))
if thresh_ineq == 'lt':
ret_arr[data_matrix[:, dimen] <= thresh_val] = -1.0
else:
ret_arr[data_matrix[:, dimen] > thresh_val] = 1.0
return ret_arr
def build_stump(data_arr, class_labels, D):
data_mat = mat(data_arr)
label_mat = mat(class_labels).T
m, n = shape(data_mat)
num_steps = 10.0
best_stump = {}
best_class_est = mat(zeros((m, 1)))
min_error = inf
for i in range(n): # 遍历特征
range_min = data_mat[:, i].min()
range_max = data_mat[:, i].max()
step_size = (range_max - range_min) / num_steps
for j in range(-1, int(num_steps) + 1): # j for calc thresh_val
for inequl in ['lt', 'gt']:
thresh_val = (range_min + step_size * j)
predict_labels = stump_classfy(data_mat, i, thresh_val, inequl) # 返回此时对应的预测值
err_arr = mat(ones((m, 1)))
err_arr[predict_labels == label_mat] = 0
weighted_err = D.T * err_arr # calc total error multiplied by D
if weighted_err < min_error:
min_error = weighted_err
best_class_est = predict_labels.copy()
best_stump['dim'] = i
best_stump['thresh'] = thresh_val
best_stump['ineq'] = inequl
return best_stump, min_error, best_class_est
def ada_boost_train_decision_stump(data_arr, class_labels, max_iter=40):
weak_class_arr = [] # 弱分类器数组 保存对应的阈值,不等号方向,alpha,dim 等
m = shape(data_arr)[0]
D = mat(ones((m, 1)) /m)
agg_class_est = mat(zeros((m, 1))) # Aggregate class estimation 累计类别估计值
for i in range(max_iter):
best_stump, min_error, best_class_est = build_stump(data_arr, class_labels, D)
alpha = float(0.5 * log((1.0 - min_error) / max(min_error, 1e-16))) # 计算 alpha
best_stump['alpha'] = alpha
weak_class_arr.append(best_stump)# 加入弱分类器数组
# 更新 D
expon = multiply(-1.0 *alpha* mat(class_labels).T, best_class_est) # 确定符号
D = multiply(D, exp(expon)) # 因为expon不同 更新方向不同
D = D / D.sum()
agg_class_est += alpha * best_class_est # 累计类别估计值
agg_errors = multiply(sign(agg_class_est) != mat(class_labels).T, ones((m, 1)))
error_rate = agg_class_est.sum() / m
print('total error rate is ' + str(error_rate))
if error_rate == 0.0:
break
#print('D is =================')
#print(D)
return weak_class_arr
def ada_classify(data_to_class, classifier_arr):
data_mat = mat(data_to_class)
m = shape(data_mat)[0]
agg_class_est = mat(zeros((m, 1)))
for i in range(len(classifier_arr)):
class_est = stump_classfy(data_mat, classifier_arr[i]['dim'], \
classifier_arr[i]['thresh'], \
classifier_arr[i]['ineq'])
agg_class_est += classifier_arr[i]['alpha'] * class_est
print(agg_class_est)
return sign(agg_class_est)
if __name__ == '__main__':
'''
# train
data_mat,class_label=load_data_set('./horseColicTraining2.txt')
classifier_arr=ada_boost_train_decision_stump(data_mat,class_label,10)
# predict
test_arr,test_label_arr=load_data_set('./horseColicTest2.txt')
prediction10=ada_classify(test_arr,classifier_arr)
error_arr=mat(ones((67,1)))
print(error_arr[prediction10==mat(test_label_arr).T].sum())
'''
data_arr,label_arr=load_simp_data()
classifier_arr=ada_boost_train_decision_stump(data_arr,label_arr,max_iter=30)
print(ada_classify([0,0],classifier_arr))
参考资料:《机器学习实战》