决策树的基本概念
决策树就是一棵树。
- 叶结点对应于决策结果,其他每个结点则对应于一个属性测试;
- 每个结点包含的样本集合根据属性测试的结果被划分到子结点中;
- 根结点包含样本全集,从根结点到每个叶子结点的路径对应了一个判定测试序列。
示例:
决策树学习的关键在于如何选择最优的划分属性,所谓的最优划分属性,对于二元分类而言,就是尽量使划分的样本属于同一类别,即“纯度”最高的属性。那么如何来度量特征(features)的纯度,这时候就要用到“经验熵(information entropy)”。
经验熵
先来看看信息熵的定义:假如当前样本集D中第k类样本所占的比例为
p
i
(
k
=
1
,
2
,
3
,
⋅
⋅
⋅
,
∣
I
∣
)
p_i(k=1,2,3,···,|I|)
pi(k=1,2,3,⋅⋅⋅,∣I∣)为类别的总数(对于二元分类来说,i=2)。则样本集的信息熵为:
p
k
=
∣
C
k
∣
D
p_k=\frac{|C_k|}{D}
pk=D∣Ck∣
H ( D ) = − ∑ k = 1 K p k l o g 2 p k H(D)=-\sum_{k=1}^Kp_klog_2p_k H(D)=−k=1∑Kpklog2pk
H
(
D
)
=
−
∑
k
=
1
K
∣
C
k
∣
∣
D
∣
l
o
g
2
∣
C
k
∣
∣
D
∣
H(D)=-\sum_{k=1}^K\frac{|C_k|}{|D|}log_2\frac{|C_k|}{|D|}
H(D)=−k=1∑K∣D∣∣Ck∣log2∣D∣∣Ck∣
H
(
D
)
H(D)
H(D)的值越小,则D的纯度越高。
反应了信息增益的一个缺点:该属性能取得值越多,信息增益,越偏向这个
信息增益算法
- 输入:训练数据集 D D D和特征 A A A;
- 输出:特征 A A A对训练数据 D D D的信息增益 g ( D , A ) g(D,A) g(D,A)
信息增益表示了得知特征X的信息而使得类Y的信息的不确定性性减少的程度。
经验条件熵
计算特征
A
A
A对数据集
D
D
D的经验条件熵
H
(
D
∣
A
)
H(D|A)
H(D∣A)
H
(
D
∣
A
)
=
∑
i
=
1
n
∣
D
i
∣
∣
D
∣
H
(
D
i
)
H(D|A)=\sum_{i=1}^n\frac{|D_i|}{|D|}H(D_i)
H(D∣A)=i=1∑n∣D∣∣Di∣H(Di)
= − ∑ i = 1 n ∣ D i ∣ ∣ D ∣ ∑ k = 1 K ∣ D i k ∣ ∣ D i ∣ l o g 2 ∣ D i k ∣ ∣ D i ∣ =-\sum_{i=1}^n\frac{|D_i|}{|D|}\sum_{k=1}^K\frac{|D_{ik}|}{|D_i|}log_2\frac{|D_{ik}|}{|D_i|} =−i=1∑n∣D∣∣Di∣k=1∑K∣Di∣∣Dik∣log2∣Di∣∣Dik∣
- D i k D_{ik} Dik的意思是在某一特征下的特征样本中满足训练目标的个数
- D i D_i Di指的是该特征的样本容量
信息增益为:
信息增益=经验熵-经验条件熵
g
(
D
,
A
)
=
H
(
D
)
−
H
(
D
∣
A
)
g(D,A)=H(D)-H(D|A)
g(D,A)=H(D)−H(D∣A)
一般而言,信息增益越大,则表示使用特征 对数据集划分所获得的“纯度提升”越大。所以信息增益可以用于决策树划分属性的选择,其实就是选择信息增益最大的属性,ID3算法就是采用的信息增益来划分属性。
信息增益比:
信息增益比
g
R
(
D
,
A
)
g_R(D,A)
gR(D,A)为信息增益
g
(
D
,
A
)
g(D,A)
g(D,A)与训练数据集
D
D
D关于特征
A
A
A的值熵
H
A
(
D
)
H_A(D)
HA(D)值之比:
g
R
(
D
,
A
)
=
g
(
D
,
A
)
H
A
(
D
)
g_R(D,A)=\frac{g(D,A)}{H_A(D)}
gR(D,A)=HA(D)g(D,A)
其中:
H
A
(
D
)
=
−
∑
i
=
1
n
∣
D
i
∣
∣
D
∣
l
o
g
2
∣
D
i
∣
∣
D
∣
H_A(D)=-\sum_{i=1}^n\frac{|D_i|}{|D|}log_2\frac{|D_i|}{|D|}
HA(D)=−i=1∑n∣D∣∣Di∣log2∣D∣∣Di∣
n
n
n是特征
A
A
A取值的个数。
ID3算法决策树生成
- 输入:数据集 D D D,特征集 A i A_i Ai,阈值 ε \varepsilon ε
- 输出:决策树 T T T
- 先对特征集求信息增益,选取最大的作为根结点的特征。
- 看特征集对训练数据集D的划分成几个子集 D i D_i Di(例如该特征集会划分 是或否具有该特征)
- 若某个子集只具有同一个类的的样本点,成为一个叶节点
- 否则,对子集从剩下的特征中选择新的特征,求得信息增益 g ( D k , A r e m a i n ) g(D_{k},A_{remain}) g(Dk,Aremain)
- 如此递归操作,直到所有的特征分类,生成决策树 T T T。
Python代码实现
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 createDataSet_temp():
#创造实例数据
labels = ['头发', '声音']
with open('TreeGrowth_ID3.txt', 'r', encoding='UTF-8') as f: #改了一个文件读写
dataSet = [[] for i in range(9)]
value = ['长', '短', '粗', '细', '男', '女']
num_line = 0
for line in f:
# re.split(r'(\s{8}\[\')|(\', \')|(\'\],)|(\'\])', line) #尝试使用正则划分,失败了
for i in line:
if (i in value):
dataSet[num_line].append(i)
num_line += 1
del(dataSet[0])
'''
dataSet = [
['长', '粗', '男'],
['短', '粗', '男'],
['短', '粗', '男'],
['长', '细', '女'],
['短', '细', '女'],
['短', '粗', '女'],
['长', '粗', '女'],
['长', '粗', '女']
]
'''
return dataSet, labels
def splitDataSet(dataSet, axis, value): #按某个特征分类后的数据
retDataSet = []
for featVec in dataSet:
if featVec[axis] == value: #axis表示指定属性在label中的标号,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]#读取每一条记录取出其中第i个属性的值,并新建一个列表
uniqueVals = set(featList) #set()创建一个无序不重复元素集,可进行关系测试,删除重复元素,进行交差并集运算
newEntropy = 0
for value in uniqueVals: #对于第i个属性值列表中每一个值
subDataSet = splitDataSet(dataSet, i, value) #subDataSet是去掉了第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): #多数表决排序,如:最后分类为2男1女则判断为男
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)
#将items返回的可遍历键值对数组,根据键值对的第二个域(值),进行降序排列
return sortedClassCount[0][0] #返回数量最大的类名
def createTree(dataSet,labels):
classList = [example[-1] for example in dataSet]#类别:男或女 对于记录集中的每一条都取其最后一个值形成列表
if classList.count(classList[0]==len(classList)):#~~~~~~~~~~~~~~~~~~~当该记录集只有一个特征时
return classList[0]
if len(dataSet[0])==1: #当最后一个属性分类完成,记录的长为一
return majorityCnt((classList)) #直接返回数量最多的值
bestFeat = chooseBestFeatureToSplit(dataSet) #找到最优特征的标号
if(bestFeat == -1): #自己添加的,不然报错
return classList[0]
bestFeatLabel = labels[bestFeat] #找到最优特征
myTree = {bestFeatLabel:{}} #分类结果以字典形式保存,得到树的当前层
del(labels[bestFeat]) #删除labels中当前最优特征
featValues = [example[bestFeat] for example in dataSet]#将记录集中当前最优特征的值形成列表
uniqueVals = set(featValues) #对值集去重
for value in uniqueVals: #对于每个值
subLabels = labels[:] #subLabels为去除当前最优特征的特征集
myTree[bestFeatLabel][value]=createTree(splitDataSet(dataSet,bestFeat,value),subLabels)
#使用递归,创造下一层树,~~~~~~~~~~~~~~~~~~~~~~
return myTree
if __name__=='__main__':
dataSet, labels = createDataSet_temp() #创造示例数据
print(createTree(dataSet, labels)) #输出决策树模型结果
参考博客: