0%

DecisionTree

基本的分类与回归方法。本文主要是关于分类的决策树。

通常包括三个步骤:特征选择、决策树的生成和决策树的修剪

1. 模型与学习

  • 模型

可以将决策模型看作是一个if-then结构,具有的重要性质是互斥且完备

  • 学习

目标是根据给定的训练数据集构建一个决策树模型, 使它能够对实例进行正确的分类。通常使用损失函数(正则化的极大似然函数)表示这一目标。

决策树学习的算法通常是一个递归地选择最优特征, 并根据该特征对训练数据进行分割, 使得对各个子数据集有一个最好的分类的过程。

如果特征数量很多, 也可以在决策树学习开始的时候,对特征进行选择,只留下对训练数据有足够分类能力的特征

一般而言决策树学习算法包含

  • 特征选择
  • 决策树的生成
  • 决策树的剪枝过程

常用的算法有ID3、C4.5、CART

2. 特征选择

特征选择在于选取对训练数据具有分类能力的特征。 如果利用一个特征进行分类的结果与随机分类的结果没有很大差别, 则称这个特征是没有分类能力的

通常选择的准则是信息增益信息增益比

2.1 信息增益

随机变量X的熵定义为
$$
H(X) = H(p) = -\sum_{i=1}^{n} p_{i} \log p_{i}
$$
熵越大,随机变量的不确定性就越大。

  • 条件熵

已知随机变量X的条件下随机变量Y的不确定性
$$
H(Y \mid X)=\sum_{i=1}^{n} p_{i} H(Y \mid X=x_{i})
$$
其中 $p_i = P(X=x_i)$ .

当熵和条件熵中的概率由数据估计(特别是极大似然估计)得到时, 所对应的熵与条件熵分别称为经验熵(empirical entropy)和经验条件熵。

  • 信息增益

表示得知特征 $X$ 的信息而使得类 $Y$ 的信息的不确定性减少的程度。

特征A对训练数据集D的信息增益 $g(D,A)$,定义为集合 $D$ 的经验熵 $H(D)$ 与特征 $A$ 给定条件下 $D$ 的经验条件熵 $H(D|A)$之差
$$
g(D,A) = H(D) - H(D|A)
$$
一般地, 熵H(y)与条件熵H(y|X)之差称为互信息(mutual information)。 决策树学习中的信息增益等价于训练数据集中类与特征的互信息

根据信息增益准则的特征选择方法是: 对训练数据集(或子集)D, 计算其每个特
征的信息增益,并比较它们的大小,选择信息增益最大的特征。

设训练数据集为 $D,|D|$ 表示其样本容量,即样本个数。设有 $K$ 个类 $C_{k}, k=$ $1,2, \cdots, K,|C_{k}|$ 为属于类 $C_{k}$ 的样本个数, $\sum_{k=1}^{K}\left|C_{k}\right|=|D|$ 设特征 $A$ 有 $n$ 个不同的取值 $\lbrace a_{1}, a_{2}, \cdots, a_{n} \rbrace$ 根据特征 $A$ 的取值将 $D$ 划分为 $n$ 个子集 $D_{1}, D_{2}, \cdots, D_{n}$ ,$|D_{i}|$ 为 $D_{i}$ 的样本个数, $\sum_{i=1}^{n}\left|D_{i}\right|=|D|$ 记子集 $D_{i}$ 中属于类 $C_{k}$ 的样本的集合为 $D_{i k}$ ,即 $D_{ik}=D_{i} \cap C_{k},|D_{ik}|$ 为 $D_{ik}$ 的样本个数。于是信息增益的算法如下。

算法5.1(信息增益的算法)
输入: 训练数据集 $D$ 和特征 $A$;

输出:特征 $A$ 对训练数据集 $D$ 的信息增益 $g(D, A)$ 。
(1) 计算数据集 $D$ 的经验熵 $H(D)$
$$
H(D)=-\sum_{k=1}^{K} \frac{\left|C_{k}\right|}{|D|} \log_{2} \frac{|C_{k}|}{|D|}
$$
(2) 计算特征 $A$ 对数据集 $D$ 的经验条件嫡 $H(D \mid A)$
$$
H(D \mid A)=\sum_{i=1}^{n} \frac{\left|D_{i}\right|}{|D|} H\left(D_{i}\right)=-\sum_{i=1}^{n} \frac{\left|D_{i}\right|}{|D|} \sum_{k=1}^{K} \frac{\left|D_{i k}\right|}{\left|D_{i}\right|} \log_{2} \frac{\left|D_{ik}\right|}{\left|D_{i}\right|}
$$
(3) 计算信息增益
$$
g(D, A)=H(D)-H(D \mid A)
$$

2.2 信息增益比

  • 定义(信息增益与熵之比)
    $$
    g_{R}(D, A)=\frac{g(D, A)}{H_{A}(D)}
    $$

其中,$H_{A}(D)=-\sum_{i=1}^{n} \frac{|D_{i}|}{|D|} \log {2} \frac{|D{i}|}{|D|}, n$ 是特征 $A$ 取值的个数。

3. 决策树的生成

3.1 ID3算法

具体方法是: 从根结点(root node)开始, 对结点计算所有可能的特征的信息增益, 选择信息增益最大的特征作为结点的特征, 由该特征的不同取值建立子结点;再对子结点递归地调用以上方法, 构建决策树; 直到所有特征的信息增益均很小或没有特征可以选择为止。最后得到一颗决策树。ID3算法相当于用极大似然法进行概率模型的选择。

算法3.1 (ID3算法)

输入:训练数据集 $D$ ,特征集 $A$ 阈值 $\varepsilon$

输出:决策树 $T$ 。

(1)若 $D$ 中所有实例属于同一类 $C_{k},$ 则 $T$ 为单结点树,并将类 $C_{k}$ 作为该结点的类标记,返回 $T$;

(2)若 $A=\varnothing$ ,则 $T$ 为,sh单结点树,并将 $D$ 中实例数最大的类 $C_{k}$ 作为该结点的类标记,返回 $T$;

(3)否则,计算 $A$ 中各特征对 $D$ 的fanjia信息增益,选择信息增益最大的特征 $A_{g}$ ;

(4)如果 $A_{g}$ 的信息增益小于阈值 $\varepsilon$ ,则设置 $T$ 为单结点树,并将 $D$ 中实例数最大的类 $C_{k}$ 作为该结点的类标记,返回 $T$;

(5)否则,对 $A_{g}$ 的每一可能值 $a_{i},$ 依 $A_{g}=a_{i}$ 将 $D$ 分割为若干非空子集 $D_{i},$ 将$D_{i}$ 中实例数最大的类作为标记,构建子结点,由结点及其子结点构成树 $T$ ,返回 $T$;

(6)对第 $i$ 个子结点,以 $D_{i}$ 为训练集,以 $A-\lbrace A_{g}\rbrace$ 为特征集,递归地调用步 (1)$\sim$ 步 $(5),$ 得到子树 $T_{i},$ 返回 $T_{i}$

缺点:只有树生成,没有树剪枝,容易过拟合

3.2 C4.5算法

与ID3相比,在生成过程中用信息增益比来选择特征。

算法 3.2 (C4.5的生成算法)

输入: 训练数据集 $D$, 特征集 $A$ 阈值 $\varepsilon ;$

输出: 决策树 $T_{\circ}$

(1)如果 $D$ 中所有实例属于同一类 $C_{k},$ 则置 $T$ 为单结点树,并将 $C_{k}$ 作为该结点的类,返回 $T$;

(2)如果 $A=\varnothing,$ 则置 $T$ 为单结点树,并将 $D$ 中实例数最大的类 $C_{k}$ 作为该结的类,返回 $T$;

(3)否则,计算 $A$ 中各特征对 $D$ 的信息增益比,选择信息增益比最大的特征 $A_{g} ;$

(4)如果 $A_{g}$ 的信息增益比小于阈值 $\varepsilon$ ,则置 $T$ 为单结点树,并将 $D$ 中实例数最大的类 $C_{k}$ 作为该结点的类,返回 $T$;

(5)否则,对 $A_{g}$ 的每一可能值 $a_{i},$ 依 $A_{g}=a_{i}$ 将 $D$ 分割为子集若干非空 $D_{i}$ ,将 $D_{i}$ 中实例数最大的类作为标记,构建子结点,由结点及其子结点构成树 $T$ ,返回 $T$;

(6)对结点 $i$,以 $D_{i}$ 为训练集,以 $A-\left{A_{g}\right}$ 为特征集,递归地调用步(1)$\sim$步 $(5),$ 得到子树 $T_{i},$ 返回 $T_{i}$ 。

4. 决策树的剪枝

以上两种算法均未考虑树的复杂度,这样产生的决策树容易出现过拟合的问题。因此需要将生成的树进行简化。

决策树的剪枝往往通过极小化决策树整体的损失函数或代价函数来实现。设树 $T$ 的叶节点个数为 $|T|$ ,$t$ 是树 $T$ 的叶节点,该叶节点有 $N_t$ 个样本点,其中 $k$ 类样本点有 $N_{tk}$ 个, $H_t(T)$ 为叶节点 $t$ 上的经验熵。 $\alpha$ 为参数。则决策树的损失函数可以定义为
$$
\begin{array}{c}
C_{\alpha}(T)=\sum_{t=1}^{|T|} N_{t} H_{t}(T)+\alpha|T| \\
H_{t}(T)=-\sum_{k} \frac{N_{tk}}{N_{t}} \log \frac{N_{tk}}{N_{t}}
\end{array}
$$
将第一项记做 $C(T)$,这时有
$$
C_{\alpha}(T) = C(T)+ \alpha|T|
$$
第一项表示对训练数据的预测误差,第二项表示模型的复杂度。$\alpha \geq 0$ 控制两者之间的影响。由此可以看出,决策树的生成学习局部的模型,而决策树剪枝学习整体的模型。

算法 4.1 (树的剪枝算法)

输入:生成算法产生的整个树 $T$ ,参数 $\alpha$

输出:修剪后的子树 $T_{\alpha}$

(1)计算每个结点的经验熵

(2)递归的从树的叶结点向上回缩。

设一组叶结点回缩到其父结点之前与之后的整体树分别为 $T_B$ 与 $T_A$ ,其对应的损失函数值分别是 $C_{\alpha}(T_B)$ 与 $C_{\alpha}(T_A)$ , 如果
$$
C_{\alpha}(T_A) \leq C_{\alpha}(T_B)
$$
则进行剪枝,即将父结点变为新的叶结点。

(3)返回2,直至不能继续为止,得到损失函数最小的子树 $T$

5. CART (classification and regresstion tree)算法 –1984

CART特征选择、树的生成及剪枝组成的二叉树。既可以用于分类也可以用于回归。左分支是“是”的分支,右分支是“否”的分支。

CART是在给定输入随机变量 $X$ 条件下输出随机变量 $Y$ 的条件概率分布的学习方法。

5.1 CART的生成

回归树:平方误差最小化准则

分类树:基尼指数(Gini index) 最小化准则

  1. 回归树的生成

每个输入空间 $R_m$ 的标签值为,所有输入空间中实例的标签的均值
$$
\hat{c} = ave(y_i|x_i \in R_m)
$$

算法 5.1 (最小二乘回归树生成算法)
输入: 训练数据集 $D$;

输出: 回归树 $f(x)$

在训练数据集所在的输入空间中,递归地将每个区域划分为两个子区域并决定每个子区域上的输出值,构建二叉决策树:

(1)选择最优切分变量 $j$ 与切分点 $s$ ,求解
$$
\min_{j, s}\left[\min_{c_{1}} \sum_{x_{i} \in R_{1}(j, s)}\left(y_{i}-c_{1}\right)^{2}+\min_{c_{2}} \sum_{x_{i} \in R_{2}(j, s)}\left(y_{i}-c_{2}\right)^{2}\right]
$$
遍历变量 $j,$ 对固定的切分变量 $j$ 扫描切分点 $s$ ,选择使上述目标函数达到最小值的对
$(j, s)$

(2)用选定的对 $(j, s)$ 划分区域并决定相应的输出值:
$$
\begin{array}{c}
R_{1}(j, s)=\lbrace x \mid x^{(j)} \leqslant s\rbrace, \quad R_{2}(j, s)=\lbrace x \mid x^{(j)}>s\rbrace \\
\hat{c}{m}=\frac{1}{N{m}} \sum_{x_{i} \in R_{m}(j, s)} y_{i}, \quad x \in R_{m}, \quad m=1,2
\end{array}
$$
(3)继续对两个子区域调用步骤 $(1),(2),$ 直至满足停止条件。

(4)将输入空间划分为 $M$ 个区域 $R_{1}, R_{2}, \cdots, R_{M}$ ,生成决策树:
$$
f(x)=\sum_{m=1}^{M} \hat{c}{m} I\left(x \in R{m}\right)
$$
当数据量大的时候,遍历的效率不高。

  1. 分类树的生成

分类树用基尼指数选择最优特征, 同时决定该特征的最优二值切分点。

基尼指数的定义
$$
\text{Gini}(p) = \sum_{k = 1}^{K} p_k (1 - p_k) = 1 - \sum_{k=1}^{K}p_k^2
$$
对于给定样本集合D, $p_k = \frac{|C_K|}{|D|}$

基尼指数表示集合D的不确定性,数值越大,样本集合的不确定性就越大。这一点与熵类似。

算法 $5.6(\mathrm{CART}$ 生成算法)

输入: 训练数据集 $D$ ,停止计算的条件;

输出: CART 决策树。

根据训练数据集,从根结点开始,递归地对每个结点进行以下操作,构建二叉决策树:

(1)设结点的训练数据集为 $D,$ 计算现有特征对该数据集的基尼指数。此时,对每一个特征 $A$ ,对其可能取的每个值 $a$,根据样本点对 $A=a$ 的测试为“是”或“否” 将 $D$ 分割成 $D_{1}$ 和 $D_{2}$ 两部分, 计算 $A=a$ 时的基尼指数。

(2)在所有可能的特征 $A$ 以及它们所有可能的切分点 $a$ 中,选择基尼指数最小的特征及其对应的切分点作为最优特征与最优切分点。依最优特征与最优切分点,从现结点生成两个子结点,将训练数据集依特征分配到两个子结点中去。

(3)对两个子结点递归地调用 $(1),(2),$ 直至满足停止条件。

(4) 生成 CART 决策树。

5.2 CART剪枝

具体地, 从整体树 $T_0$ 开始剪枝。 对 $T_0$ 的任意内部结点 $t$ ,以 $t$ 为单结点树的损失函数是
$$
C_{\alpha}(t) = C(t) + \alpha
$$
以 $t$ 为根节点的子树 $T_t$ 的损失函数是
$$
C_{\alpha}(T_t) = C(T_t) + \alpha |T_t|
$$
当 $\alpha = 0$ 及 $\alpha$ 充分小时,有
$$
C_{\alpha}(T_t) < C_{\alpha}(t)
$$
当 $\alpha$ 增大时,在某一 $\alpha$ 有
$$
C_{\alpha}(T_t) = C_{\alpha}(t)
$$
所以只要 $\alpha =\frac{C(t)-C\left(T_{t}\right)}{\left|T_{t}\right|-1}$,就可以选择结点更少的t。

而Beriman等人证明:可以用递归的方法对树进行剪枝。将 $\alpha$ 逐渐增大,$0 = \alpha_0 < \alpha_1 <\cdots <\alpha_n < +\infty$ ,产生一系列的区间 $[\alpha_i, \alpha_{i+1}), i = 0,1,…,n$ ;剪枝得到的子树序列对应着区间 $\alpha \in [\alpha_i, \alpha_{i+1})$,的最优子树序列 $\lbrace T_0, T_1, \cdots, T_n \rbrace$。然后利用交叉验证挑出一个最好的

算法 5.7 ($\mathbf{C A R T}$ 剪枝算法)

输入: $\mathrm{CART}$ 算法生成的决策树 $T_{0} ;$

输出:最优决策树 $T_{\alpha^{\circ}}$

(1)设 $k=0, T=T_{0}$ 。

(2)设 $\alpha=+\infty$ 。

(3)自下而上地对各内部结点 $t$ 计算 $C\left(T_{t}\right),\left|T_{t}\right|$ 以及
$$
\begin{aligned}
g(t) &=\frac{C(t)-C\left(T_{t}\right)}{\left|T_{t}\right|-1} \\
\alpha &=\min (\alpha, g(t))
\end{aligned}
$$
这里, $T_{t}$ 表示以 $t$ 为根结点的子树, $C\left(T_{t}\right)$ 是对训练数据的预测误差, $\left|T_{t}\right|$ 是 $T_{t}$ 的叶 结点个数。

(4)对 $g(t)=\alpha$ 的内部结点 $t$ 进行剪枝,并对叶结点 $t$ 以多数表决法决定其类,
得到树 $T$ 。

(5)设 $k=k+1, \alpha_{k}=\alpha, T_{k}=T_{\circ}$

(6)如果 $T_{k}$ 不是由根结点及两个叶结点构成的树,则回到步骤 (2)$;$ 否则令
$T_{k}=T_{n}$

(7)采用交叉验证法在子树序列 $T_{0}, T_{1}, \cdots, T_{n}$ 中选取最优子树 $T_{\alpha} \circ$

6. 代码实现

1
2
3
4
5
6
7
8
9
10
11
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from collections import Counter

import math
from math import log
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# 经验熵
def entropy(datasets):
data_length = len(datasets)
label_count = {}
for i in range(data_length):
label = datasets[i][-1]
if label not in label_count:
label_count[label] = 0
label_count[label] += 1
entropy = - sum([(p / data_length) * log(p / data_length, 2)
for p in label_count.values()])
return entropy

# 条件经验熵
def cond_entropy(datasets, axis=0):
"""
求数据集datasets中第axis列的条件经验熵
"""
data_length = len(datasets)
feature_sets = {}
for i in range(data_length):
feature = datasets[i][axis]
if feature not in feature_sets:
feature_sets[feature] = []
feature_sets[feature].append(datasets[i])
cond_entropy = sum([(len(p) / data_length) * entropy(p)
for p in feature_sets.values()])
return cond_entropy

# 信息增益 = 经验熵 - 条件经验熵
def info_gain(entropy, cond_entropy):
return entropy - cond_entropy

# 利用信息增益选择根节点
def info_gain_train(datasets):
data_dim = len(datasets[0]) - 1
ent = entropy(datasets)
info_gain_feature = []
for i in range(data_dim):
i_info_gain = info_gain(ent, cond_entropy(datasets, axis=i))
info_gain_feature.append(i_info_gain)
print('特征{}的信息增益为:{}'.format(i + 1, i_info_gain))
best_feature = max(info_gain_feature)
return '特征{}的信息增益最大,选择根节点特征'.format(info_gain_feature.index(best_feature) + 1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
datasets = [['青年', '否', '否', '一般', '否'],
['青年', '否', '否', '好', '否'],
['青年', '是', '否', '好', '是'],
['青年', '是', '是', '一般', '是'],
['青年', '否', '否', '一般', '否'],
['中年', '否', '否', '一般', '否'],
['中年', '否', '否', '好', '否'],
['中年', '是', '是', '好', '是'],
['中年', '否', '是', '非常好', '是'],
['中年', '否', '是', '非常好', '是'],
['老年', '否', '是', '非常好', '是'],
['老年', '否', '是', '好', '是'],
['老年', '是', '否', '好', '是'],
['老年', '是', '否', '非常好', '是'],
['老年', '否', '否', '一般', '否'],
]
labels = [u'年龄', u'有工作', u'有自己的房子', u'信贷情况', u'类别']
train_data = pd.DataFrame(datasets, columns=labels)
1
info_gain_train(np.array(datasets))

ID3算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# 定义节点类 二叉树
class Node:
def __init__(self, root=True, label=None, feature_name=None, feature=None):
self.root = root
self.label = label
self.feature_name = feature_name
self.feature = feature
self.tree = {}
self.result = {
'label:': self.label,
'feature_name': self.feature_name,
'tree': self.tree
}

def __repr__(self):
return '{}'.format(self.result)

def add_node(self, val, node):
self.tree[val] = node

def predict(self, features):
if self.root is True:
return self.label
return self.tree[features[self.feature]].predict(features)


class DTree:
def __init__(self, epsilon=0.1):
self.epsilon = epsilon
self._tree = {}

# 熵
@staticmethod
def calc_ent(datasets):
data_length = len(datasets)
label_count = {}
for i in range(data_length):
label = datasets[i][-1]
if label not in label_count:
label_count[label] = 0
label_count[label] += 1
ent = -sum([(p / data_length) * log(p / data_length, 2)
for p in label_count.values()])
return ent

# 经验条件熵
def cond_ent(self, datasets, axis=0):
data_length = len(datasets)
feature_sets = {}
for i in range(data_length):
feature = datasets[i][axis]
if feature not in feature_sets:
feature_sets[feature] = []
feature_sets[feature].append(datasets[i])
cond_ent = sum([(len(p) / data_length) * self.calc_ent(p)
for p in feature_sets.values()])
return cond_ent

# 信息增益
@staticmethod
def info_gain(ent, cond_ent):
return ent - cond_ent

def info_gain_train(self, datasets):
count = len(datasets[0]) - 1
ent = self.calc_ent(datasets)
best_feature = []
for c in range(count):
c_info_gain = self.info_gain(ent, self.cond_ent(datasets, axis=c))
best_feature.append((c, c_info_gain))
# 比较大小
best_ = max(best_feature, key=lambda x: x[-1])
return best_

def train(self, train_data):
"""
input:数据集D(DataFrame格式),特征集A,阈值eta
output:决策树T
"""
_, y_train, features = train_data.iloc[:, :
-1], train_data.iloc[:,
-1], train_data.columns[:
-1]
# 1,若D中实例属于同一类Ck,则T为单节点树,并将类Ck作为结点的类标记,返回T
if len(y_train.value_counts()) == 1:
return Node(root=True, label=y_train.iloc[0])

# 2, 若A为空,则T为单节点树,将D中实例树最大的类Ck作为该节点的类标记,返回T
if len(features) == 0:
return Node(
root=True,
label=y_train.value_counts().sort_values(
ascending=False).index[0])

# 3,计算最大信息增益 同5.1,Ag为信息增益最大的特征
max_feature, max_info_gain = self.info_gain_train(np.array(train_data))
max_feature_name = features[max_feature]

# 4,Ag的信息增益小于阈值eta,则置T为单节点树,并将D中是实例数最大的类Ck作为该节点的类标记,返回T
if max_info_gain < self.epsilon:
return Node(
root=True,
label=y_train.value_counts().sort_values(
ascending=False).index[0])

# 5,构建Ag子集
node_tree = Node(
root=False, feature_name=max_feature_name, feature=max_feature)

feature_list = train_data[max_feature_name].value_counts().index
for f in feature_list:
sub_train_df = train_data.loc[train_data[max_feature_name] ==
f].drop([max_feature_name], axis=1)

# 6, 递归生成树
sub_tree = self.train(sub_train_df)
node_tree.add_node(f, sub_tree)

# pprint.pprint(node_tree.tree)
return node_tree

def fit(self, train_data):
self._tree = self.train(train_data)
return self._tree

def predict(self, X_test):
return self._tree.predict(X_test)
1
2
3
4
datasets, labels = create_data()
data_df = pd.DataFrame(datasets, columns=labels)
dt = DTree()
tree = dt.fit(data_df)
1
2
tree
# {'label:': None, 'feature_name': '有自己的房子', 'tree': {'否': {'label:': None, 'feature_name': '有工作', 'tree': {'否': {'label:': '否', 'feature_name': None, 'tree': {}}, '是': {'label:': '是', 'feature_name': None, 'tree': {}}}}, '是': {'label:': '是', 'feature_name': None, 'tree': {}}}}
1
2
dt.predict(['老年', '否', '否', '一般'])
# '否'

scikit-learn实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
## sklearn里并没有实现后剪枝,只有预剪枝操作,例如设置树的最大深度max_depth

iris = load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df['label'] = iris.target
df.columns = [
'sepal length', 'sepal width', 'petal lenght', 'petal width', 'label'
]
def shuffle(X,Y):
randomize = np.arange(len(X))
np.random.shuffle(randomize)
return X[randomize], Y[randomize]

data = np.array(df)
X, Y = data[:, :2], data[:,-1]
X, y = shuffle(X,Y)
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size = 0.3)
1
2
3
4
from sklearn.tree import DecisionTreeClassifier
clf = DecisionTreeClassifier()
clf.fit(X_train, Y_train)
clf.score(X_test,Y_test)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
from sklearn.tree import DecisionTreeClassifier
from sklearn import preprocessing
import numpy as np
import pandas as pd

from sklearn import tree
import graphviz

features = ["年龄", "有工作", "有自己的房子", "信贷情况"]
X_train = pd.DataFrame([
["青年", "否", "否", "一般"],
["青年", "否", "否", "好"],
["青年", "是", "否", "好"],
["青年", "是", "是", "一般"],
["青年", "否", "否", "一般"],
["中年", "否", "否", "一般"],
["中年", "否", "否", "好"],
["中年", "是", "是", "好"],
["中年", "否", "是", "非常好"],
["中年", "否", "是", "非常好"],
["老年", "否", "是", "非常好"],
["老年", "否", "是", "好"],
["老年", "是", "否", "好"],
["老年", "是", "否", "非常好"],
["老年", "否", "否", "一般"]
])
y_train = pd.DataFrame(["否", "否", "是", "是", "否",
"否", "否", "是", "是", "是",
"是", "是", "是", "是", "否"])

#数据预处理
le_x = preprocessing.LabelEncoder()
le_x.fit(np.unique(X_train)) # 找出X_tran中的特征值有哪些
# array(['一般', '中年', '否', '好', '是', '老年', '青年', '非常好'], dtype=object)
# 这样 0代表‘一般’,1代表‘中年’,....
X_train = X_train.apply(le_x.transform) # apply()可以传入一个函数
le_y = preprocessing.LabelEncoder()
le_y.fit(np.unique(y_train))
y_train = y_train.apply(le_y.transform)

# 调用sklearn.DT训练模型
model_tree = DecisionTreeClassifier()
model_tree.fit(X_train, y_train)

Reference

[1]. https://github.com/fengdu78/lihang-code/

[2]. 《统计学习与方法》 – 李航