基本介绍:
决策树(decision tree)是一种常见的机器学习方法,表示基于特征对实例进行分类的过程,代表了对象属性与对象值之间的映射关系。模型具有可读性,分类速度快的优点。学习时利用训练数据,根据损失函数最小化的原则建立决策树模型,预测时根据所得的决策树进行分类。决策树是一种树型结构,每个内部节点表示一个属性上的测试,每个分支代表一个测试输出,而每个叶子代表一种种类。如图所示:
决策树:
用决策树分类即从根节点开始,对实例的特征进行测试,根据测试结果将实例分配到子节点中。如根据人头发的长或短将其分到两个子节点中。即将决策树有非此即彼的性质,并且所规定的集合应该是完备的,即每个实例都只能被一条路径或者规则所覆盖,也就是说一个人不可能头发即长又短,同时我们的数据中头发的类别应该只有两种(长、短),若是出现了第三种可能,则应该相应增加类别。
同时,决策树也表示了一定的概率分布,即最终每个叶节点上的分类往往代表着属于该类的概率较大。
决策树实际上代表着一种分类的规则,如何选择分类的特征来构造树则是问题的关键。因此我们需要找到一种方法来从诸多特征中选择最优的特征,如果一个特征有大于二个类,可能还要寻找最优候选值。
特征选择:
一个良好的特征要对于数据具有较好的分类能力,若利用某个特征进行分类与随机分类的结果并没有很大差异,那么这样的特征对我们的分类并没有什么作用。但是我们如何从这么多的特征中选择合适的特征?在这里利用熵引入了信息增益和信息增益比的概念。
熵、条件熵、信息增益:
在信息论中熵表示一种对信息的度量,是一种消除不确定性所需信息量的度量,也可看做对信息量的期望。
信息量:$$ I(x_{i})=log(\frac{1}{p(x_{i})}) $$
$ P(x_{i}) $表示离散随机变量的概率分布,概率越大表示事件所含的信息量较小。例如中国队进了世界杯,这是一件小概率事件,因此它发生了会给你带来很多的信息。而熵则是平均信息量,即:$$ H(p)=-\sum p_{i}log(p_{i}) $$
熵越大,随机变量的不确定性就越大。
再引入条件熵的概念,即在X给定下随机变量Y的条件熵:$$ H(Y|X)=\sum p_{i}H(Y|X=x_{i}) $$
至此我们终于可以引入信息增益的概念,即信息论中的互信息量: $$ g(D,A)=H(D)-H(D|A) $$
信息增益代表了得知X后使得Y不确定性减少的程度。即可表示特征A可使数据集的分类不确定性减少的程度,我们可以认为信息增益越大的特征能够具有更好的分类效果。
ID3算法:
ID3算法的核心思想即在决策树的各个节点上使用信息增益选择特征。从根节点开始,计算所有特征的信息增益,选择信息增益最大的特征作为节点特征,再根据该特征的取值建立子节点,对子节点递归调用上述过程。即:
输入:训练数据集D,特征A,阈值beta :
输出:决策树T
(1)若D中所有实例属于同一类Ck则T为单节点树,并将Ck作为该节点的类标记,返回T
(2)若A为空集,则通过统计D中实例数最大的类作为该节点类标记(即对于某一节点而言,没有其它特征可以进行分辨了,则对将该节点的类设置为分到该节点数据最多属于的类中),返回T
(3)否则计算A中各特征对D的信息增益,选择信息增益最大的特征 Ag :$$ H(D)=-\sum \frac{\left | C_{k} \right |}{\left | D \right |}log(\frac{\left | C_{k} \right |}{\left | D \right |}) $$
$$ H(D|A)=-\sum \frac{\left | D_{i} \right |}{\left | D \right |}H(D_{i}) $$
信息增益:$$ g\left ( D,A \right )=H\left ( D \right )-H\left ( D|A \right ) $$
(4)若Ag的信息增益小于阈值,则将T作为单节点树,将D中实例数最大的类作为该节点的标记
(5)否则,对Ag每一个可能值 ai 将D划分为若干非空子集,将子集中实例数最大的类作为标记,构建子节点,返回树T
(6)对第i个子节点,以(5)中的子集作为训练集,A-Ag
作为特征集,递归调用(1)~(5),构建决策树。
上述就是ID3算法的核心步骤,即用信息增益来选择分类特征。但是以信息增益作为划分的标准会带来一个问题,观察公式我们可以看出,由于对于数据集而言经验熵是给定的,我们想要获得大的信息增益就要使条件熵较小,当我们的特征有较多取值时条件熵就会相应较小,因此我们的算法会倾向于选择取值较多的特征值。为了解决这个问题,就有了C4.5算法。
C4.5算法:
信息增益比:
信息增益与训练数据集D关于特征A的值的熵之比:$$ g_{r}(D,A)=\frac{g(D,A)}{H_{A}(D)} $$
其中:$$ H_{A}(D)=\sum \frac{\left | D_{i} \right |}{\left | D \right |}log(\frac{\left | D_{i} \right |}{\left | D \right |}) $$
C4.5算法即是将ID3算法中选择特征的标准用信息增益比代替
ID3算法Python实现如下:
from math import log
import operator
def create_data():#构建数据和标签
dataSet=[['short', 'long hair', 'thin', 'female'],
['high', 'short hair', 'thin', 'male'],
['short', 'long hair', 'fat', 'female'],
['high', 'long hair', 'thin', 'female'],
['short', 'short hair', 'fat', 'male'],
['short', 'short hair', 'thin', 'female'],
['high', 'short hair', 'fat', 'male'],
['high', 'long hair', 'fat', 'male'],
['short', 'short hair', 'thin', 'male'],
['high', 'short hair', 'thin', 'female'],
['short', 'long hair', 'fat', 'female']]
labels= ['stature', 'hair', 'weight', 'gender']
return dataSet, labels
def cal_entropy(dataSet):
num=len(dataSet)
label_count={}
for fea in dataSet:
current_label=fea[-1]#统计每条数据的类
if current_label not in label_count.keys():
label_count[current_label]=0
label_count[current_label]+=1 #计算每个类中有多少数据
entropy=0.0
for i in label_count:#计算经验熵
Pi=float(label_count[i])/num
entropy-=Pi*log(Pi,2)
return entropy
def remove_feature(dataSet,axis,feature):#去除某个特征
retdataset=[]
for featVec in dataSet:
if featVec[axis]==feature:
reducedata=featVec[:axis]#某个特征前数据
reducedata.extend(featVec[axis+1:])#某个特征后数据
#去掉了axis
retdataset.append(reducedata)
return retdataset
def choose_best_feature(dataSet):
entropy=cal_entropy(dataSet)
feature_num=len(dataSet[0])-1
max_mutual_info=0
best_feature=-1
for i in range(feature_num):
feature_list=[example[i] for example in dataSet]
feature_class=set(feature_list)#得到该特征的所有可能取值
conditional_entropy=0
for value in feature_class:
retdataset=remove_feature(dataSet, i, value)
Pi=len(retdataset)/float(len(dataSet))
conditional_entropy+=Pi*cal_entropy(retdataset)#求条件熵
mutual_info=entropy-conditional_entropy#互信息量
if (mutual_info>max_mutual_info):
max_mutual_info=mutual_info
best_feature=i
return best_feature
def majority_vote(class_list):
class_count={}
for vote in class_list:
if vote not in class_count.keys():
class_count[vote]=0
class_count[vote]+=1
sort_class_count=sorted(class_count.items(),key=operator.itemgetter(1),reverse=True)
#排序来决定该节点的类
return sort_class_count[0][0]
def create_tree(dataSet,labels):
class_list=[example[-1] for example in dataSet]
if class_list.count(class_list[0])==len(class_list):
return class_list[0]
if len(dataSet[0])==1:
#节点已没有特征可以继续分解
return majority_vote(class_list)
best_feature=choose_best_feature(dataSet)
best_feature_label=labels[best_feature]
my_tree={best_feature_label:{}}
del(labels[best_feature])
#删掉已选择的特征
feature=[example[best_feature] for example in dataSet]
feature_class=set(feature)
for value in feature_class:
sublabels=labels[:]
my_tree[best_feature_label][value]=create_tree(remove_feature(dataSet,best_feature,value),sublabels)
#迭代生成决策树
return my_tree
if __name__=='__main__':
dataSet,labels=create_data()
print(create_tree(dataSet,labels))
参考书籍:李航《统计学习方法》