决策树
-
决策树学习使用决策树(作为预测模型)从关于项目(在分支中表示)的观察到关于项目的目标值(在叶子中表示)的结论。它是统计,数据挖掘和机器学习中使用的预测建模方法之一。目标变量可以采用一组离散值的树模型称为分类树 ; 在这些树结构中,叶子代表类标签,分支代表连词导致这些类标签的功能。目标变量可以采用连续值(通常是实数)的决策树称为回归树。(建议仔细过一遍西瓜书上的例子计算过程,讲的很明白了)
-
决策树是一种关于分类的有监督机器学习的方法。决策树的生成算法有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).
-
-
假定样本集合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的纯度越高。)
-
求离散特征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(D∣a)=∑v=1VDDvEnt(Dv)。
-
计算信息增益(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(D∣a)。
-
注意:这里在手算的过程中一般会计算出多个信息增益,选最大的!。
-
-
-
-
C4.5决策树
-
C4.5是为了解决ID3的一个缺点而产生的。缺点是啥?如果某个属性的分类很多,也就是分叉超多,那么该属性下的样本就很少,此时的信息增益就非常高,ID3这个愣头青就会认为这个属性适合用作划分。是,它确实是能划分,但取值较多的属性用作划分依据时,它的泛化能力弱,没法对新样本有效预测,所以C4.5不依靠信息增益划分样本,而是依靠“信息增益率”。
-
这里有个新名词,属性a的“固有值(Intrinsic Value)”,当属性a的可取值数量越大(也就是V越大),那IV(a)的值就越大。以下是C4.5的计算步骤:
-
-
根据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(D∣a)。
-
计算属性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=1V∣D∣∣Dv∣log2∣D∣∣Dv∣。
-
根据信息增益和固有值求信息增益率 ,信息增益率计算公式: 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)。
-
注意:这里不是无脑选择最高的信息增益率,而是启发式地选择:先从划分出的属性中找到信息增益高于平均的那些属性,然后再从这些属性中选信息增益率最高的。
-
-
-
-
CART决策树
-
CART决策树(Classification and Regression Tree)独立于另外两种决策树,一方面它使用基尼指数(Gini Index)作为划分依据,另一方面它既可以做分类,也可以做回归。Python中的sklearn决策树模型就是采用的CART来选择分支的。刚才提到CART决策树是用基尼系数来进行属性划分,那什么是基尼系数?直观讲:基尼指数Gini(D) 反映的是数据集中随机抽取两个样本,而他们类别标志不一致的概率。基尼指数越小,代表数据集D的纯度越高。
-
-
计算基尼指数 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=1∣n∣∑i′=ipipi′=1−∑i=1∣n∣pi2。
-
对于属性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=1V∣D∣∣Dv∣Gini(Dv)。
-
注意:在属性集合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_data:string ,树模型 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%的数据做训练集的决策边界可以看出有不一样的地方。