决策树
1 决策树
1.1 决策树简介
决策树(Decision Tree)属于有监督学习算法,用于解决分类和回归问题。决策树模型能够从一系列带有特征和标签的数据中提炼出决策规则,通过树状结构将表格数据以及决策过程直观简明地表示出来。
树状图中每个内部节点表示对数据的某个特征进行判断,节点下的每个分枝表示输出一个判断结果,最后每个叶子节点表示一种标签的分类结果。
决策树中每个节点上的判断基本上都是if-else条件判断,因此便于理解中间的决策过程。
1.2 节点介绍
根节点:没有进边,有出边,包含最初的针对特征的提问。
中间节点:既有进边也有出边,进边只有一条,出边可以有很多条。都是针对特征的提问。
叶子节点:有进边,没有出边,每个叶子节点都是一个类别标签。
子节点和父节点:在两个相连的节点中,靠近根节点的是父节点,靠近叶子节点的是子节点。
树的根节点和中间节点对应的是样本的特征,如果选择不同的特征,将构建出不同的决策树。因此一个数据集可以生成许多种决策树。如果两个不同的决策树的决策性能相同,根据奥卡姆剃刀原理,通常会去选择结构更加简单的决策树。
2 构建决策树
2.1 问题引入
构建决策树主要是解决两个问题:
- 如何从数据表格中找出最佳节点或最佳分枝;
- 如何让决策树停止生长,防止过拟合。
决策树是基于训练集数据构建出来的,如果构建的决策树过于复杂,分枝过多,则这个决策树模型会过度依赖训练集数据,产生过拟合问题。
2.2 全局最优树
数据集的所有特征都可以作为叶子节点进行分枝,将所有的特征进行排列组合,可以构造出大量不同的决策树。这其中一定存在一棵树,其决策效果比其它决策树都好,那么这棵决策树就称为全局最优树。
全局最优:经过某种组合形成的决策树,整体来说分类效果最好。
局部最优:每一次分枝时都向着最佳的分类效果发展,但无法保证以局部最优的方式构建的树在全局上也是最优的。
有些算法用于寻找全局最优树,这些算法基本上都采用”贪心策略“,即通过局部最优来接近甚至达到全局最优。
2.3 不纯度
2.3.1 介绍
数据集中有的特征可以将数据集全部或大部分分开,这样的特征构成的节点称为最佳节点。如果将最佳节点放在前面,尽可能地将数据分开,之后只需要再补充一些特征节点就能实现分类。这样的决策树分层少,结构上简单,且分类效果好,几乎是全局最优树。
参照贪心策略,构建全局最优树需要从上向下在每个局部找出最佳节点,而衡量最佳的指标是不纯度。
2.3.2 节点分类策略
分类型决策树在节点上的决策规则是少数服从多数。在一个节点上,如果某一类标签所占的比例较大,那么这个节点下的样本都会被认为是属于这一类别。
例如,如果节点下90%的样本都属于类别A,那么如果一个新的测试样本被划分到这个节点下,就可以认为这个新样本也属于类别A,此时认为这个节点的不纯度很低。
但是,如果51%的样本属于类别A,49%的样本属于类别B,此时进入这个节点的测试样本有一半的可能性属于类别A,有一半的可能性属于类别B,那么就无法利用少数服从多数的分类策略对新样本进行分类,此时认为这个节点的不纯度非常高。
2.3.3 不纯度
决策树的每个根节点和中间节点下都会包含一组数据,数据中如果有某一类标签占有较大的比例,则说明这个节点很“纯”。某一类标签占的比例越大,叶子就越纯,不纯度就越低,分枝就越好。
反之,如果数据中各类标签数量差不多,则说明这个节点”不纯“,不纯度高,分枝不好。
不纯度是基于叶子节点来计算的,树中每个节点都会有一个不纯度,并且子节点的不纯度一定低于父节点, 也就是说,在同一棵决策树上,叶子节点的不纯度是最低的。
不纯度属于感性的,用高或低来描述,而模型中使用信息熵从数值上衡量节点的不纯度,即使用信息熵对不纯度进行量化。
2.4 信息熵与信息增益
2.4.1 信息熵
信息熵,entropy,用于度量信息的混乱程度。信息越有序,信息熵越低,不纯度越低。
信息熵与消除不确定性是相关联的。
信息熵越大,信息的不确定性越大,获得正确结果的代价越大;
信息熵越小,信息的不确定性越小,获得正确结果的代价越小。
E n t r o p y ( X ) = − ∑ i = 1 n p ( x i ) log 2 p ( x i ) Entropy(X) = -\sum_{i=1}^n p(x_i)\log_2p(x_i) Entropy(X)=−i=1∑np(xi)log2p(xi)
信息熵用于衡量系统的不确定性,即样本集
X
X
X的不纯度。
样本集
X
X
X中,50%的数据属于一个类别,50%的数据属于另一个类别。
E
n
t
r
o
p
y
(
X
)
=
−
0.5
log
2
0.5
−
0.5
log
2
0.5
=
1.0
Entropy(X) = -0.5\log_20.5 - 0.5\log_20.5 = 1.0
Entropy(X)=−0.5log20.5−0.5log20.5=1.0
信息熵的最大值可以取到1.0,此时系统拥有最高程度的不确定性,也可以说数据集不纯度达到最高。
相反,样本集
X
X
X中,所有数据都属于一个类别,没有数据属于另一个类别。
E
n
t
r
o
p
y
(
X
)
=
−
1.0
log
2
1.0
−
0.0
log
2
0.0
=
0.0
Entropy(X) = -1.0\log_21.0 - 0.0\log_20.0 = 0.0
Entropy(X)=−1.0log21.0−0.0log20.0=0.0
信息熵的最小值可以取到0.0,此时系统拥有最低程度的不确定性,也可以说数据集的不纯度达到最低,即数据集是纯的。
2.4.2 信息增益
数据中某个特征越重要,其对应的节点越应该处于决策树中靠上的节点位置。模型要从决策树的根节点开始自上而下的逐步判断,从而获得正确的分类结果。树中越靠上的节点的重要性越高,模型越能够减少分类的不确定性。
如何衡量决策树中节点(特征)的重要性?
如果一个节点在减少数据分类的不确定性方面越明显,则该节点就越重要。
可以使用信息增益衡量特征的重要性。
在根据某个特征对数据集进行分类前后,数据集信息熵发生的变化或差异程度称为信息增益,即
信
息
增
益
=
划
分
前
熵
−
划
分
后
熵
信息增益 = 划分前熵 - 划分后熵
信息增益=划分前熵−划分后熵。
G
a
i
n
(
S
,
A
)
=
E
n
t
r
o
p
y
(
S
)
−
∑
v
∈
A
S
v
S
E
n
t
r
o
p
y
(
S
v
)
Gain(S, A) = Entropy(S) - \sum_{v \in A} {{S_v} \over {S}}Entropy(S_v)
Gain(S,A)=Entropy(S)−v∈A∑SSvEntropy(Sv)
式中
S
v
S_v
Sv表示
S
S
S中特征A取值
v
v
v的子集。
信息增益作为决策树的划分依据,决定了决策树中每个特征对应的节点的存放位置。构建全局最优树需要计算出每个特征的信息增益,一个特征获得的信息增益越高,这个特征对应的节点就越重要,应该放在决策树越靠上的位置。
2.4.3 举例
1.1节的例子中计算特征District和Income的信息增益。
G
a
i
n
(
S
,
D
i
s
t
r
i
c
t
)
=
E
n
t
r
o
p
y
(
S
)
−
5
14
E
n
t
r
o
p
y
(
S
D
i
s
t
r
i
c
t
=
S
u
b
u
r
b
a
n
)
−
5
14
E
n
t
r
o
p
y
(
S
D
i
s
t
r
i
c
t
=
U
r
b
a
n
)
−
4
14
E
n
t
r
o
p
y
(
S
D
i
s
t
r
i
c
t
=
R
u
r
a
l
)
G
a
i
n
(
S
,
D
i
s
t
r
i
c
t
)
=
0.940
−
5
14
×
0.971
−
5
14
×
0.971
−
4
14
×
0.000
=
0.247
\begin{aligned} Gain(S, District) =& Entropy(S) - {5 \over 14}Entropy(S_{District=Suburban}) - {5 \over 14}Entropy(S_{District=Urban}) - {4 \over 14}Entropy(S_{District=Rural}) \\ Gain(S, District) =& 0.940 - {5 \over 14}\times0.971-{5 \over 14}\times0.971-{4 \over 14}\times0.000=0.247 \end{aligned}
Gain(S,District)=Gain(S,District)=Entropy(S)−145Entropy(SDistrict=Suburban)−145Entropy(SDistrict=Urban)−144Entropy(SDistrict=Rural)0.940−145×0.971−145×0.971−144×0.000=0.247
G
a
i
n
(
S
,
I
n
c
o
m
e
)
=
E
n
t
r
o
p
y
(
S
)
−
7
14
E
n
t
r
o
p
y
(
S
I
n
c
o
m
e
=
H
i
g
h
)
−
7
14
E
n
t
r
o
p
y
(
S
I
n
c
o
m
e
=
L
o
w
)
G
a
i
n
(
S
,
I
n
c
o
m
e
)
=
0.940
−
7
14
×
0.985
−
7
14
×
0.592
=
0.152
\begin{aligned} Gain(S, Income) =& Entropy(S) - {7 \over 14}Entropy(S_{Income=High}) - {7 \over 14}Entropy(S_{Income=Low}) \\ Gain(S, Income) =& 0.940 - {7 \over 14}\times0.985-{7 \over 14}\times0.592=0.152 \end{aligned}
Gain(S,Income)=Gain(S,Income)=Entropy(S)−147Entropy(SIncome=High)−147Entropy(SIncome=Low)0.940−147×0.985−147×0.592=0.152
特征District获得的信息增益大于特征Income,因此特征District的重要程度大于特征Income,在决策树中位置靠前。
2.5 ID3算法
2.5.1 ID3算法介绍
ID3算法,Interative Dichotomizer-3,是构建全局最优树算法的鼻祖。
ID3算法的核心是选择特征构造最佳节点。
ID3算法中使用了递归,在每一次递归中:
- 根据特征的信息增益找到最佳节点,使用这个节点对数据进行分类;
- 如果分类后每个子集数据都是同一类别的,无需进一步分类,则可以跳出递归,当前节点就是决策树的叶子节点,模型构建完毕;
- 如果分类后每个子集数据不是同一类别,需要根据剩余的特征再次找到最佳节点,进入下一次递归进行进一步分类,注意特征不能重复使用;
- 如果特征全部用完,分类的子数据集仍不纯,则按照少数服从多数原则对子集进行分类 ;
- 特例,如果某个子集数据为空,则对其父节点按照少数服从多数原则进行分类,将父节点的分类作为空子集的分类。
2.5.2 ID3的局限性
- 不能直接处理连续型数据集。
由于连续型数据集中数据的目标值是连续的,无法直接计算出各个类别下的样本数量。若使用ID3算法处理连续型数据,则需要提前对连续型数据进行离散化处理。 - 对缺失值比较敏感。
如果存在缺失值,则无法使用信息熵的公式进行计算,因此使用ID3算法前需要提前对缺失值进行处理。 - 容易出现过拟合问题,需要剪枝操作。
2.5.3 剪枝操作
剪枝操作就是在决策树中从下向上对分支进行合并操作。
一般需要将样本的训练集分出一部分作为验证集,这部分验证集不参与模型训练。对模型进行剪枝操作时,随着剪枝的增多,模型对验证集的分类结果一般是先变好再变坏,这样通过验证集可以确定剪枝操作所需进行的程度。
2.6 C4.5算法与信息增益比
C4.5算法引入了分支度(Information Value)的概念,对信息增益的计算方法进行修正。
使用分支度可以避免信息熵减小过快的特征对模型产生影响,尽量避免出现过拟合问题。
I
n
f
o
r
m
a
t
i
o
n
V
a
l
u
e
(
S
,
A
)
=
−
∑
v
∈
A
S
v
S
log
2
S
v
S
InformationValue(S, A) = -\sum_{v \in A}{S_v \over S} \log_2 {S_v \over S}
InformationValue(S,A)=−v∈A∑SSvlog2SSv
分支度会随着子节点上样本量的变小而逐渐变大,说明一个特征中如果标签分类太多,即使信息熵比较大,但分支度也比较大,则信息熵除以分支度后就会变小。
在C4.5算法中,分支度作为惩罚项加入到节点的信息熵计算中,使用之前的信息增益除以分支度作为分支的参考指标,这个指标被称作信息增益比(GainRatio)。
G
a
i
n
R
a
t
i
o
=
I
n
f
o
r
m
a
t
i
o
n
G
a
i
n
I
n
f
o
r
m
a
t
i
o
n
V
a
l
u
e
GainRatio = {InformationGain \over InformationValue}
GainRatio=InformationValueInformationGain
信息增益比是C4.5算法中衡量节点 重要程度的指标。信息增益比越大,则这个节点的信息增益越大,同时分支度较小。依靠信息增益比选出的最佳节点不需要将类别划分得特别细,就能够使数据的纯度得到大量提升。
2.7 CART树与基尼系数
CART的全称是分类与回归树,CART既可以用于分类问题,也可以用于回归问题。
CART与ID3和C4.5的不同之处在于CART生成的树必须是二叉树,也就是说,无论是回归问题还是分类问题,无论特征是离散的还是连续的,无论属性取值有两个还是多个,内部节点只能根据属性值分为两份。
CART树一般使用基尼系数衡量数据的不确定度。
G
i
n
i
=
1
−
∑
i
=
0
c
−
1
[
p
(
i
∣
t
)
]
2
Gini = 1-\sum^{c-1}_{i=0}{[p(i|t)]^2}
Gini=1−i=0∑c−1[p(i∣t)]2
2.8 如何选取算法
- 在实际使用中,信息熵和基尼系数的效果基本相同,信息熵的计算比基尼系数缓慢一些,因为基尼系数的计算不需要处理对数。
- 信息熵对不纯度更加敏感,因此使用信息熵作为指标时,决策树的生长会更加“精细”,因此对于高维数据或者噪音很多的数据,信息熵很容易过拟合,基尼系数在这种情况下效果往往比较好。
- 当模型拟合程度不足的时候,即当模型在训练集和测试集上都表现不太好的时候,推荐使用信息熵。
3 代码实现
3.1 DecisionTreeClassifier
class sklearn.tree.DecisionTreeClassifier(criterion=’gini’, splitter=’best’, max_depth=None, min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_features=None, random_state=None, max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, class_weight=None, presort=False)
主要参数介绍
criterion:选择结点划分质量的度量标准,默认使用‘gini’,即基尼系数,基尼系数是CART算法中采用的度量标准,该参数还可以设置为 “entropy”,表示信息增益,是C4.5算法中采用的度量标准。
splitter:结点划分时的策略,默认使用‘best’。‘best’ 表示依据选用的criterion标准,选用最优划分属性来划分该结点,一般用于训练样本数据量不大的场合,因为选择最优划分属性需要计算每种候选属性下划分的结果;该参数还可以设置为“random”,表示最优的随机划分属性,一般用于训练数据量较大的场合,可以减少计算量,但是具体如何实现最优随机划分暂时不太明白,这需要查看该部分的源码。
max_depth:设置决策树的最大深度,默认为None。None表示不对决策树的最大深度作约束,直到每个叶子结点上的样本均属于同一类,或者少于min_samples_leaf参数指定的叶子结点上的样本个数。也可以指定一个整型数值,设置树的最大深度,在样本数据量较大时,可以通过设置该参数提前结束树的生长,改善过拟合问题,但一般不建议这么做,过拟合问题还是通过剪枝来改善比较有效。
min_samples_split:当对一个内部结点划分时,要求该结点上的最小样本数,默认为2。
min_samples_leaf:设置叶子结点上的最小样本数,默认为1。当尝试划分一个结点时,只有划分后其左右分支上的样本个数不小于该参数指定的值时,才考虑将该结点划分,换句话说,当叶子结点上的样本数小于该参数指定的值时,则该叶子节点及其兄弟节点将被剪枝。在样本数据量较大时,可以考虑增大该值,提前结束树的生长。
min_weight_fraction_leaf:在引入样本权重的情况下,设置每一个叶子节点上样本的权重和的最小值,一旦某个叶子节点上样本的权重和小于该参数指定的值,则该叶子节点会联同其兄弟节点被减去,即其父结点不进行划分。该参数默认为0,表示不考虑权重的问题,若样本中存在较多的缺失值,或样本类别分布偏差很大时,会引入样本权重,此时就要谨慎设置该参数。
max_features:划分结点、寻找最优划分属性时,设置允许搜索的最大属性个数,默认为None。假设训练集中包含的属性个数为n,None表示搜索全部n个的候选属性;‘auto’表示最多搜索sqrt(n)个属性;sqrt表示最多搜索sqrt(n)个属性;‘log2’表示最多搜索log2(n)个属性;用户也可以指定一个整数k,表示最多搜索k个属性。需要说明的是,尽管设置了参数max_features,但是在至少找到一个有效(即在该属性上划分后,criterion指定的度量标准有所提高)的划分属性之前,最优划分属性的搜索不会停止。
random_state:当将参数splitter设置为‘random’时,可以通过该参数设置随机种子号,默认为None,表示使用np.random产生的随机种子号。
max_leaf_nodes:设置决策树的最大叶子节点个数,该参数与max_depth等参数参数一起,限制决策树的复杂度,默认为None,表示不加限制。
min_impurity_decrease:打算划分一个内部结点时,只有当划分后不纯度(可以用criterion参数指定的度量来描述)减少值不小于该参数指定的值,才会对该结点进行划分,默认值为0。可以通过设置该参数来提前结束树的生长。
min_impurity_split:打算划分一个内部结点时,只有当该结点上的不纯度不小于该参数指定的值时,才会对该结点进行划分,默认值为1e-7。该参数值0.25版本之后将取消,由min_impurity_decrease代替。
class_weight:设置样本数据中每个类的权重,这里权重是针对整个类的数据设定的,默认为None,即不施加权重。用户可以用字典型或者字典列表型数据指定每个类的权重,假设样本中存在4个类别,可以按照 [{0: 1, 1: 1}, {0: 1, 1: 5}, {0: 1, 1: 1}, {0: 1, 1: 1}] 这样的输入形式设置4个类的权重分别为1、5、1、1,而不是 [{1:1}, {2:5}, {3:1}, {4:1}]的形式。该参数还可以设置为‘balance’,此时系统会按照输入的样本数据自动的计算每个类的权重,计算公式为:n_samples / ( n_classes * np.bincount(y) ),其中n_samples表示输入样本总数,n_classes表示输入样本中类别总数,np.bincount(y) 表示计算属于每个类的样本个数,可以看到,属于某个类的样本个数越多时,该类的权重越小。若用户单独指定了每个样本的权重,且也设置了class_weight参数,则系统会将该样本单独指定的权重乘以class_weight指定的其类的权重作为该样本最终的权重。
presort:设置对训练数据进行预排序,以提升结点最优划分属性的搜索,默认为False。在训练集较大时,预排序会降低决策树构建的速度,不推荐使用,但训练集较小或者限制树的深度时,使用预排序能提升树的构建速度。
3.2 案例 红酒
3.2.1 构建模型
使用datasets中的红酒数据集
from sklearn import tree
from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
dt = load_wine()
feature = dt.data
target = dt.target
x_train, x_test, y_train, y_test = train_test_split(feature, target, test_size=0.2, random_state=2020)
clf = DecisionTreeClassifier(criterion='entropy')
clf.fit(x_train, y_train)
clf.score(x_test, y_test) # 0.9722222222222222
注意,每次测评的分数不一致。
决策树的构建是通过在局部优化节点来逼近甚至达到一棵全局最优树,但是局部的最优节点不能保证构建出一颗全局最优的树。
sklearn表示,既然一棵树不能保证最优,那么就构建许多不同的树,然后从中选择最好的。
因此在每次分枝时,不使用全部特征,而是随机选取一部分特征,从中选取不纯度相关指标最优的作为分枝用的节点。这样每次生成的树也就不同了。
参数random_state用来设置分枝中的随机模式的参数,默认None,在高维度时随机性会表现得更明显,但是对于低维度的数据(例如鸢尾花数据集),随机性几乎不会显现,即使输入任意整数,也会获得同一棵树。
3.2.2 特征的重要程度
利用决策树计算出各个特征的重要程度。
clf.feature_importances_
'''
array([0.0147939 , 0. , 0. , 0.04012018, 0. ,
0. , 0.4874266 , 0.01618011, 0. , 0.2763588 ,
0. , 0. , 0.16512041])
'''
feature_name = ['酒精', '苹果酸', '灰', '灰的碱性', '镁', '总酚', '类黄酮', '非黄烷类酚类', '花青素', '颜 色强度', '色调', 'od280/od315稀释葡萄酒', '脯氨酸']
[*zip(feature_name, clf.feature_importances_)]
'''
[('酒精', 0.01479390227172555),
('苹果酸', 0.0),
('灰', 0.0),
('灰的碱性', 0.04012017944401059),
('镁', 0.0),
('总酚', 0.0),
('类黄酮', 0.48742660098390633),
('非黄烷类酚类', 0.01618010841300014),
('花青素', 0.0),
('颜 色强度', 0.2763587955899273),
('色调', 0.0),
('od280/od315稀释葡萄酒', 0.0),
('脯氨酸', 0.16512041329743002)]
'''
可以看出,特征类黄酮的重要程度最高,约为0.487,因此特征类黄酮对应的节点将作为决策树的根节点。
参数splitter
- splitter也是用来控制决策树中的随机选项的,有两种输入值:
输入”best",决策树在分枝时虽然随机,但是还是会优先选择更重要的特征进行分枝(重要性可以通过属性feature_importances_查看)
输入“random",决策树在分枝时会更加随机,树会因为含有更多的不必要信息而更深更大,并因这些不必要信息而降低对训练集的拟合,这也是避免过拟合的一种方式。 - 当你预测到你的模型会过拟合,用splitter和random_state这两个参数来帮助你降低树建成之后过拟合的可能性。
3.2.3 绘制并保存决策树
备注,Graphviz的版本是2.28。
import graphviz
feature_name = ['酒精', '苹果酸', '灰', '灰的碱性', '镁', '总酚', '类黄酮', '非黄烷类酚类', '花青素', '颜色强度', '色调', 'od280/od315稀释葡萄酒', '脯氨酸']
dot_data = tree.export_graphviz(
clf,
out_file=None, # 图片保存路径
feature_names=feature_name,
class_names=["琴酒", "雪莉", "贝尔摩德"],
filled=True, # 使用颜色表示分类结果
rounded=True, # 树节点为圆角矩形
)
graph = graphviz.Source(dot_data)
根节点中类黄酮 <= 2.31 表示样本中特征类黄酮的值小于等于2.31的数据都会被划分到这个节点下。
根节点类黄酮的信息熵entropy为1.571,包含样本数量为142,标签"琴酒"、“雪莉”、“贝尔摩德"对应的样本数量分别为46,56和40,按照少数服从多数的原则,这个节点被划分的标签为"雪莉”。
保存图片
feature_name = ['酒精', '苹果酸', '灰', '灰的碱性', '镁', '总酚', '类黄酮', '非黄烷类酚类', '花青素', '颜色强度', '色调', 'od280/od315稀释葡萄酒', '脯氨酸']
with open("tree.dot", 'w') as f:
tree.export_graphviz(
clf,
out_file=f,
feature_names=feature_name,
class_names=["琴酒", "雪莉", "贝尔摩德"],
filled=True,
rounded=True,
)
!dot -Tpng tree.dot -Gcharset=latin1 -o output.png
!dot -Tpdf tree.dot -Gcharset=latin1 -o output.pdf
3.2.4 剪枝参数
在不加限制的情况下,一棵决策树会生长到衡量不纯度的指标最优,或者没有更多的特征可用为止。这样的决策树 往往会过拟合,这就是说,它会在训练集上表现很好,在测试集上却表现糟糕。我们收集的样本数据不可能和整体 的状况完全一致,因此当一棵决策树对训练数据有了过于优秀的解释性,它找出的规则必然包含了训练样本中的噪 声,并使它对未知数据的拟合程度不足。
为了让决策树有更好的泛化性,我们要对决策树进行剪枝。剪枝策略对决策树的影响巨大,正确的剪枝策略是优化 决策树算法的核心。sklearn为我们提供了不同的剪枝策略:
- max_depth:限制树的最大深度,超过设定深度的树枝全部剪掉。
这是用得最广泛的剪枝参数,在高维度低样本量时非常有效。决策树多生长一层,对样本量的需求会增加一倍,所 以限制树深度能够有效地限制过拟合。在集成算法中也非常实用。实际使用时,建议从=3开始尝试,看看拟合的效 果再决定是否增加设定深度。 - min_samples_leaf & min_samples_split:
min_samples_leaf限定,一个节点在分枝后的每个子节点都必须包含至少min_samples_leaf个训练样本,否则分 枝就不会发生,或者,分枝会朝着满足每个子节点都包含min_samples_leaf个样本的方向去发生。一般搭配max_depth使用。这个参数的数量设置得太小会引 起过拟合,设置得太大就会阻止模型学习数据。一般来说,建议从=5开始使用。
min_samples_split限定,一个节点必须要包含至少min_samples_split个训练样本,这个节点才允许被分枝,否则 分枝就不会发生。
import graphviz
clf = tree.DecisionTreeClassifier(
criterion="entropy",
random_state=30,
splitter="random",
max_depth=4,
min_samples_leaf=10,
min_samples_split=10
)
clf = clf.fit(x_train, y_train)
dot_data = tree.export_graphviz(
clf,
feature_names=feature_name,
class_names=["琴酒", "雪莉", "贝尔摩德"],
filled=True,
out_file=None # 图片保存路径
)
graph = graphviz.Source(dot_data)
3.3 案例 泰坦尼克号乘客生存分类
3.3.1 数据介绍
目标:使用决策树模型预测哪些人可能幸存。
特征介绍
特征名 | 描述 |
---|---|
Survived | 是否生存(标签) |
Pclass | 船票等级,表示乘客社会经济地位。 |
Name | 姓名 |
Sex | 性别 |
Age | 年龄 |
SibSp | 船上的兄弟姐妹数和配偶数 |
Parch | 船上的父母数量和子女数量 |
Ticket | 船票号 |
Fare | 票价 |
Cabin | 船舱号 |
Embarked | 登船港口号 |
3.3.2 代码实现
导入数据
import pandas as pd
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
data = pd.read_csv(r"./datasets/data.csv",index_col = 'PassengerId')
data.head()
'''
Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
PassengerId
1 0 3 Braund, Mr. Owen Harris male 22.0 1 0 A/5 21171 7.2500 NaN S
2 1 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 0 PC 17599 71.2833 C85 C
3 1 3 Heikkinen, Miss. Laina female 26.0 0 0 STON/O2.3101282 7.9250 NaN S
4 1 1 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 0 113803 53.1000 C123 S
5 0 3 Allen, Mr. William Henry male 35.0 0 0 373450 8.0500 NaN S
'''
data.info()
'''
<class 'pandas.core.frame.DataFrame'>
Int64Index: 891 entries, 1 to 891
Data columns (total 11 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Survived 891 non-null int64
1 Pclass 891 non-null int64
2 Name 891 non-null object
3 Sex 891 non-null object
4 Age 714 non-null float64
5 SibSp 891 non-null int64
6 Parch 891 non-null int64
7 Ticket 891 non-null object
8 Fare 891 non-null float64
9 Cabin 204 non-null object
10 Embarked 889 non-null object
dtypes: float64(2), int64(4), object(5)
memory usage: 83.5+ KB
'''
数据预处理
删除缺失值较多的特征以及无关的特征
data.drop(labels=['Cabin', 'Name', 'Ticket'], inplace=True, axis=1)
填充age列
data["Age"] = data["Age"].fillna(data["Age"].mean())
清洗空值
data.dropna(inplace=True)
将性别特征由文字转换为数值
data["Sex"] = (data["Sex"]== "male").astype("int")
将登船港口号特征由三分类变量转换为数值
data["Embarked"].unique().tolist() # ['S', 'C', 'Q']
labels = data["Embarked"].unique().tolist()
data["Embarked"] = data["Embarked"].map(lambda x: labels.index(x))
准备特征和标签
X = data.iloc[:, data.columns != "Survived"]
y = data.iloc[:, data.columns == "Survived"]
划分数据集
from sklearn.model_selection import train_test_split
Xtrain, Xtest, Ytrain, Ytest = train_test_split(X, y, test_size=0.3)
训练模型
from sklearn.tree import DecisionTreeClassifier
clf = DecisionTreeClassifier(random_state=25)
clf = clf.fit(Xtrain, Ytrain)
score = clf.score(Xtest, Ytest) # 0.8089887640449438
3.4 用网格搜索调整参数
网格搜索,Grid Search,是一种调参手段,属于穷举搜索。
在所有候选的参数选择中,通过循环遍历,尝试每一种可能性,表现最好的参数就是最终的结果。
为什么叫网格搜索?
以有两个参数的模型为例,参数a有3种可能,参数b有4种可能,把所有可能性列出来,可以表示成一个3*4的表格,其中每个cell就是一个网格,循环过程就像是在每个网格里遍历、搜索,所以叫网格搜索。
import numpy as np
from sklearn.model_selection import GridSearchCV
parameters = {
'splitter': ('best', 'random'),
'criterion': ('gini', 'entropy'),
'max_depth': [*range(1, 10)],
'min_samples_leaf': [*range(1, 50, 5)],
}
clf = DecisionTreeClassifier(random_state=25)
GS = GridSearchCV(clf, parameters, cv=10)
GS.fit(Xtrain, Ytrain)
GS.best_params_
'''
{'criterion': 'entropy',
'max_depth': 6,
'min_samples_leaf': 6,
'splitter': 'best'}
'''
GS.best_score_ # 0.8184587813620071