决策树模型
重要任务:是为了理解数据中所蕴含的知识信息,因此决策树可以使用不熟悉的数据集合,并从中提取出一系列规则,这些机器根据数据集创建规则的过程就是机器学习的过程。
优点:计算复杂度不高,输出结果易于理解,对中间值的缺失不敏感,可以处理不相关特征数据。
缺点:可能会产生过度匹配问题。
范例:专家系统。
如何构造决策树:
- 编写构造树的python代码
- 度量算法成功率
- 递归建立分类器
- Matplotlib绘制决策树图
利用信息论划分数据集,伪代码:
IF (整个数据集属于同一个类型) return 类标签
Else
寻找划分数据集的最好特征
划分数据集
创建分支节点
for 每个划分的子集
递归继续划分
return 分支节点
划分数据集的大原则是:将无序的数据变得更加有序。
信息增益
定义:在划分数据集之前之后的信息发生的变化称为信息增益。
可以用于评价一次信息划分的优劣。
集合信息的度量方式称为:香农熵。
熵定义为信息的期望值。
如果待分类的事务可能划分在多个分类之中,则符号
x
i
x_i
xi?的信息定义为:
l
(
x
i
)
=
?
l
o
g
2
p
(
x
i
)
l(x_i)=-log_2p(x_i)
l(xi?)=?log2?p(xi?)
p
(
x
i
)
p(x_i)
p(xi?)是选择该分类的概率。
根据期望的公式,得
H
=
?
∑
i
=
1
n
p
(
x
i
)
l
o
g
2
p
(
x
i
)
H=-\sum^n_{i=1}p(x_i)log_2p(x_i)
H=?i=1∑n?p(xi?)log2?p(xi?) 计算给定那个数据集的香农熵:
from math import log
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
ShannonEnt = 0.0
for key in labelCounts:
prob = float(labelCounts[key])/numEntries
ShannonEnt -= prob *log(prob,2)
return ShannonEnt
基尼不纯度:另一种度量集合无序程度的方法。
决策树的构建过程实际上就是降低香农熵(无序度量值)的过程。
对于一个特征集合我们可以选择的划分方式就是各个特征,选择哪个特征能够更加快的降低数据集的无序度?
我们只需要尝试按照每一种方式划分,看看哪种划分更快的降低无序度,那么便选择它,这也就是上面伪代码讲的如何划分。
如何理解以下这段代码(由于书中并没有对这段代码解释,以下属于笔者的猜测):
for value in uniqueVals:
subDataSet = splitDataSet(dataSet, i, value)
prob = len(subDataSet)/float(len(dataSet))
newEntropy += prob * calcShannonEnt(subDataSet)
我们可以知道信息熵的定义是期望,我们想要得到的是按照某一特征划分之后整体的期望。
那么可以得到一个新的公式,根据期望的计算
E
=
∑
i
=
1
n
p
(
x
i
)
?
x
i
E=\sum_{i=1}^{n}p(x_i)*x_i
E=∑i=1n?p(xi?)?xi??。?
其中
x
i
x_i
xi?表示的便是划分后的第i个集合的香农熵的值,
p
(
x
i
)
p(x_i)
p(xi?)??表示选择该数据集的概率。
最终建立的树是用字典表示的,例如:
{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}
若字典中的关键字的值对印的是类标签,则表示这是一个叶子结点,若为仍是字典,则表示这是一个判断结点。
利用matplotlib注释绘制属性图。
直接上结果吧
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ArgJhgty-1640959413711)(C:\Users\86187\Desktop\桌面\机器学习\图片\决策树图形化.png)]
trees.py:
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
ShannonEnt = 0.0
for key in labelCounts:
prob = float(labelCounts[key])/numEntries
ShannonEnt -= prob *log(prob,2)
return ShannonEnt
def createDataSet():
dataSet = [[1,1,'maybe'],
[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+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 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]
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
main.py
import treePlotter
import trees
myDat,labels=trees.createDataSet()
print(myDat)
print(trees.calcShannonEnt(myDat))
print(trees.splitDataSet(myDat,0,1))
print(trees.splitDataSet(myDat,1,1))
print(trees.chooseBestFeatureToSplit(myDat))
mytree=trees.createTree(myDat,labels)
print(mytree)
结语:
-
决策树的工作原理就是将原本无序的数据集变得有序,并且这种有序是用树的方式呈现的,该模型的核心便是如何指导模型,使得数据集向有序变化。 -
决策树使用的无序度,尽量让每一次的划分都能最大化的降低整体的无序度,通过这种方式一步一步的走向有序。 -
同时,信息熵的概念同样让人着迷,尽管不知道为什么是
?
l
o
g
2
(
p
(
x
)
)
-log_2(p(x))
?log2?(p(x)),但他确实好用,类似的还有交叉熵,等等,这些度量函数值得了解和学习。 -
今天也是2021年的最后一天,在过去的一年,acm参加了七个赛站,最后以两铜结束,参加了数学建模没能完成论文,混了省一。高中初中小学,都有一点点的竞赛经验,但是都没有获奖,也不是十分重视,到了大学acm也算是圆了竞赛梦,尽管现实残酷。过去这一年的种种遗憾都将随时间流走,希望在新的一年里向着新的方向前行。
|