前言
先梳理一下决策树的预测过程。预测时,从根节点开始,每次对一个特征分量进行判断,然后进入左子节点或者右子节点,直到抵达叶子节点,得到对应的类别标签或者回归值。
1.决策过程
先来看一个简单的决策树例子: 银行要判断能否给一个人贷款,需要满足判断两个特征,一个是年收入,一个是是否有房产,这是一个二分类问题。 来走一下这个决策树的流程,首先是根节点,判断年收入是否大于20万。年收入大于20就可以给这个人贷款,不大于20万就去下一个判断,下一个节点的判断是是否有房产,有房产就可以贷款,没有就不能贷款。 决策树有两种类型的节点: (1)决策节点:用特征分量的值和设定好的阈值比较,判断进入哪个分支,决策节点定有两个子节点。 (2)叶子节点:表示最终决策结果,对于分类问题就是类别标签,对于回归问题就是回归值。 决策树是一个判别模型,天然支持多分类。
2.训练流程
介绍一下决策树的训练流程,决策树的训练流程是一个递归的过程。首先创建根节点,然后递归地创建左子树和右子树。训练样本为X,训练算法流程如下:
1.用样本集X建立根节点,找到一个判定规则,将样本分裂称X1,X2两部分,同时为根节点设定判定规则。
2.用样本集X1递归建立左子树。
3.用样本集X2递归建立右子树。
4.如果不能再进行分裂,则把节点标记为叶子节点,同时为它赋值。
下面说说上面算法流程中的一些问题。第一,如何寻找最佳分类,对于分类问题,要保证分类之后左子树,右子树的样本尽量纯,也就是左右子树的样本尽可能属于不相交的某一类或者几类,简单来说,就是尽量让两个子树的样本里面尽量没有样本的类别重合。 第二个问题,何时停止分裂,建立叶子节点?有两种方法,一种是当节点的所有样本属于一个类的时候,另一种时当节点的样本树小于某一个阈值的时候。
3.寻找最佳分裂
1.不纯度定义
下面来介绍一下怎么寻找最佳分裂吧。首先,我们定义一个不纯度指标,当样本都属于一个类的时候,不纯度为0。当样本均匀地属于所有类的时候不纯度最大。满足这个条件的有熵不纯度,Gini不纯度,以及误分类不纯度。 不纯度指标用样本集中每类样本出现的概率构造: pi=Ni/N 其中Ni为第i类样本数,N为样本总数。 样本的熵不纯度定义为: Gini不纯度定义为: 样本的误分类不纯度定义为: 上面定义出了样本集的不纯度,我们需要评价分裂的好坏,我们的目标时尽可能时分裂的子集都尽可能纯。因此计算左右两个子集的不纯度之和作为分裂的不纯度。但是分裂之后两边样本不一定时均衡的,因此还得用左右子树的样本数占根节点的样本数的比例最为权重。因此最终的分裂不纯度定义为:
2.对于分类问题
如果采用Gini不纯度作为指标,对于样本集X(x1,x2…,xi),其中xi为某个特征分量,求最佳分裂。我们使用样本集的每个样本特征值xi依次作为分裂阈值,依次求出用这个阈值分裂之后得到左右子树的Gini不纯度,得到最小Gini不纯度对应的阈值既为所求,便是根节点的判定规则,根节点的分裂阈值。当然也可以对上面公式代入Gini不纯度公式,把它转换称一个求最大值的问题。
3.对于回归问题
对于回归问题,我们也是类似的处理。回归问题,节点的回归值就是所有样本标签值得均值。定义回归误差: 分裂的回归指标即为(这个使用根节点的回归误差减去左右子树的回归误差,所以在下面要求对应的最大值的特征值) : 这也是类似的过程,用每个样本对用的特征值作为分裂阈值,对应的最大的回归指标的特征值即为我们根节点的分裂阈值。当然,这个回归指标也能对公式代入化简,简化一下计算。
4.sklearn实现
sklearn中用DecisionTreeClassifier类实现分类决策树,使用DecisionTreeRegressor类实现回归决策树。 一个用决策树做分类问题代码示例如下:
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn import tree
import matplotlib
def make_meshgrid(x, y, h=.02):
x_min, x_max = x.min() - 1, x.max() + 1
y_min, y_max = y.min() - 1, y.max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
np.arange(y_min, y_max, h))
return xx, yy
def plot_test_results(ax, clf, xx, yy, **params):
Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
ax.contourf(xx, yy, Z, **params)
iris = datasets.load_iris()
X = iris.data[:, :2]
y = iris.target
clf = tree.DecisionTreeClassifier()
clf.fit(X,y)
title = ('DecisionTreeClassifier')
fig, ax = plt.subplots(figsize = (5, 5))
plt.subplots_adjust(wspace=0.4, hspace=0.4)
X0, X1 = X[:, 0], X[:, 1]
xx, yy = make_meshgrid(X0, X1)
plot_test_results(ax, clf, xx, yy, cmap=plt.cm.coolwarm, alpha=0.8)
ax.scatter(X0, X1, c=y, cmap=plt.cm.coolwarm, s=20, edgecolors='k')
ax.set_xlim(xx.min(), xx.max())
ax.set_ylim(yy.min(), yy.max())
ax.set_xlabel('x1')
ax.set_ylabel('x2')
ax.set_xticks(())
ax.set_yticks(())
ax.set_title(title)
plt.show()
运行结果如下: 看运行结果,感觉决策树就像分段函数。
|