决策树及树模型的参数选择
决策树的基本概念
在数据结构中树是一个重要的数据结构,这里树被我们根据分支起到一个决策的作用。什么是决策?通俗的说就是判断或者决定,我们引用周志华的西瓜书中的例子:这是一个好瓜吗?当我们对这个问题进行决策的时候通常会进行一系列的子决策或者判断,比如,我们先看“它是什么颜色的?”,如果是“青绿色”我们再看“它的根蒂是什么形态的?”,如果是“蜷缩”,我们再判断“它敲起来的声音是什么样的?”,最终我们判断它是一个好瓜。这样的一个判断的过程就是决策,是基于下面的树的结构完成的。
决策树有下面几个比较重要的概念:
- 树模型:
决策树:从根节点慢慢走到叶子结点的过程就是决策的过程
最后所用的数据都会落到叶子结点,既可以做分类也可以做回归,根据我们的需要去建成一棵分类树或者是回归树
- 树的组成:
根节点:第一个选择点。在上面的例子中根节点就是“它是什么颜色的?”
我们选择根节点一般要满足这个结点能够区分绝大部分的结点或者说是分类的效果比其他的结点的效果要好。
非叶子节点:未到判断的结果,是判断的中间过程的结点,比如上面的例子“它的根蒂是什么颜色的?”就是一个非叶子节点
叶子结点:最终的决策结果,即是一个好瓜还是不是一个好瓜
-
节点
节点就是一个划分标准,增加一个节点相当于在数据中切了一刀。 -
决策树的训练与测试
- 训练阶段:从给定的训练集构造出一棵树(从节点开始选择特征,如何进行特征切分)
- 测试阶段:根据构造出来的树模型,从上到下走一遍就好了
无论是分类还是回归都只需要在树上走一回就好了,所以关键就是要构造出这么一棵决策树。
特征的切分
- 问题:根节点应该选择哪个特征呢?接下来应该如何切分?
根节点可以大致分成两类,且效率较高,分类效果较好
接下来选择剩下的特征中分类效果最好的,其他的以此类推即可。那么分类效果好的标准是什么呢?我们的目标就是要通过一种衡量标准,来计算不同的特征分支选择后的情况,找出“最高分”的当做那个根节点,其余的以此类推。
分类标准的确定
熵的定义
熵:表示随机变量的不确定程度的量(物体内部的混乱程度,比如杂货铺里面什么都有,混乱程度高;专卖店里面只卖一个牌子,较为稳定)
计算的方法:
H
(
x
)
=
−
∑
i
=
1
n
P
i
l
o
g
P
i
H(x)=-\sum\limits_{i=1}^nP_ilogP_i
H(x)=−i=1∑nPilogPi
公式中的
P
i
P_i
Pi为取到某一个类别的概率。
对于下面的例子,
有两个集合,分别记作A和B,有:
A
=
[
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
2
,
2
]
B
=
[
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
,
1
]
A=[1,1,1,1,1,1,1,1,2,2]\\ B=[1,2,3,4,5,6,7,8,9,1]\\
A=[1,1,1,1,1,1,1,1,2,2]B=[1,2,3,4,5,6,7,8,9,1]
显然A的熵值低,因为A里面只有两种类别,相对稳定,而B中类别较多,熵值也就更大,那么在分类任务中我们是希望通过分支节点后数据类别的熵值更大还是更小呢?当然是希望得到一个熵值更小的集合,这样才满足我们分类的过程越分类越纯的特征。那么通过对不同的特征进行分类后的数据集的熵值的大小比较,将他们从小到大的排列,我们就可以得到从根节点到下的一系列的结点了。
熵的特征
我们观察一下 H ( x ) H(x) H(x)的图像:
当熵越大时,数据集的不确定性也就越大
- 当 P i = 0 P_i=0 Pi=0或者 P i = 1 P_i=1 Pi=1时, H ( x ) = 0 H(x)=0 H(x)=0,数据完全确定,随机变量没有不确定性
- 当 P i = 0.5 P_i=0.5 Pi=0.5时, H ( x ) = 1 H(x)=1 H(x)=1,此时的随机变量的不确定度最大
那么如何选择一个节点呢?
这里我们引入一个概念:信息增益,表示特征X使类Y的不确定度减少的程度。
那么我们决策树的构造过程就是根据信息增益的大小排名的,某个特征的信息增益越大,那么他就在决策树中处于越“高”的结点位置。下面我们举例说明一个决策树的构造的过程:
决策树的分类标准算法选择
信息增益存在的问题
不同的算法的分类标准的选择是不同的
不同的分类标准
ID3:信息增益
(引入ID划分的例子)
C4.5:信息增益率,需要考虑自身的熵,计算方法为 信 息 增 益 自 身 的 熵 \frac{信息增益}{自身的熵} 自身的熵信息增益
GART:使用GINI系数来当做衡量的标准,GINI系数和熵的衡量的标准相似,只是计算的方式不同。
G i n i ( p ) = ∑ k = 1 K P k ( 1 − P k ) = 1 − ∑ k = 1 K P k 2 Gini(p)=\sum\limits_{k=1}^KP_k(1-P_k)=1-\sum\limits_{k=1}^KP_k^2 Gini(p)=k=1∑KPk(1−Pk)=1−k=1∑KPk2
连续问题的处理
连续问题离散化
常常在连续值中选取结点进行划分,即二分的方式将连续的问题进行离散化处理。
决策树的剪枝策略
为什么要剪枝呢?因为决策树的过拟合的风险太大:未剪枝时,当建立完一棵树后,在训练集上的效果很好,但是在测试集上效果很差,这种现象就叫做过拟合。
如果决策树很大的话,那么一个叶子上只有一个数据,理论上可以完全分的开数据。
剪枝的策略:
- 预剪枝
- 边建立决策树边剪枝(更实用,更容易实现)。
- 限制深度——特征数。(最常用)
- 叶子结点的个数——达到叶子节点数后不再分裂
- 叶子结点的样本数——各自叶子节点的样本数要小于一定的值
- 信息增益量等
- 判断一下这个过多的特征是不是都要用上,去掉不必要的特征。建立的时候其实在不断的简化,减少了时间复杂度。
- 后剪枝
- 在决策树建完之后再进行剪枝的操作。通过一定的衡量标准 C α ( T ) = C ( T ) + α ⋅ ∣ T l e a f ∣ C_\alpha(T)=C(T)+\alpha·|T_{leaf}| Cα(T)=C(T)+α⋅∣Tleaf∣来判断这个结点是否有必要进行分裂。
决策树比较于其他的分类,比如支持向量机,神经网络等,具有较好的可视化效果。方便进行数据的分析。
决策树案例分析
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
从sklearn的dataset内置数据集中导入房价数据集,直接作为后续的数据样例
from sklearn.datasets.california_housing import fetch_california_housing
housing = fetch_california_housing()
print(housing.DESCR)
.. _california_housing_dataset:
California Housing dataset
--------------------------
**Data Set Characteristics:**
:Number of Instances: 20640
:Number of Attributes: 8 numeric, predictive attributes and the target
:Attribute Information:
- MedInc median income in block
- HouseAge median house age in block
- AveRooms average number of rooms
- AveBedrms average number of bedrooms
- Population block population
- AveOccup average house occupancy
- Latitude house block latitude
- Longitude house block longitude
:Missing Attribute Values: None
This dataset was obtained from the StatLib repository.
http://lib.stat.cmu.edu/datasets/
The target variable is the median house value for California districts.
This dataset was derived from the 1990 U.S. census, using one row per census
block group. A block group is the smallest geographical unit for which the U.S.
Census Bureau publishes sample data (a block group typically has a population
of 600 to 3,000 people).
It can be downloaded/loaded using the
:func:`sklearn.datasets.fetch_california_housing` function.
.. topic:: References
- Pace, R. Kelley and Ronald Barry, Sparse Spatial Autoregressions,
Statistics and Probability Letters, 33 (1997) 291-297
# 观察一下数据集的规模
housing.data.shape
(20640, 8)
# 注意,这不再是一个DataFrame对象了,是由一个个的ndarray组成的。
print(type(housing))
print(type(housing.data))
<class 'sklearn.utils.Bunch'>
<class 'numpy.ndarray'>
housing.data[0]
array([ 8.3252 , 41. , 6.98412698, 1.02380952,
322. , 2.55555556, 37.88 , -122.23 ])
模型的建立
- 实例化模型
在实例化模型的时候,我们就需要传入一个参数了,这里我们传入的就是树的最大深度:max_depth - 模型的建立
这个数据集中有很多的特征,在这里我们只选取其中的两个特征:精度和维度作为分类的标准
这里传入数据即可,第一个参数为选用的特征数据,第二个为标签值
from sklearn import tree
dtr = tree.DecisionTreeRegressor(max_depth = 2)
dtr.fit(housing.data[:,[6,7]], housing.target)
DecisionTreeRegressor(max_depth=2)
树模型参数:
-
1.判断的标准criterion: gini or entropy
-
2.splitter best or random 前者是在所有特征中找最好的切分点 后者是在部分特征中(数据量大的时候)
特征量不大的时候默认即可
- 3.max_features None(所有),log2,sqrt,N 特征小于50的时候一般使用所有的
一般都默认所有的,不会去进行选择
- 4.max_depth 数据少或者特征少的时候可以不管这个值,如果模型样本量多,特征也多的情况下,可以尝试限制下
经常决策树的最大深度,预剪枝的核心模块:限制树的深度。特征较多的时候使用。
- 5.min_samples_split 如果某节点的样本数少于min_samples_split,则不会继续再尝试选择最优特征来进行划分如果样本量不大,不需要管这个值。如果样本量数量级非常大,则推荐增大这个值。
经常叶子结点的样本数分裂限
- 6.min_samples_leaf 这个值限制了叶子节点最少的样本数,如果某叶子节点数目小于样本数,则会和兄弟节点一起被剪枝,如果样本量不大,不需要管这个值,大些如10W可是尝试下5
限制叶子结点的数量,也是剪枝的时候使用
- 7.min_weight_fraction_leaf 这个值限制了叶子节点所有样本权重和的最小值,如果小于这个值,则会和兄弟节点一起被剪枝默认是0,就是不考虑权重问题。一般来说,如果我们有较多样本有缺失值,或者分类树样本的分布类别偏差很大,就会引入样本权重,这时我们就要注意这个值了。
剪枝方法
- 8.max_leaf_nodes 通过限制最大叶子节点数,可以防止过拟合,默认是"None”,即不限制最大的叶子节点数。如果加了限制,算法会建立在最大叶子节点数内最优的决策树。如果特征不多,可以不考虑这个值,但是如果特征分成多的话,可以加以限制具体的值可以通过交叉验证得到。
剪枝方法
-
9.class_weight 指定样本各类别的的权重,主要是为了防止训练集某些类别的样本过多导致训练的决策树过于偏向这些类别。这里可以自己指定各个样本的权重如果使用“balanced”,则算法会自己计算权重,样本量少的类别所对应的样本权重会高。
-
10.min_impurity_split 这个值限制了决策树的增长,如果某节点的不纯度(基尼系数,信息增益,均方差,绝对差)小于这个阈值则该节点不再生成子节点。即为叶子节点 。
-
n_estimators:要建立树的个数
数据可视化设置
要进行树模型的可视化,首先要安装一个工具:graphviz , http://www.graphviz.org/Download.php
dot_data = \
tree.export_graphviz(
dtr, #这里也是根据需要修改的
out_file = None,
feature_names = housing.feature_names[6:8], #这里是需要修改的
filled = True,
impurity = False,
rounded = True
)
# pip install pydotplus
import pydotplus
graph = pydotplus.graph_from_dot_data(dot_data)
graph.get_nodes()[7].set_fillcolor("#70DB93") #颜色可以改
from IPython.display import Image
Image(graph.create_png())
# 树的可视化方法其实有很多,但大都涉及命令行的额外操作,这种方法是最为直接的方式
# 存储图片
graph.write_png("dtr_white_background.png")
True
树模型的参数选择
首先我们调用train_test_split函数进行训练集和测试集的划分,test_size意为测试集的比例,random_size的设置保证每一次的数据是相同的,以便选择出树模型的最佳参数.
from sklearn .model_selection import train_test_split
data_train, data_test, target_train, target_test = \
train_test_split(housing.data, housing.target, test_size = 0.1, random_state = 42)
dtr = tree.DecisionTreeRegressor(random_state=42)
dtr.fit(data_train, target_train)
dtr.score(data_test, target_test)
0.6310922690494536
##### 随机森林模块
from sklearn.ensemble import RandomForestRegressor
rfr = RandomForestRegressor( random_state = 42)
rfr.fit(data_train, target_train)
rfr.score(data_test, target_test)
0.8103647255362918
设置参数组合,选择最佳的参数
from sklearn.model_selection import GridSearchCV
#设定最小分裂的样本数参数min_samples_split和树的个数,一半写成字典的形式
tree_param_grid = {"min_samples_split": list((3,6,9)),"n_estimators":list((10,50,100))}
#实例化一个对象,传入指定的算法(这里是随机森林),参数为设定为参数候选项字典,cv为几次的交叉验证
grid = GridSearchCV(RandomForestRegressor(),param_grid = tree_param_grid, cv=5)
#打印出来最后的结果:
#grid.cv_results_["mean_test_score"],
grid.best_params, grid.best_score
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-21-d5c16f26a104> in <module>
6 #打印出来最后的结果:
7 #grid.cv_results_["mean_test_score"],
----> 8 grid.best_params, grid.best_score
AttributeError: 'GridSearchCV' object has no attribute 'best_params'
这里输出展示还有问题,原写法是grid.grid_results_,输出结果也是报错。查了一些资料显示grid_results已经过时,应该使用cv_results_
,但是输出还是有问题。没找到解决的方法,希望有大佬指正。