“信息熵”(information entropy)是度量样本集合纯度的一种常用指标。商越小代表纯度越高。商越大越混乱。
熵的计算公式:
其中为集合中第K类属性所占样本的比例。Ent(D)的值越小,则D的纯度越高
信息增益
开始的ID3算法就是根据信息增益来进行分类的。
通过不断得到信息增益最大的特征作为分类的依据,不断划分。缺点是倾向于可取数目多的属性有所偏好;
C4.5算法使用增益率来选择最优的划分属性,但是对可取数目较少的属性有所偏好:
代码的实现与公式一致,不断拿信息增益最大的特征进行分类,直到分类结束–特征使用完毕。
看到有个例子使用了信息熵和条件熵,这里用来方便理解。
信息熵是代表随机变量的复杂度(不确定度),条件熵代表在某一个条件下,随机变量的复杂度(不确定度)
而我们的信息增益恰好是:信息熵-条件熵。
换句话说,信息增益代表了在一个条件下,信息复杂度(不确定性)减少的程度。
如果选择一个特征后,信息增益最大(信息不确定性减少的程度最大),那么我们就选取这个特征。
求得随机变量X(嫁与不嫁)的信息熵为:嫁的个数为6个,占1/2,那么信息熵为-1/2log1/2-1/2log1/2 = -log1/2=0.301
现在假如我知道了一个男生的身高信息。
身高有三个可能的取值{矮,中,高} 矮包括{1,2,3,5,6,11,12},嫁的个数为1个,不嫁的个数为6个中包括{8,9} ,嫁的个数为2个,不嫁的个数为0个高包括{4,7,10},嫁的个数为3个,不嫁的个数为0个。
我们先求出公式对应的:
H(Y|X = 矮) = -1/7log1/7-6/7log6/7=0.178
H(Y|X=中) = -1log1-0 = 0
H(Y|X=高) = -1log1-0=0
p(X = 矮) = 7/12,p(X =中) = 2/12,p(X=高) = 3/12
则可以得出条件熵为:
7/120.178+2/120+3/12*0 = 0.103
那么我们知道信息熵与条件熵相减就是我们的信息增益,为
0.301-0.103=0.198
所以我们可以得出我们在知道了身高这个信息之后,信息增益是0.198
代码实现:
# -*- coding: utf-8 -*-
"""
Created on Thu Dec 20 00:17:49 2018
@author: keqkeq
"""
from math import log
import operator
def calcShannonEnt(dataSet): # 计算数据的熵(entropy)
numEntries=len(dataSet) # 数据条数
labelCounts={}
for featVec in dataSet:
currentLabel=featVec[-1] # 每行数据的最后一个是类别
if currentLabel not in labelCounts.keys():
labelCounts[currentLabel]=0
labelCounts[currentLabel]+=1 # 统计有多少个类以及每个类的数量
shannonEnt=0
for key in labelCounts:
prob=float(labelCounts[key])/numEntries # 计算单个类的熵值
shannonEnt-=prob*log(prob,2) # 累加每个类的熵值
return shannonEnt
def createDataSet1(): # 创造示例数据
dataSet = [['0_5K', 'Bad','Low', 'Reject'],
['0_5K', 'Good', 'Low','Approve'],
['0_5K', 'Unknown', 'High','Reject'],
['0_5K', 'Unknown', 'Low','Approve'],
['0_5K', 'Unknown', 'Low','Approve'],
['0_5K', 'Unknown', 'Low','Reject'],
['5_10K', 'Bad', 'High','Reject'],
['5_10K', 'Good', 'High','Approve'],
['5_10K', 'Unknown', 'High','Approve'],
['5_10K', 'Unknown', 'Low','Approve'],
['Over_10K', 'Bad', 'Low','Reject'],
['Over_10K', 'Good', 'Low','Approve']]
labels = ['Income','Credit_History','Debt'] #三个特征
return dataSet,labels
def splitDataSet(dataSet,axis,value): # 按某个特征分类后的数据
retDataSet=[]
for featVec in dataSet:
if featVec[axis]==value:
reducedFeatVec =featVec[:axis]
reducedFeatVec.extend(featVec[axis+1:])
retDataSet.append(reducedFeatVec)
return retDataSet
def chooseBestFeatureToSplit(dataSet): # 选择最优的分类特征
numFeatures = len(dataSet[0])-1
baseEntropy = calcShannonEnt(dataSet) # 原始的熵
bestInfoGain = 0
bestFeature = -1
for i in range(numFeatures):
featList = [example[i] for example in dataSet]
uniqueVals = set(featList)
newEntropy = 0
for value in uniqueVals:
subDataSet = splitDataSet(dataSet,i,value)
prob =len(subDataSet)/float(len(dataSet))
newEntropy +=prob*calcShannonEnt(subDataSet) # 按特征分类后的熵
infoGain = baseEntropy - newEntropy # 原始熵与按特征分类后的熵的差值
if (infoGain>bestInfoGain): # 若按某特征划分后,熵值减少的最大,则次特征为最优分类特征
bestInfoGain=infoGain
bestFeature = i
return bestFeature
def majorityCnt(classList): #按分类后类别数量排序,消除噪音,比如:最后分类为2R1A,则判定为R;
classCount={}
for vote in classList:
if vote not in classCount.keys():
classCount[vote]=0
classCount[vote]+=1
sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
return sortedClassCount[0][0]
def createTree(dataSet,labels):
classList=[example[-1] for example in dataSet] # 类别:Reject or Approve
if classList.count(classList[0])==len(classList):
return classList[0]
if len(dataSet[0])==1:
return majorityCnt(classList)
bestFeat=chooseBestFeatureToSplit(dataSet) #选择最优特征
bestFeatLabel=labels[bestFeat]
myTree={bestFeatLabel:{}} #分类结果以字典形式保存
del(labels[bestFeat])
featValues=[example[bestFeat] for example in dataSet]
uniqueVals=set(featValues)
for value in uniqueVals:
subLabels=labels[:]
myTree[bestFeatLabel][value]=createTree(splitDataSet\
(dataSet,bestFeat,value),subLabels)
return myTree
if __name__=='__main__':
dataSet, labels=createDataSet1() # 创造示列数据
print(createTree(dataSet, labels)) # 输出决策树模型结果
GIni计算实例
其中是否逾期为我们的目标变量,这是一个二分类的问题,上面公式中的n = 2,那么要对每个字段的每一种切割方法计算Gini不纯度:
在这个节点如果按照性别分割Gini不纯度为0.4283。
教育程度:
第一种分割方法:把高中及以下和本科/大专分到一组,硕士及以上单独一组。
第二种分割方法:把高中及以下和硕士及以上分到一组,本科/大专单独一组
第三种分割方法:把本科/大专和硕士及以上分到一组,高中及以下单独一组
可以看到在教育程度的三种切割方法中,第二种(把高中及以下和硕士及以上分到一组,本科/大专单独一组)有最小的Gini不纯度0.3667。
年龄:年龄为连续型变量,一共有14个不同的取值,每两个相邻值之间都可以是一个切割点,一共有13种不同的切割方法,切割点往往选择为两个相邻值的均值。
在这13种切割方法中最小的Gini不纯度为0.4105。再对比根据性别、教育程度和年龄切割的三个Gini不纯度可以得出,在这个节点应该根据教育程度进行切割,且将高中及以下和硕士及以上分到一组,本科/大专单独一组。