目录
认识决策树
其实就是程序的if-else结果。
我们看一下这个例子,已知年龄、工作、房子、信贷情况来判断是否把贷款给个人。我们通过这些样本内容进行学习,来预测如果已知某个人的这些特征对应的特征值的时候,就可以判断是否可以把贷款贷给这个人。样本集合如下:
样本编号 | 年龄 | 有工作 | 有房子 | 信贷情况 | 是否可以贷款 |
1 | 青年 | 否 | 否 | 一般 | 否 |
2 | 青年 | 否 | 否 | 好 | 否 |
3 | 青年 | 是 | 否 | 好 | 是 |
4 | 青年 | 是 | 是 | 一般 | 是 |
5 | 青年 | 否 | 否 | 一般 | 否 |
6 | 中年 | 否 | 否 | 一般 | 否 |
7 | 中年 | 否 | 否 | 好 | 否 |
8 | 中年 | 是 | 是 | 好 | 是 |
9 | 中年 | 否 | 是 | 非常好 | 是 |
10 | 中年 | 否 | 是 | 非常好 | 是 |
11 | 老年 | 否 | 是 | 非常好 | 是 |
12 | 老年 | 否 | 是 | 好 | 是 |
13 | 老年 | 是 | 否 | 好 | 是 |
14 | 老年 | 是 | 否 | 非常好 | 是 |
15 | 老年 | 否 | 否 | 一般 | 否 |
我们进行分析下该样本:
从上面的样本集中我们可以看出:
(1)红色标记的有房子的这个特征值,如果是是有房子,则一定可以贷款;而没有房子的人,有的人可以贷到款,有的人却不可以;
(2)剩下的没房子的样本中,在看蓝色标记的工作这个特征对应的特征值,有工作,则一定可以贷到款;而没有工作的一定不可以贷到款。
这样经过上面两步就可以知道这个人能不能贷款。我们从另外一个维度再来分析一次:
(1)先分析红色标记的年龄这个特征值,我们看到年龄的特征值不同,我们只看中年这个特征值下,并不能决定是可以贷到款还是贷不到款,有是也有否;
(2)在看蓝色标记的工作这个特征值,我们只看下中年年龄下的,有工作可以贷款,但是没有工作的还是有是也有否;
(3)最后粉色标记的特征值,我们在中年&无工作下看,有房子的一定可以贷到款,然后没有房子的一定不可以贷到款。
同样一个问题,我们经过了3次才能决定是否把贷款给到个人。显然特征的先后顺序决定了是否可以高效的决定是否可以贷款给个人,那么怎么才能高效的来决定特征的先后顺序呢?这就需要引入一种数学的方式来快速高效的解决这个问题。从而引入信息熵。
信息熵
1.信息
信息的奠基人香农认为:消息就是用来消除随机不确定的东西。举个例子说明下什么是消息。
假设在和小明聊天的过程中,问了小明一个问题:小明今年年龄是多少?
小明说:我今年18岁
这句话就是一个信息,因为这句话你消除了对小明年龄的不确定性。假设在小明回答的过程中,小红也说了一句:小明明年19岁。那么这句话是消息吗?
其实在小明告知18岁的时候,已经可以知道小明的年龄,完全可以推测出小明明年19岁。所以小红的这句话,显然对我们认为小明年龄这个问题并不能消除什么不确定的东西。所以小红的这句话并不是信息。
那如何来衡量消除不确定性的程度大小呢?
2.信息熵
信息熵是用来表示事物的不确定性。变量的不确定性越大,对应的信息熵越大,用来消除这个不确定性所需要的信息就越多。信息熵就是用来衡量这种不确定性的多少。
H表示信息熵,单位比特,用公式表示如下:
P(yi)就是概率值,表示随机事件yi发生的概率,也就是目标值即分类类别发生的概率。用公式来表示如下:
其中Ci对应着该类别的样本数,D就是总的样本数。logb 显然就是对刚才的这个概率值取对数,通常取底数为2的对数。
那么根据上面的公式就分步来计算我们在开头提到的例子中的信息熵的计算过程。
上述的问题特征有年龄、工作、房子、信贷情况四个特征,二期目标值就是是否可以贷款,显然是一个二分类问题。信息熵的公式中提到的就是每个分类的类别对应的概率值代入到上述公式中,然后求和。
这个H(贷款)就是总的总的不确定性,分别将上面的样本集的个数代入到求概率的公式中:
代入到上述的公式中:
那怎么去看哪个特征对消除这个不确定性最大,那么我们就可以认为该特征为我们建立这棵决策树根节点。那么这个特征选择的准则主要有三种方式:ID3算法、C4.5算法、CART。
ID3算法
ID3算法使用的就是个最大信息增益(Information Gain)来选择特征。选择信息增益最大的来作为决策树的根结点。通过最大信息增益建立的决策树就是ID3树。
- 信息增益
特征A对训练集D的信息增益g(D,A)定义为:集合D的信息熵H(D)与特征A已知条件下的集合D的信息熵H(D|A)之差,用公式如下:
简单的语言在描述下这个公式的意思就是:信息增益=总的信息熵-知道某个特征之后的总的信息熵。我们通过这个公式计算出来的信息增益越大,那么在进行决策的时候顺序越靠前。
- 条件信息熵
刚才在公式中提到一个H(D|A)为条件信息熵,即在已知特征A的情况下,训练集D的信息熵,类似于条件概率,用公式表示如下:
P(x):即该特征下对应的特征值在所有样本的占比,用公式表示如下:
P(D|x):在该特征下对应的特征值下产生的不同的分类类别对应的样本占该特征下所有的样本集合占比,用公式表示如下:
即在特征值x发生的条件下,不同类别发生的概率。
分别计算下刚才提到的四个特征值的信息增益:
- (1)年龄
在年龄的已知的情况下的,贷款的信息增益用公式表示如下:
1)H(贷款)就是我们刚才计算出来的
2)年龄的特征值有三个:青年、中年、老年。那么H(贷款|年龄)
分别代入到上面提到的公式如下:
3)结合1)和2)得出的值,得到贷款这个特征值对应的信息增益如下:
- (2)工作
在已知工作的这个特征的条件下,工作的信息增益如下:
1)H(贷款)就是我们刚才计算出来的
2)工作的特征值有两个:有工作和无工作
分别代入上述的公式如下:
3)结合1)和2)得出的值,得到工作这个特征值对应的信息增益如下:
- (3)房子
在已知房子这个特征值的情况下,房子的信息增益如下:
1)H(贷款)就是我们刚才计算出来的
2)房子的特征值有两个:有房子和无房子
分别代入上述的公式如下:
3)结合1)和2)得出的值,得到房子这个特征值对应的信息增益如下:
- (4)信贷情况
在已知信贷情况这个特征值的情况下,信贷情况的信息增益如下:
1)H(贷款)就是我们刚才计算出来的
2)信贷情况的特征值有三个:非常好、 好、一般
分别代入上述的公式如下:
3)结合1)和2)得出的值,得到信贷情况这个特征值对应的信息增益如下:
从上面的四个值我们可以很明显的看出来,房子的信息增益最大,那么我们在构建决策树的时候,房子的特征值最靠前,这和我们刚才推断出来的是一致的。我们把房子作为根结点,来构建出决策树。
上述的几个公式的计算值,可以通过下面的代码运行得到:
def info():
age = -5.0 / 15.0*(log(2.0 / 5.0) + log(3.0 / 5.0))-5.0 / 15.0*(log(2.0 / 5.0) + log(3.0 / 5.0))-5.0 / 15.0*(log(4.0 / 5.0) + log(1.0 / 5.0))
print(age)
age = 0.970951 - age
print("age 信息增益 = ", age)
job = -5.0/15*(log(1.0))-10.0/15.0*(log(4.0/10)+log(6.0/10))
print(job)
job = 0.970951 - job
print("job 信息增益 = ", job)
house = -6.0/15*(log(1))-9.0/15*(log(2.0/3)+log(1.0/3))
print(house)
house = 0.970951 - house
print("house 信息增益 = ", house)
credit = -4.0/15*(log(1))-6.0/15*(log(2.0/3)+log(1.0/3))-5.0/15*(log(1.0/5)+log(4.0/5))
print(credit)
credit = 0.970951 - credit
print("credit 信息增益 = ", credit)
return None
def log(base):
bb = base * math.log(base, 2)
return bb
缺点:在于信息增益会偏向于取值较多的特征。
原因:当特征对应的特征值较多的时候,每个特征值对应的样本会减少,会导致信息增益更多。因此ID3算法会偏向于取值较多的特征。
而C4.5算法可以解决这个问题。
C4.5算法
使用信息增益比率(Gain Ratio)来作为划分特征的依据。在使用信息增益比率的时候,并不是直接选择信息增益最大的特征,而是找出信息增益高于平均水平的特征,然后在从这些特征中在选择信息增益比率最高的特征,来作为决策树的根结点。
- 信息增益比率
信息增益比率就是在信息增益的基础上除以每个特征的信息熵,从而消除有大量子节点,但是每个子节点样本量很少的情况。用公式表示如下:
其中为对应样本集合D,当前特征A作为随机变量来求得的信息熵。
又称为惩罚系数。特征对应的特征值个数较多的时候,惩罚系数较小;个数比较少的时候,惩罚系数较大。
怎么理解呢?之前求解H(D)的时候,都是将训练集的类别作为随机变量,即求解的是所有的类别对应的样本集合的占比,从而求得这个信息熵。那么同理
这个就是该特征下不同的特征值对应的样本集合的占比,用公式表示如下:
其中K为特征值个数,n为分类的类别的个数
缺点:信息增益比率偏向于取值较少的特征
原因:当特征值比较少的时候的值较小,其倒数较大,因此信息增益比率会较大。因此信息增益比率趋向于取值较少的饿特征。
GART
Classify And Regression Tree。
使用基尼系数(GINI)作为划分特征的依据。选择GINI较小的特征作为决策树的根结点。
- 基尼系数
基尼系数:在一个样本集合中随机选中样本被分错的概率。
基尼系数反映了从样本集中随机抽取两个样本,其类别不一样的概率。基尼系数越小,纯度越高,则分类效果越好。基尼系数为0,表示集合类别一致。
基尼系数=样本被选中的概率x样本被分错的概率。
其中Pk是选中的样本属于k类别的概率,样本集合中有k个类别,一个随机选中的样本可以属于这k个类别中的任意一个。
(1-Pk)是该样本被分错的概率。
- 集合D的GINI系数
那么对于集合D的GINI系数,假设集合中有K个类别,那么
- 基于特征A划分的集合D的GINI系数
由于GART是一个二叉树,也就是当使用特征A进行划分集合的时候只有两种集合:
(1)给定的特征值的集合D1
(2)不等于给定的特征值的集合D2
也就是将多于2个特征值的特征进行二值处理,然后代入到下面的公式中计算GINI系数
要把所有特征A的特征值作为划分点,然后代入到上述公式中,计算出每个特征值对应的GINI系数,值最下的就是该特征的最佳划分点,也就是特征A的GINI系数。我们在进行构建决策树的时候,也就是取该GINI值最小的特征作为决策树的根结点。
举个例子:
刚才的年龄这个特征就有三种情况:青年,中年,老年。那么当使用年龄这个特征来进行划分训练集合D的时候,就会有三种情况:
(1)D1:{青年} D2:{中年 , 老年}
(2)D1:{中年} D2: {青年 , 老年}
(3)D1:{老年} D2:{中年 , 青年}
那么年龄的这个特征的GINI系数就是分别将D1和D2分别代入到下面的公式中,
取值最小的说明该划分点就是特征年龄的最佳划分点,最小值也就是年龄这个特征的GINI系数值。对于上述那个贷款问题,那我们就需要分别计算年龄、房子、工作、信贷情况的GINI系数,取值最小的就是决策树的根结点。
优势:划分更加细致。
sklearn的API
sklearn.tree.DecisionTreeClassifier(criterion="gini",
splitter="best",
max_depth=None,
min_samples_split=2,
min_samples_leaf=1,
min_weight_fraction_leaf=0.,
max_features=None,
random_state=None,
max_leaf_nodes=None,
min_impurity_decrease=0.,
min_impurity_split=None,
class_weight=None,
presort=False)
其中参数如下:
参数 | 含义 |
criterion | 就是刚才提到的划分特征的准则的算法。 默认为gini,基尼系数; entropy:信息增益 |
splitter | 默认值为best:在所有的特征中选择最好的切分点;适合于样本量不大的情况 random:在部分特征中选择;样本量比较大的时候推荐使用random |
max_depth | 决策树的最大深度,深度越大,越容易过拟合,对训练集效果比较好,但是对测试效果不太好 推荐深度5~20 |
min_samples_split | 设置节点的最小样本数量。当样本数量小于此值的时候,节点将不会在划分 默认为2。 |
min_samples_leaf | 限制叶子节点最少的样本数。如果某叶子节点的<样本数,则会和兄弟节点一起被剪枝 默认为1. |
min_weight_fraction_leaf | 限制叶子节点所有样本权重和最小值,如果<该值,则和兄弟节点一起被剪枝 默认为0:即不考虑权重问题。 |
max_features | 划分数据集时考虑的最多的特征值的数量 默认为None,样本数<50的时候,使用该值 auto:其值就是sqrt(特征值) sqrt:其值就是sqrt(特征值) log2:其值就是log2(特征值) None:其值就是最大特征值数 |
random_state | 随机种子 |
max_leaf_nodes | 最大叶子节点数。为防止过度拟合,默认不设置。如果设置该值,则会在最大叶子节点数内的最优决策树。 特征不多的时候,不需设置该值;如果特征过多,需要增加设置,具体设置值可通过交叉验证得到 |
min_impurity_decrease | 节点划分最小不纯度。限制决策树的增长,如果节点的不纯度<该值,则该节点不在生成子节点 |
min_impurity_split | 信息增益的阀值。决策树在创建分支的时候,信息增益必须>该值,否则不分类 |
class_weigh | 类别权重。主要为了防止训练集中某些类别的样本过多,导致建立的决策树偏向于该类别 默认为None。 balanced:算法自己计算权重,样本量少的类别所对应的权重会更高。 如果样本类别没有很明显的不平衡,可不设置该值。 该值不适合回归树 |
presort | 在进行拟合之前,是否预分数据来加速树的构建。 默认为False。 对于数据集比较庞大的分类,true会使得分类变得缓慢。只有数据集较小的时候,且树深度有限制的时候,才会加速分类 |
鸢尾花实例
先通过一个简单的鸢尾花的实例来看下这个算法的基本用法,之前我们在机器学习入门研究(六)-KNN算法使用KNN来进行对鸢尾花进行分类,我们知道KNN在进行分类的时候,就是在计算所有的距离然后排序,准确度高,但是计算复杂度也高,适合样本量比较少的分类。而决策树适合于数据量比较大的情况。另外当我们在进行使用不同的算法在进行分类的时候,不知道该选择哪种预估器的时候,可以把预估器都用一下,看看结果情况,选择出一个在效率和准确度都合适的范围内,在进行局部调优。
我们直接用代码实现下:
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier, export_graphviz
def tree_iris():
#使用决策树来来进行鸢尾花分类
#加载数据集
iris = load_iris()
#划分数据集
x_train, x_test, y_train, y_test = train_test_split(iris.data,iris.target,test_size=0.2, random_state=10)
#特征工程 标准化 决策树不需要进行标准化
#使用决策树预估器进行预估
classifier = DecisionTreeClassifier(criterion='entropy')
classifier.fit(x_train,y_train)
y_predict = classifier.predict(x_test)
print("预测值为:\n", y_predict)
print("预测值为:\n", y_predict == y_test)
score = classifier.score(x_test, y_test)
print("准确度:\n", score)
#模型调优和评估 交叉
return None
步骤和之前的都是一样的。就是先加载数据->划分数据集->特征工程->预估器进行预估->模型评估和调优。由于决策树算法的核心是计算的概率值,所以此时的鸢尾花数据不需要进行标准化,可以直接代入预估器进行计算。我们运行下可以看下结果:
预测值为:
[1 2 0 1 0 1 2 1 0 1 1 2 1 0 0 2 1 0 0 0 2 2 2 0 1 0 1 1 2 2]
预测值为:
[ True True True True True True False True True True True True
True True True True True True True True True True True True
True True True True False True]
准确度:
0.9333333333333333
决策树的可视化的API
我们知道该算法会生成一个决策树,skelarn中也提供了相应的API可以将该树进行可视化。对于的API为
sklearn.tree.export_graphviz(decision_tree, out_file=None, max_depth=None,
feature_names=None, class_names=None, label='all',
filled=False, leaves_parallel=False, impurity=True,
node_ids=False, proportion=False, rotate=False,
rounded=False, special_characters=False, precision=3)
主要看几个重要的参数:
参数 | 含义 |
decision_tree | 预估器,就是我们之前创建的决策树的预估器 |
out_file | 生成的文件的保存目录 |
feature_names | 特征名称。如果不赋值,则创建决策树的时候无法看到对应的特征 |
我们把刚才的实例生成一下对应的决策树。
#可视化决策树
export_graphviz(classifier, out_file='/Users/j1/Documents/机器学习/code/machinelearning/estimator/tree.dot', feature_names=iris.feature_names)
我们可以看到在对应的目录下已经生成一个tree.out文件,打开文件如图所示,我们并不能直观的去查看决策树。
digraph Tree {
node [shape=box] ;
0 [label="petal width (cm) <= 0.8\nentropy = 1.582\nsamples = 120\nvalue = [40, 37, 43]"] ;
1 [label="entropy = 0.0\nsamples = 40\nvalue = [40, 0, 0]"] ;
0 -> 1 [labeldistance=2.5, labelangle=45, headlabel="True"] ;
2 [label="petal length (cm) <= 4.75\nentropy = 0.996\nsamples = 80\nvalue = [0, 37, 43]"] ;
0 -> 2 [labeldistance=2.5, labelangle=-45, headlabel="False"] ;
3 [label="petal width (cm) <= 1.65\nentropy = 0.191\nsamples = 34\nvalue = [0, 33, 1]"] ;
2 -> 3 ;
4 [label="entropy = 0.0\nsamples = 33\nvalue = [0, 33, 0]"] ;
3 -> 4 ;
5 [label="entropy = 0.0\nsamples = 1\nvalue = [0, 0, 1]"] ;
3 -> 5 ;
6 [label="petal length (cm) <= 5.15\nentropy = 0.426\nsamples = 46\nvalue = [0, 4, 42]"] ;
2 -> 6 ;
7 [label="petal width (cm) <= 1.85\nentropy = 0.787\nsamples = 17\nvalue = [0, 4, 13]"] ;
6 -> 7 ;
8 [label="sepal width (cm) <= 3.05\nentropy = 0.971\nsamples = 10\nvalue = [0, 4, 6]"] ;
7 -> 8 ;
9 [label="petal width (cm) <= 1.75\nentropy = 0.811\nsamples = 8\nvalue = [0, 2, 6]"] ;
8 -> 9 ;
10 [label="petal width (cm) <= 1.55\nentropy = 1.0\nsamples = 4\nvalue = [0, 2, 2]"] ;
9 -> 10 ;
11 [label="entropy = 0.0\nsamples = 2\nvalue = [0, 0, 2]"] ;
10 -> 11 ;
12 [label="entropy = 0.0\nsamples = 2\nvalue = [0, 2, 0]"] ;
10 -> 12 ;
13 [label="entropy = 0.0\nsamples = 4\nvalue = [0, 0, 4]"] ;
9 -> 13 ;
14 [label="entropy = 0.0\nsamples = 2\nvalue = [0, 2, 0]"] ;
8 -> 14 ;
15 [label="entropy = 0.0\nsamples = 7\nvalue = [0, 0, 7]"] ;
7 -> 15 ;
16 [label="entropy = 0.0\nsamples = 29\nvalue = [0, 0, 29]"] ;
6 -> 16 ;
}
但是我们可以通过graphviz来去将这棵树可视化,我们首先要安装graphviz
brew install graphviz
这样我们就可以在dot文件所在的目录下,通过终端输入下面的命令就可以生成对应的决策树
#输入以下命令生成图片
dot tree.dot -T png -o tree.png
#输入以下命令生成PDF
dot -T pdf tree.dot -o tree.pdf
上面的决策树如图:
从我们可以很明显的看到鸢尾花的四个特征sepal length(花萼的长度),sepal width (花萼的宽度), petal length(花瓣的长度), petal width(花瓣的宽度),其中petal length(花瓣的长度)为决策树的根结点,也就是petal length对进行分类其关键作用。
总结
- 优点:
简单易于理解,有决策树可视化,能够更清晰明了的去解释问题
对中间值的缺失不敏感
可以处理不相关的特征数据
- 缺点:
容易产生过度拟合
- 改进
剪枝cart算法(决策树API已经实现,结合后面的随机森林进行学习)
随机森林