决策树(Decision Tree)是一种基本的分类和回归算法。该算法模型呈树形结构,主要由结点和有向边组成。结点又分为两种类型:内部结点和叶子结点。内部结点表示在一个属性或特征上的测试,每一个结点分枝代表一个测试输出,每一个叶子结点代表一个类别。决策树学习是以实例为基础的归纳学习。
决策树学习的算法通常是一个递归地选择最优特征,并根据该特征对训练数据进行分割,使得对各个子数据集有一个最好的分类的过程。这一过程对应着特征空间的划分,也对应着决策树的构建。
决策树学习常用的算法有ID3、C4.5和CART算法。
由于ID3算法和C4.5算法内部都涉及到熵,所以先对熵及其相关概念作一个简单介绍。
ID3算法内部使用信息增益作为特征选择方法。信息增益表示得知特征X的信息而使得类Y的信息不确定性减少的程度,标记为 g ( Y , X ) g(Y,X) g(Y,X)。其公式如下: g ( Y , X ) = H ( Y ) − H ( Y ∣ X ) g(Y,X)=H(Y)-H(Y|X) g(Y,X)=H(Y)−H(Y∣X)信息增益大的特征,具有较强的分类能力。
C4.5算法内部使用信息增益比进行特征选择,是对ID3算法的一种改进。使用信息增益作为划分训练数据集的特征,存在偏向于选择取值较多的特征的问题,而信息增益比可以对该问题进行矫正。信息增益比 g R ( Y , X ) g_{R}(Y,X) gR(Y,X)定义为特征A对类Y的信息增益 g ( Y , A ) g(Y,A) g(Y,A)与特征A的熵之比。其公式如下: g R ( Y , X ) = g ( Y , X ) H ( X ) g_{R}(Y,X)=\frac{g(Y,X)}{H(X)} gR(Y,X)=H(X)g(Y,X)信息增益比大的特征,具有较强的分类能力。
理论上,ID3算法和C4.5算法只用于解决分类问题,并且只用于处理离散特征(连续型特征一般要先离散化)。ID3算法和C4.5算法建立的树模型是多叉树。
CART(Classification And Regression Tree,分类与回归树)是一种应用非常广泛的决策树算法。CART算法是在给定输入变量X条件下输出随机变量Y的条件概率分布的学习方法,该方法假设决策树是二叉树,内部节点特征的取值为“是”和“否”。这样的决策树等价于递归地二分划分每个特征,将特征空间划分为有限个单元,并在这些单元上确定预测的条件概率分布。
CART决策树的生成就是递归地构造二叉树的过程。对于回归树利用平方误差最小化准则进行特征选择,对于分类树则使用基尼指数进行特征选择。基尼指数的定义如下:
在分类问题中,假设有 K K K个类,样本点属于第 k k k个类的概率为 p k p_{k} pk,则概率分布的基尼指数为: G i n i ( p ) = ∑ k = 1 K p k ( 1 − p k ) = 1 − ∑ k = 1 K p k 2 Gini(p)=\sum_{k=1}^{K}p_{k}(1-p_{k})=1-\sum_{k=1}^{K}p_{k}^{2} Gini(p)=k=1∑Kpk(1−pk)=1−k=1∑Kpk2
对于给定的样本集合 D D D,其基尼指数为 G i n i ( D ) = 1 − ∑ k = 1 K ( ∣ C k ∣ ∣ D ∣ ) 2 Gini(D)=1-\sum_{k=1}^{K}(\frac{|C_{k}|}{|D|})^2 Gini(D)=1−k=1∑K(∣D∣∣Ck∣)2其中 C k C_{k} Ck是 D D D中属于第 k k k类的样本子集, K K K是类的个数。 G i n i ( D ) Gini(D) Gini(D)表示数据集 D D D的不确定性。
如果样本集合 D D D根据特征 A A A是否取某一可能值 a a a将数据集分割成 D 1 D_{1} D1和 D 2 D_{2} D2两部分,则在特征 A A A的条件小,集合 D D D的基尼指数定义为: G i n i ( D , A ) = ∣ D 1 ∣ ∣ D ∣ G i n i ( D 1 ) + ∣ D 2 ∣ ∣ D ∣ G i n i ( D 2 ) Gini(D,A)=\frac{|D_{1}|}{|D|}Gini(D_{1})+\frac{|D_{2}|}{|D|}Gini(D_{2}) Gini(D,A)=∣D∣∣D1∣Gini(D1)+∣D∣∣D2∣Gini(D2)改值表示经过 A = a A=a A=a分割后集合 D D D的不确定性。基尼指数值越大,样本集合的不确定性就越大。CART算法就是选择一个基尼指数最小的最优特征和最优切分点。
假设决策树 T T T的叶结点个数为 ∣ T ∣ |T| ∣T∣, t t t是树的叶结点,该结点有 N t N_{t} Nt个样本,其中 k k k类的样本点有 N t k N_{tk} Ntk个, H t ( T ) H_{t}(T) Ht(T)为叶结点 t t t上的经验熵, α ≥ 0 \alpha \ge 0 α≥0为参数,则决策树的损失函数可以定义为: C α ( T ) = ∑ t = 1 ∣ T ∣ N t H t ( T ) + α ∣ T ∣ C_{\alpha}(T)=\sum_{t=1}^{|T|}N_{t}H_{t}(T)+\alpha|T| Cα(T)=t=1∑∣T∣NtHt(T)+α∣T∣第一项表示模型对训练数据的预测误差,第二项中的 ∣ T ∣ |T| ∣T∣表示模型复杂度。其中经验熵为: H t ( T ) = − ∑ k N t k N t l o g N t k N t H_{t}(T)=-\sum_{k}\frac{N_{tk}}{N_{t}}log\frac{N_{tk}}{N_{t}} Ht(T)=−k∑NtNtklogNtNtk当 α \alpha α确定时,子树越大,往往与训练数据的拟合越好,但是模型的复杂度就越高;相反,子树越小,模型的复杂度越低,但是与训练数据的拟合就不够好。
剪枝是决策树算法对付“过拟合”的主要手段。其基本策略主要有“预剪枝”和“后剪枝”两种。具体如下:
决策树的剪枝通过极小化决策树整体的损失函数来实现的。
以后剪枝为例,设一组叶结点回缩到其父结点之前与之后的整体树分别为 T A T_{A} TA和 T B T_{B} TB,其对应的损失函数值分别为 C α ( T A ) C_{\alpha}(T_{A}) Cα(TA)和 C α ( T B ) C_{\alpha}(T_{B}) Cα(TB),如果 C α ( T A ) ≥ C α ( T B ) C_{\alpha}(T_{A}) \ge C_{\alpha}(T_{B}) Cα(TA)≥Cα(TB),则进行剪枝。
sklearn中提供了专门的决策树分类算法(DecisionTreeClassifier)和决策树回归算法(DecisionTreeRegressor)。下面以DecisionTreeClassifier为例进行说明。
其中几个重要参数的作用如下:
理论上决策树既可以处理连续型变量也可以处理离散型变量,但该算法内部无法区分这两种类型变量,其内部都当作连续型变量进行处理。该方法内部使用二分法对连续属性进行处理,所以DecisionTreeClassifier()产生的树模型均为二叉树。
import pandas as pd
from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import load_iris
import pydotplus
from IPython.display import Image,display
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn import tree
iris=load_iris()
X=pd.DataFrame(iris.data,columns=iris.feature_names)
y=pd.DataFrame(iris.target,columns=['target'])
X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.2,random_state=0)
clf=DecisionTreeClassifier(criterion='entropy',max_depth=4)
clf.fit(X_train,y_train)
y_test_pred=clf.predict(X_test)
y_train_pred=clf.predict(X_train)
print("训练数据准确率:",accuracy_score(y_train,y_train_pred))
print("训练数据准确率:",accuracy_score(y_test, y_test_pred))
##树结构可视化
dot_data=tree.export_graphviz(clf,
feature_names=iris.feature_names,
class_names=iris.target_names,
filled=True,
rounded=True)
graph=pydotplus.graph_from_dot_data(dot_data)
display(Image(graph.create_png()))
其最后的树结构为:
Tips:sklearn中使用前序遍历(根->左孩子->右孩子)顺序对上述树结构中的结点进行编号,编号从0开始。
DecisionTreeClassifier()还提供了两个方法可以返回具体每一个记录的决策路径与叶结点编号,具体如下:
方法 | 作用 |
---|---|
apply | 返回每一条记录对应的叶子结点编号 |
decision_path | 返回每一条记录的决策路径 |
对于类别特征,尤其是当类别特征中类别个数很多时,并不推荐使用one-hot编码。除了降低了空间和时间效率之外,主要会存在以下两方面问题: