决策树算法是机器学习中监督学习算法中的一种,其主要意义是根据已有数据对算法进行不断训练,从而得到决策树,在新数据输入时对结果进行预测判定的一种算法。监督学习就是指需要提供大量的已有数据,包括输入和输出,其目的就是得出一种规则来对任意的输入对应一个输出。
如上图,右侧就是决策树算法得到的一个结果,对于输入的数据会进行不断的判断,最简单的就是二叉树的实现,对于每一个问题只有两个分支进行判断。得到结果后再进入下一层条件分支进行判断,依次递归,最终得到预测的结果。其中每个分支处(分支节点)的判断条件称为属性或者标签,最后的节点(叶节点)为判断结果。在分类过程中当出现所有数据结果处于同一类时则停止继续分支,得到叶节点。
在寻找分类特征时主要依据的就是概率统计方面的一些原理,主要的算法有ID3、C4.5、CRAT等。拿ID3来说,主要依据的是信息熵的理论。熵表示的是一种确定程度,熵值越小表示确定度越大,反之表示越混乱。同理,在通信中使用了信息熵的概念,熵值越小表示信息量越小,即确定性越大;反之表示不确定性越大。因此,分类特质的选择就是在所有特质中选择信息熵值最小的进行分类,之后删除该特征后剩下的特征中选择信息熵最小的,以此递归,直到分类完成。ID3使用信息增益值进行特征选择,C4.5使用信息增益率,这两种算法本质原理都是选取特征信息熵最小,其中C4.5的算法是在信息增益的基础上除以一个系数,算是算法的一个优化。另外CRAT算法特征选取依据是计算基尼系数,也是一种统计的方法。但最终都是在所有特征中计算某个统计值进行比较,选择一个最大或者最小的值进行分类,之后在剩余特征中递归进行。
以下是python实现的一个简单示例,帮助理解:
from math import log
import operator
'''计算给定数据集的熵'''
def calcShannonEnt(dataSet):
numEntries = len(dataSet) #样本总数
labelCounts = {} #创建一个字典,存放分类信息,各类含样本数
for featVec in dataSet:
currentLabel = featVec[-1]
if currentLabel not in labelCounts.keys():
labelCounts[currentLabel] = 0
labelCounts[currentLabel] += 1 #labelCount中存放每种结果对应的数量值
shannonEnt = 0.0
'''信息量的计算公式为-log2(p(x)),其中p(x)为某信息的概率,所有特征信息量相加得到的便是信息熵'''
for key in labelCounts:
prob = float(labelCounts[key])/numEntries
shannonEnt -= prob * log(prob,2)
return shannonEnt
'''创建数据集,dataSet中子列表最后一个元素表示的是结果'''
def createDataSet():
dataSet = [
[1,1,'yes'],
[1,1,'yes'],
[1,0,'no'],
[0,1,'no'],
[0,1,'no']
]
labels = ['without water','flippers'] #标签对应dataSet子列表的前两列
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.0
bestFeature = -1
for i in range(numFeatures):
featList = [example[i] for example in dataSet]
uniqueVals = set(featList)
newEntropy = 0.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 createTree(dataSet,labels):
classList = [example[-1] for example in dataSet]
if classList.count(classList[0]) == len(classList):#类别完全相同则停止划分
return classList[0]
bestFeat = chooseBestFeatureToSplit(dataSet) #得到的是下标
bestFeatLabel = labels[bestFeat] #取值:no water 或者 flippers
myTree = {bestFeatLabel:{}}
del(labels[bestFeat])
featValues = [example[bestFeat] for example in dataSet]
uniqueVals = set(featValues)
'''对特征值进行循环,返回的结果为两种,如果划分之后正好是同一类则停止划分,返回classList[0],得到叶节点;如果不是同一类,则返回myTree作为该特征的下一层分支节点,一次递归'''
for value in uniqueVals:
subLables = labels[:]
myTree[bestFeatLabel][value] =
createTree(splitDataSet(dataSet,bestFeat,value),subLables)
return myTree
if __name__ == '__main__':
dataSet, label = createDataSet()
print(createTree(dataSet,label))
------------output------------
{'without water': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}
可以实际运行,得到的决策树相当于下图: