4.1基本流程
决策树学习基本算法:大概思想就是通过信息增益来递归构建决策树,返回条件:
(1)当前节点属于同一类别
(2)当前属性集为空,或者当前所有属性相同(返回值为样本数量最多的类)
(3)当前节点包含的样本集为空集
算法流程如下图:
4.2 划分选择
我们希望每个节点包含的样本“纯度”高,即差异性不大,此时可以用信息熵作为判定标准。
信息熵是用来度量样本集合纯度的指标,简单来说,信息熵越大,样本越“混乱”,信息熵越小,样本纯度越高,信息熵公式下:
4.2.1 信息增益
ID3决策树是以信息增益为准则来划分属性的,信息增益和信息熵的关系
算法C4.5和CART决策树与ID3的不同点就在于划分属性的判定标准。
C4.5判定标准是增益率:
CART决策树的判定标准是基尼指数:
4.3 剪枝处理
剪枝是决策树算法对付“过拟合”的主要手段,主要有两种,“预剪枝”和“后剪枝”
4.3.1预剪枝
预剪枝基于贪心策略,减少了决策树的分支,预剪枝虽然会降低过拟合的风险,并且还显著减少了决策树的训练时间开销和测试时间开销,但是降低了决策树的泛化性能。
4.3.2后剪枝
后剪枝是在决策树生成之后,判定每个节点被替换成叶节点之后的精度,来决定是否进行剪枝,后剪枝决策树比预剪枝决策树保留了更多的分支,欠拟合风险小,但是训练时间开销比未剪枝决策树和预剪枝决策树都要大的多。
4.4连续值和缺省值
4.4.1连续值
对连续值来说最简单的处理方式就是二分法,划分候选的点的集合如下:
同时,我们可以对前面的信息增益的公式稍稍改变:
当信息增益最大时,得到的属性划分点就是我们要的点.
4.4.2缺失值
样本缺失之后,有两个重要的问题(1)如何在属性值缺失的情况下进行划分样本属性的选择(2)给定划分属性,若样本在该属性上的值缺失了,如何对样本进行划分。
4.5多变量决策树
决策树的特点之一就是分类边界的每一段都是与坐标轴平行的,但是在这种情况下当分类边界比较复杂是,要使用很多段划分才能获得较好的近似效果例如:
在多变量决策树中使用了斜的划分边界,不是为每个结点寻找一个最优划分属性,而是试图建立一个合适的线性分类器,例如:
下面是创建决策树的代码,代码来源是《机器学习实战》
from typing import List, Union
import operator
def calcShannonEnt(dataSet):
numEntries=len(dataSet)
labelCounts={}
for featVec in dataSet:
currentLabel=featVec[-1]
if currentLabel not in labelCounts:
labelCounts[currentLabel]=0
labelCounts[currentLabel]+=1
shannonEnt=0.0
for key in labelCounts:
prob=float(labelCounts[key])/numEntries;
shannonEnt-=prob*log(prob,2);
return shannonEnt
def creatDataSet():
dataSet=[[1,1,'yes'],[1,1,'yes'],[1,0,'no'],[0,1,'no'],[0,1,'no']]
labels=['no surfacing','flippers']
return dataSet,labels
def splitDataSet(dataSet,axis,value):
retDataSet=[]
for featVec in dataSet:
if featVec[axis]==value:
reducedFeatVec=featVec[:axis]
reducedFeatVec.extend(featVec[axis,:])
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]#取一列dataSet
uniqueVals=set(featList)
newEntropy=0.0
#当以第i个元素划分时,信息增益的值
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):
calssCount={}
for vote in classList:
if vote not in classCount.keys():classCount[vote]=0
classCount[vote]+=1
sortedClassCount=sorted(classCount.iteritems(),key=operator.itemgetter(1),reverse=True)#iteritems()将字典以列表的形式返回 operator.itemgetter
return sortedClassCount[0][0]
def createTree(dataSet,labels):
classList=[example[-1] for example in dateSet]
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]
uniqueaVals=set(featvalues)
for value in uniqueaVals:
subLabels=labels[:]
myTree[bestFeatLabel][value]=createTree(splitDataSet(dataSet,bestFeat,value),subLabels)
return myTree
def classify(inputTree,featLabels,testVec):
firstStr=inputTree.keys()[0]
secondDic=inputTree[firstStr]
featIndex=featLabels.index(firstStr)
for key in secondDic.keys():
if testVec[featIndex]==key:
if type(secondDic[key]).__name__=='dict':
classLabel=classify(secondDic[key],featLabels,testVec)
else: classLabel=secondDict[key]
return classLabel