遥感之机器学习树模型-错误率降低剪枝

在前文的基础上,本文继续对决策树中剪枝算法进行讨论,本文讲解错误率降低算法(REP)
遥感之机器学习树集成模型专栏

1 REP算法介绍

错误率降低剪枝法属于后剪枝算法,由Quinlan提出,是一种简单的剪枝方法。
在该方法中,可用的数据被分成两个样例集合:一个训练集用来形成学习到的决策树,一个分离的验证集用来评估这个决策树在后续数据上的精度,确切地说是用来评估修剪决策树的效果。

这种方法的动机是:即使学习器可能会被训练集中的随机错误和巧合规律所误导,但验证集合不大可能表现出同样的随机波动,所以验证集可以用来对过拟合训练集中的虚假特征提供防护检验。

其思路是自底向上,从已经构建好的完全决策树中找出一个子树,然后用子树的根节点代替这棵子树,作为新的叶子节点。叶子节点所表示的类别通过大多数原则确定,这样就构建出一个简化版决策树。然后使用交叉验证数据集来测试简化版本的决策树,看其错误率是不是降低了。如果错误率降低了,则可以用这个简化版的决策树来代替完全决策树,否则还采用原来的决策树。遍历所有的子树,直到针对交叉验证数据集无法进一步降低错误率为止。这虽然是一种有点朴素的修剪方法,但其具有速度快和简单的优点。

该剪枝方法考虑将决策树上的每个分支节点作为修剪的候选对象,决定是否修剪这个分支节点由如下步骤组成:
1)删除以此节点为根的子树;
2)使其成为叶子节点;
3)赋予该叶子节点关联的训练数据的类别为属于此叶子节点的所有样本数据中最常见的分类;
4)当修剪后的树对于验证集合的性能不会比原来的树差时,才真正删除该节点。

训练集合的过拟合使得验证集合数据能够对其进行修正,反复进行上面的操作,自底向上地处理节点,删除那些能够最大限度地提高验证集合的精度的节点,直到进一步修剪有害为止(有害是指修剪会降低验证集合的精度)。
REP是最简单的后剪枝方法之一,不过由于使用了独立的测试集,与原始决策树相比,修改后的决策树可能偏向于过度修剪,这是因为训练数据集中存在的特性在剪枝过程中都被忽略了,当剪枝数据集比训练数据集小得多时,这个问题特别值得注意。尽管REP有这个缺点,不过REP仍然可作为一种基准来评价其他剪枝算法的性能。由于验证集合没有参与决策树的创建,所以用REP剪枝后的决策树对于测试样例的偏差要好很多,能够解决一定程度的过拟合问题。

下面以图3.10为例说明REP方法如何对决策树进行剪枝。图3.11a为剪枝数据集,图3.11b和图3.11c显示的是基于REP方法,使用图3.11a剪枝数据集裁剪决策树的部分过程。根据图3.10,图3.11中的每个节点对剪枝数据集的分类误差在括号中表示。在遍历树的过程中,采用自底向上的方式,该方式可以保证剪枝后的结果是关于剪枝数据集的具有最小误差的最小剪枝树。以图3.11b为例,节点t4本身关于剪枝数据集的误差为0,而它的子树t8和t9的误差之和为1。根据REP算法,则节点t4被转换为叶子,如图3.11c所示,余下的剪枝过程同上。
在这里插入图片描述添加图片注释,不超过 140 字(可选)

2 代码

def pruning_rep(self, X_train, y_train, X_test, y_test):
        """错误率降低剪枝REP
            X_train: 训练集属性值   numpy.array(float)  
            y_train: 训练集目标变量 numpy.array(float)  
            X_test: 训练集属性值   numpy.array(float)  
            y_test: 训练集目标变量 numpy.array(float) 
            return tree: 剪枝后的决策树 dict  
        """
        if self.tree is None:
            return None

        # 处理字符串映射
        X_train_copy, y_train_copy = X_train.copy(), y_train.copy()
        X_test_copy, y_test_copy = X_test.copy(), y_test.copy()
        X_train_copy, y_train_copy = self.__deal_value_map(X_train_copy, y_train_copy)
        X_test_copy, y_test_copy = self.__deal_value_map(X_test_copy, y_test_copy)
        trainSet = np.vstack((X_train_copy.T, y_train_copy)).T
        testSet = np.vstack((X_test_copy.T, y_test_copy)).T

        # REP剪枝
        self.tree = self.__rep_prune(self.tree, trainSet, testSet)
        
        # 返回剪枝后的树
        return self.tree

def __rep_prune(self, Tree, trainSet, testSet):
        """将决策树按照REP规则进行剪枝
            Tree: 当前子树 dict  
            trainSet: 训练集 numpy.array(float)  
            testSet: 训练集目标变量 numpy.array(float)  
            return tree: 剪枝后的决策树 dict  
        """
        # 后序遍历并修剪Tree
        import copy
        Tree_list = []
        labels = self.feature_names.tolist()
        classList = [example[-1] for example in trainSet]
        majorClass = self.majorityCnt(classList)              # 找到数量最多的类别
        firstFeat = list(Tree.keys())[0]                 # 取出tree的第一个键名
        secondDict = Tree[firstFeat]                     # 取出tree第一个键值
        labelIndex = labels.index(firstFeat)             # 找到键名在特征属性的索引值
        for keys in secondDict.keys():                   # 遍历第二个字典的键
            if type(secondDict[keys]).__name__ == 'dict':
                subDataSet = self.splitDataSet(trainSet, labelIndex, keys)  # 划分数据集
                subTreeTemp = secondDict[keys] # 暂存被剪掉的子树
                secondDict[keys] = majorClass # 剪枝成叶节点
                Tree_list.append(copy.deepcopy(self.tree))
                secondDict[keys] = subTreeTemp # 将叶节点还原成子树
                best_tree = self.__rep_prune(secondDict[keys], subDataSet, testSet)
                if best_tree is not None:
                    Tree_list.append(copy.deepcopy(best_tree))
        if len(Tree_list)==0:
            return None
        
        # 选出最优修剪Tree
        cur_tree = self.tree
        best_i, best_accury = -1,-1.0
        X_test = testSet[:, :-1]
        y_test = testSet[:, -1]
        for i in range(1, len(Tree_list)):
            self.tree = Tree_list[i]
            y_pred = self.predict(X_test)
            cnt = np.sum([1 for i in range(len(y_test)) if y_test[i]==y_pred[i]])
            accury = 100.0*cnt/len(y_test)
            if accury > best_accury:
                best_i,best_accury = i,accury
        self.tree = cur_tree

        # 返回剪枝后的树
        return Tree_list[best_i]

3 总结

书中代码细节比较繁琐,了解其思想即可,主要就是递归和迭代的熟练使用。
如果对具体的详细示例有兴趣,可以参考本专栏的参考书目:
<现代决策树模型及其编程实践:从传统决策树到深度决策树 黄智濒>
其电子书和其相关的源码可通过私信咨询获取。
欢迎点赞,收藏,关注,支持小生,打造一个好的遥感领域知识分享专栏。
同时欢迎私信咨询讨论学习,咨询讨论的方向不限于:地物分类/语义分割(如水体,云,建筑物,耕地,冬小麦等各种地物类型的提取),变化检测,夜光遥感数据处理,目标检测,图像处理(几何矫正,辐射矫正(大气校正),图像去噪等),遥感时空融合,定量遥感(土壤盐渍化/水质参数反演/气溶胶反演/森林参数(生物量,植被覆盖度,植被生产力等)/地表温度/地表反射率等反演)以及高光谱数据处理等领域以及深度学习,机器学习等技术算法讨论,**以及相关实验指导/**论文指导等多方面。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值