文章目录
决策树
组成
- 一个根节点: 包含样本全集
- 若干内部结点: 对应的属性测试
- 若干叶结点: 决策结果
目的
- 为了产生一颗泛化能力强的,处理未见示例能力强的决策树。
遵循的策略
- 分而治之
流程
- 1 取数据集D, 和属性集A
- 2 生成根节点
- 3 判断D是否完全属于同一类
– 如果是,就不需要继续决策 - 4 判断属性集A是否为空 或者 数据集D在属性集A中的取值相同
– 如果是,则无法决策 - 5 从属性集A中选出最优划分属性a*
- 6 得到D在a*上的子集
- 7 判断D在a*上的子集是否为空
– 是则不能判断 - 8 将D在a*的每个值上的子集作为生成一个分支,除a *以外的属性集作为新的属性集,递归进入下次决策
分析
决策树的生成是一个递归过程,这个递归的跳出条件有三种,分别对应流程中第3 4 7步的判断。
△划分选择
决策树学习的关键是如何选择最优划分属性。
判断最优划分属性的的依据是随着划分过程不断进行,我们希望分支结点所包含的样本尽可能属于同一类,即结点的纯度越来越高。
简单的说就是每一次根据某个条件分类之后,尽可能使样本都符合这个条件,说明我们的分类条件是极具区分意义的,能够明显的将样本分开。
信息增益-ID3决策树
信息熵
首先我们用信息熵来度量样本集合的纯度,一个样本集合的信息熵越低,则其纯度越高。
信息熵定义:
E
n
t
(
D
)
=
−
∑
k
=
1
∣
y
∣
p
k
log
2
p
k
Ent(D)=-\sum_{k=1}^{\left|y\right|}p_k\log_2p_k
Ent(D)=−k=1∑∣y∣pklog2pk
D
D
D指样本集
y
y
y指样本总共有多少类
k
k
k指第k类样本
p
k
p_k
pk指第k类样本在D中的比例
信息增益定义
假设一个属性有多种取值,根据每一种取值对样本集进行划分都可以得到一个子集,也即一个分支结点。我们将某一个结点作为一个数据集,通过信息熵公式得到这个结点的信息熵,再考虑到同样的属性不同的取值,划分总数据集而来的结点,所含的样本数不一定一相同,所以根据每个结点所包含的样本数量,比上总数据集的数量,作为这个子集的权重,与这个子集的信息熵相乘,得到一个结果。
重复上述步骤,将这个属性所有的取值所划分得到的子集,进行计算,再求和,与样本总集的信息熵想减,即得到这个属性对总数据集的信息增益。
所以信息增益定义为:
G
a
i
n
(
D
,
a
)
=
E
n
t
(
D
)
−
∑
v
=
1
V
∣
D
v
∣
∣
D
∣
E
n
t
(
D
v
)
Gain(D, a)=Ent(D)-\sum_{v=1}^V\frac{\left|D^v\right|}{\left|D\right|}Ent(D^v)
Gain(D,a)=Ent(D)−v=1∑V∣D∣∣Dv∣Ent(Dv)
D
D
D指样本集
a
a
a指属性
v
v
v和
V
V
V指
a
a
a所有可能的取值
D
v
D^v
Dv指属性值为
v
v
v时划分得到的子集
根据信息增益求最优划分属性
信息增益越大,意味着由这个属性来进行划分对纯度的提升越大,即对决策的帮助越大。
所以对每个属性均求出信息增益,再取最大的那个,就是最优划分属性。
a
∗
=
arg
max
a
∈
R
G
a
i
n
(
D
,
a
)
a_*=\arg\max_{a\in R}Gain(D,a)
a∗=arga∈RmaxGain(D,a)
著名的ID3决策树就是以信息增益来选取最优划分属性。
增益率-C4.5决策树
增益率是对信息增益的优化,因为信息增益准则对可取值数目较多的属性有偏好。
如果一个属性对每个样本的取值都是不同的,那么针对这个属性的每个取值只包含一个样本并且分支结点的纯度已达最大,这样的决策树显然没有泛化能力,无法对新样本进行预测。因为新的样本在这个属性的值与决策树所学习的均不相同。
所以,我们定义增益率:
G
a
i
n
_
r
a
t
i
o
n
(
D
,
a
)
=
G
a
i
n
(
D
,
a
)
I
V
(
a
)
Gain\_ration(D,a)=\frac{Gain(D,a)}{IV(a)}
Gain_ration(D,a)=IV(a)Gain(D,a)
其中
I
V
(
a
)
=
−
∑
v
=
1
V
∣
D
v
∣
∣
D
∣
log
2
∣
D
v
∣
∣
D
∣
IV(a)=-\sum_{v=1}^V\frac{\left|D^v\right|}{\left|D\right|}\log_2\frac{\left|D^v\right|}{\left|D\right|}
IV(a)=−v=1∑V∣D∣∣Dv∣log2∣D∣∣Dv∣
I
V
(
a
)
IV(a)
IV(a)称为属性a的固有值,属性a的可能取值数目越多,
I
V
(
a
)
IV(a)
IV(a)的值越大,增益率就越小。
所以我们在应用增益率准则的时候,先从划分属性中选择信息增益高的,再进行二次选择,选择增益率最高的作为最终的最优化分属性。
基尼指数-CART决策树
基尼值
基尼值是另一种度量数据集纯度的指标,与信息熵性质一样。
基尼值定义:
G
i
n
i
(
D
)
=
∑
k
=
1
∣
y
∣
∑
k
′
≠
k
p
k
p
k
′
Gini(D)=\sum_{k=1}^{\left|y\right|}\sum_{k' \neq k}p_kp_{k'}
Gini(D)=k=1∑∣y∣k′̸=k∑pkpk′
=
1
−
∑
k
=
1
∣
y
∣
p
k
2
=1-\sum_{k=1}^{\left|y\right|}p_k^2
=1−k=1∑∣y∣pk2
符号与信息熵相同
基尼值反映了从数据集中随机抽取两个样本,其类别不一致的概率。因此,基尼值越小,则数据集的纯度越高。
基尼指数定义
对于一个数据集D,其属性集A中的每个属性a的基尼指数定义为:
G
i
n
i
_
i
n
d
e
x
(
D
,
a
)
=
∑
v
=
1
V
∣
D
v
∣
∣
D
∣
G
i
n
i
(
D
v
)
Gini\_index(D,a)=\sum_{v=1}^V\frac{\left|D^v\right|}{\left|D\right|}Gini(D^v)
Gini_index(D,a)=v=1∑V∣D∣∣Dv∣Gini(Dv)
由式可见,我们选基尼指数最小的那个属性作为最优化分属性:
a
∗
=
arg
min
a
∈
A
G
i
n
i
(
D
,
a
)
a_*=\arg\min_{a\in A}Gini(D,a)
a∗=arga∈AminGini(D,a)
剪枝处理
目的:防止过拟合
预剪枝
在决策树生成过程中,对每个结点在划分前先进行估计,如果划分这个结点不能带来决策树的泛化性能提升,那么就不进行划分,直接将当前结点标记为叶结点。
优点
- 降低过拟合风险
- 显著减少决策树的训练时间和测试时间
缺点
- 可能欠拟合
后剪枝
先训练一颗完整的决策树,然后自底向上对非叶结点进行考察,如果将该结点直接标记为叶结点,能够带来泛化性能的提升,那么就将该结点以后的子树直接替换为叶结点。
优点
- 欠拟合风险小
- 泛化性能往往优于预剪枝
缺点
- 时间开销大得多
预剪枝与后剪枝的差别
后剪枝决策树通常比预剪枝决策树保留了更多的分支。
连续与缺失值
多变量决策树
练习代码
import pandas as pd
from sklearn.feature_extraction import DictVectorizer
from sklearn.tree import DecisionTreeClassifier
import numpy as np
def decide_play():
# ID3
df = pd.read_csv('dtree.csv')
# 将数据转为字典格式,oriten='record'参数指定数据格式为「column:value」的形式
dict_train = df.loc[:, ['Outlook', 'Temperatur', 'Humidity', 'Windy']].to_dict(orient='record')
print(dict_train)
dict_target = pd.DataFrame(df['PlayGolf'], columns=['PlayGolf']).to_dict(orient='record')
print(dict_target)
# 训练数据字典向量化
dv_train = DictVectorizer(sparse=False)
x_train = dv_train.fit_transform(dict_train)
print(x_train)
# 目标数据字典向量化
dv_target = DictVectorizer(sparse=False)
y_target = dv_target.fit_transform(dict_target)
print(y_target)
# 创建决策树训练模型,并训练
d_tree = DecisionTreeClassifier()
d_tree.fit(x_train, y_target)
# 待预测数据
data_predict = [{
'Humidity': 85,
'Outlook': 'sunny',
'Temperatur': 85,
'Windy': False,
}]
# 测试数据字典向量化
x_data = dv_train.transform(data_predict)
print(x_data)
# 利用决策树模型预测,再将向量数据反向转换为字典
print(dv_target.inverse_transform(d_tree.predict(data_predict)))
decide_play()