决策树类似于流程图的树结构,其中,每个内部节点表示在一个属性上面的测试,每个分支代表一个属性输出,每个树叶节点代表类或者类分布。构成它的元素是节点和边,节点会根据样本的特征做出判断,最初的分支点被成为根结点,其余的被成为子节点,不再有分支的节点被称为叶子节点,他们代表样本的分类结果。边则指示着方向。
为了确定许多的特征中谁来做根节点,接下来的节点又是哪些,引入了熵的概念,在决策树中,熵代表的是分支下样本种类的丰富性,样本种类越多越混乱,熵就越大,随着树的深度即树的层数增加,熵的降低速度越快,代表决策树分类效率越高。
决策树最大的优点是天然的可解释性,对各种属性的具象判断;但是数据都是有特例的,如果一棵树能够将训练样本完美分类,那它一定是过拟合的。解决的方法:剪枝,剪枝有两种:预剪枝是在训练开始前规定的条件,比如树达到某一个深度就停止训练;后剪枝则是依据一定条件如限制叶子结点的个数去掉一部分分支。
代码实现:通过样本声音粗细和头发长短来对男女性别进行分类
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 # 计算单个类的熵值 eg:男 和 女
shannonEnt-=prob*log(prob,2) # 累加每个类的熵值 男+女
return shannonEnt
def createDataSet1(): # 创造示例数据
dataSet = [['长', '粗', '男'],
['短', '粗', '男'],
['短', '粗', '男'],
['长', '细', '女'],
['短', '细', '女'],
['短', '粗', '女'],
['长', '粗', '女'],
['长', '粗', '女']]
labels = ['头发','声音'] #两个特征
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): # 按分类后类别数量排序,比如:最后分类为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)
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) # 选择最优特征
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
dataSet, labels = createDataSet1() # 创造示列数据
print(createTree(dataSet, labels)) # 输出决策树模型结果
运行结果: