决策树算法总结

本文深入讲解了ID3算法的工作原理及实现过程,包括如何通过计算信息增益来选择最佳属性进行决策树的构建。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一种树结构 if else来分类。使用决策树进行决策的过程就是从根节点开始,测试待分类项中相应的特征属性,并按照其值选择输出分支,直到到达叶子节点,将叶子节点存放的类别作为决策结果。

ID3算法:

根据信息论中期望信息越小,信息增益越大,从而纯度越高。所以ID3算法的核心思想就是以信息增益度量属性选择,选择分裂后信息增益最大的属性进行分裂。下面先定义几个要用到的概念。

     设D为用类别对训练元组进行的划分,则D的(entropy)表示为:

熵越大,随机变量的不确定性就越大

其中pi表示第i个类别在整个训练元组中出现的概率,可以用属于此类别元素的数量除以训练元组元素总数量作为估计。熵的实际意义表示是D中元组的类标号所需要的平均信息量。

     现在我们假设将训练元组D按属性A进行划分,则A对D划分的期望信息为:


而信息增益即为两者的差值:


选择 gain(A)较大者进行分裂

ID3算法思想描述:

 

1)                对当前例子集合,计算属性的信息增益;

 

2)                选择信息增益大的属性A

 

3)                把在A处取值相同的例子归于同一子集,A取几个值就得几个子集

 

4)                依次对每种取值情况下的子集,返回1)递归调用建树算法,

 

5)                若子集中只含有单个属性,则分支为叶子节点,判断其属性值并标上相应的符号,然后返回调用处。

例如样本


首先根据样本计算出信息增益大的属性:不浮出水面是否可以生存,然后再有该属性进行分裂直至没有分类。

算法开始:

1:创建数据集:

def createDataSet():

   dataSet = [ [1, 1, 'yes'],

           [1, 1, 'yes'],

           [1, 0, 'no'],

           [0, 1, 'no'],

           [0, 1, 'no']]

   labels = ['no surfacing','flippers']

return dataSet,labels


2:计算数据集中的香农熵  

def calcShannonEnt(dataSet): 

2.   #calculate the shannon value 

3.   numEntries = len(dataSet) 

4.   labelCounts = {} 

5.   for featVec in dataSet:     #create the dictionary for all of the data 

6.        currentLabel = featVec[-1] 

7.        if currentLabel not in labelCounts.keys(): 

8.            labelCounts[currentLabel] = 0 

#计算出分类属性各类别个数,例如”yes”=3,”no"=2

9.       labelCounts[currentLabel] +=1 

10.   shannonEnt = 0.0 

11.   for key in labelCounts: 

12.        prob =float(labelCounts[key])/numEntries 

13.        shannonEnt -= prob*log(prob,2) #get thelog value 

14.   return shannonEnt                                         

labelCounts 是存储所有label个数的字典,key为label,key_value为label个数。for循环计算label个数,并打印出字典值。函数返回熵值。

myDat, labels = createDataSet()

shannonEnt = calcShannonEnt(myDat)

计算结果为:

numEntries = 5

yes : 2

no : 3

shannonEnt = 0.970950594455

熵值越高,数据集越混乱(label越多,越混乱)。试着改变label值可以观察熵值的变化。

myDat[0][-1] = ‘maybe

shannonEnt = calcShannonEnt(myDat)

输出结果:

numEntries = 5

maybe : 1

yes : 1

no : 3

shannonEnt = 1.37095059445

得到熵值后即可计算各属性信息增益值,选取最大信息增益值作为当前分类节点,知道分类结束。

3.划分数据集,按照给定的特征划分数据集

列表方法的extend和append的区别

其中featVec[:axis] 返回的是一个列表,其元素是featVec这个列表的索引从0到axis - 1的元素,

也就是不包括axis这个索引上的值,若axis为0,则返回空列表

其中featVec[axis + 1: ]返回的是一个列表,其元素是featVec这个列表的索引从axis + 1开始的所有元素

[python] view plain copy

 

 def splitDataSet(dataSet, axis, value): #找出第axis元素为value的行

   retDataSet = []

   for featVec in dataSet: #dataset中各元素是列表,遍历每个列表

       if featVec[axis] == value: #找出第axis元素为value的行

            reducedFeatVec =featVec[:axis]   #抽取符合特征的数据去掉axis对应的属性

           reducedFeatVec.extend(featVec[axis+1:]) #把抽取出该特征以后的所有特征组成一个列表#

           retDataSet.append(reducedFeatVec)  #创建抽取该特征以后的dataset

   print 'retDataSet = ',retDataSet

   return retDataSet

splitDataSet函数参数为:dataSet为输入数据集,包含你label值;axis为每行的第axis元素,对应属性特征;value为对应元素的值,即特征的值。

函数功能:找出所有行中第axis个元素值为value的行,去掉该元素,返回对应行矩阵。

当需要按照某个特征值划分数据时,需要将所有符合要求的元素抽取出来,便于计算信息增益。

例如:

splitDataSet(myDat,0,1)

执行结果:

dataSet = [[1, 1, ‘yes’], [1, 1, ‘yes’], [1, 0, ‘no’], [0,1, ‘no’], [0, 1, ‘no’]]

retDataSet = [[1, ‘yes’], [1, ‘yes’], [0, ‘no’]]

splitDataSet(myDat,1,1)

执行结果:

dataSet = [[1, 1, ‘yes’], [1, 1, ‘yes’], [1, 0, ‘no’], [0,1, ‘no’], [0, 1, ‘no’]]

retDataSet = [[1, ‘yes’], [1, ‘yes’], [0, ‘no’], [0, ‘no’]]

即该函数实现不同属性下的分类,本例子中有不浮出水面和是否有脚蹼两种属性。

 

4:下面我们的函数为选择最好的数据集划分方式的特征,也就是信息增益最大的特征。函数代码:

#选取特征,划分数据集,计算得出最好的划分数据集的特征 

defchooseBestFeatureToSplit(dataSet): 

   numFeatures = len(dataSet[0]) - 1 #剩下的是特征的个数 

   baseEntropy = calcShannonEnt(dataSet)#计算数据集的熵,放到baseEntropy中 

   bestInfoGain = 0.0;bestFeature = -1 

   for i in range(numFeatures): 

       featList = [example[i] for example in dataSet]  #遍历所有属性

       uniqueVals = set(featList) #python的set是一个无序不重复元素集

       newEntropy = 0.0 

       for value in uniqueVals:#下面是计算每种划分方式的信息熵,特征i个,每个特征value个值 

            subDataSet = splitDataSet(dataSet,i ,value) 

            prob =len(subDataSet)/float(len(dataSet)) 

            newEntropy = prob *calcShannonEnt(subDataSet) 

       infoGain = baseEntropy - newEntropy #计算i个特征的信息熵 

       #print(infoGain) 

       if(infoGain > bestInfoGain): 

            bestInfoGain = infoGain 

            bestFeature = i 

returnbestFeature 

先获取属性个数,dataset最后一列为label,所以需要-1。

for循环嵌套即用来计算信息增益。

外层for循环用于遍历所有特征。featList = [example[i] forexample in dataSet] 语句用于查找该属性下所有属性值,并使用set函数对属性值列表进行唯一化,防止重复计算。

内侧for循环用于遍历当前属性下所有属性值。计算每一个属性值对应的熵值并求和。结果与原始熵值的差即为信息增益。

信息增益越大,说明该特征越利于分类,即当前分类节点应该选择该属性。

函数返回用来分类的属性标号。

本函数实现不同属性下的增益值,然后可以选择最大增益的属性进行划分。

结果是第一个属性增益大。

 

到目前为止,已经做到了寻找划分数据集的最佳属性,接下来就是递归调用,不断划分下去。划分到什么时候结束呢?这里有两个依据,第一,划分后的某个数据集中所有数据都同属于一类,这个时候就没必要再划分了,再者,由于这里所讲的决策树是消耗属性的,所以当所有属性都用完了,划分也就停止了。可能有人会问,如果所有属性都用完了,某堆数据集中的数据仍不统一怎么办?解决方法就是少数服从多数,比如10个数据,8个属于A类,2个属于B类,那么就认为他们都属于A类。

下面递归创建树

 

 

 

5:用于找出出现次数最多的分类名称的函数,输入参数classList即为dataset的所有label号。sorted即对字典按降序排列,返回label次数最多的label。

 

[python]view plain copy

 

1.defmajorityCnt(classList):  #少数服从多数

2.    classCount = {}  #创建字典,返回出现频率最高label

3.    for vote in classList: 

4.        if vote not in classCount.keys():classCount[vote] = 0 

5.        classCount[vote] += 1  #该类标签下数据个数+1

6.    sortedClassCount =sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)  #对所有类标签下的数据个数排序

7.    return sortedClassCount[0][0]  #[0][0]就代表数据量最大的那个类标签

 

6:用于创建树的函数代码

 

def createTree(dataSet,labels):

   classList = [example[-1] for example in dataSet]

   print 'classList:',classList  #获取所有label列表

 

   #停止迭代1:classList中所有label相同,直接返回该label

   if classList.count(classList[0]) == len(classList):

       return classList[0]

   #停止迭代2:用完了所有特征仍然不能将数据集划分成仅包含唯一类别的分组

   if len(dataSet[0]) == 1:

       return majorityCnt(classList) #否则执行少数服从多数程序

   bestFeat = chooseBestFeatureToSplit(dataSet)

   bestFeatLabel = labels[bestFeat]

   #print 'bestFeat:',bestFeat

   #print 'bestFeatLabel:',bestFeatLabel

 

   myTree = {bestFeatLabel:{}}#多级字典的形式展现树,类似多层json结构

   del(labels[bestFeat])

   featValues = [example[bestFeat] for example in dataSet]

   uniqueVals = set(featValues)

   for value in uniqueVals:

       subLabels = labels[:]       #copyall of labels, so trees don't mess up existing labels

       myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet,bestFeat, value),subLabels)

   print 'myTree = ',myTree

returnmyTree

 

 

 

7:然后是在python 名利提示符号输入如下命令

 

[python]view plain copy

 

 1.myDat, labels = trees.createDataSet() 

2.myTree =trees.createTree(myDat,labels) 

3.printmyTree 

 

 结果是:

 

{'nosurfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}


8:实用决策树进行分类的函数 

[python]view plain copy

 

 1.def classify(inputTree, featLabels,testVec): 

2.    firstStr = inputTree.keys()[0] 

3.    secondDict = inputTree[firstStr] 

4.    featIndex = featLabels.index(firstStr) 

5.    for key in secondDict.keys(): 

6.        if testVec[featIndex] == key: 

7.            if type(secondDict[key]).__name__== 'dict': 

8.                classLabel =classify(secondDict[key], featLabels, testVec) 

9.            else: classLabel =secondDict[key] 

10.    return classLabel 

 

 9:在Python命令提示符,输入:

 1.trees.classify(myTree,labels,[1,0]) 

 

 得到结果:

 

'no'

Congratulation.Oh yeah. You did it.!!!






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值