欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

《机器学习实战》Adaboost算法中buildstump函数的最直观详解

程序员文章站 2022-07-01 18:25:44
...

学习《机器学习实战》Adaboost算法时,发现书中对build_stump函数的讲解非常笼统,理解起来有难度,而这个函数又是理解后面完整Adaboost算法的重点与基础,因此写此文详细阐述我对buildstump函数的理解

相关代码详解

单层决策树分类函数stump_classify

def stump_classify(data_mat, dimen, thresh_val, thresh_ineq):
    """
    :param data_mat: Mat据集
    :param dimen: 特征的哪一个列
    :param thresh_val: 特征列要比较的值
    :param thresh_ineq: 分类标志
    :return: np.array
    """
    ret_array = np.ones((np.shape(data_mat)[0], 1))  #将分类向量的值先全部初始化为1

    if thresh_ineq == 'lt':   #若标志为"lt"则把小于阈值的点判定为"-1",否则把大于阈值的点判定为"-1"类
        ret_array[data_mat[:, dimen] <= thresh_val] = -1.0      # data_mat[:, dimen] 表示数据集中第dimen列的所有值
    else:
        ret_array[data_mat[:, dimen] > thresh_val] = -1.0
    return ret_array

建立单层决策树的弱分类器函数buildstump

def buildstump(data_arr, class_labels, D):
    """
    :param data_arr: 特征标签集合
    :param class_labels: 分类标签集合
    :param D: 最初的特征权重值
    :return: bestStump    最优的分类器模型
             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)))   #建立一个初始值全为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):   #第二层循环,即对每一步的跨度,下面的图可以很好的说明,也就是垂直于X轴的一条条直线

            for inequal in ['lt', 'gt']:   #这里太长,见下<1>
                thresh_val = (range_min + float(j) * step_size)  #计算阈值,即为划分轴在x轴上的值
                predicted_vals = stump_classify(data_mat, i, thresh_val, inequal)  #带入stump_classify函数 得到返回的分类列表
                err_arr = np.mat(np.ones((m, 1)))   #先将错误向量的值全部初始化为1
                err_arr[predicted_vals == label_mat] = 0  #若得到的分类列表的值与训练集的类标签值一直,则设置为0
                
                weighted_err = D.T * err_arr   #这里太长,见下<2>
                print("split:第%d列,阈值:%.2f,标志:%s,错误率:%.3f"%(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
                    best_stump['thresh'] = thresh_val
                    best_stump['ineq'] = inequal
    # best_stump 表示分类器的结果,在第几个列上,用大于/小于比较,阈值是多少 (单个弱分类器)
    print("最优分类器为%s\n最小错误率为:%.6f\n最优分类结果为\n%s"%(best_stump, min_err, best_class_est))
    return best_stump, min_err, best_class_est

<1>第三层循环,这里书上说是对每个不等号,太难懂了,其实这里lt的意思是less tan少于,gt的意思是great than 多于,也就是在当前步长的划分下,分别对数据进行"lt"和"gt"的标示,然后将数据集带入stum_classify进行错误测试,lt表示把小于阈值的点判成是"-1"类,gt标示把大于阈值的点判成"-1"类,逐一对比在同一个步长的划分下,哪种标示方法的错误率低

<2>这里是矩阵乘法,要重点说明一下,这个乘法得到的是一个具体的数值,即错误率,例如,当数据集有4行时,因为D=[1/4,1/4,1/4,1/4],若在当前的步长与标志下,err_arr中前3个判定值是正确的,即err_arr=[0,0,0,1],计算得weighted_err=1/4,正好是:判错数目/总样本数,现在能体会到上面设置为1或0的巧妙了吧?另外,weighted_err也是与Adaboost分类器交互的地方,利用它计算每个弱分类器的α值,从而进一步更新特征权重矩阵D,在原有的基础上得到更准确的D,该部分内容可以参考书P117页的讲解,还是比较好理解的

理解了上面的注解后,接下来我们跑一下函数

1.导入数据
import adaboost
datMat,classLabels=adaboost.load_sim_data()
《机器学习实战》Adaboost算法中buildstump函数的最直观详解
2.给特征权重D赋初始值
D = mat(ones( (5,1) / ) 5 )
3.加载buidstump函数
adaboost.buidstump(datMat,classLebels,D)
《机器学习实战》Adaboost算法中buildstump函数的最直观详解

利用图讲接树桩分类器的构建过程,再加深一下大家的理解.

《机器学习实战》Adaboost算法中buildstump函数的最直观详解
如上图,biuldstump其实就是在构建一个单决策树的弱分类器,之所以弱, 是因为你找不到任何一种单层决策树(其实就是与坐标轴平行的分割线)能将数据完全正确的分割开来,在构建过程中,通过对每个特征下的数值,以一定的步长进行遍历(取决于你设置的步数),其实就相当于上图黄线逐渐向右移动,对比移动到哪个位置的时候错误率最低,于是就能得到该数据集下的最优单层决策树,理解了这些,在此基础上,利用错误率计算出→α,再用α计算出新的特征权重矩阵D→D,最后,利用这几次得到几个弱分类器集合,就能得到不错的分类结果了。

最后,Adboost实战代码参考

相关标签: Ai学习