Adaboost 元算法改进分类性能

概述

元算法(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 ,每次训练完分类器后,计算它的错误率。对于错分的样本,它的权重将会提高,便于后续继续优化;对于分对的样本,对应的权重将会降低。

对于每一个分类器也有一个权重值 alphaalpha根据错误率 ε \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))

参考资料:《机器学习实战》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值