Adaboost算法

74 篇文章 2 订阅
53 篇文章 0 订阅

Adaboost算法

集成学习概述

集成学习算法定义

  • 集成学习(Ensemble learning)就是讲若干个弱分类器通过一定策略组合后产生一个强分类器。弱分类器(weak Classifier)指的就是那些分类准确率只比随机猜测好一点的分类器。而强分类器(strong Classifier)的分类准确率会高很多,这里的弱和强是相对的,弱分类器也叫做基分类器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gEJDogFz-1578039599583)(/home/ach/.config/Typora/typora-user-images/image-20200103151902690.png)]

  • 分类:

    bagging

    boosting

bagging(装袋)

  • bagging方法又叫做自举汇聚法(boostrap aggregating),是一种根据均匀概率分布从数据集中重复抽样(有放回)的技术,每个数据集和原始数据集大小相等,由于新数据集的每一个样本都是从原数据集合中有放回随机抽样出来的,所以每个数据集中可能有重复的值,而原始数据集中的某些样本可能根本就没有出现在新数据集中

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M8qcVehe-1578039599585)(/home/ach/.config/Typora/typora-user-images/image-20200103152923402.png)]

  • 有放回的随机抽样:自主采样法(Bootstap sampling),也就是说对于m个样本的原始数据集,每次随机选取一个样本放回采样集合中,然后这个样本重新放回,再进行下一次随机抽样,直到采样集合中样本数量达到m,这样一个采样集合就构建好了,重复过程,生成n个采样集合

  • 将n个采样集合,分别进行训练,得到n个弱分类器,根据每个结果进行组合,得到强分类器

  • 降低弱分类器的方差

boosting(提升)

  • 迭代过程,用来自适应的改变训练样本的分布,使得弱分类器聚焦到那些很难分类的样本上,它的做法是给每一个训练样本赋予一个权重,在每一轮训练中自动调整权重

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-03eeK0yc-1578039599585)(/home/ach/.config/Typora/typora-user-images/image-20200103153545741.png)]

  • 组合策略

    1. 平均法:

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Alltc62K-1578039599586)(/home/ach/.config/Typora/typora-user-images/image-20200103153643025.png)]

    2. 投票法:

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vcTYyp3O-1578039599587)(/home/ach/.config/Typora/typora-user-images/image-20200103153713147.png)]

    3. 学习法:

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UGTcaD7X-1578039599587)(/home/ach/.config/Typora/typora-user-images/image-20200103153822956.png)]

Adaboost算法(自适应提升算法)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JIwmajVC-1578039599588)(/home/ach/.config/Typora/typora-user-images/image-20200103153935339.png)]

  • 收集数据:可以使用任意方法
    准备数据:依赖于所使用的弱分类器类型,本章使用的是单层决策树,这种分类器可以处理任何数据类型。
    当然也可以使用任意分类器作为弱分类器,第2章到第6章中的任一分类器都可以充当弱分类器。
    作为弱分类器,简单分类器的效果更好。
    分析数据:可以使用任意方法。
    训练算法:AdaBoost 的大部分时间都用在训练上,分类器将多次在同一数据集上训练弱分类器。
    测试算法:计算分类的错误率。
    使用算法:通SVM一样,AdaBoost 预测两个类别中的一个。如果想把它应用到多个类别的场景,那么就要像多类 SVM 中的做法一样对 AdaBoost

  • 优点:泛化(由具体的、个别的扩大为一般的)错误率低,易编码,可以应用在大部分分类器上,无参数调节。

    缺点:对离群点敏感。

    适用数据类型:数值型和标称型数据

案例一:自适应算法实现

  • 导入数据:

    def load_sim_data():
        """
        测试数据,
        :return: data_arr   feature对应的数据集
                label_arr  feature对应的分类标签
        """
        data_mat = np.matrix([[1.0, 2.1],
                              [2.0, 1.1],
                              [1.3, 1.0],
                              [1.0, 1.0],
                              [2.0, 1.0]])
        class_labels = [1.0, 1.0, -1.0, -1.0, 1.0]
        return data_mat, class_labels
    
  • 算法测试:将符合条件的数据转化,测试是否有某个值小于或者大于我们正在测试的阈值,如果大于某个阈值就是错误的

    def stump_classify(data_mat, dimen, thresh_val, thresh_ineq):
        """
        (将数据集,按照feature列的value进行 二分法切分比较来赋值分类)
        :param data_mat: Matrix数据集
        :param dimen: 特征的哪一个列
        :param thresh_val: 特征列要比较的值
        :param thresh_ineq:
        :return: np.array
        """
        ret_array = np.ones((np.shape(data_mat)[0], 1))
        # data_mat[:, dimen] 表示数据集中第dimen列的所有值
        # thresh_ineq == 'lt'表示修改左边的值,gt表示修改右边的值
        # (这里其实我建议理解为转换左右边,就是一棵树的左右孩子,可能有点问题。。。待考证)
        if thresh_ineq == 'lt':# 假设左边比较一下
            ret_array[data_mat[:, dimen] <= thresh_val] = -1.0
        else:# 假设右边比较一下
            ret_array[data_mat[:, dimen] > thresh_val] = -1.0
        return ret_array
    
  • 单层决策树的实现:最优化单层决策树

    # 这个算法是为了寻找最好的单层决策树
    def build_stump(data_arr, class_labels, D):
        """
        得到决策树的模型 (这个比较重要,需要看懂)
        :param data_arr: 特征标签集合
        :param class_labels: 分类标签集合
        :param D: 最初的特征权重值
        :return: best_Stump    最优的分类器模型
                min_error     错误率
                best_class_est  训练后的结果集
        """
        data_mat = np.mat(data_arr)
        label_mat = np.mat(class_labels).T
    
        m, n = np.shape(data_mat)
        num_steps = 10.0
        best_stump = {}
        best_class_est = np.mat(np.zeros((m, 1)))#训练后的结果集
        # 无穷大
        min_err = np.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):
                for inequal in ['lt', 'gt']:
                    thresh_val = (range_min + float(j) * step_size)
                    predicted_vals = stump_classify(data_mat, i, thresh_val, inequal)
                    err_arr = np.mat(np.ones((m, 1)))
                    err_arr[predicted_vals == label_mat] = 0
                    # 这里是矩阵乘法
                    weighted_err = D.T * err_arr
                    '''
                    dim            表示 feature列
                    thresh_val      表示树的分界值
                    inequal        表示计算树左右颠倒的错误率的情况
                    weighted_error  表示整体结果的错误率
                    best_class_est    预测的最优结果 (与class_labels对应)
                    '''
                    # print('split: dim {}, thresh {}, thresh inequal: {}, the weighted err is {}'.format(
                    #     i, thresh_val, inequal, weighted_err
                    # ))
                    if weighted_err < min_err:
                        min_err = weighted_err
                        best_class_est = predicted_vals.copy()#可以保存的结果集储存
                        best_stump['dim'] = i# 第i列
                        best_stump['thresh'] = thresh_val# 阈值
                        best_stump['ineq'] = inequal#比较范围,是用大于还是用小于
        # best_stump 表示分类器的结果,在第几个列上,用大于/小于比较,阈值是多少 (单个弱分类器)
        # print(best_stump)
        return best_stump, min_err, best_class_est
    # print(np.mat(np.ones((5,1))/5))#赋值相同的权重
    # [[0.2]
    #  [0.2]
    #  [0.2]
    #  [0.2]
    #  [0.2]]
    # print(build_stump(datMat,classLables,np.mat(np.ones((5,1))/5)))
    
  • 构建自适应算法

    def ada_boost_train_ds(data_arr, class_labels, num_it=40):
        """
        adaBoost训练过程放大
        :param data_arr: 特征标签集合
        :param class_labels: 分类标签集合
        :param num_it: 迭代次数
        :return: weak_class_arr  弱分类器的集合
                agg_class_est   预测的分类结果值
        """
        weak_class_arr = []
        m = np.shape(data_arr)[0]
        # 初始化 D,设置每个特征的权重值,平均分为m份
        D = np.mat(np.ones((m, 1)) / m)
        agg_class_est = np.mat(np.zeros((m, 1)))#初始化0矩阵
        for i in range(num_it):
            # 得到决策树的模型
            best_stump, error, class_est = build_stump(data_arr, class_labels, D)# 寻找最佳的单层决策树
            print('D: {}'.format(D.T))
            # alpha 目的主要是计算每一个分类器实例的权重(加和就是分类结果)
            # 计算每个分类器的 alpha 权重值
            alpha = float(0.5 * np.log((1.0 - error) / max(error, 1e-16)))
            best_stump['alpha'] = alpha
            # store Stump Params in Array
            weak_class_arr.append(best_stump)
            # print('class_est: {}'.format(class_est.T))
            # 分类正确:乘积为1,不会影响结果,-1主要是下面求e的-alpha次方
            # 分类错误:乘积为 -1,结果会受影响,所以也乘以 -1
            expon = np.multiply(-1 * alpha * np.mat(class_labels).T, class_est)
            # 判断正确的,就乘以-1,否则就乘以1, 为什么? 书上的公式有问题
            # print('(-1取反)预测值 expon=', expon.T)
            # 计算e的expon次方,然后计算得到一个综合的概率的值
            # 结果发现: 判断错误的样本,D对于的样本权重值会变大。
            # multiply是对应项相乘
            D = np.multiply(D, np.exp(expon))
            D = D / D.sum()
            # 预测的分类结果值,在上一轮结果的基础上,进行加和操作
            # print('叠加前的分类结果class_est: {}'.format(class_est.T))
            agg_class_est += alpha * class_est
            print('叠加后的分类结果agg_class_est: {}'.format(agg_class_est.T))
            # sign 判断正为1, 0为0, 负为-1,通过最终加和的权重值,判断符号。
            # 结果为:错误的样本标签集合,因为是 !=,那么结果就是0 正, 1 负,这里1就是表示是错误分辨的
            agg_errors = np.multiply(np.sign(agg_class_est) != np.mat(class_labels).T,
                                     np.ones((m, 1)))
            error_rate = agg_errors.sum() / m
            print('total error: {}\n'.format(error_rate))
            if error_rate == 0.0:
                break
        print(D)
        return weak_class_arr
    
  • 测试代码

    def ada_classify(data_to_class, classifier_arr):
        """
        通过刚刚上面那个函数得到的弱分类器的集合进行预测
        :param data_to_class: 数据集
        :param classifier_arr: 分类器列表
        :return: 正负一,也就是表示分类的结果
        """
        data_mat = np.mat(data_to_class)# 测试9
    
        m = np.shape(data_mat)[0]
        agg_class_est = np.mat(np.zeros((m, 1)))
        for i in range(len(classifier_arr)):
            class_est = stump_classify(
                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 np.sign(agg_class_est)
    
  • 完整代码:

    # 自适应算法实现实例一
    import numpy as np
    
    
    def load_sim_data():
        """
        测试数据,
        :return: data_arr   feature对应的数据集
                label_arr  feature对应的分类标签
        """
        data_mat = np.matrix([[1.0, 2.1],
                              [2.0, 1.1],
                              [1.3, 1.0],
                              [1.0, 1.0],
                              [2.0, 1.0]])
        class_labels = [1.0, 1.0, -1.0, -1.0, 1.0]
        return data_mat, class_labels
    datMat,classLables = load_sim_data()
    def stump_classify(data_mat, dimen, thresh_val, thresh_ineq):
        """
        (将数据集,按照feature列的value进行 二分法切分比较来赋值分类)
        :param data_mat: Matrix数据集
        :param dimen: 特征的哪一个列
        :param thresh_val: 特征列要比较的值
        :param thresh_ineq:
        :return: np.array
        """
        ret_array = np.ones((np.shape(data_mat)[0], 1))
        # data_mat[:, dimen] 表示数据集中第dimen列的所有值
        # thresh_ineq == 'lt'表示修改左边的值,gt表示修改右边的值
        # (这里其实我建议理解为转换左右边,就是一棵树的左右孩子,可能有点问题。。。待考证)
        if thresh_ineq == 'lt':# 假设左边比较一下
            ret_array[data_mat[:, dimen] <= thresh_val] = -1.0
        else:# 假设右边比较一下
            ret_array[data_mat[:, dimen] > thresh_val] = -1.0
        return ret_array
    # 这个算法是为了寻找最好的单层决策树
    def build_stump(data_arr, class_labels, D):
        """
        得到决策树的模型 (这个比较重要,需要看懂)
        :param data_arr: 特征标签集合
        :param class_labels: 分类标签集合
        :param D: 最初的特征权重值
        :return: best_Stump    最优的分类器模型
                min_error     错误率
                best_class_est  训练后的结果集
        """
        data_mat = np.mat(data_arr)
        label_mat = np.mat(class_labels).T
    
        m, n = np.shape(data_mat)
        num_steps = 10.0
        best_stump = {}
        best_class_est = np.mat(np.zeros((m, 1)))#训练后的结果集
        # 无穷大
        min_err = np.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):
                for inequal in ['lt', 'gt']:
                    thresh_val = (range_min + float(j) * step_size)
                    predicted_vals = stump_classify(data_mat, i, thresh_val, inequal)
                    err_arr = np.mat(np.ones((m, 1)))
                    err_arr[predicted_vals == label_mat] = 0
                    # 这里是矩阵乘法
                    weighted_err = D.T * err_arr
                    '''
                    dim            表示 feature列
                    thresh_val      表示树的分界值
                    inequal        表示计算树左右颠倒的错误率的情况
                    weighted_error  表示整体结果的错误率
                    best_class_est    预测的最优结果 (与class_labels对应)
                    '''
                    # print('split: dim {}, thresh {}, thresh inequal: {}, the weighted err is {}'.format(
                    #     i, thresh_val, inequal, weighted_err
                    # ))
                    if weighted_err < min_err:
                        min_err = weighted_err
                        best_class_est = predicted_vals.copy()#可以保存的结果集储存
                        best_stump['dim'] = i# 第i列
                        best_stump['thresh'] = thresh_val# 阈值
                        best_stump['ineq'] = inequal#比较范围,是用大于还是用小于
        # best_stump 表示分类器的结果,在第几个列上,用大于/小于比较,阈值是多少 (单个弱分类器)
        # print(best_stump)
        return best_stump, min_err, best_class_est
    # print(np.mat(np.ones((5,1))/5))#赋值相同的权重
    # [[0.2]
    #  [0.2]
    #  [0.2]
    #  [0.2]
    #  [0.2]]
    # print(build_stump(datMat,classLables,np.mat(np.ones((5,1))/5)))
    def ada_boost_train_ds(data_arr, class_labels, num_it=40):
        """
        adaBoost训练过程放大
        :param data_arr: 特征标签集合
        :param class_labels: 分类标签集合
        :param num_it: 迭代次数
        :return: weak_class_arr  弱分类器的集合
                agg_class_est   预测的分类结果值
        """
        weak_class_arr = []
        m = np.shape(data_arr)[0]
        # 初始化 D,设置每个特征的权重值,平均分为m份
        D = np.mat(np.ones((m, 1)) / m)
        agg_class_est = np.mat(np.zeros((m, 1)))#初始化0矩阵
        for i in range(num_it):
            # 得到决策树的模型
            best_stump, error, class_est = build_stump(data_arr, class_labels, D)# 寻找最佳的单层决策树
            print('D: {}'.format(D.T))
            # alpha 目的主要是计算每一个分类器实例的权重(加和就是分类结果)
            # 计算每个分类器的 alpha 权重值
            alpha = float(0.5 * np.log((1.0 - error) / max(error, 1e-16)))
            best_stump['alpha'] = alpha
            # store Stump Params in Array
            weak_class_arr.append(best_stump)
            # print('class_est: {}'.format(class_est.T))
            # 分类正确:乘积为1,不会影响结果,-1主要是下面求e的-alpha次方
            # 分类错误:乘积为 -1,结果会受影响,所以也乘以 -1
            expon = np.multiply(-1 * alpha * np.mat(class_labels).T, class_est)
            # 判断正确的,就乘以-1,否则就乘以1, 为什么? 书上的公式有问题
            # print('(-1取反)预测值 expon=', expon.T)
            # 计算e的expon次方,然后计算得到一个综合的概率的值
            # 结果发现: 判断错误的样本,D对于的样本权重值会变大。
            # multiply是对应项相乘
            D = np.multiply(D, np.exp(expon))
            D = D / D.sum()
            # 预测的分类结果值,在上一轮结果的基础上,进行加和操作
            # print('叠加前的分类结果class_est: {}'.format(class_est.T))
            agg_class_est += alpha * class_est
            print('叠加后的分类结果agg_class_est: {}'.format(agg_class_est.T))
            # sign 判断正为1, 0为0, 负为-1,通过最终加和的权重值,判断符号。
            # 结果为:错误的样本标签集合,因为是 !=,那么结果就是0 正, 1 负,这里1就是表示是错误分辨的
            agg_errors = np.multiply(np.sign(agg_class_est) != np.mat(class_labels).T,
                                     np.ones((m, 1)))
            error_rate = agg_errors.sum() / m
            print('total error: {}\n'.format(error_rate))
            if error_rate == 0.0:
                break
        print(D)
        return weak_class_arr
    # print(ada_boost_train_ds(datMat,classLables,9))
    classLables_arr = ada_boost_train_ds(datMat,classLables,9)
    
    def ada_classify(data_to_class, classifier_arr):
        """
        通过刚刚上面那个函数得到的弱分类器的集合进行预测
        :param data_to_class: 数据集
        :param classifier_arr: 分类器列表
        :return: 正负一,也就是表示分类的结果
        """
        data_mat = np.mat(data_to_class)# 测试9
    
        m = np.shape(data_mat)[0]
        agg_class_est = np.mat(np.zeros((m, 1)))
        for i in range(len(classifier_arr)):
            class_est = stump_classify(
                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 np.sign(agg_class_est)
    # print(ada_classify([0,0],classLables_arr))
    
    

疝病马数据使用自适应算法实现

  • 和上边代码差不多,就是加了一个rank测试

    import numpy as np
    def stump_classify(data_mat, dimen, thresh_val, thresh_ineq):
        """
        (将数据集,按照feature列的value进行 二分法切分比较来赋值分类)
        :param data_mat: Matrix数据集
        :param dimen: 特征的哪一个列
        :param thresh_val: 特征列要比较的值
        :param thresh_ineq:
        :return: np.array
        """
        ret_array = np.ones((np.shape(data_mat)[0], 1))
        # data_mat[:, dimen] 表示数据集中第dimen列的所有值
        # thresh_ineq == 'lt'表示修改左边的值,gt表示修改右边的值
        # (这里其实我建议理解为转换左右边,就是一棵树的左右孩子,可能有点问题。。。待考证)
        if thresh_ineq == 'lt':# 假设左边比较一下
            ret_array[data_mat[:, dimen] <= thresh_val] = -1.0
        else:# 假设右边比较一下
            ret_array[data_mat[:, dimen] > thresh_val] = -1.0
        return ret_array
    # 这个算法是为了寻找最好的单层决策树
    def build_stump(data_arr, class_labels, D):
        """
        得到决策树的模型 (这个比较重要,需要看懂)
        :param data_arr: 特征标签集合
        :param class_labels: 分类标签集合
        :param D: 最初的特征权重值
        :return: best_Stump    最优的分类器模型
                min_error     错误率
                best_class_est  训练后的结果集
        """
        data_mat = np.mat(data_arr)
        label_mat = np.mat(class_labels).T
    
        m, n = np.shape(data_mat)
        num_steps = 10.0
        best_stump = {}
        best_class_est = np.mat(np.zeros((m, 1)))#训练后的结果集
        # 无穷大
        min_err = np.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):
                for inequal in ['lt', 'gt']:
                    thresh_val = (range_min + float(j) * step_size)
                    predicted_vals = stump_classify(data_mat, i, thresh_val, inequal)
                    err_arr = np.mat(np.ones((m, 1)))
                    err_arr[predicted_vals == label_mat] = 0
                    # 这里是矩阵乘法
                    weighted_err = D.T * err_arr
                    '''
                    dim            表示 feature列
                    thresh_val      表示树的分界值
                    inequal        表示计算树左右颠倒的错误率的情况
                    weighted_error  表示整体结果的错误率
                    best_class_est    预测的最优结果 (与class_labels对应)
                    '''
                    # print('split: dim {}, thresh {}, thresh inequal: {}, the weighted err is {}'.format(
                    #     i, thresh_val, inequal, weighted_err
                    # ))
                    if weighted_err < min_err:
                        min_err = weighted_err
                        best_class_est = predicted_vals.copy()#可以保存的结果集储存
                        best_stump['dim'] = i# 第i列
                        best_stump['thresh'] = thresh_val# 阈值
                        best_stump['ineq'] = inequal#比较范围,是用大于还是用小于
        # best_stump 表示分类器的结果,在第几个列上,用大于/小于比较,阈值是多少 (单个弱分类器)
        # print(best_stump)
        return best_stump, min_err, best_class_est
    # print(np.mat(np.ones((5,1))/5))#赋值相同的权重
    # [[0.2]
    #  [0.2]
    #  [0.2]
    #  [0.2]
    #  [0.2]]
    # print(build_stump(datMat,classLables,np.mat(np.ones((5,1))/5)))
    def ada_boost_train_ds(data_arr, class_labels, num_it=40):
        """
        adaBoost训练过程放大
        :param data_arr: 特征标签集合
        :param class_labels: 分类标签集合
        :param num_it: 迭代次数
        :return: weak_class_arr  弱分类器的集合
                agg_class_est   预测的分类结果值
        """
        weak_class_arr = []
        m = np.shape(data_arr)[0]
        # 初始化 D,设置每个特征的权重值,平均分为m份
        D = np.mat(np.ones((m, 1)) / m)
        agg_class_est = np.mat(np.zeros((m, 1)))#初始化0矩阵
        for i in range(num_it):
            # 得到决策树的模型
            best_stump, error, class_est = build_stump(data_arr, class_labels, D)# 寻找最佳的单层决策树
            # print('D: {}'.format(D.T))
            # alpha 目的主要是计算每一个分类器实例的权重(加和就是分类结果)
            # 计算每个分类器的 alpha 权重值
            alpha = float(0.5 * np.log((1.0 - error) / max(error, 1e-16)))
            best_stump['alpha'] = alpha
            # store Stump Params in Array
            weak_class_arr.append(best_stump)
            # print('class_est: {}'.format(class_est.T))
            # 分类正确:乘积为1,不会影响结果,-1主要是下面求e的-alpha次方
            # 分类错误:乘积为 -1,结果会受影响,所以也乘以 -1
            expon = np.multiply(-1 * alpha * np.mat(class_labels).T, class_est)
            # 判断正确的,就乘以-1,否则就乘以1, 为什么? 书上的公式有问题
            # print('(-1取反)预测值 expon=', expon.T)
            # 计算e的expon次方,然后计算得到一个综合的概率的值
            # 结果发现: 判断错误的样本,D对于的样本权重值会变大。
            # multiply是对应项相乘
            D = np.multiply(D, np.exp(expon))
            D = D / D.sum()
            # 预测的分类结果值,在上一轮结果的基础上,进行加和操作
            # print('叠加前的分类结果class_est: {}'.format(class_est.T))
            agg_class_est += alpha * class_est
            # print('叠加后的分类结果agg_class_est: {}'.format(agg_class_est.T))
            # sign 判断正为1, 0为0, 负为-1,通过最终加和的权重值,判断符号。
            # 结果为:错误的样本标签集合,因为是 !=,那么结果就是0 正, 1 负,这里1就是表示是错误分辨的
            agg_errors = np.multiply(np.sign(agg_class_est) != np.mat(class_labels).T,
                                     np.ones((m, 1)))
            error_rate = agg_errors.sum() / m
            print('total error: {}\n'.format(error_rate))
            if error_rate == 0.0:
                break
        # print(D)
        return weak_class_arr
    def loadDataSet(fileName):
        numFeat = len(open(fileName).readline().split('\t'))# 22个列 21个特征 1个标签
        dataMat = [];labelMat = []
        fr = open(fileName)
        for line in fr.readlines():
            lineArr = []
            curLine = line.strip().split('\t')
            # 将21个特征保存起来
            for i in range(numFeat-1):
                lineArr.append((float(curLine[i])))
            # print(len(lineArr))
            dataMat.append(lineArr)
            labelMat.append(float(curLine[-1]))
        return dataMat,labelMat
    datArr,labelArr = loadDataSet(r'/home/ach/桌面/machine-learning/ai/Adaboost/7.AdaBoost/horseColicTraining2.txt')
    # print(ada_boost_train_ds(datArr,labelArr,10))
    classifiterArray = ada_boost_train_ds(datArr,labelArr,10000)
    def ada_classify(data_to_class, classifier_arr):
        """
        通过刚刚上面那个函数得到的弱分类器的集合进行预测
        :param data_to_class: 数据集
        :param classifier_arr: 分类器列表
        :return: 正负一,也就是表示分类的结果
        """
        data_mat = np.mat(data_to_class)# 测试9
    
        m = np.shape(data_mat)[0]
        agg_class_est = np.mat(np.zeros((m, 1)))
        for i in range(len(classifier_arr)):
            class_est = stump_classify(
                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 np.sign(agg_class_est)
    def test():
        testArr,testLabelArr = loadDataSet(r'/home/ach/桌面/machine-learning/ai/Adaboost/7.AdaBoost/horseColicTest2.txt')
        pridict = ada_classify(testArr,classifiterArray)
        errArr = np.mat(np.ones((67,1)))
        error = errArr[pridict!=np.mat(testLabelArr).T].sum()
        print('error = {}'.format(error/len(testLabelArr)))
    test()
    
  • 和书上结果差不多:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z5bLqxDu-1578039599589)(/home/ach/.config/Typora/typora-user-images/image-20200103161235836.png)]

处理非均衡问题

  • 代码

    import numpy as np
    def stump_classify(data_mat, dimen, thresh_val, thresh_ineq):
        """
        (将数据集,按照feature列的value进行 二分法切分比较来赋值分类)
        :param data_mat: Matrix数据集
        :param dimen: 特征的哪一个列
        :param thresh_val: 特征列要比较的值
        :param thresh_ineq:
        :return: np.array
        """
        ret_array = np.ones((np.shape(data_mat)[0], 1))
        # data_mat[:, dimen] 表示数据集中第dimen列的所有值
        # thresh_ineq == 'lt'表示修改左边的值,gt表示修改右边的值
        # (这里其实我建议理解为转换左右边,就是一棵树的左右孩子,可能有点问题。。。待考证)
        if thresh_ineq == 'lt':# 假设左边比较一下
            ret_array[data_mat[:, dimen] <= thresh_val] = -1.0
        else:# 假设右边比较一下
            ret_array[data_mat[:, dimen] > thresh_val] = -1.0
        return ret_array
    # 这个算法是为了寻找最好的单层决策树
    def build_stump(data_arr, class_labels, D):
        """
        得到决策树的模型 (这个比较重要,需要看懂)
        :param data_arr: 特征标签集合
        :param class_labels: 分类标签集合
        :param D: 最初的特征权重值
        :return: best_Stump    最优的分类器模型
                min_error     错误率
                best_class_est  训练后的结果集
        """
        data_mat = np.mat(data_arr)
        label_mat = np.mat(class_labels).T
    
        m, n = np.shape(data_mat)
        num_steps = 10.0
        best_stump = {}
        best_class_est = np.mat(np.zeros((m, 1)))#训练后的结果集
        # 无穷大
        min_err = np.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):
                for inequal in ['lt', 'gt']:
                    thresh_val = (range_min + float(j) * step_size)
                    predicted_vals = stump_classify(data_mat, i, thresh_val, inequal)
                    err_arr = np.mat(np.ones((m, 1)))
                    err_arr[predicted_vals == label_mat] = 0
                    # 这里是矩阵乘法
                    weighted_err = D.T * err_arr
                    '''
                    dim            表示 feature列
                    thresh_val      表示树的分界值
                    inequal        表示计算树左右颠倒的错误率的情况
                    weighted_error  表示整体结果的错误率
                    best_class_est    预测的最优结果 (与class_labels对应)
                    '''
                    # print('split: dim {}, thresh {}, thresh inequal: {}, the weighted err is {}'.format(
                    #     i, thresh_val, inequal, weighted_err
                    # ))
                    if weighted_err < min_err:
                        min_err = weighted_err
                        best_class_est = predicted_vals.copy()#可以保存的结果集储存
                        best_stump['dim'] = i# 第i列
                        best_stump['thresh'] = thresh_val# 阈值
                        best_stump['ineq'] = inequal#比较范围,是用大于还是用小于
        # best_stump 表示分类器的结果,在第几个列上,用大于/小于比较,阈值是多少 (单个弱分类器)
        # print(best_stump)
        return best_stump, min_err, best_class_est
    # print(np.mat(np.ones((5,1))/5))#赋值相同的权重
    # [[0.2]
    #  [0.2]
    #  [0.2]
    #  [0.2]
    #  [0.2]]
    # print(build_stump(datMat,classLables,np.mat(np.ones((5,1))/5)))
    def ada_boost_train_ds(data_arr, class_labels, num_it=40):
        """
        adaBoost训练过程放大
        :param data_arr: 特征标签集合
        :param class_labels: 分类标签集合
        :param num_it: 迭代次数
        :return: weak_class_arr  弱分类器的集合
                agg_class_est   预测的分类结果值
        """
        weak_class_arr = []
        m = np.shape(data_arr)[0]
        # 初始化 D,设置每个特征的权重值,平均分为m份
        D = np.mat(np.ones((m, 1)) / m)
        agg_class_est = np.mat(np.zeros((m, 1)))#初始化0矩阵
        for i in range(num_it):
            # 得到决策树的模型
            best_stump, error, class_est = build_stump(data_arr, class_labels, D)# 寻找最佳的单层决策树
            # print('D: {}'.format(D.T))
            # alpha 目的主要是计算每一个分类器实例的权重(加和就是分类结果)
            # 计算每个分类器的 alpha 权重值
            alpha = float(0.5 * np.log((1.0 - error) / max(error, 1e-16)))
            best_stump['alpha'] = alpha
            # store Stump Params in Array
            weak_class_arr.append(best_stump)
            # print('class_est: {}'.format(class_est.T))
            # 分类正确:乘积为1,不会影响结果,-1主要是下面求e的-alpha次方
            # 分类错误:乘积为 -1,结果会受影响,所以也乘以 -1
            expon = np.multiply(-1 * alpha * np.mat(class_labels).T, class_est)
            # 判断正确的,就乘以-1,否则就乘以1, 为什么? 书上的公式有问题
            # print('(-1取反)预测值 expon=', expon.T)
            # 计算e的expon次方,然后计算得到一个综合的概率的值
            # 结果发现: 判断错误的样本,D对于的样本权重值会变大。
            # multiply是对应项相乘
            D = np.multiply(D, np.exp(expon))
            D = D / D.sum()
            # 预测的分类结果值,在上一轮结果的基础上,进行加和操作
            # print('叠加前的分类结果class_est: {}'.format(class_est.T))
            agg_class_est += alpha * class_est
            # print('叠加后的分类结果agg_class_est: {}'.format(agg_class_est.T))
            # sign 判断正为1, 0为0, 负为-1,通过最终加和的权重值,判断符号。
            # 结果为:错误的样本标签集合,因为是 !=,那么结果就是0 正, 1 负,这里1就是表示是错误分辨的
            agg_errors = np.multiply(np.sign(agg_class_est) != np.mat(class_labels).T,
                                     np.ones((m, 1)))
            error_rate = agg_errors.sum() / m
            print('total error: {}\n'.format(error_rate))
            if error_rate == 0.0:
                break
        # print(D)
        return weak_class_arr,agg_class_est
    def loadDataSet(fileName):
        numFeat = len(open(fileName).readline().split('\t'))# 22个列 21个特征 1个标签
        dataMat = [];labelMat = []
        fr = open(fileName)
        for line in fr.readlines():
            lineArr = []
            curLine = line.strip().split('\t')
            # 将21个特征保存起来
            for i in range(numFeat-1):
                lineArr.append((float(curLine[i])))
            # print(len(lineArr))
            dataMat.append(lineArr)
            labelMat.append(float(curLine[-1]))
        return dataMat,labelMat
    datArr,labelArr = loadDataSet(r'/home/ach/桌面/machine-learning/ai/Adaboost/7.AdaBoost/horseColicTraining2.txt')
    # print(ada_boost_train_ds(datArr,labelArr,10))
    classifiterArray,aggClassEst = ada_boost_train_ds(datArr,labelArr,100)
    def plot_roc(pred_strengths, class_labels):
        """
        (打印ROC曲线,并计算AUC的面积大小)
        :param pred_strengths: 最终预测结果的权重值
        :param class_labels: 原始数据的分类结果集
        :return:
        """
        import matplotlib.pyplot as plt
        # variable to calculate AUC
        y_sum = 0.0
        # 对正样本的进行求和
        num_pos_class = np.sum(np.array(class_labels) == 1.0)
        # 正样本的概率
        y_step = 1 / float(num_pos_class)
        # 负样本的概率
        x_step = 1 / float(len(class_labels) - num_pos_class)
        # np.argsort函数返回的是数组值从小到大的索引值
        # get sorted index, it's reverse
        sorted_indicies = pred_strengths.argsort()
        # 测试结果是否是从小到大排列
        # 可以选择打印看一下
        # 开始创建模版对象
        fig = plt.figure()
        fig.clf()
        ax = plt.subplot(111)
        # cursor光标值
        cur = (1.0, 1.0)
        # loop through all the values, drawing a line segment at each point
        for index in sorted_indicies.tolist()[0]:
            if class_labels[index] == 1.0:
                del_x = 0
                del_y = y_step
            else:
                del_x = x_step
                del_y = 0
                y_sum += cur[1]
            # draw line from cur to (cur[0]-delX, cur[1]-delY)
            # 画点连线 (x1, x2, y1, y2)
            # print cur[0], cur[0]-delX, cur[1], cur[1]-delY
            ax.plot([cur[0], cur[0] - del_x], [cur[1], cur[1] - del_y], c='b')
            cur = (cur[0] - del_x, cur[1] - del_y)
        # 画对角的虚线线
        ax.plot([0, 1], [0, 1], 'b--')
        plt.xlabel('False positive rate')
        plt.ylabel('True positive rate')
        plt.title('ROC curve for AdaBoost horse colic detection system')
        # 设置画图的范围区间 (x1, x2, y1, y2)
        ax.axis([0, 1, 0, 1])
        plt.show()
        '''
        参考说明:http://blog.csdn.net/wenyusuran/article/details/39056013
        为了计算 AUC ,我们需要对多个小矩形的面积进行累加。
        这些小矩形的宽度是x_step,因此可以先对所有矩形的高度进行累加,最后再乘以x_step得到其总面积。
        所有高度的和(y_sum)随着x轴的每次移动而渐次增加。
        '''
        print("the Area Under the Curve is: ", y_sum * x_step)
    plot_roc(aggClassEst.T,labelArr)
    
    
  • 运行结果:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BloCDxcE-1578039599589)(/home/ach/.config/Typora/typora-user-images/image-20200103161919589.png)]

    Skleanring实现adaboost算法

from sklearn.ensemble import AdaBoostClassifier
from sklearn.tree import  DecisionTreeClassifier
import numpy as np
def loadDataSet(fileName):
    numFeat = len(open(fileName).readline().split('\t'))# 22个列 21个特征 1个标签
    dataMat = [];labelMat = []
    fr = open(fileName)
    for line in fr.readlines():
        lineArr = []
        curLine = line.strip().split('\t')
        # 将21个特征保存起来
        for i in range(numFeat-1):
            lineArr.append((float(curLine[i])))
        # print(len(lineArr))
        dataMat.append(lineArr)
        labelMat.append(float(curLine[-1]))
    return dataMat,labelMat
datArr,labelArr = loadDataSet(r'/home/ach/桌面/machine-learning/ai/Adaboost/7.AdaBoost/horseColicTraining2.txt')
clf = AdaBoostClassifier(DecisionTreeClassifier(max_depth=2,min_samples_split=5,min_samples_leaf=5),n_estimators=40,random_state=10000)
clf.fit(np.mat(datArr),labelArr)
datArr1,labelArr1 = loadDataSet(r'/home/ach/桌面/machine-learning/ai/Adaboost/7.AdaBoost/horseColicTest2.txt')
print(clf.score(np.mat(datArr1),labelArr1))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值