文章主要参考以下博客:决策树算法原理(上) - 刘建平Pinard - 博客园
最近心情比较糟糕,对未来比较迷茫。将自己这几年擅长的东西整理记录下来,希望能够为自己带来好运,也希望能够帮助有需要的人。谢谢。
决策树算法是机器学习中经典且重要的一个算法。它既可以用于分类,也可用于回归,同时也是很多复杂集成模型的基模型。所以掌握决策树模型算是机器学习很好的入门了。
一、决策树简介
决策树由节点和有向边组成。节点有两种类型:内部节点(根节点、中间节点)和叶节点。内部节点表示一个特征或属性,叶节点表示一个类别或者某个值。
决策树算法的核心是要解决两个问题:
1) 如何从数据表中找出最佳节点和最佳分枝?
2) 如何让决策树停止生长,防止过拟合?
决策树主要分为:ID3、C4.5、CART三种,其中scikit-learn使用了优化版的CART算法作为其决策树的实现。
二、决策树ID3算法
ID3决策树使用信息增益大小来判断当前节点应该用什么特征来构建决策树,用计算出的信息增益最大的特征来建立决策树的当前节点。
样本D在特征A上的信息增益等于样本D的熵减去样本D在特征A下的条件熵。
熵是度量了事物的不确定性,越不确定的事物,它的熵越大。
ID3决策树不足:
1) 没有考虑连续值特征;
2) 采用信息增益最大作为最优节点选择不完善。相同条件下,取值较多的特征比取值少的特征信息增益大;
3) 对于有缺失值的情况没有考虑;
4) 没有考虑过拟合问题;
三、决策树C4.5算法
决策树C4.5算法的出现就是为了拟补ID3算法的四个主要不足。一是不能处理连续特征,二是信息增益作为标准容易偏向于取值较多特征,三是缺失值处理问题,四是过拟合问题。
1、不能处理连续特征,C4.5思路是将连续特征离散化。具体操作为:将连续特征值从小到大排列取相邻两样本值平均数作为划分点,分别计算各点作为二元分类点时的信息增益,选择信息增益最大的点作为该连续特征的二元离散分类点。需要注意:与离散属性不同,如果当前节点为连续属性,则该属性后面还可以参与子节点的产生选择过程。
2、信息增益作为标准容易偏向于取值较多的特征的问题,C4.5引入一个信息增益比替换信息增益作为最优节点的选择。信息增益比是信息增益和特征熵的比值,特征数越多的特征对应的特征熵越大。
3、缺失值处理的问题,主要包含:
一是如何在属性值缺失的情况下选择最优划分属性?对于有缺失值的属性,其信息增益就是无缺失值样本所占的比例乘以无缺失值样本子集的信息增益。
二是选定了划分属性,若样本在该属性上的值是缺失的,那么该如何对这个样本进行划分?将缺失值样本按不同的概率划分到了所有分支中,而概率则等于无缺失值样本在每个分支中所占的比例。
三是决策树构造完成后,如果测试样本的属性值不完整,该如何确定该样本的类别?如果分类进入某个属性值未知的分支节点时,探索所有可能的分类结果并把所有结果结合起来考虑,选择概率最高的类别作为预测类别。
4、过拟合问题,C4.5引入了正则化系数进行初步的剪枝。剪枝是决策树中重要的消除过拟合方法,下面会详细讲解。
C4.5决策树不足:
1) 决策树算法非常容易过拟合,因此对于生成的决策树必须要进行剪枝。剪枝的算法有非常多,C4.5的剪枝方法有优化的空间。
剪枝的思路主要是两种,一种是预剪枝,即在生成决策树的时候就决定是否剪枝。另一个是后剪枝,即先生成决策树,再通过交叉验证来剪枝。
2) C4.5生成的是多叉树,效率比二叉树低。如果采用二叉树,可以提高效率。
3) C4.5只能用于分类,而不能用于回归,存在局限性。
4) C4.5使用了熵模型,里面有大量耗时的对数运算,如果是连续值还有大量的排序运算。如果能够加以模型简化可以减少运算强度但又不牺牲太多准确性的话就更好了。
四、决策树CART算法
上面提到C4.5决策树存在如下问题:模型是用较为复杂的熵来度量,使用了相对较为复杂的多叉树,只能处理分类不能处理回归,剪枝方法存在优化点等。CART算法对于这些问题作了大部分改进。下面先从CART分类树算法开始,重点比较和C4.5算法的不同点;接着介绍CART回归树算法,重点介绍和CART分类树的不同点。
1、CART分类树算法
CART分类树算法最优特征选择方法采用了基尼系数来代替信息增益/信息增益比,基尼系数代表了模型的不纯度,基尼系数越小,则不纯度越低,特征越好。这和信息增益(比)是相反的。
对于CART分类树连续值的处理问题,其思想和C4.5是相同的,都是将连续的特征离散化,唯一的区别在于在选择划分点时的度量方式不同,C4.5使用的是信息增益比,则CART分类树使用的是基尼系数。要注意的是,与ID3或者C4.5处理离散属性不同的是,如果当前节点为连续属性,则该属性后面还可以参与子节点的产生选择过程。
对于CART分类树离散值的处理问题,采用的思路是不停的二分离散特征。ID3或者C4.5建立的是多叉树,但是CART分类树采用的是不停的二分,建立二叉树。
2、CART回归树算法
如果样本输出是离散值,那么这是一颗分类树。如果样本输出是连续值,那么那么这是一颗回归树。
CART回归树和CART分类树的建立和预测的区别主要有下面两点:
1) 连续值的处理方法不同
2) 决策树建立后做预测的方式不同
对于连续值的处理,CART分类树采用的是基尼系数的大小来度量特征的各个划分点的优劣。CART回归树使用了常见的和方差的度量方式,对于任意划分特征A,对应的任意划分点s两边划分成的数据集D1和D2,求出使D1和D2各自集合的均方差最小,同时D1和D2的均方差之和最小所对应的特征和特征值划分点。
对于决策树建立后做预测的方式,CART分类树采用叶子节点里概率最大的类别作为当前节点的预测类别;回归树输出不是类别,它采用的是用最终叶子的均值或者中位数来预测输出结果。
五、CART树算法的剪枝
CART回归树和CART分类树的剪枝策略除了在度量损失的时候一个使用均方差,一个使用基尼系数,算法基本完全一样。
CART采用的办法是后剪枝法,即先生成决策树,然后产生所有可能的剪枝后的CART树,然后使用交叉验证来检验各种剪枝的效果,选择泛化能力最好的剪枝策略。
CART树的剪枝算法可以概括为两步,第一步是从原始决策树生成各种剪枝效果的决策树,第二部是用交叉验证来检验剪枝后的预测能力,选择泛化预测能力最好的剪枝后的数作为最终的CART树。
六、决策树算法小结
算法 | 支持模型 | 树结构 | 特征选择 | 连续值处理 | 缺失值处理 | 剪枝 |
ID3 | 分类 | 多叉树 | 信息增益 | 不支持 | 不支持 | 不支持 |
C4.5 | 分类 | 多叉树 | 信息增益比 | 支持 | 支持 | 支持 |
CART | 分类,回归 | 二叉树 | 基尼系数,均方差 | 支持 | 支持 | 支持 |
七、决策树算法优缺点
决策树优点:
1) 模型简单直观。
2) 基本不需要预处理,不需要提前归一化,处理缺失值。
3) 既可以处理离散值也可以处理连续值。很多算法只是专注于离散值或者连续值。
4) 可以处理多维度输出的分类问题。
5) 相比于神经网络之类的黑盒分类模型,决策树在逻辑上可以得到很好的解释
6) 可以交叉验证的剪枝来选择模型,从而提高泛化能力。
7) 对于异常点的容错能力好,健壮性高。
决策树缺点:
1) 决策树算法非常容易过拟合,导致泛化能力不强。可以通过设置节点最少样本数量和限制决策树深度来改进。
2) 决策树会因为样本发生一点点的改动,就会导致树结构的剧烈改变。这个可以通过集成学习之类的方法解决。
3) 寻找最优的决策树是一个NP难的问题,一般通过启发式方法,容易陷入局部最优。可以通过集成学习之类的方法来改善。
4) 有些比较复杂的关系,决策树很难学习,比如异或。这个就没有办法了,一般这种关系可以换神经网络分类方法来解决。
5) 如果某些特征的样本比例过大,生成决策树容易偏向于这些特征。这个可以通过调节样本权重来改善。
八、sklearn中的决策树
sklearn中决策树的类都在模块sklearn.tree下,该模块共包含五个类:
tree.DecisionTreeClassifier | 分类树 |
tree.DecisionTreeRegressor | 回归树 |
tree.export_graphviz | 将生成的决策树导出为DOT格式,画图专用 |
tree.ExtraTreeClassifier | 高随机版本的分类树 |
tree.ExtraTreeRegressor | 高随机版本的回归 |
sklearn的建模流程下,分类树对应的代码是:
from sklearn import tree # 导入需要的模块
clf = tree.DecisionTreeClassifier() # 实例化
clf = clf.fit(X_train,y_train) # 用训练集数据训练模型
result = clf.score(X_test,y_test) # 导入测试数据,从接口中调用需要的信息
clf.apply(X_test) # apply返回每个测试样本所在的叶子节点的索引
clf.predict(X_test) # predict返回每个测试样本的分类/回归结果
DecisionTreeClassifier:
八个参数:分枝参数(criterion),两个随机性参数(random_state,splitter),五个剪枝参数(max_depth,min_samples_split,min_sample_leaf,max_feature,min_impurity_decrease)
一个属性:feature_importances_
四个接口:fit,score,apply,predict
class sklearn.tree.DecisionTreeClassifier(
criterion='gini' # 计算不纯度的方法:输入'entropy',使用信息熵;输入'gini',使用基尼系数
,splitter='best' # 用来控制决策树中的随机选项:输入'best',选择更重要的特征进行分枝;输入'random',在分枝时会更加随机
,max_depth=None # 限制树的最大深度,超过限定深度的树枝全部剪掉
,min_samples_split=2 # 节点必须包含该样本数才允许被分枝叶,否则不被允许分枝
,min_samples_leaf=1 # 限制节点在分枝后的每个子节点所包含的样本数,少于这个数分枝不会发生
,min_weight_faction_leaf=0.0 # 基于权重的剪枝参数
,max_features=None # 限制分枝时考虑的特征个数
,random_state=None # 设置分枝中的随机模式参数
,max_leaf_nodes=None
,min_impurite_decrease=0.0 # 限制信息增益的大小,信息增益小于设定数值的分枝不会发生
,min_impurity_split=None
,class_weight=None # 样本标签平衡参数
,presort=False)
DecisionTreeRegressor:
几乎所有参数,属性及接口都和分类树一摸一样。需要注意的是,在回归树中,没有标签分布是否均衡问题,因此没有class_weight这样的参数。
class sklearn.tree.DecisionTreeRegressor(
criterion='mse' # 回归树衡量分枝质量的指标:输入'mse',使用均方误差;输入'friedman_mse',使用菲尔德曼均方误差;输入'mae',使用绝对平均误差MAE
,splitter='best'
,max_depth=None
,min_samples_split=2
,min_samples_leaf=1
,min_weight_faction_leaf=0.0
,max_features=None
,random_state=None
,max_leaf_nodes=None
,min_impurite_decrease=0.0
,min_impurity_split=None
,presort=False)