决策树
-
介绍
决策树是基于树状结构来进行决策的。决策过程提出的每个判定问题都是对某个属性的测试,测试的结果,或导出决策结果,或导出下一个判定问题,其考虑范围是上一次决策结果的限定范围内。
例如:一个瓜是否为好瓜,可以先判定瓜的色泽=?,然后根据判定结果,导出结果或者下一个判定问题。
一棵决策树包含一个根节点,若干个内部节点和若干个叶节点。叶节点对应于决策结果,而其他每个节点则对应于一个属性测试。 每个节点包含的样本集合根据属性测试分到子节点中,根节点包含样本节点全集。 -
涉及问题
- 属性测试划分,应该如何选择划分属性,即每次属性的划分如何选出最优的划分属性,什么情况下它是最优的。
- 如何处理拟合问题。(预剪枝,后剪枝的差别)
- 属性集中使用连续属性和当属性值缺失时如何处理。
决策树的基本流程遵循“分而治之”的策略,决策树基本算法是一个递归的过程,三种情况导致递归返回:
- 当前节点数据集只包含同一类别的数据
- 当前属性集为空或在所有样本在所有属性上取值相同,无法划分。(分类为含样本数最多的类)
- 当前节点包含数据集为空。(把当前节点标记为叶节点,但类别设定为其父节点所含样本最多的类别)
输入: 训练集D={(x_1,y_1),(x_2,y_2),...,(x_m,y_m)}
属性集A={a_1,a_2,...a_d}
过程:函数TreeGenerate(D,A)
生成节点node;
if D中的样本属于同一类别C:
将node标记为C类叶节点;return;
if A==空集 or D中样本在A中的取值都相同
将node标记为叶节点,其类别为D中样本数最多的类;
return;
从A中选择最优划分属性a*
for a* 的每个值 a*v:
为node生成一个分支,令D_v表示D中在a上取值为a*v的样本子集;
if D_v 为空
将分支标记为叶节点,其类别为D中样本数最多的类;return;
else:
以TreeGenerate(D_v, A\{a*});
输出:以node为根节点的一棵决策树。`
由上面的算法可知,决策树学习最关键的是如何选择最优的属性a*然后进行划分。
一般随着划分过程的进行,我们希望决策树分支节点所包含的类别尽可能的属于同一类别,即纯度越来越高。
- 划分的选择
先给出信息熵的定义:样本集合纯度常用的一种指标
Ent(D)=−∑k=1∣y∣Pklog2PkEnt(D)=-\sum_{k=1}^{|y|}{P_klog_2P_k}Ent(D)=−k=1∑∣y∣Pklog2Pk
PkP_kPk : D 中 k 类样本所占比例
属性划分选择 离散属性a有V个取值{a1,…av}。 若使用 a 属性来划分样本集 D,则有 V 个分支节点,其中第v个节点包含了 D 中所有在属性 a 上取值为 av 的样本,记为 Dv。 考虑到不同分支所包含的样本数不同,给分支节点赋予权重 |Dv| / |D|,即样本数越多的分支节点影响越大。
信息增益(ID3算法)
D: 当前样本集
k:样本第k类。k取值为1~|y|
V: 属性a的可能取值
信息增益:
Gain(D,a)=Ent(D)−∑v=1V∣Dv∣∣D∣Ent(Dv)Gain(D,a)=Ent(D)-\sum_{v=1}^V{\frac{|D^v|}{|D|}Ent(D^v)}Gain(D,a)=Ent(D)−v=1∑V∣D∣∣Dv∣Ent(Dv)
信息增益越大,使用该属性划分所获得的纯度提升越大。 信息增益准则对可取值数目较多的属性有所偏好
增益率(C4.5决策树算法)
GainRatio(D,a)=Gain(D,a)IV(a)GainRatio(D,a)=\frac{Gain(D,a)}{IV(a)}GainRatio(D,a)=IV(a)Gain(D,a)
IV(a)=−∑v=1V∣Dv∣∣D∣log2∣Dv∣∣D∣IV(a)=-\sum_{v=1}^{V}{\frac{|D^v|}{|D|}log_2\frac{|D_v|}{|D|}}IV(a)=−v=1∑V∣D∣∣Dv∣log2∣D∣∣Dv∣
其中 IV 为属性 a 的固有值,属性 a 的可能值越多,IV(a) 的值通常会越大。 增益率准则对可取值较少的属性有所偏好,因此C4.5算法并不直接选择增益率最大的属性来划分属性,而是先从候选划分属性中找出信息增益高于平均值的属性,再从中选择增益率最高的。
基尼指数(基尼不纯度):
Gini(D)=1−∑k=1∣y∣pk2Gini(D)=1-\sum_{k=1}^{|y|}{p_k^2}Gini(D)=1−k=1∑∣y∣pk2
Gini(D)反映了从数据集D中随机抽取两个样本,其类别标记不一致的概率,因此,Gini(D)越小,则数据集D的纯度越高。 属性a的基尼指数定义为:
GiniIndex(D,a)=∑v=1V∣Dv∣∣D∣Gini(Dv)GiniIndex(D,a)=\sum_{v=1}^{V}{\frac{|D_v|}{|D|}Gini(D^v)}GiniIndex(D,a)=v=1∑V∣D∣∣Dv∣Gini(Dv)
我们在候选集A中,选择那个使得划分后基尼指数最小的属性作为最优划分属性。
- 剪枝处理
剪枝是决策树算法对付过拟合的一种策略,决策树在进行节点划分的时候,有时候会由于分支过多而导致模型在训练集上学习得太好了,从而导致过拟合。 - 预剪枝
预剪枝指的是在决策树生成的分支的时候,先估计分支前的的决策树的泛化能力,在估计分支后的泛化能力,如果当前的分支不能时决策树的泛化能力提升,则不进行分支。 - 后剪枝
后剪枝指在决策树模型训练完毕后,自底向上的对所有非叶节点进行考察,若该非叶节点替换成叶节点,能使模型的泛化能力提升,则进行剪枝,将该节点替换为叶节点。
如何对模型的泛化能力进行估计呢,我们可以采用留出法,即在训练集中分出一部分验证集(区分于测试集),训练的时候用验证集进行估计,然后进行前后比较(信息增益,信息率或者基尼指数,主要看采用了那个算法)。
后剪枝决策树通常比预剪枝决策树保留了更多的分支,所以后剪枝的欠拟合风险可能更小,但是由于后剪枝是在训练完整个决策树后进行处理的,所以训练时间开销要更大一点。 - 连续值的处理
训练数据通常会有一些属性是连续(密度,含糖率之类的)的而不是离散的。连续属性的取值是无限的,所以不能对连续属性的可能取值来对节点进行划分。在 C4.5 算法中采用二分法来讲连续属性离散化。
即先对连续属性 aaa 进行排序,然后在相邻属性 ai−1a^{i-1}ai−1 和 aia^{i}ai 中取任意值,这样对所产生的划分结果是相同的,因此对连续属性(假设有 n 个取值)我们可以有 n-1 个候选划分集合。
Ta=ai+ai−12∣1≦i≦n−1T_a=\frac{{a^i + a^{i-1}}}{2} |1≦i≦n-1Ta=2ai+ai−1∣1≦i≦n−1
然后使用信息增益时,选择某个划分点,然后对左右进行划分,计算出信息增益
Gain(D,a)=Ent(D)−∑λ∈{−,+}∣Dv∣∣D∣Ent(Dv)Gain(D,a)=Ent(D)-\sum_{λ∈\{-,+\}}{\frac{|D^v|}{|D|}Ent(D^v)}Gain(D,a)=Ent(D)−λ∈{−,+}∑∣D∣∣Dv∣Ent(Dv)
然后选择最优的划分点。
需要注意的是连续属性在进行划分后,该属性还可以作为后代节点的划分属性。 - 缺失值的处理
需要解决的问题是:(1)如何在属性值缺失的情况下进行划分属性选择。(2)给定划分属性,若该样本在属性的值缺失,如何对样本进行划分。
(不太理解,后面补充) - 使用 sklearn 加深对决策树的印象
from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import load_iris
import numpy as np
import matplotlib.pyplot as plt
from sklearn.tree import export_graphviz
from sklearn import preprocessing
def draw(model,X,y):
h = 0.01
x_min, x_max = iris_X[:, 0].min() - 1, iris_X[:, 0].max() + 1
y_min, y_max = iris_X[:, 1].min() - 1, iris_X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h)) # 一维数组变为矩阵将第一个变为行向量,第二个为列向量
Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
plt.contourf(xx, yy, Z, cmap=plt.cm.Paired, alpha=0.8)
plt.scatter(X[y == 2, 0], X[y == 2, 1], c='y', marker='*', label='2')
plt.scatter(X[y == 1, 0], X[y == 1, 1], c='b', marker='x', label='1')
plt.scatter(X[y == 0, 0], X[y == 0, 1], c='r', marker='o', label='0')
plt.show()
iris = load_iris()
iris_X = iris.data[:, [2, 3]]
iris_y = iris.target
iris_X = preprocessing.scale(iris_X)#标准化
'''
criterion : string,optional(default =“gini”)
衡量划节点分的功能。支持的标准是基尼杂质的“gini”和信息增益的“entropy”。
splitter : string,optional(default =“best”)
用于在每个节点处选择拆分的策略。支持的策略是“best”选择最佳分割和“random”选择最佳随机分割
max_depth : int或None,可选(默认=无)
树的最大深度。如果为None,则扩展节点直到所有叶子都是纯的或直到所有叶子包含少于min_samples_split样本。
min_samples_split : int,float,optional(default = 2)
拆分内部节点所需的最小样本数:
'''
tree = DecisionTreeClassifier(criterion='entropy', max_depth=3, random_state=0)
tree.fit(iris_X, iris_y)
draw(tree,iris_X,iris_y)
export_graphviz(tree, out_file='tree.dot', feature_names=['petal length', 'petal width'])#安装graphviz做可视化树图 dot -Tpng tree.dot -o tree.png
'''
随机森林
使用bootstrap抽样方法随机选择n个样本用于训练(随机可重复)
(2)不重复随机选取d个特征,根据目标函数要求,如最大化信息增益,使用所选取的特征进行节点划分
重复(2)多次
汇总每课决策树的类标进行多数投票。
'''
'''
n_estimators : 整数,可选(默认= 10)
森林里的树木数量
criterion :(同上)
max_depth :(同上)
n_jobs: int或None,可选(默认=无)
处理器内核数量
'''
# from sklearn.ensemble import RandomForestClassifier
# forest = RandomForestClassifier(criterion='gini', n_estimators=5, random_state=1,n_jobs=2)
# forest.fit(iris_X,iris_y)
# draw(forest, iris_X, iris_y)
决策树分类:
随机森林: