【机器学习算法复现】决策树,树形结构解决属性选择问题,一种可回归可分类的有监督学习算法

决策树

  • 决策树学习使用决策树(作为预测模型)从关于项目(在分支中表示)的观察到关于项目的目标值(在叶子中表示)的结论。它是统计,数据挖掘和机器学习中使用的预测建模方法之一。目标变量可以采用一组离散值的树模型称为分类树 ; 在这些树结构中,叶子代表类标签,分支代表连词导致这些类标签的功能。目标变量可以采用连续值(通常是实数)的决策树称为回归树。(建议仔细过一遍西瓜书上的例子计算过程,讲的很明白了)

  • 决策树是一种关于分类的有监督机器学习的方法。决策树的生成算法有ID3决策树、C4.5决策树、CART决策树。(ID3基于信息熵和信息增益做分类;C4.5基于信息增益率做分类,是ID3的改进版;CART基于基尼系数,既可以做分类也可以做回归)。决策树是一种树形结构,其中每个内部节点表示一个属性上的判断,每个分支代表一个判断结果的输出,最后每个叶节点代表一种分类结果。它是一种以树形数据结构来展示决策规则和分类结果的模型,作为一种归纳学习算法,其重点是将看似无序、杂乱的已知数据,通过某种技术手段将它们转化成可以预测未知数据的树状模型,每一条从根结点(对最终分类结果贡献最大的属性)到叶子结点(最终分类结果)的路径都代表一条决策的规则。

    • 机器学习算法分类:监督学习、无监督学习、强化学习;基本的机器学习算法:线性回归、支持向量机(SVM)、最近邻居(KNN)、逻辑回归、决策树、k平均、随机森林、朴素贝叶斯、降维、梯度增强…

    • 监督学习算法 (Supervised Algorithms):在监督学习训练过程中,可以由训练数据集学到或建立一个模式(函数 / learning model),并依此模式推测新的实例。该算法要求特定的输入/输出,首先需要决定使用哪种数据作为范例。例如,文字识别应用中一个手写的字符,或一行手写文字。主要算法包括神经网络、支持向量机、最近邻居法、朴素贝叶斯法、决策树等。

    • 无监督学习算法 (Unsupervised Algorithms):这类算法没有特定的目标输出,算法将数据集分为不同的组。

    • 强化学习算法 (Reinforcement Algorithms):强化学习普适性强,主要基于决策进行训练,算法根据输出结果(决策)的成功或错误来训练自己,通过大量经验训练优化后的算法将能够给出较好的预测。类似有机体在环境给予的奖励或惩罚的刺激下,逐步形成对刺激的预期,产生能获得最大利益的习惯性行为。在运筹学和控制论的语境下,强化学习被称作“近似动态规划”(approximate dynamic programming,ADP)。

  • ID3决策树

    • 熵(Entropy -1984年香农提出)的概念:一条信息的信息量大小与它的不确定性有直接关系,而熵就是用来度量这件事不确定性的大小。为什么需要了解“熵”这个概念?因为决策树模型的构建和评估都与信息熵密切相关。而 信息熵(Information Entropy)是用来度量样本纯度的指标。

    • ID3决策树需要“最大化信息增益”来对节点进行划分,以下是信息增益计算步骤:

      • 输入:训练集D和属性a (这里每个属性a有V个可能的取值{ a 1 , a 2 , . . . , a v a^1,a^2,...,a^v a1,a2,...,av }) ;

      • 输出:属性a对训练数据集D的信息增益 G a i n ( D , a ) Gain(D,a) Gain(D,a).

        1. 假定样本集合D中第i类样本所占的比例为 p i ( i = 1 , 2 , . . . , n ) p_i(i=1,2,...,n) pi(i=1,2,...,n) ,需要先求信息熵 E n t ( D ) = − ∑ i = 1 n p i l o g 2 p i Ent(D)=-\sum^n_{i=1}p_ilog_2p_i Ent(D)=i=1npilog2pi , (PS: 这里 E n t ( D ) Ent(D) Ent(D) 的值越小,样本D的纯度越高。)

        2. 求离散特征a对数据集D的条件信息熵 条件信息熵公式: E n t ( D ∣ a ) = ∑ v = 1 V D v D E n t ( D v ) Ent(D|a)=\sum^V_{v=1}\frac{D^v}{D}Ent(D^v) Ent(Da)=v=1VDDvEnt(Dv)

        3. 计算信息增益(Information Gain), 信息增益公式: G a i n ( D , a ) = E n t ( D ) − E n t ( D ∣ a ) Gain(D,a)=Ent(D)-Ent(D|a) Gain(D,a)=Ent(D)Ent(Da)

        4. 注意:这里在手算的过程中一般会计算出多个信息增益,选最大的!

  • C4.5决策树

    • C4.5是为了解决ID3的一个缺点而产生的。缺点是啥?如果某个属性的分类很多,也就是分叉超多,那么该属性下的样本就很少,此时的信息增益就非常高,ID3这个愣头青就会认为这个属性适合用作划分。是,它确实是能划分,但取值较多的属性用作划分依据时,它的泛化能力弱,没法对新样本有效预测,所以C4.5不依靠信息增益划分样本,而是依靠“信息增益率”

    • 这里有个新名词,属性a的“固有值(Intrinsic Value)”,当属性a的可取值数量越大(也就是V越大),那IV(a)的值就越大。以下是C4.5的计算步骤:

        1. 根据ID3的信息熵和条件信息熵 求信息增益 ,信息增益公式: G a i n ( D , a ) = E n t ( D ) − E n t ( D ∣ a ) Gain(D,a)=Ent(D)-Ent(D|a) Gain(D,a)=Ent(D)Ent(Da)

        2. 计算属性a的固有值IV(a) ,固有值计算公式: I V ( a ) = − ∑ v = 1 V ∣ D v ∣ ∣ D ∣ l o g 2 ∣ D v ∣ ∣ D ∣ IV(a)=-\sum^V_{v=1}\frac{|D^v|}{|D|}log_2\frac{|D^v|}{|D|} IV(a)=v=1VDDvlog2DDv

        3. 根据信息增益和固有值求信息增益率 ,信息增益率计算公式: G a i n R a t i o ( D , a ) = G a i n ( D , a ) I V ( a ) GainRatio(D,a)=\frac{Gain(D,a)}{IV(a)} GainRatio(D,a)=IV(a)Gain(D,a)

        4. 注意:这里不是无脑选择最高的信息增益率,而是启发式地选择:先从划分出的属性中找到信息增益高于平均的那些属性,然后再从这些属性中选信息增益率最高的

  • CART决策树

    • CART决策树(Classification and Regression Tree)独立于另外两种决策树,一方面它使用基尼指数(Gini Index)作为划分依据,另一方面它既可以做分类,也可以做回归。Python中的sklearn决策树模型就是采用的CART来选择分支的。刚才提到CART决策树是用基尼系数来进行属性划分,那什么是基尼系数?直观讲:基尼指数Gini(D) 反映的是数据集中随机抽取两个样本,而他们类别标志不一致的概率。基尼指数越小,代表数据集D的纯度越高

      1. 计算基尼指数 G i n i ( D ) = − ∑ i = 1 ∣ n ∣ ∑ i ′ ≠ i p i p i ′ = 1 − ∑ i = 1 ∣ n ∣ p i 2 Gini(D)=-\sum_{i=1}^{|n|}\sum_{i'\neq i}p_ip_i'=1-\sum_{i=1}^{|n|}p_i^2 Gini(D)=i=1ni=ipipi=1i=1npi2

      2. 对于属性a计算条件基尼指数 G i n i I n d e x ( D , a ) = − ∑ v = 1 V ∣ D v ∣ ∣ D ∣ G i n i ( D v ) GiniIndex(D,a)=-\sum^V_{v=1}\frac{|D^v|}{|D|}Gini(D^v) GiniIndex(D,a)=v=1VDDvGini(Dv)

      3. 注意:在属性集合A中,一般选基尼指数最小的属性,即 a ∗ = a r g M i n G i n i I n d e x ( D , a ) a_*=argMinGiniIndex(D,a) a=argMinGiniIndex(D,a)

  • 决策树模型优缺点

    • 因为是非参数模型,不需要对样本进行预先假设,可以处理复杂样本。 计算速度快,结果可解释性强。 可以同时处理分类和预测问题,对缺失值不敏感。

    • 容易过拟合 , 特征之间存在相互关联时,数据结果表现较差。对于那些各类别样本数量不一致的数据,在决策树中,进行属性划分时,不同的判定准则会带来不同的属性选择倾向;信息增益准则对可取数目较多的属性有所偏好(典型代表ID3算法),而增益率准则(CART)则对可取数目较少的属性有所偏好,但CART进行属性划分时候不再简单地直接利用增益率尽心划分,而是采用一种启发式规则)(只要是使用了信息增益,都有这个缺点,如RF)。ID3算法计算信息增益时结果偏向数值比较多的特征。

  • 分类树 (Classification Tree):目标是分类数据、离散数据。例如:动物种类、人的性别。

  • 回归树 (Regression Tree):目标是连续的数据。例如:人的年龄、收入。

  • 决策树调优方法

    • 剪枝包括预剪枝(Prepruning)和后剪枝(Postpruning),前者通过对连续型变量设置阈值,来控制树的深度,或者控制节点的个数,在节点开始划分之前就进行操作,进而防止过拟合现象。后者是自底向上对非叶节点进行考察,如果这个内部节点换成叶节点能提升决策树的泛化能力,那就把它换掉。

    • 运用交叉验证的方法选择合适的参数

    • 通过模型集成的方法(Bagging/Boosting),基于决策树构建更复杂的模型

  • 决策树学习的 3 个步骤

    • 特征选择:特征选择决定了使用哪些特征来做判断。在训练数据集中,每个样本的属性可能有很多个,不同属性的作用有大有小。因而特征选择的作用就是筛选出跟分类结果相关性较高的特征,也就是分类能力较强的特征。

    • 决策树生成:选择好特征后,就从根节点触发,对节点计算所有特征的信息增益,选择信息增益最大的特征作为节点特征,根据该特征的不同取值建立子节点;对每个子节点使用相同的方式生成新的子节点,直到信息增益很小或者没有特征可以选择为止。

    • 决策树剪枝:剪枝的主要目的是对抗“过拟合”,通过主动去掉部分分支来降低过拟合的风险。

使用决策树完成鸢尾花分类任务

  • 导包,准备鸢尾花数据集,iris_training.csv训练数据集,120条样本数据;iris_test.csv测试数据集,30条数据。本文只用到训练数据集,其中有花萼长度(Sepal Length)、花萼宽度(Sepal Width)、花瓣长度(Petal Length)、花瓣宽度(Petal Width)四个属性。标签0、1、2分别表示山鸢尾(Setosa)、变色鸢尾(Versicolor)、维吉尼亚鸢尾(Virginical)。https://sklearn.apachecn.org

  • %pip install pydotplus
    from sklearn import datasets
    from sklearn.model_selection import train_test_split
    from sklearn.tree import DecisionTreeClassifier
    import numpy as np
    from matplotlib.colors import ListedColormap
    import matplotlib.pyplot as plt
    from pydotplus import graph_from_dot_data
    from sklearn.tree import export_graphviz
    iris = datasets.load_iris()
    print(iris['data'].shape, iris['target'].shape) # (150, 4) (150,) 一共有4个特征
    X = iris.data[:,[2,3]] # 只使用2个特征
    y = iris.target
    print('Class labels:', np.unique(y))
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1, stratify=y)
    print(X_train.shape, y_train.shape)
    
  • (150, 4) (150,)
    Class labels: [0 1 2]
    (105, 2) (105,)
    
  • 训练并绘制决策边界:

  • def plot_decision_regions(X, y, classifier, test_idx=None, resolution=0.02):
         # setup marker generator and color map
         markers = ('s', 'x', 'o', '^', 'v')
         colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan')
         cmap = ListedColormap(colors[:len(np.unique(y))])
         # plot the decision surface
         x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1
         x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1
         xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution),  np.arange(x2_min, x2_max, resolution))
         Z = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T)
         Z = Z.reshape(xx1.shape)   
        # 要画出等高线,核心函数是plt.contourf(),但在这个函数中输入的参数是x,y对应的网格数据以及此网格对应的高度值,因此我们调用np.meshgrid(x,y)把x,y值转换成网格数据
        # 如果想显示热力图,那只要在plt.contourf()函数中添加属性cmap=plt.cm.hot就能显示热力图,其中cmap代表为color map,我们把color map映射成hot(热力图)
         plt.contourf(xx1, xx2, Z, alpha=0.3, cmap=cmap)
         plt.xlim(xx1.min(), xx1.max())
         plt.ylim(xx2.min(), xx2.max())
         for idx, cl in enumerate(np.unique(y)):
             plt.scatter(x=X[y == cl, 0], y=X[y == cl, 1],
             alpha=0.8, c=colors[idx],
             marker=markers[idx], label=cl,
             edgecolor='black')
         # highlight test samples
         if test_idx:
             # plot all samples
             X_test, y_test = X[test_idx, :], y[test_idx]
             plt.scatter(X_test[:, 0], X_test[:, 1],edgecolor='black', alpha=1.0, linewidth=1, marker='o',s=100, label='test set')
    tree = DecisionTreeClassifier(criterion='gini', max_depth=4, random_state=1)  # skrean中的决策树模型
    tree.fit(X_train, y_train)
    X_combined = np.vstack((X_train, X_test))
    y_combined = np.hstack((y_train, y_test))
    # 绘制决策边界
    plot_decision_regions(X_combined, y_combined, classifier=tree, test_idx=range(105, 150))
    plt.xlabel('petal length [cm]')
    plt.ylabel('petal width [cm]')
    plt.legend(loc='upper left')
    plt.show()
    
  • 在这里插入图片描述

  • 对比学习一下K近邻算法的决策边界,我的另一篇关于 k近邻博客

    • ListedColormap:matplotlib.colors.ListedColormap类属于matplotlib.colors模块。 matplotlib.colors模块用于将颜色或数字参数转换为RGBA或RGB。此模块用于将数字映射到颜色或以一维颜色数组(也称为colormap)进行颜色规格转换。matplotlib.colors.ListedColormap类用于从颜色列表创建colarmap对象。这对于直接索引到颜色表中很有用,也可以用于为法线贴图创建特殊的颜色表。Python Matplotlib.colors.ListedColormap用法及代码示例 - 纯净天空 (vimsky.com)

    • import matplotlib.pyplot as plt 
      import numpy as np 
      import matplotlib.colors 
      a = np.linspace(-3, 3) 
      A, B = np.meshgrid(a, a) 
      X = np.exp(-(A**2 + B**2)) 
      figure, (axes1, axes2) = plt.subplots(ncols = 2) 
      colors =["green", "orange",  "gold", "blue", "k", "#550011", "purple",  "red"]  # 自定义颜色列表
      axes1.set_title(" color list") 
      contour = axes1.contourf(A, B, X,   colors = colors) 
      axes2.set_title("with colormap") 
      cmap = matplotlib.colors.ListedColormap(colors) 
      contour = axes2.contourf(A, B, X, cmap = cmap) 
      figure.colorbar(contour) 
      plt.show()
      
    • 在这里插入图片描述

    • import matplotlib.pyplot as plt 
      import numpy as np 
      import matplotlib.colors as colors 
      from mpl_toolkits.axes_grid1 import make_axes_locatable 
      res = np.array([[0, 2], [3, 4]], dtype = int) 
      u = np.unique(res) 
      bounds = np.concatenate(([res.min()-1], u[:-1]+np.diff(u)/2., [res.max()+1])) 
      norm = colors.BoundaryNorm(bounds, len(bounds)-1) 
      color_map1 = ['#7fc97f', '#ffff99',  '#386cb0', '#f0027f'] 
      color_map = colors.ListedColormap(color_map1)  
      fig, axes = plt.subplots() 
      img = axes.imshow(res, cmap = color_map,  norm = norm) 
      divider = make_axes_locatable(axes) 
      cax = divider.append_axes("right", size ="5 %") 
      color_bar = plt.colorbar(img, cmap = color_map,  norm = norm, cax = cax) 
      color_bar.set_ticks(bounds[:-1]+np.diff(bounds)/2.) 
      color_bar.ax.set_yticklabels(color_map1) 
      color_bar.ax.tick_params(labelsize = 10)  
      plt.show()
      
    • 在这里插入图片描述

    • import matplotlib.pyplot as plt
      from matplotlib import cm
      plt.figure(dpi=150)
      ##ListedColormap
      #取多种颜色
      plt.subplot(1,4,1)
      #plt.bar(range(5),range(1,6),color=plt.cm.Accent(range(5)))
      #plt.bar(range(5),range(1,6),color=plt.cm.get_cmap('Accent')(range(5)))
      plt.bar(range(5),range(1,6),color=plt.get_cmap('Accent')(range(5)))
      #取某一种颜色
      plt.subplot(1,4,2)
      plt.bar(range(5),range(1,6),color=plt.cm.Accent(4))
      ##LinearSegmentedColormap
      #取多种颜色
      plt.subplot(1,4,3)
      plt.bar(range(5),range(1,6),color=plt.get_cmap('Blues')(np.linspace(0, 1, 5)))
      #取一种颜色
      plt.subplot(1,4,4)
      plt.bar(range(5),range(1,6),color=plt.get_cmap('Blues')(3))
      
    • 在这里插入图片描述

  • 绘制决策树的结构和结果:

    • from matplotlib.image import imread
      dot_data = export_graphviz(tree, filled=True, rounded=True,class_names=['Setosa', 'Versicolor', 'Virginica'], feature_names=['petal length', 'petal width'],out_file=None)
      graph = graph_from_dot_data(dot_data) # Image data of dtype object cannot be converted to float,所以要先存为图片
      graph.write_png('./tree.png')
      plt.figure(figsize=(10,8))
      im = imread('./tree.png')
      plt.imshow(im)
      plt.show()
      
    • 在这里插入图片描述

  • export_graphvi:经过训练的决策树,我们可以使用 export_graphviz 导出器以 Graphviz 格式导出决策树. 如果你是用 conda 来管理包,那么安装 graphviz 二进制文件和 python 包可以用以下指令安装。参数sklearn.tree.export_graphviz — scikit-learn 1.2.2 documentation

    • decision_tree:用来做可视化的决策树模型

    • out_file:输出文件的句柄或名称。 如果为None,则结果以字符串形式返回

    • max_depth:描绘的最大深度。如果为None,则这树完全生长。

    • feature_names:每个特征的名字

    • class_names:每个目标类别的名称、按升序排列。 仅与分类相关,不支持多输出。 如果为True,则显示类名称的符号表示。

    • label:是否显示不纯度的信息性标签等。选项包括“ all”显示在每个节点上,“ root”显示在顶部根节点上,“ none”显示在任何节点上。

    • filled:设置为True时,绘制节点以表示多数类用于分类问题,值的极值用于回归问题,或表示节点的纯度用于多输出问题。

    • leaves_parallel:设置为True时,在树的底部绘制所有叶节点。

    • impurity:设置为True时,显示每个节点上的不纯度。

    • node_ids:设置为True时,显示每个节点上的ID号。

    • proportion:设置为True时,将“值”和/或“样本”的显示分别更改为比例和百分比。

    • rotate:设置为True时,将树从左到右而不是自上而下定向。

    • rounded:设置为True时,绘制带有圆角的节点框,并使用Helvetica字体代替Times-Roman

    • special_characters:设置为False时,请忽略特殊字符以实现PostScript兼容性。

    • precision:每个节点的杂质值,阈值和值属性中浮点数的精度位数。

    • 返回值dot_datastring ,树模型 GraphViz dot 格式的字符串表现形式。仅仅在out_file为None时返回。

  • import numpy as np
    import matplotlib.pyplot as plt
    from sklearn.datasets import load_iris
    from sklearn.tree import DecisionTreeClassifier
    from matplotlib.colors import ListedColormap
    # Parameters
    n_classes = 3
    plot_colors = "ryb"
    plot_step = 0.02 # Load data
    iris = load_iris()
    markers = ('s', 'x', 'o', '^', 'v')
    colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan')
    cmap = ListedColormap(colors[:len(np.unique(y))])
    resolution=0.02
    for pairidx, pair in enumerate([[0, 1], [0, 2], [0, 3],
                                   [1, 2], [1, 3], [2, 3]]):  # 4个属性任选两个作为组合
        # We only take the two corresponding features
        X = iris.data[:, pair]
        # Train
        clf = DecisionTreeClassifier(criterion='gini', max_depth=4, random_state=1).fit(X, y)
        # Plot the decision boundary
        plt.subplot(2, 3, pairidx + 1)
        x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1
        x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1
        xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution),
                                np.arange(x2_min, x2_max, resolution))
        plt.tight_layout(h_pad=0.5, w_pad=0.5, pad=2.5)
        Z = clf.predict(np.array([xx1.ravel(), xx2.ravel()]).T)
        Z = Z.reshape(xx1.shape)
        plt.contourf(xx1, xx2, Z, alpha=0.3, cmap=cmap)
        plt.xlabel(iris.feature_names[pair[0]])
        plt.ylabel(iris.feature_names[pair[1]])
        plt.xlim(xx1.min(), xx1.max())
        plt.ylim(xx2.min(), xx2.max())
        for idx, cl in enumerate(np.unique(y)):
             plt.scatter(x=X[y == cl, 0], y=X[y == cl, 1],
             alpha=0.8, c=colors[idx],
             marker=markers[idx], label=cl)
    plt.suptitle("Decision surface of a decision tree using paired features")
    plt.legend(loc='lower right', borderpad=0, handletextpad=0)
    plt.axis("tight")
    plt.show()
    
  • 在这里插入图片描述

  • 由于这个地方的将全部数据作为训练集,和上文经过只用70%的数据做训练集的决策边界可以看出有不一样的地方。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

羞儿

写作是兴趣,打赏看心情

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值