决策树
本博文是《统计学习方法》第5章决策树的学习笔记,后面的Python代码来源于《机器学习实战》(代码中的例子是《统计学习方法》中的)
1 决策树模型
决策树就是一个if-the规则的集合,这个集合具有互斥和完备的性质:每一个实例都被一条路径或一条规则所覆盖,而且只被一条路径或一条规则所覆盖。
因此,决策树学习本质上就是从训练数据集中归纳出一组分类规则,主要包括:特征选择、决策树的生成、决策树的剪枝。下面将围绕这三个方面进行介绍。
1.1 特征选择
在决策树学习时,首先就是选取特征对数据集进行划分。考虑如下决策树学习:
假设给定训练数据集
D={(x1,y1),(x2,y2),⋯,(xN,yN)}
,其中
xi=(x(1)i,x(2)i,⋯,x(n)i)
为一个样例,
n
为特征个数,
数据集有
n
个特征,那么选取哪个特征进行划分呢?或者说,要利用所有特征对数据集进行划分,特征选择的顺序如何安排才是好的呢?这就是特征选择问题。常用的度量选取特征优劣的方法就是计算信息增益(如ID3算法)、信息增益比(如C4.5算法)。
信息增益
首先介绍熵(entropy)和条件熵(conditional entropy)的概念。
熵是信息论和概率统计中度量信息的一种方法,表示随机变量不确定性的度量。设
熵只依赖于 X 的分布,与
而条件熵 H(Y|X) 则表示在已知随机变量 X 的条件下,随机变量
这里的 pi=P(X=xi),i=1,2,⋯,n 。
当熵和条件熵中的概率由数据估计(如极大似然估计)得到时,所对应的熵和条件熵分别称为 经验熵(empirical entropy)和 经验条件熵(empirical conditional entropy)。而 信息增益表示得知特征 X 的信息而使得类
信息增益 information gain:特征 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 和特征A ;
输出:特征 A 对训练数据集D 的信息增益 g(D,A) .
(1)计算数据集 D 的经验熵H(D)
H(D)=−∑k=1K|Ck||D|log2|Ck||D|
(2)计算特征 A 对数据集D 的经验条件熵 H(D|A)
H(D|A)=∑i=1n|Di||D|H(Di)=−∑i=1n|Di||D|∑k=1K|Dik||Di|log2|Dik||Di|
(3)计算信息增益
g(D,A)=H(D)−H(D|A)
信息增益比
以信息增益作为划分训练数据集的特征,存在偏向于选择取值较多的特征的问题,可以使用信息增益比(information gain ratio)对这一问题进行校正。
信息增益比:特征 A 对训练数据集的信息增益比
gR(D,A) 定义为信息增益 g(D,A) 与训练数据集 D 关于特征A 的值的熵 HA(D) 之比,即
gR(D,A)=g(D,A)HA(D)
1.2 决策树生成
主要介绍两种经典的决策树生成算法:ID3和C4.5。
1.2.1 ID3算法
ID3算法核心是:在决策树各个结点上应用信息增益准则选择特征,递归地建立决策树。具体方法:从根结点开始,对结点计算所有可能的特征的信息增益,选择信息增益最大的特征作为结点的特征,由该特征的不同取值建立子结点;再对子结点递归调用以上方法。
ID3算法
输入:训练数据集 D ,特征集A ,阈值 ϵ ;
输出:决策树 T
(1)若D 中所有实例属于同一类 Ck ,则 T 为单结点树,并将类Ck 最为该结点的类标记,返回 T ;
(2)若A=∅ ,则 T 为单结点树,并将D 中实例数最大的类 Ck 作为该结点的类标记,返回 T ;
(3)否则,按信息增益计算方法计算A 中特征对 D 的信息增益,选择信息增益最大的特征Ag ;
(4)如果 Ag 的信息增益小鱼阈值 ϵ ,则置 T 为单结点树,并将D 中实例数最多的类 Ck 作为该结点的类标记,返回 T ;
(5)否则,对Ag 的每一可能值 ai ,依 Ag=ai 将 D 分割为若干非空子集Di ,将 Di 中实例数最大的类作为标记,构建子结点,由结点及其子结点构成树 T ,返回T ;
(6)对第 i 个子结点,以Di 为训练集,以 A−{Ag} 为特征集,递归调用(1)-(5)得到字数 Ti ,返回 Ti 。
1.2.2 C4.5算法
C4.5算法与ID3算法相似,只不过在生成的过程中,用信息增益比来选择特征。
1.3 决策树剪枝
如果不对生成的决策树进行剪枝操作,生成的决策树容易过拟合,其原因在于:学习时过多地考虑如何提高对训练数据的正确分类,从而构建出过于复杂的决策树。解决这个问题的办法是考虑决策树的复杂度,对已生成的决策树进行简化。
(剪枝操作的具体过程,后面再补充)
2 决策树算法的Python实现
这里实现的是ID3算法,且未带剪枝操作。参考来源:Machine Learning in Action.
实例来自于《统计学习方法》第5章决策树
# -*- coding: utf-8 -*-
'''
Decision Tree.
Reference:
1.Machine Learning in Action.
2.Statistical Learning Methods,Li,Hang.
'''
from math import log
import operator
def calc_entropy(dataset):
num=len(dataset)#number of samples.
label_count={}
for sample in dataset:
current_label=sample[-1]
if current_label not in label_count.keys():
label_count[current_label]=0
label_count[current_label]+=1
entropy=0.0
for key in label_count:
p=float(label_count[key])/num
entropy-=p*log(p,2)
return entropy
def partition_dataset(dataset,axis,value):
partitioned_dataset=[]
for sample in dataset:
if sample[axis]==value:
reduced_feature=sample[:axis]#this two lines exclude feature sample[axis].
reduced_feature.extend(sample[axis+1:])
partitioned_dataset.append(reduced_feature)
return partitioned_dataset
def choose_best_feature_to_partition(dataset):
num_features=len(dataset[0])-1 #last one element is label.
num_samples=len(dataset)
base_entropy=calc_entropy(dataset)
best_info_gain=0.0
best_feature=-1
for i in range(num_features):
feature_value_list=[sample[i] for sample in dataset]
feature_value_list=set(feature_value_list)
current_entropy=0.0
for value in feature_value_list:
sub_dataset=partition_dataset(dataset,i,value)
p=float(len(sub_dataset))/num_samples
current_entropy+=p*calc_entropy(sub_dataset)
info_gain=base_entropy-current_entropy
if info_gain>best_info_gain:
best_feature=i
best_info_gain=info_gain
return best_feature#,best_info_gain
def majority_vote(label_list):
label_count={}
for label in label_list:
if label not in label_list.key():
label_count[label]=0
label_count[label]+=1
sorted_label_count=sorted(label_count.iteritems(),key=operator.itemgetter(1),reverse=True)
return sorted_label_count[0][0]
def create_tree(dataset,labels):
label_list=[sample[-1] for sample in dataset]
if label_list.count(label_list[0])==len(label_list):
return label_list[0]
if len(dataset[0])==1:
return majority_vote(label_list)
best_feature=choose_best_feature_to_partition(dataset)
best_feature_label=labels[best_feature]
tree={best_feature_label:{}}
del(label_list[best_feature])
feature_value=[sample[best_feature] for sample in dataset]
unique_feature_value=set(feature_value)
for value in unique_feature_value:
sub_labels=labels[:]
tree[best_feature_label][value]=create_tree(partition_dataset(dataset,best_feature,value),sub_labels)
return tree
def create_dataset():
dataset=[['youth','no','no','ordinary','no'],['youth','no','no','good','no'],['youth','yes','no','good','yes'],['youth','yes','yes','ordinary','yes'],['youth','no','no','ordinary','no'],
['middle','no','no','ordinary','no'],['middle','no','no','good','no'],['middle','yes','yes','good','yes'],['middle','no','yes','very good','yes'],['middle','no','yes','very good','yes'],
['old','no','yes','very good','yes'],['old','no','yes','good','yes'],['old','yes','no','good','yes'],['old','yes','no','very good','yes'],['old','no','no','ordinary','no']]
return dataset
dataset=create_dataset()
feature_attributes=['age','having job','own house','credit condition']
best_feature=choose_best_feature_to_partition(dataset)
my_tree=create_tree(dataset,feature_attributes)
print my_tree