您的位置:首页 > 编程语言

机器学习——分类算法2:决策树 思想和代码解释

2018-01-28 21:52 543 查看

思想:

将原始数据集根据决定性特征划分为几个数据子集,这些数据子集会分布在第一个决策点的所有分支上,如果某个分支下的数据属于同一类型,则表示到达终止模块,可以得到结论,无需进一步对数据集进行分割;如果子集内的数据不属于同一类型,则需重复划分数据子集,直到所有具有相同类型的数据均在一个数据子集内。但是应该怎样划分数据呢,显然是根据决定性特征,这里引进一个度量标准--信息增益(划分数据集之前之后信息发生的变化),我们可以计算每个特征值划分划分数据集获得的信息增益,获得信息增益最高的特征就是最好的选择。

数据:

no surfacing(不浮出水面是否可以生存)flippers(是否有脚蹼)file(是否为鱼类)
11yes
11yes
10no
01no
01no
说明:如果对于香农熵和信息增益不懂得话请点这里

代码

python3版本代码(对于小白,在每次测试时候,可以打断点到测试那里,更容易理解,对于print过多,自行删除):

from math import log
import operator
#创建数据集,自己可以定义
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

#显示数据集和标签
myDat, labels = creatDataSet()
#测试
print("dataSet:")
print(myDat)
print("labels:")
print(labels)
print("----------------"*10)
################################################################################################################
#计算给定数据集的香农熵
"""
1、计算各个分类出现的次数
2、计算各个分类出现的概率
3、根据香农公式:H(X)=−∑p(xi)*log2(p(xi)),其中p(xi)为各个分类的期望,计算香农熵
"""
def calcShannonEnt(dataSet):
"""
:param dataSet: 数据集
:return: 香农熵
"""
numEntries = len(dataSet)#数据集的总数
labelCounts = {}#字典,{类别:数量}---{'yes':1}

#1、为所有可能分类创建字典,计算各个分类出现的次数
for featVec in dataSet:
currentLabel = featVec[-1]#数组下表-1表示该数据的最后一个,也就是'yes'或者'no'
if currentLabel not in labelCounts.keys():#如果没有改分类,则创建该字典
labelCounts[currentLabel] = 0
labelCounts[currentLabel] += 1#累加分类

#显示该字典数据
print("此时该字典数据:", labelCounts)

#2、3、计算概率、计算香农熵,公式:H(X)=−∑p(xi)*log2(p(xi)),其中p(xi)为各个分类的期望
shannonEnt = 0.0#初始化香农熵
for key in labelCounts:
prob = float(labelCounts[key])/numEntries#计算该分类的期望值,即概率
shannonEnt -= prob * log(prob, 2)#以2为底求对数

# 显示计算后的香农熵
print("此时香农熵:", shannonEnt)

return shannonEnt

#测试
calcShannonEnt(myDat)
##################################################################################################################
#先讲一下extend和append的区别
print("--------------------"*10)
a = [1, 2, 3]
b = [4, 5, 6]
a.extend(b)
print("extend:先将自己分解成元素,然后加到其他元素后面")
print(a)
A = [1, 2, 3]
B = [4, 5, 6]
A.append(B)
print("append:不分解,直接加到其他元素后面,是什么样还是什么样")
print(A)
print("--------------------"*10)
###################################################################################################################
#按照给定特征划分数据集
def splitDataSet(dataSet, axis, value):
"""
:param dataSet: 待划分的数据集
:param axis: 划分数据集的特征列,就是第几列
:param value: 特征列的值,就是第几列的值
:return:返回的是与特征列值匹配后并且去除特征列的数组对象
"""
retDataSet = []#创建新的list对象,这个list里面各个元素也都是list
for featVec in dataSet:
if featVec[axis] == value:#如果该特征与值对应
reducedFeatVec = featVec[:axis]#该特征列前面列
reducedFeatVec.extend(featVec[axis+1:])#该特征列+1后面的列,extend是将自身变为元素再追加到数组元素后面
retDataSet.append(reducedFeatVec)#直接追加到list对象中

return retDataSet
#测试
print(myDat)
print(splitDataSet(myDat, 0, 1))
print(splitDataSet(myDat, 0, 0))
print("--------------------"*10)
###################################################################################################################
#选择最好的数据集划分方式
"""
1、记录原始熵
2、对每个特征进行循环计算各个特征的熵:
2.1获取每个特征所有值
2.2对每个特征值循环进行划分计算熵
3、计算信息增益,公式:gain(S ,A) = Entropy(S) - ∑|Sv|/|S|*Entropy(Sv),其中,S为整个集合,v为特征A的值,A表示特征,Entropy(S)为原始熵,Sv为特征为A的,值为v的子集
"""
def chooseBestFeature(dataSet):
"""
:param dataSet: 待分类的数据集
:return: 返回最好的特征列
"""
numFeatures = len(dataSet[0])-1#特征总数,最后那个分类不算
print("原始香农熵:")
# 1、计算原始香农熵
baseEntropy = calcShannonEnt(dataSet)
print("给定特征之后,进入for循环:\n", "-------"*14)
#2、对每个特征进行循环计算各个特征的熵
bestInfoGain = 0.0     #最好的信息增益初始化
beatFeature = -1     #最好的特征初始化
for i in range(numFeatures):
print("第一层for循环:")
# 2.1、获取第i个特征,所有的可能取值
featList = [example[i] for example in dataSet]
print("原始数据集:", dataSet)
print("第%d个特征列的所有值:" %(i), featList)
uniqueVals = set(featList)  #set集合,重复的元素会合并,也就是uniqueVals是{0,1}
newEntropy = 0.0    #新的香农熵

# 2.2、计算每一种划分方式的香农熵
for value in uniqueVals:
print("第二层for循环:", "-------"*5)
subDataSet = splitDataSet(dataSet, i, value)    #按照给定特征划分数据集
print("此时给定特征划分后的数据集为:", subDataSet)
prob = len(subDataSet)/float(len(dataSet))      #子集在原始集的期望
newEntropy += prob * calcShannonEnt(subDataSet)     #子集的香农熵*期望,其中此处是∑|Sv|/|S|*Entropy(Sv)

#3、计算最好的信息增益
print("计算最好的信息增益:", "-----"*8)
infoGain = baseEntropy - newEntropy
print("原始香农熵:", baseEntropy)
print("新分类后的香农熵:", newEntropy)
print("相减之后:", infoGain)
if(infoGain > bestInfoGain):
bestInfoGain =infoGain
beatFeature = i
print("-------"*10)
print("最好的特征列:", beatFeature)
print("-------------------------"*10)
return beatFeature

#测试
chooseBestFeature(myDat)
#下面就是决策树,进入正题,对于前面的要理解清楚,后面的都是调用前面的方法,将前面可以单独运行
##########################################################################################################
#特征用完仍不能将数据集划分为只包含唯一类别的分组,返回出现次数最多的
def majorityCnt(classList):
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]#获取数据集的每一个类别
print("classList", classList)
if classList.count(classList[0]) == len(classList): #终止条件之一:所有数据的类别的都相同
return classList[0]
if len(dataSet[0]) == 1:#终止条件之二:特征用完仍不能将数据集划分为只包含唯一类别的分组,返回出现次数最多的。
return majorityCnt(classList)
bestFeat = chooseBestFeature(dataSet)   #选择最佳划分特征
bestFeatLabel = labels[bestFeat]    #对应的标签
myTree = {bestFeatLabel:{}}     #用字典来存储树
print("myTree", myTree)
del(labels[bestFeat])#删除最好的标签留下其他标签
featValues = [example[bestFeat] for example in dataSet]     #跟上个函数一样,不赘述
print("featValues", featValues)
uniqueVals = set(featValues)#uniqueVals为0,1
for value in uniqueVals:
subLabels = labels[:]
print("subLabels :", subLabels)
myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value), subLabels)    #按照给定特征划分数据集,在数据集上递归调用
return myTree

#测试
print(createTree(myDat,labels))

结果:{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}

图片形式:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐