目录:

1. 引言

2. 回归树

    2.1 决策树简介

    2.2 理论解释

    2.3 算法流程

3. 回归树示例

4. 完整的代码示例

    4.1 根据图3的训练数据用Python3实现二叉回归树

    4.2 用sklearn实现二叉回归树

5. 关于回归树的若干问题思考

6. 总结

7. Reference

1. 引言

在全民人工智能时代下,机器学习算法已经成为研究和应用的热点。目前,最流行的两类算法莫过于神经网络算法(卷积神经网络、循环神经网络、生成式对抗网络和图神经网络)树形算法(随机森林、GBDT、XGBoost和LightGBM)。树形算法的基础就是决策树,由于其易理解、易构建、速度快等特点,被广泛的应用在数据挖掘、机器学习等领域。因此,决策树是经典的机器学习算法,很多复杂的机器学习算法都是由决策树演变而来。对于决策树的学习,是我们机器学习课程中非常重要的一个环节。

根据处理数据类型的不同,决策树又分为两类:分类决策树与回归决策树。分类决策树可用于处理离散型数据,回归决策树可用于处理连续型数据。

由于我在学习GBDT算法时,了解到GBDT中的树是回归树,但是在之前的学习中对于回归树了解比较少,这直接影响我对GBDT算法原理的理解。因此,本文首先简单介绍回归树,然后详细介绍CART回归树算法及流程,其次会给出完整的示例以加深理解,最后会讨论ID3、C4.5能不能用来做回归问题,及讨论回归树的研究进展。

2. 回归树

2.1 决策树简介

决策树是一种基本的分类与回归方法。决策树由结点(node)和有向边(diredcted edge)组成。结点有两种类型:内部结点(internal node)和叶结点(leaf node)。内部结点表示一个特征或属性,叶结点表示一个类别或者某个值。

图1:决策树模型

用决策树做分类或回归任务时,从根节点开始,对样本的某一特征进行测试,根据测试结果,将样本分配到其子结点;这时,每一个子节点对应着该特征的一个取值。如此递归地对样本进行测试并分配,直至到达叶结点。

其实,决策树是将空间用超平面进行划分的一种方法,每次分割的时候,都将当前的空间根据特征的取值进行划分, 这样使得每一个叶子节点都是在空间中的一个不相交的区域,在进行决策的时候,会根据输入样本每一维特征的值,一步一步往下,最后使得样本落入N个区域中的一个(假设有N个叶子节点)。


图2:ID3、C4.5和CART算法比较

分类回归树(Classification and Regression Tree, CART)模型由Breiman等人在1984年提出,是应用广泛的决策树学习方法。CART同样由特征选择、树的生成及剪枝组成,既可以用于分类也可以用于回归。图2给出了三种比较常见决策树的一个比较总结,希望可以帮助大家更好的理解。我们这里重点介绍一下CART回归树算法。

2.2 理论解释

假设X和Y分别为输入和输出变量,并且Y是连续变量,给定训练数据集考虑如何生成回归树。既然是回归树,那么必然会存在以下两个核心问题:

一个回归树对应着输入空间(即特征空间)的一个划分以及在划分的单元上的输出值。假设已将输入空间划分为M个单元,并且在每个单元上有一个固定的输出值,于是回归树模型可以表示为:

当输入空间的划分确定时,可以用平方误差来表示回归树对于训练数据的预测误差,用平方误差最小的准则求解每个单元上的最优输出值。易知,单元上的的最优值上的所有输入实例对应的输出的均值,即:

(1)问题1:怎样对输入空间进行划分?即如何选择划分点?

CART回归树采用启发式的方法对输入空间进行划分,选择第j个变量和它取的值s,作为切分变量(splitting variable)和切分点(splitting point),并定义两个区域:

然后寻找最优切分变量j和最优切分点s。具体地,求解:

对固定输入变量j可以找到最优切分点s。

(2)问题2:如何决定树中叶节点的输出值?

用选定的最优切分变量j和最优切分点s划分区域并决定相应的输出值:

遍历所有输入变量,找到最优的切分变量j,构成一个对(j, s)。依此将输入空间划分为两个区域。接着,对每个区域重复上述划分过程,直到满足停止条件为止。这样就生成一颗回归树。这样的回归树通常称为最小二乘回归树(least squares regression tree)。

如果已将输入空间划分为M个区域,并且在每个区域上有一个固定的输出值,于是回归树模型可以表示为:

2.3 算法流程

3. 回归树示例

本示例来源于李航著的《统计学习方法》第5章决策树习题中的5.2题。已知如图3所示的训练数据,试用平方误差损失准则生成一个二叉回归树。

图3:训练数据表

寻找最优切分变量j和最优切分点s的方法为:

其中,

例如,取s=1。此时,这两个区域的输出值分别为:

根据上面的计算方法,可以得到下表:

的值代入到均方差中,如下:

同理,可以获得下表:

显然取s=5时,m(s)最小。因此,第一个最优切分变量为j=x、最优切分点为s=5。

1)用选定的(j,s)划分区域,并决定输出值:

两个划分的区域分别是:

输出值用公式:

得到

2)对两个子区域继续调用算法流程中的步骤(1),(2):

继续进行划分:

取切分点分别为:[1, 2, 3, 4, 5],则各个区域的输出值c如下表:

计算m(s):

s=3时,m(3)最小。之后的递归过程同上,我就不在赘述啦!最后,如图4所示给出完整的二叉回归树。

图4:一棵完整的二叉回归树


4. 完整的代码示例

本篇文章所有数据集和代码均在我的GitHub中,地址:https://github.com/Microstrong0305/WeChat-zhihu-csdnblog-code/tree/master/Decision%20Tree/lihang_5.2

4.1 根据图3的训练数据用Python3实现二叉回归树

from numpy import *
def loadDataSet(fileName): dataMat = [] fr = open(fileName) for line in fr.readlines(): curLine = line.strip().split('\t') fltLine = list(map(float, curLine)) dataMat.append(fltLine)    return dataMat
def binSplitDataSet(dataSet, feature, value): mat0 = dataSet[nonzero(dataSet[:, feature] > value)[0], :] mat1 = dataSet[nonzero(dataSet[:, feature] <= value)[0], :]     return mat0, mat1
def regLeaf(dataSet):     return mean(dataSet[:, -1])
def regErr(dataSet):     return var(dataSet[:, -1]) * shape(dataSet)[0]
def chooseBestSplit(dataSet, leafType=regLeaf, errType=regErr, ops=(0, 1)): tolS = ops[0] tolN = ops[1] if len(set(dataSet[:, -1].T.tolist()[0])) == 1: return None, leafType(dataSet) m, n = shape(dataSet) S = errType(dataSet) bestS = inf bestIndex = 0 bestValue = 0 for featIndex in range(n - 1): for splitVal in set((dataSet[:, featIndex].T.A.tolist())[0]): mat0, mat1 = binSplitDataSet(dataSet, featIndex, splitVal) if (shape(mat0)[0] < tolN) or (shape(mat1)[0] < tolN): continue newS = errType(mat0) + errType(mat1) if newS < bestS: bestIndex = featIndex bestValue = splitVal bestS = newS if (S - bestS) < tolS: return None, leafType(dataSet) mat0, mat1 = binSplitDataSet(dataSet, bestIndex, bestValue) if (shape(mat0)[0] < tolN) or (shape(mat1)[0] < tolN): return None, leafType(dataSet)    return bestIndex, bestValue  
def createTree(dataSet, leafType=regLeaf, errType=regErr, ops=(0, 1)): feat, val = chooseBestSplit(dataSet, leafType, errType, ops) if feat == None: return val retTree = {} retTree['spInd'] = feat retTree['spVal'] = val lSet, rSet = binSplitDataSet(dataSet, feat, val) retTree['left'] = createTree(lSet, leafType, errType, ops) retTree['right'] = createTree(rSet, leafType, errType, ops)    return retTree    if __name__ == "__main__": myDat = mat(loadDataSet('5.2test.txt')) print(createTree(myDat))
import matplotlib.pyplot as plt
plt.plot(myDat[:, 0], myDat[:, 1], 'ro') plt.show()

创建的二叉回归树为:

4.2 用sklearn实现二叉回归树

import numpy as npimport matplotlib.pyplot as pltfrom sklearn.tree import DecisionTreeRegressorfrom sklearn import linear_model
x = np.array(list(range(1, 11))).reshape(-1, 1)y = np.array([4.50, 4.75, 4.91, 5.34, 5.80, 7.05, 7.90, 8.23, 8.70, 9.00]).ravel()
model1 = DecisionTreeRegressor(max_depth=1)model2 = DecisionTreeRegressor(max_depth=3)model3 = linear_model.LinearRegression()model1.fit(x, y)model2.fit(x, y)model3.fit(x, y)
X_test = np.arange(0.0, 10.0, 0.01)[:, np.newaxis]y_1 = model1.predict(X_test)y_2 = model2.predict(X_test)y_3 = model3.predict(X_test)
plt.figure()plt.scatter(x, y, s=20, edgecolor="black", c="darkorange", label="data")plt.plot(X_test, y_1, color="cornflowerblue", label="max_depth=1", linewidth=2)plt.plot(X_test, y_2, color="yellowgreen", label="max_depth=3", linewidth=2)plt.plot(X_test, y_3, color='red', label='liner regression', linewidth=2)plt.xlabel("data")plt.ylabel("target")plt.title("Decision Tree Regression")plt.legend()plt.show()

用sklearn实现了回归树与线性回归的比较,结果如下图所示:

5. 关于回归树的若干问题

(1)CART实现分类树与回归树的区别?

CART分类树是一种二分递归分割的技术,分割方法采用基于最小距离的基尼指数估计函数,将当前的样本集分为两个子样本集,使得生成的的每个非叶子节点都有两个分支。因此,CART算法生成的决策树是结构简洁的二叉树。

CART分类树是针对目标变量是离散型变量,通过二叉树将数据进行分割成离散类的方法。而回归树则是针对目标变量是连续性的变量,通过选取最优分割特征的某个值,然后数据根据大于或者小于这个值进行划分进行树分裂最终生成回归树。

(2)树形结构为什么不需要归一化?

因为数值缩放不影响分裂点位置,对树模型的结构不造成影响。按照特征值进行排序的,排序的顺序不变,那么所属的分支以及分裂点就不会有不同。而且,树模型是不能进行梯度下降的,因为构建树模型(回归树)寻找最优点时是通过寻找最优分裂点完成的,因此树模型是阶跃的,阶跃点是不可导的,并且求导没意义,也就不需要归一化。

既然树形结构(如决策树、RF)不需要归一化,那为何非树形结构比如Adaboost、SVM、LR、KNN、K-Means之类则需要归一化?

对于线性模型,特征值差别很大时,运用梯度下降的时候,损失等高线是椭圆形,需要进行多次迭代才能到达最优点。但是如果进行了归一化,那么等高线就是圆形的,促使SGD往原点迭代,从而导致需要的迭代次数较少。

(3)决策树如何剪枝?

决策树的剪枝基本策略有预剪枝 (Pre-Pruning)和后剪枝 (Post-Pruning)。

在第3节回归树的示例中,我没有对生成的二叉回归树进行剪枝,感兴趣的同学可以自己尝试实现预剪枝和后剪枝,来避免生成的二叉回归树过拟合。

(4)树分裂的终止条件?

有了选取分割特征和最佳分割点的方法,树便可以依此进行分裂,但是分裂的终止条件是什么呢?

(5)ID3和C4.5能不能用来回归?

CART 是一棵二叉树,那么只要回归树不是一棵二叉树,那么就不是 CART 树了。

在分类问题中,ID3、C4.5 和 CART 的区别就在于划分子节点的策略不同,信息增益、信息增益比、基尼指数;而在回归问题中,用平方误差最小的准则求解每个特征上的最优输出值,这种情况下,分类时的 ID3、C4.5、CART 之间的区别就没了,那么就是每个父节点划分成多少个子节点的问题了,如果还是二叉树,那么就认为是 CART 回归树,否则就不是了。

如果同一个时刻对某一个特征选择两个切分点来划分父节点,那么将产生三个区间,这种做法无疑增大了遍历的难度,如果选择更多个切分点,那么遍历的难度会指数上升。如果我们想要细分多个区域,让 CART 回归树更深即可,这样遍历的难度会小很多。所以,固然可以构建非 CART 回归树,但是不如 CART 回归树来的更简单。

6. 总结

实际上,回归树总体流程类似于分类树,分枝时穷举每一个特征的每一个阈值,来寻找最优切分特征和最优切分点,衡量的方法是平方误差最小化。分枝直到达到预设的终止条件为止。

当然,在处理具体的实际问题时,使用单一的回归树肯定是不够的。我们可以利用集成学习中的boosting框架,对回归树进行改良升级,得到的新模型就是提升树(Boosting Decision Tree),在进一步改造,就可以得到梯度提升树(Gradient Boosting Decision Tree,GBDT),再进一步可以升级为XGBoost或者LightGBM。我们在学习这些模型时,可以把它们归为一个学习系列,这样便于我们系统理解模型进展。

7. Reference

【1】《统计学习方法》,李航著,P55-P75。

【2】《机器学习实战》,Peter Harrington著,李锐、李鹏、曲亚东、王斌译,P159-P182。

【3】Regression Tree 回归树,地址:https://blog.csdn.net/weixin_40604987/article/details/79296427

【4】机器学习笔记十二:分类与回归树CART,地址:https://blog.csdn.net/xierhacker/article/details/64439601

【5】回归树(Regression Tree),地址:https://www.cnblogs.com/wuliytTaotao/p/10724118.html

【6】机器学习算法实践-树回归 - 少整酱的文章 - 知乎https://zhuanlan.zhihu.com/p/30744760

【7】sklearn实现决策树,地址:http://www.siyuanblog.com/?p=821

【8】决策树学习笔记(三):CART算法,决策树总结,地址:https://ask.hellobi.com/blog/ai_shequ/19290

【9】决策树(ID3 & C4.5 & CART)及正则剪枝, 地址:http://www.mayexia.com/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/%E5%86%B3%E7%AD%96%E6%A0%91(ID3%20&%20C4.5%20&%20CART)%E5%8F%8A%E6%AD%A3%E5%88%99%E5%89%AA%E6%9E%9D/

【10】李航 统计学习方法 第五章 决策树 课后 习题 答案,地址:https://blog.csdn.net/familyshizhouna/article/details/72551841

【11】NLP-LOVE/ML-NLP , GitHub,https://github.com/NLP-LOVE/ML-NLP/tree/master/Machine%20Learning/3.Desition%20Tree#134-%E4%B8%89%E7%A7%8D%E4%B8%8D%E5%90%8C%E7%9A%84%E5%86%B3%E7%AD%96%E6%A0%91