添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

在使用决策树模型时,如果训练集中的样本数很多,则会使得生成的决策树过于庞大,即分化出了很多的枝节。这时会产生过拟合问题,也就是在模型在训练集上的表现效果良好,而在测试集的效果却很差。因此在生成一棵决策树之后,需要对它进行必要的剪枝,从而提高它的泛化能力。本文将讲述后剪枝算法——REP方法。

剪枝是指将决策树的一些枝节去掉,将中间节点变成叶子节点,该叶子节点的预测值便是该分组训练样本

重新定义树结构

为了方便处理不同节点间的调用,CART回归树的树模型不再用字典进行存储,而改用自定义的类对象(参考leetcode中的树节点),每个节点可以通过成员变量来调用分裂出来的左右节点。

class TreeNode:
    def __init__(self, val, fea_name=None, fea_c=None):
        self.left = None
        self.right = None
        self.val = round(val,2)
        self.fea_name = fea_name  
        self.fea_c = fea_c if fea_c is None else round(fea_c,2)  
    y值的均值,若当前节点是叶子节点,则该变量代表这种分支的预测值,若是中间节点,则可以表示为对该节点进行剪枝后的节点预测值。

生成剪枝后的子树

def sub_tree(tree, num): # 返回后序剪枝得到的子树
    stack = [(False, tree)]
    while stack:
        flag, t = stack.pop()
        if not t:continue
        if flag:
            if t.left or t.right:
                if num==0:
                    t.left = None
                    t.right = None
                    return tree
                else:
                    num -= 1
        else:
            stack.append((True, t))
            stack.append((False, t.right))
            stack.append((False, t.left))
    return tree

采用后序遍历的方式来搜索中间节点,参数

计算中间节点的个数

def mid_leaf_num(tree): 
    if not tree or (not tree.left and not tree.right):
        return 0
    return 1 + mid_leaf_num(tree.left) + mid_leaf_num(tree.right)

效果比较函数

def ifmore(self, temp_tree, test_x, test_y):
    orig_ = []
    temp_ = []
    for i in range(len(test_x)):
        orig_.append(self.check(self.tree, test_x[i]))
        temp_.append(self.check(temp_tree, test_x[i]))
    orig_sum = sum(np.power(np.array(orig_)-test_y, 2))
    temp_sum = sum(np.power(np.array(temp_)-test_y, 2))
    if orig_sum>temp_sum: # and (orig_sum-temp_sum)/orig_sum>0.0001:
        self.tree = temp_tree
        return True
    else:
        return False

REP剪枝函数

def prune_tree(self, test_x, test_y):
    mid_num = mid_leaf_num(self.tree)
    i = 0
    while i<mid_num: 
        temp_tree = sub_tree(self.tree, i)
        if self.ifmore(temp_tree, test_x, test_y):
            i = 0
            mid_num -= 1
        else:
            i += 1

实例化演示

X,y = make_regression(n_samples=1000, n_features=4, noise=0.1)
X_name = np.array(list('abcd'))
clf = Tree_Regress()
train_x, test_x, train_y, test_y = train_test_split(X, y, test_size=0.30)
train_size = len(train_x)//4
train_test_x, train_test_y = train_x[:train_size], train_y[:train_size]
train_train_x, train_train_y = train_x[train_size:], train_y[train_size:]
print('不剪枝')
clf.fit(train_x, train_y, X_name)
predict_y = clf.predict(test_x)
pre_error = sum(np.power(test_y-predict_y,2))
print('误差为:', pre_error,' 节点数:', clf.node_num())
print('有剪枝')
clf.fit(train_train_x, train_train_y, X_name)
clf.prune_tree(train_test_x, train_test_y)
predict_y = clf.predict(test_x)
pre_error = sum(np.power(test_y-predict_y,2))
print('误差为:', pre_error,' 节点数:', clf.node_num())
# 不剪枝
# 误差为: 9860.41223121167  节点数: 249
# 有剪枝
# 误差为: 6892.04066950184  节点数: 33

在对模型进行后剪枝之后,模型的泛化能力有所提升。

REP方法虽然在一定程度上简化了决策树,提高了模型的性能,但在有些情况下反而会造成相反的结果,使得模型表现更差。我试了几个不同的数据集,发现效果其实不是很好,可能是这个方法考虑到的东西比较少,它单单考虑到了模型拟合的误差平方和,却没考虑生成的节点个数,粗暴地将影响模型性能的枝节都剪去,使得模型太过简单。另外,该方法的计算开销是很大的,需要遍历搜索两次中间节点。

----end---- 背景在使用决策树模型时,如果训练集中的样本数很多,则会使得生成的决策树过于庞大,即分化出了很多的枝节。这时会产生过拟合问题,也就是在模型在训练集上的表现效果良好,而在测试集的效果却很差。因此在生成一棵决策树之后,需要对它进行必要的剪枝,从而提高它的泛化能力。本文将讲述后剪枝算法——REP方法。原理剪枝是指将决策树的一些枝节去掉,将中间节点变成叶子节点,该叶子节点的预测值便是该分组训练样本yyy值的均值。剪枝算法分为预剪枝和后剪枝,预剪枝是在决策树生成的过程中同步进行,而后剪枝是在决策树生成完之后再剪枝
决策树的剪枝什么是决策树的剪枝?为什么要剪枝剪枝策略的分类预剪枝优缺点后剪枝剪枝算法的分类优缺点奥卡姆剃刀定律预告andTODOReference 什么是决策树的剪枝? 对比日常生活中,环卫工人在大街上给生长茂密的树进行枝叶的修剪。在机器学习的决策树算法中,有对应的剪枝算法。将比较复杂的决策树,化简为较为简单的版本,并且不损失算法的性能。 为什么要剪枝剪枝是决策树算法防止过拟合的一种手段,...
图1 未剪枝前的决策树 基于上图,后剪枝首先考虑纹理,若将其领衔的分支剪除,则相当于把纹理替换成叶节点,替换后的叶节点包含编号{7,15},于是,该叶节点的类别标记为“好瓜”,此时决策树验证集的精确度提升至57.1%。于是后剪枝策略决定剪枝
当我们输入的原始数据有较多的变量时,通过决策树算法生成的决策树可能会非常的庞大。这样的一颗决策树在训练集上有很好的表现,但是在测试集上的表现往往不甚理想,这样的问题也被叫做过拟合问题。面对这样的问题,我们一般所采用的方法是对决策树进行剪枝操作。 决策树的过拟合问题 决策树算法生成的决策树非常庞大,每个变量都被详细地考虑过。在每一个叶节点上,只要继续分支会有信息增益的情况,不管信息增益有多大,都会进...
数据集:西瓜数据集2.0共17条数据 训练集(用来建立决策树):西瓜数据集2.0中的第1,2,3,6,7,10,14,15,16,17,4 请注意,书上说是10条,其实是上面列出的11条。 验证集(用来对决策树剪枝):西瓜数据集2.0中的5,8,9,11,12,13 注意书上特指了其中一些数据集,不可自己随意更改建造决策树的数据集,否则出不来书上的效果   根据交易单为(T1,T2,T3,T4,T5,T6,T7,T8,T9),每笔交易的货物清单为{{I1,I2,I5},{I2,I4},{I2,I3},{I1,I2,I4},{I1,I3},{I2,I3},{I1,I3},{I1,I2,I3,I5},{I1,I2,I3}},编写代码得到关联规则。 1. 获取数据集 data_set = [['I1', 'I2', 'I5'], ['I2', 'I4'], ['I2', 'I3'], ['I1', 'I2', 'I4'], ['I1', 'I3'], context = zmq.Context() socket = context.socket(zmq.REQ) socket.connect("tcp://localhost:5555") while True: message = input("请输入要发送的消息:") socket.send_string(message) response = socket.recv() print("接收到的响应为:{}".format(response.decode())) REP端代码: ```python import zmq context = zmq.Context() socket = context.socket(zmq.REP) socket.bind("tcp://*:5555") while True: message = socket.recv() print("接收到的消息为:{}".format(message.decode())) response = input("请输入要回复的消息:") socket.send_string(response) 在这个示例中,REQ端通过创建一个ZMQ的Context对象和一个REQ类型的socket对象来与REP端通信。它使用socket.connect()方法来连接到REP端,然后通过socket.send_string()方法发送消息,使用socket.recv()方法接收响应。 REP端通过创建一个ZMQ的Context对象和一个REP类型的socket对象来接收REQ端的消息。它使用socket.bind()方法将socket对象绑定到一个特定的IP地址和端口号,然后使用socket.recv()方法接收REQ端发送的消息,并使用input()方法来回复消息,最后使用socket.send_string()方法将回复发送回REQ端。 IllegalArgumentException: Property ‘sqlSessionFactory‘ or ‘sqlSessionTemplate‘ are require 报错
IllegalArgumentException: Property ‘sqlSessionFactory‘ or ‘sqlSessionTemplate‘ are require 报错 要奶茶也要啵啵吖: 跟你的一样,可是我开了数据库 IllegalArgumentException: Property ‘sqlSessionFactory‘ or ‘sqlSessionTemplate‘ are require 报错 一根小草飘呀飘: 好兄弟,饱受其苦哈哈哈 IllegalArgumentException: Property ‘sqlSessionFactory‘ or ‘sqlSessionTemplate‘ are require 报错 使用idea将web项目打成war包