决策树

文章目录

  • 决策树
  • 概述
  • sklearn中的决策树
  • sklearn的基本建模流程
  • 分类树
  • DecisionTreeClassifier
  • 重要参数说明
  • criterion
  • random_state & splitter
  • 剪枝参数
  • 目标权重参数
  • 重要属性和接口
  • 回归树
  • DecisionTreeRegressor
  • 重要属性,参数及接口
  • 一维回归的图像绘制
  • 多输出
  • 决策树的优缺点
  • 使用技巧
  • 决策树算法: ID3, C4.5, C5.0 和 CART
  • sklearn官网地址:http://scikit-learn.org/stable/index.html

    概述

    决策树(Decision Tree)是一种非参数的有监督学习方法,它能够从一系列有特征和标签的数据中总结出决策规则,并用树状图的结构来呈现这些规则,以解决分类和回归问题。决策树算法容易理解,适用各种数据,在解决各种问题时都有良好表现,尤其是以树模型为核心的各种集成算法,在各个行业和领域都有广泛的应用。

    我们来简单了解一下决策树是如何工作的。决策树算法的本质是一种图结构,我们只需要问一系列问题就可以对数据进行分类了。比如说,来看看下面这组数据集,这是一系列已知物种以及所属类别的数据:

    1632461708629

    我们现在的目标是,将动物们分为哺乳类和非哺乳类。那根据已经收集到的数据,决策树算法为我们算出了下面的这棵决策树:

    1632461745335

    假如我们现在发现了一种新物种Python,它是冷血动物,体表带鳞片,并且不是胎生,我们就可以通过这棵决策树来判断它的所属类别。

    可以看出,在这个决策过程中,我们一直在对记录的特征进行提问。最初的问题所在的地方叫做根节点,在得到结论前的每一个问题都是中间节点,而得到的每一个结论(动物的类别)都叫做叶子节点。

    关键概念:节点

  • 根节点:没有进边,有出边。包含最初的,针对特征的提问。
  • 中间节点:既有进边也有出边,进边只有一条,出边可以有很多条。都是针对特征的提问。
  • 叶子节点:有进边,没有出边,每个叶子节点都是一个类别标签。
  • 子节点和父节点:在两个相连的节点中,更接近根节点的是父节点,另一个是子节点。
  • 决策树算法的核心是要解决两个问题:

    1. 如何从数据表中找出最佳节点和最佳分枝?
    2. 如何让决策树停止生长,防止过拟合?

    sklearn中的决策树

    sklearn中决策树的类都在”tree“这个模块之下,这个模块总共包含五个类:

    tree.DecisionTreeClassifier 分类树
    tree.DecisionTreeRegressor 回归树
    tree.export_graphviz 将生成的决策树导出为DOT格式,画图专用
    tree.ExtraTreeClassifier 高随机版本的分类树
    tree.ExtraTreeRegressor 高随机版本的回归树

    详细可以参考:决策树

    sklearn的基本建模流程

    1632462144005

    案例

    from sklearn import tree #导入需要的模块
    clf = tree.DecisionTreeClassifier() #实例化模型对象
    clf = clf.fit(X_train,y_train) #用训练集数据训练模型
    result = clf.score(X_test,y_test) #对我们训练的模型精度进行打分
    

    分类树

    DecisionTreeClassifier

    class sklearn.tree.DecisionTreeClassifier (
    criterion=’gini’, splitter=’best’, max_depth=None,min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_features=None,random_state=None, max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None,class_weight=None, presort=False
    )
    

    重要参数说明

    criterion

    criterion
    这个参数正是用来决定不纯度的计算方法的。sklearn提供了两种选择
    1)输入”entropy“,使用信息熵(Entropy)
    2)输入”gini“,使用基尼系数(Gini Impurity)
    

    为了要将表格转化为一棵树,决策树需要找出最佳节点和最佳的分枝方法,对分类树来说,衡量这个“最佳”的指标叫做“不纯度”。通常来说,不纯度越低,决策树对训练集的拟合越好。现在使用的决策树算法在分枝方法上的核心大多是围绕在对某个不纯度相关指标的最优化上。不纯度基于节点来计算,树中的每个节点都会有一个不纯度,并且子节点的不纯度一定是低于父节点的,也就是说,在同一棵决策树上,叶子节点的不纯度一定是最低的。

    Criterion这个参数正是用来决定不纯度的计算方法的。sklearn提供了两种选择:

    1632462406460

    其中t代表给定的节点,i代表标签的任意分类,P(i/t)代表标签分类i在节点t上所占的比例,注意,当使用信息熵时,sklearn实际计算的是基于信息熵的信息增益(Information Gain),即父节点的信息熵和子节点的信息熵之差。比起基尼系数,信息熵对不纯度更加敏感,对不纯度的惩罚最强。但是在实际使用中,信息熵和基尼系数的效果基本相同。信息熵的计算比基尼系数缓慢一些,因为基尼系数的计算不涉及对数。另外,因为信息熵对不纯度更加敏感,所以信息熵作为指标时,决策树的生长会更加“精细”,因此对于高维数据或者噪音很多的数据,信息熵很容易过拟合,基尼系数在这种情况下效果往往比较好。当模型拟合程度不足的时候,即当模型在训练集和测试集上都表现不太好的时候,使用信息熵。当然,这些不是绝对的。

    参数 criterion
    如何影响模型? 确定不纯度的计算方法,帮忙找出最佳节点和最佳分枝,不纯度越低,决策树对训练集的拟合越好,可以把不纯度理解为不确定性,不确定性越小,效果越好。
    可能的输入有哪 些? 不填默认基尼系数,填写gini使用基尼系数,填写entropy使用信息增益
    怎样选取参数? 通常就使用基尼系数数据维度很大,噪音很大时使用基尼系数维度低,数据比较清晰的时候,信息熵和基尼系数没区别 ,当决策树的拟合程度不够的时候,使用信息熵 ,两个都试试,不好就换另外一个

    决策树的基本流程其实可以简单概括如下:

    1632462643416

    直到没有更多的特征可用,或整体的不纯度指标已经最优,决策树就会停止生长。下面进行案例演示:

    代码说明

    # 导入所需要的模块
    from sklearn import tree #树的模块
    from sklearn.datasets import load_wine #导入红酒数据集
    from sklearn.model_selection import train_test_split # 划分数据集的模块
    
    # 探索数据
    wine = load_wine()
    # 数据有178个样本,13个特征
    wine.data.shape
    # 标签
    wine.target
    #如果wine是一张表,应该长这样:
    import pandas as pd
    pd.concat([pd.DataFrame(wine.data),pd.DataFrame(wine.target)],axis=1)
    wine.feature_names
    wine.target_names
    # 划分数据为训练集和测试集,test_size标示测试集数据占的百分比
    Xtrain, Xtest, Ytrain, Ytest = train_test_split(wine.data,wine.target,test_size=0.3)
    Xtrain.shape
    Xtest.shape
    
    # 建立模型
    clf = tree.DecisionTreeClassifier(criterion="entropy")# 实例化模型,添加criterion参数
    clf = clf.fit(Xtrain, Ytrain)# 使用实例化好的模型进行拟合操作
    score = clf.score(Xtest, Ytest) #返回预测的准确度
    score
    # 在这里,我们发现每一次运行程序时,返回的准确率都不相同,这是因为sklearn每次都在全部的特征中选取若干个特征建立一棵树
    # 最后选择准确率最高的决策树返回,如果我们添加上random_state参数,那么sklearn每一次建立决策树使用的特征都相同,返回的预测分数也会一样
    
    # random_state是决定随机数的参数,随机数不变,那么每一次创建的决策树也一样
    
    # 对数据进行可视化
    feature_name = ['酒精','苹果酸','灰','灰的碱性','镁','总酚','类黄酮','非黄烷类酚类','花青素','颜色强度','色调','稀释葡萄酒','脯氨酸']
    
    import graphviz
    dot_data = tree.export_graphviz(clf #训练好的模型
                                    ,out_file = None
                                    ,feature_names= feature_name
                                    ,class_names=["琴酒","雪莉","贝尔摩德"]
                                    ,filled=True #进行颜色填充
                                    ,rounded=True #树节点的形状控制
    )
    graph = graphviz.Source(dot_data)
    graph
    
    #特征重要性
    clf.feature_importances_# 查看每一个特征对分类的贡献率
    [*zip(feature_name,clf.feature_importances_)]
    
    # 在这里,我们发现每一次运行程序时,返回的准确率都不相同,这是因为sklearn每次都在全部的特征中选取若干个特征建立一棵树
    # 最后选择准确率最高的决策树返回,如果我们添加上random_state参数,那么sklearn每一次建立决策树使用的特征都相同,返回的
    # 预测分数也会一样clf = tree.DecisionTreeClassifier(criterion="entropy",random_state=30)
    clf = clf.fit(Xtrain, Ytrain)
    score = clf.score(Xtest, Ytest) #返回预测的准确度
    score
    

    我们已经在只了解一个参数的情况下,建立了一棵完整的决策树。但是回到步骤4建立模型,score会在某个值附近波动,引起步骤5中画出来的每一棵树都不一样。它为什么会不稳定呢?如果使用其他数据集,它还会不稳定吗?我们之前提到过,无论决策树模型如何进化,在分枝上的本质都还是追求某个不纯度相关的指标的优化,而正如我们提到的,不纯度是基于节点来计算的,也就是说,决策树在建树时,是靠优化节点来追求一棵优化的树,但最优的节点能够保证最优的树吗?集成算法被用来解决这个问题:sklearn表示,既然一棵树不能保证最优,那就建更多的不同的树,然后从中取最好的。怎样从一组数据集中建不同的树?在每次分枝时,不使用全部特征,而是随机选取一部分特征,从中选取不纯度相关指标最优的作为分枝用的节点。这样,每次生成的树也就不同了。

    最后,我们画出的决策树如下:

    1632463001390

    从返回的结果我们可以发现,类黄酮的信息增益最大,所以选取类黄酮作为树的根节点来画树,接下来,算法依次算出每一个特征的信息增益,选取信息增益大的特征作为子树的根节点,递归建立决策树,注意,从根节点到叶子结点,信息增益的值一直在减小。

    random_state & splitter

    random_state用来设置分枝中的随机模式的参数,默认None,在高维度时随机性会表现更明显,低维度的数据(比如鸢尾花数据集),随机性几乎不会显现。输入任意整数,会一直长出同一棵树,让模型稳定下来。也就是每次选取的特征都是一样的。

    splitter也是用来控制决策树中的随机选项的,有两种输入值,输入”best",决策树在分枝时虽然随机,但是还是会优先选择更重要的特征进行分枝(重要性可以通过属性feature_importances_查看),输入“random",决策树在分枝时会更加随机,树会因为含有更多的不必要信息而更深更大,并因这些不必要信息而降低对训练集的拟合。这也是防止过拟合的一种方式。当你预测到你的模型会过拟合,用这两个参数来帮助你降低树建成之后过拟合的可能性。当然,树一旦建成,我们依然是使用剪枝参数来防止过拟合。

    案例

    clf = tree.DecisionTreeClassifier(criterion="entropy"
                                      ,random_state=30 # 保证模型稳定,每一次运行,所选取的特征不变
                                      ,splitter="random"
    )
    clf = clf.fit(Xtrain, Ytrain)
    score = clf.score(Xtest, Ytest)
    
    import graphviz
    # filled参数标示填充颜色
    # rounded参数标示显示圆角矩形
    dot_data=tree.export_graphviz(clf,feature._names=feature_name,class_names=["琴酒","雪莉","贝尔摩德"],filled=True,rounded=True)
    graph=graphviz.Source(dot_data)
    graph
    [*zip(feature_name,clf.feature_importances_)]
    

    剪枝参数

    在不加限制的情况下,一棵决策树会生长到衡量不纯度的指标最优,或者没有更多的特征可用为止。这样的决策树往往会过拟合,这就是说,它会在训练集上表现很好,在测试集上却表现糟糕。我们收集的样本数据不可能和整体的状况完全一致,因此当一棵决策树对训练数据有了过于优秀的解释性,它找出的规则必然包含了训练样本中的噪声,并使它对未知数据的拟合程度足。

    score_train = clf.score(Xtrain, Ytrain)
    # 对于训练数据集的预测分数为100%,也就是过拟合了,需要我们做剪枝处理
    score_train # 1
    

    为了让决策树有更好的泛化性,我们要对决策树进行剪枝。剪枝策略对决策树的影响巨大,正确的剪枝策略是优化决策树算法的核心。sklearn为我们提供了不同的剪枝策略:

    max_depth

    树的最大深度

    限制树的最大深度,超过设定深度的树枝全部剪掉,这是用得最广泛的剪枝参数,在高维度低样本量时非常有效。决策树多生长一层,对样本量的需求会增加一倍,所以限制树深度能够有效地限制过拟合。在集成算法中也非常实用。实际使用时,建议从=3开始尝试,看看拟合的效果再决定是否增加设定深度。

    min_samples_leaf & min_samples_split

    min_samples_leaf限定,一个节点在分枝后的每个子节点都必须包含至少min_samples_leaf个训练样本,否则分枝就不会发生,或者,分枝会朝着满足每个子节点都包含min_samples_leaf个样本的方向去发生。一般搭配max_depth使用,在回归树中有神奇的效果,可以让模型变得更加平滑。这个参数的数量设置得太小会引起过拟合,设置得太大就会阻止模型学习数据。一般来说,建议从=5开始使用。如果叶节点中含有的样本量变化很大,建议输入浮点数作为样本量的百分比来使用。同时,这个参数可以保证每个叶子的最小尺寸,可以在回归问题中避免低方差,过拟合的叶子节点出现。对于类别不多的分类问题,=1通常就是最佳选择。

    min_samples_split限定,一个节点必须要包含至少min_samples_split个训练样本,这个节点才允许被分枝,否则分枝就不会发生。

    clf1 = tree.DecisionTreeClassifier(criterion="entropy"
                                      ,random_state=30
                                      ,splitter="random"
                                      ,max_depth=3
                                      ,min_samples_leaf=10 #一个节点分支后,每一个子节点至少包含10个样本
                                      ,min_samples_split=10 #一个节点至少包含10个样本才会分支
    )
    clf1=clf1.fit(Xtrain,Ytrain)#拟合模型
    dot_data=tree.export_graphviz(clf,feature_names=feature_name,class_names=["琴酒","雪莉","贝尔摩德"],filled=True,rounded=True)
    graph=graphviz.Source(dot_data)
    graph
    

    1632463487815

    max_features & min_impurity_decrease

    一般max_depth使用,用作树的”精修“。max_features限制分枝时考虑的特征个数,超过限制个数的特征都会被舍弃。和max_depth异曲同工,max_features是用来限制高维度数据的过拟合的剪枝参数,但其方法比较暴力,是直接限制可以使用的特征数量而强行使决策树停下的参数,在不知道决策树中的各个特征的重要性的情况下,强行设定这个参数可能会导致模型学习不足。如果希望通过降维的方式防止过拟合,建议使用PCA,ICA或者特征选择模块中的降维算法。

    min_impurity_decrease限制信息增益的大小,信息增益小于设定数值的分枝不会发生。这是在0.19版本中更新的功能,在0.19版本之前时使用min_impurity_split。

    确定最优的剪枝参数

    那具体怎么来确定每个参数填写什么值呢?这时候,我们就要使用确定超参数的曲线来进行判断了,继续使用我们已经训练好的决策树模型clf。超参数的学习曲线,是一条以超参数的取值为横坐标,模型的度量指标为纵坐标的曲线,它是用来衡量不同超参数取值下模型的表现的线。在我们建好的决策树里,我们的模型度量指标就是score。也就是所谓的学习曲线。

    import matplotlib.pyplot as plt
    test = []
    for i in range(10):
        clf = tree.DecisionTreeClassifier(max_depth=i+1
                                          ,criterion="entropy"
                                          ,random_state=30
                                          ,splitter="random"
        )
        clf = clf.fit(Xtrain, Ytrain)
        score = clf.score(Xtest, Ytest)
        test.append(score)
    plt.plot(range(1,11),test,color="red",label="max_depth")
    plt.legend()
    plt.show()
    

    思考:

    1. 剪枝参数一定能够提升模型在测试集上的表现吗? – 调参没有绝对的答案,一切都是看数据本身。
    2. 这么多参数,一个个画学习曲线?
      无论如何,剪枝参数的默认值会让树无尽地生长,这些树在某些数据集上可能非常巨大,对内存的消耗。所以如果
      你手中的数据集非常大,你已经预测到无论如何你都是要剪枝的,那提前设定这些参数来控制树的复杂性和大小会
      比较好。

    目标权重参数

    class_weight & min_weight_fraction_leaf

    完成样本标签平衡的参数。样本不平衡是指在一组数据集中,标签的一类天生占有很大的比例。比如说,在银行要判断“一个办了信用卡的人是否会违约”,就是是vs否(1%:99%)的比例。这种分类状况下,即便模型什么也不做,全把结果预测成“否”,正确率也能有99%。因此我们要使用class_weight参数对样本标签进行一定的均衡,给少量的标签更多的权重,让模型更偏向少类,向捕获少数类的方向建模。该参数默认None,此模式表示自动给与数据集中的所有标签相同的权重。

    有了权重之后,样本量就不再是单纯地记录数目,而是受输入的权重影响了,因此这时候剪枝,就需要搭配min_weight_fraction_leaf这个基于权重的剪枝参数来使用。另请注意,基于权重的剪枝参数(例如min_weight_fraction_leaf)将比不知道样本权重的标准(比如min_samples_leaf)更少偏向主导类。如果样本是加权的,则使用基于权重的预修剪标准来更容易优化树结构,这确保叶节点至少包含样本权重的总和的一小部分。

    重要属性和接口

    属性是在模型训练之后,能够调用查看的模型的各种性质。对决策树来说,最重要的是feature_importances_,能够查看各个特征对模型的重要性。

    sklearn中许多算法的接口都是相似的,比如说我们之前已经用到的fit和score,几乎对每个算法都可以使用。除了这两个接口之外,决策树最常用的接口还有apply和predict。

  • apply中输入测试集返回每个测试样本所在的叶子节点的索引,
  • predict输入测试集返回每个测试样本的标签。
  • 所有接口中要求输入X_train和X_test的部分,输入的特征矩阵必须至少是一个二维矩阵。sklearn不接受任何一维矩阵作为特征矩阵被输入。如果你的数据的确只有一个特征,那必须用reshape(-1,1)来给矩阵增维;如果你的数据只有一个特征和一个样本,使用reshape(1,-1)来给你的数据增维。

    #apply返回每个测试样本所在的叶子节点的索引
    clf.apply(Xtest)
    #predict返回每个测试样本的分类/回归结果
    clf.predict(Xtest)
    

    至此,我们已经学完了分类树DecisionTreeClassifier和用决策树绘图(export_graphviz)的所有基础。

    分类树的八个参数,一个属性,四个接口,以及绘图所用的代码。

  • 八个参数:
  • Criterion,
  • 两个随机性相关的参数(random_state,splitter),
  • 五个剪枝参数(max_depth,min_samples_split,min_samples_leaf,max_feature,min_impurity_decrease)
  • 一个属性:feature_importances_
  • 四个接口:fit,score,apply,predict
  • 回归树

    DecisionTreeRegressor

    class sklearn.tree.DecisionTreeRegressor (
    criterion=’mse’, splitter=’best’, max_depth=None,min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_features=None,random_state=None, max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, presort=False
    )
    

    几乎所有参数,属性及接口都和分类树一模一样。需要注意的是,在回归树种,没有标签分布是否均衡的问题,因此没有class_weight这样的参数。因为回顾对应的是连续性的数据。

    重要属性,参数及接口

    criterion

    回归树衡量分枝质量的指标,支持的标准有三种:

    1. 输入"mse"使用均方误差mean squared error(MSE),父节点和叶子节点之间的均方误差的差额将被用来作为特征选择的标准,这种方法通过使用叶子节点的均值来最小化L2损失
    2. 输入“friedman_mse”使用费尔德曼均方误差,这种指标使用弗里德曼针对潜在分枝中的问题改进后的均方误差
    3. 输入"mae"使用绝对平均误差MAE(mean absolute error),这种指标使用叶节点的中值来最小化L1损失

    属性中最重要的依然是feature_importances_,接口依然是apply, fit, predict, score最核心。

    均方误差:

    1632471219768

    其中N是样本数量,i是每一个数据样本,fi是模型回归出的数值,yi是样本点i实际的数值标签。所以MSE的本质,其实是样本真实数据与回归结果的差异。在回归树中,MSE不只是我们的分枝质量衡量指标,也是我们最常用的衡量回归树回归质量的指标,当我们在使用交叉验证,或者其他方式获取回归树的结果时,我们往往选择均方误差作为我们的评估(在分类树中这个指标是score代表的预测准确率)。在回归中,我们追求的是,MSE越小越好。

    然而,回归树的接口score返回的是R平方,并不是MSE。R平方被定义如下:

    1632463939408

    其中u是残差平方和(MSE * N),v是总平方和,N是样本数量,i是每一个数据样本,fi是模型回归出的数值,yi是样本点i实际的数值标签。y帽是真实数值标签的平均数。R平方可以为正为负(如果模型的残差平方和远远大于模型的总平方和,模型非常糟糕,R平方就会为负,R的平方越接近1越好),而均方误差永远为正。

    值得一提的是,虽然均方误差永远为正,但是sklearn当中使用均方误差作为评判标准时,却是计算”负均方误差“(neg_mean_squared_error)。这是因为sklearn在计算模型评估指标的时候,会考虑指标本身的性质,均方误差本身是一种误差,所以被sklearn划分为模型的一种损失(loss),因此在sklearn当中,都以负数表示。真正的均方误差MSE的数值,其实就是neg_mean_squared_error去掉负号的数字。

    交叉验证的使用方法:

    from sklearn.datasets import load_boston
    from sklearn.model_selection import cross_val_score
    from sklearn.tree import DecisionTreeRegressor
    
    boston = load_boston()
    # scoring 表示使用军方误差衡量模型
    # 如果不写参数,对于回归模型,默认返回R平方,越接近1越好
    # 均方误差越小越好
    regressor = DecisionTreeRegressor(random_state=0)
    cross_val_score(regressor, boston.data, boston.target, cv=10,scoring = "neg_mean_squared_error")
    #交叉验证cross_val_score的用法
    

    交叉验证是用来观察模型的稳定性的一种方法,我们将数据划分为n份,依次使用其中一份作为测试集,其他n-1份作为训练集,多次计算模型的精确性来评估模型的平均准确程度。训练集和测试集的划分会干扰模型的结果,因此用交叉验证n次的结果求出的平均值,是对模型效果的一个更好的度量。

    1632464024029

    一维回归的图像绘制

    接下来我们到二维平面上来观察决策树是怎样拟合一条曲线的。我们用回归树来拟合正弦曲线,并添加一些噪声来观察回归树的表现。

    import numpy as np
    from sklearn.tree import DecisionTreeRegressor
    import matplotlib.pyplot as plt
    #设置随机数种子,然后产生一个80*1的二维随机数数组,添加噪音
    rng=np.random.RandomState(1)# 设置随机数的种子
    X=np.sort(5*rng.rand(80,1),axis=0)
    y = np.sin(X).ravel()# ravel()函数是降维函数
    # 建立模型,并且进行拟合,建立回归树的深度不同
    regr_1 = DecisionTreeRegressor(max_depth=2)
    regr_2 = DecisionTreeRegressor(max_depth=5)
    regr_1.fit(X, y)
    regr_2.fit(X, y)
    # np.arrange(开始,结束点,步长),生成有序数组的函数
    # 产生一个序列,并且进行升维工作,np.newaxis是升维函数,然后进行预测
    X_test = np.arange(0.0, 5.0, 0.01)[:, np.newaxis]
    y_1 = regr_1.predict(X_test)
    y_2 = regr_2.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=2", linewidth=2)
    plt.plot(X_test, y_2, color="yellowgreen", label="max_depth=5", linewidth=2)
    plt.xlabel("data")
    plt.ylabel("target")
    plt.title("Decision Tree Regression")
    plt.legend()
    plt.show()
    

    可见,回归树学习了近似正弦曲线的局部线性回归。我们可以看到,如果树的最大深度(由max_depth参数控制)设置得太高,则决策树学习得太精细,它从训练数据中学了很多细节,包括噪声得呈现,从而使模型偏离真实的正弦曲线,形成过拟合。

    1632464071878

    多输出

    一个多值输出问题是一个类似当 Y 是大小为 [n_samples, n_outputs] 的2d数组时,有多个输出值需要预测的监督学习问题。

    当输出值之间没有关联时,一个很简单的处理该类型的方法是建立一个n独立模型,即每个模型对应一个输出,然后使用这些模型来独立地预测n个输出中的每一个。然而,由于可能与相同输入相关的输出值本身是相关的,所以通常更好的方法是构建能够同时预测所有n个输出的单个模型。首先,因为仅仅是建立了一个模型所以训练时间会更短。第二,最终模型的泛化性能也会有所提升。对于决策树,这一策略可以很容易地用于多输出问题。 这需要以下更改:

  • 在叶中存储n个输出值,而不是一个;
  • 通过计算所有n个输出的平均减少量来作为分裂标准.
  • 该模块通过在 DecisionTreeClassifierDecisionTreeRegressor 中实现该策略来支持多输出问题。如果决策树与大小为 [n_samples, n_outputs] 的输出数组Y向匹配,则得到的估计器:

  • predict 是输出n_output的值
  • predict_proba 上输出 n_output 数组列表
  • 案例

    # 多输出案例
    import numpy as np
    import matplotlib.pyplot as plt
    from sklearn.tree import DecisionTreeRegressor
    
    # 生成测试数据集
    # Z制作数据集
    rng=np.random.RandomState(1)# 设置随机数种子
    X=np.sort(200*rng.rand(100,1)-100,axis=0)
    X.shape # 100*1
    
    # y有两个标签
    y = np.array([np.pi * np.sin(X).ravel(), np.pi * np.cos(X).ravel()]).T
    
    # 给标签产生噪音
    y[::5, :] += (0.5 - rng.rand(20, 2))
    
    # 训练模型
    regr_1 = DecisionTreeRegressor(max_depth=2)
    regr_2 = DecisionTreeRegressor(max_depth=5)
    regr_3 = DecisionTreeRegressor(max_depth=8)
    regr_1.fit(X, y)
    regr_2.fit(X, y)
    regr_3.fit(X, y)
    
    X_test = np.arange(-100.0, 100.0, 0.01)[:, np.newaxis]
    y_1 = regr_1.predict(X_test)
    y_2 = regr_2.predict(X_test)
    y_3 = regr_3.predict(X_test)
    y_1.shape #(2000,2)
    

    预测结果

    max_depth=2

    plt.figure()
    s = 25
    # 原始图像
    plt.scatter(y[:, 0], y[:, 1], c="navy", s=s,
                edgecolor="black", label="data")
    plt.scatter(y_1[:, 0], y_1[:, 1], c="cornflowerblue", s=s,
                edgecolor="red", label="max_depth=2")
    plt.xlim([-6, 6])
    plt.ylim([-6, 6])
    plt.xlabel("target 1")
    plt.ylabel("target 2")
    plt.title("Multi-output Decision Tree Regression")
    plt.legend(loc="best")
    plt.show()
    

    1632467162317

    可以看到,在决策树的高度是2的时候,几乎预测不到什么有用的信息。

    max_depth=5

    plt.figure()
    s = 25
    plt.scatter(y[:, 0], y[:, 1], c="navy", s=s,
                edgecolor="black", label="data")
    plt.scatter(y_2[:, 0], y_2[:, 1], c="red", s=s,
                edgecolor="black", label="max_depth=5")
    plt.xlim([-6, 6])
    plt.ylim([-6, 6])
    plt.xlabel("target 1")
    plt.ylabel("target 2")
    plt.title("Multi-output Decision Tree Regression")
    plt.legend(loc="best")
    plt.show()
    

    1632467221891

    max_depth=8

    plt.figure()
    s = 25
    plt.scatter(y[:, 0], y[:, 1], c="navy", s=s,
                edgecolor="black", label="data")
    plt.scatter(y_3[:, 0], y_3[:, 1], c="red", s=s,
                edgecolor="black", label="max_depth=8")
    plt.xlim([-6, 6])
    plt.ylim([-6, 6])
    plt.xlabel("target 1")
    plt.ylabel("target 2")
    plt.title("Multi-output Decision Tree Regression")
    plt.legend(loc="best")
    plt.show()
    

    1632467273267

    最大高度是5的时候,几乎全部预测到。

    决策树的优缺点

    决策树的优点:

    1. 易于理解和解释,因为树木可以画出来被看见
    2. 需要很少的数据准备。其他很多算法通常都需要数据规范化,需要创建虚拟变量并删除空值等。但请注意,sklearn中的决策树模块不支持对缺失值的处理。
    3. 使用树的成本(比如说,在预测数据的时候)是用于训练树的数据点的数量的对数,相比于其他算法,这是一个很低的成本。
    4. 能够同时处理数字和分类数据,既可以做回归又可以做分类。其他技术通常专门用于分析仅具有一种变量类型的数据集。
    5. 能够处理多输出问题,即含有多个标签的问题,注意与一个标签中含有多种标签分类的问题区别开
    6. 是一个白盒模型,结果很容易能够被解释。如果在模型中可以观察到给定的情况,则可以通过布尔逻辑轻松解释条件。相反,在黑盒模型中(例如,在人工神经网络中),结果可能更难以解释。
    7. 可以使用统计测试验证模型,这让我们可以考虑模型的可靠性。
    8. 即使其假设在某种程度上违反了生成数据的真实模型,也能够表现良好。

    决策树的缺点:

    1. 决策树学习者可能创建过于复杂的树,这些树不能很好地推广数据。这称为过度拟合。修剪,设置叶节点所需的最小样本数或设置树的最大深度等机制是避免此问题所必需的,而这些参数的整合和调整对初学者来说会比较晦涩
    2. 决策树可能不稳定,数据中微小的变化可能导致生成完全不同的树,这个问题需要通过集成算法来解决。
    3. 决策树的学习是基于贪婪算法,它靠优化局部最优(每个节点的最优)来试图达到整体的最优,但这种做法不能保证返回全局最优决策树。这个问题也可以由集成算法来解决,在随机森林中,特征和样本会在分枝过程中被随机采样。
    4. 有些概念很难学习,因为决策树不容易表达它们,例如XOR,奇偶校验或多路复用器问题。
    5. 如果标签中的某些类占主导地位,决策树学习者会创建偏向主导类的树。因此,建议在拟合决策树之前平衡数据集。

    使用技巧

  • 对于拥有大量特征的数据决策树会出现过拟合的现象。获得一个合适的样本比例和特征数量十分重要,因为在高维空间中只有少量的样本的树是十分容易过拟合的。
  • 考虑事先进行降维( PCA , ICA ,使您的树更好地找到具有分辨性的特征。
  • 通过 export 功能可以可视化您的决策树。使用 max_depth=3 作为初始树深度,让决策树知道如何适应您的数据,然后再增加树的深度。
  • 请记住,填充树的样本数量会增加树的每个附加级别。使用 max_depth 来控制输的大小防止过拟合。
  • 通过使用 min_samples_splitmin_samples_leaf 来控制叶节点上的样本数量。当这个值很小时意味着生成的决策树将会过拟合,然而当这个值很大时将会不利于决策树的对样本的学习。所以尝试 min_samples_leaf=5 作为初始值。如果样本的变化量很大,可以使用浮点数作为这两个参数中的百分比。两者之间的主要区别在于 min_samples_leaf 保证叶结点中最少的采样数,而 min_samples_split 可以创建任意小的叶子,尽管在文献中 min_samples_split 更常见。
  • 在训练之前平衡您的数据集,以防止决策树偏向于主导类.可以通过从每个类中抽取相等数量的样本来进行类平衡,或者优选地通过将每个类的样本权重 (sample_weight) 的和归一化为相同的值。还要注意的是,基于权重的预修剪标准 (min_weight_fraction_leaf) 对于显性类别的偏倚偏小,而不是不了解样本权重的标准,如 min_samples_leaf
  • 如果样本被加权,则使用基于权重的预修剪标准 min_weight_fraction_leaf 来优化树结构将更容易,这确保叶节点包含样本权重的总和的至少一部分。
  • 所有的决策树内部使用 np.float32 数组 ,如果训练数据不是这种格式,将会复制数据集。
  • 如果输入的矩阵X为稀疏矩阵,建议您在调用fit之前将矩阵X转换为稀疏的csc_matrix ,在调用predict之前将 csr_matrix 稀疏。当特征在大多数样本中具有零值时,与密集矩阵相比,稀疏矩阵输入的训练时间可以快几个数量级。
  • 决策树算法: ID3, C4.5, C5.0 和 CART

    所有种类的决策树算法有哪些以及它们之间的区别?scikit-learn 中实现何种算法呢?

    ID3(Iterative Dichotomiser 3)由 Ross Quinlan 在1986年提出。该算法创建一个多路树,找到每个节点(即以贪心的方式)分类特征,这将产生分类目标的最大信息增益。决策树发展到其最大尺寸,然后通常利用剪枝来提高树对未知数据的泛华能力。

    C4.5 是 ID3 的后继者,并且通过动态定义将连续属性值分割成一组离散间隔的离散属性(基于数字变量),消除了特征必须被明确分类的限制。C4.5 将训练的树(即,ID3算法的输出)转换成 if-then 规则的集合。然后评估每个规则的这些准确性,以确定应用它们的顺序。如果规则的准确性没有改变,则需要决策树的树枝来解决。

    C5.0 是 Quinlan 根据专有许可证发布的最新版本。它使用更少的内存,并建立比 C4.5 更小的规则集,同时更准确。

    CART(Classification and Regression Trees (分类和回归树))与 C4.5 非常相似,但它不同之处在于它支持数值目标变量(回归),并且不计算规则集。CART 使用在每个节点产生最大信息增益的特征和阈值来构造二叉树。

    scikit-learn 使用 CART 算法的优化版本

    最后感谢哔哩哔哩up主菜菜老师的课程,非常不错。


    参考资料:

    [1] https://www.bilibili.com/video/BV1WJ411k7L3?p=228

    [2] https://scikit-learn.org/stable/

    物联沃分享整理
    物联沃-IOTWORD物联网 » 使用Sklearn学习决策树

    发表评论