1 说明
该书主要以原理简介+项目实战为主,本人学习的主要目的是为了结合李航老师的《统计学习方法》以及周志华老师的西瓜书的理论进行学习,从而走上机器学习的“不归路”。因此,该笔记主要详细进行代码解析,从而透析在进行一项机器学习任务时候的思路,同时也积累自己的coding能力。
正文由如下几部分组成:
1、实例代码(详细注释)
2、知识要点(函数说明)
3、调试及结果展示
2 正文
(1)计算给定数据集的信息熵
1、给定数据集为:
def createDataSet():
dataSet = [[1, 1, 'yes'],
[1, 1, 'yes'],
[1, 0, 'no'],
[0, 1, 'no'],
[0, 1, 'no']]
labels = ['no surfacing','flippers']
#change to discrete values
return dataSet, labels
该函数将书中表3-1海洋生物数据存在了一个python列表中,方便后续的处理。
接下来我们定义一个calcShannonEnt函数来计算香农信息熵:
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
2、在python命令提示符下输入下列命令:
******
PyDev console: starting.
Python 3.6.7 |Anaconda, Inc.| (default, Oct 28 2018, 19:44:12) [MSC v.1915 64 bit (AMD64)] on win32
>>>import trees
>>>myDat, labels = trees.createDataSet()
>>>myDat
[[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]
>>>trees.calcShannonEnt(myDat)
0.9709505944546686
>>>myDat[0][-1] = 'maybe'
>>>myDat
[[1, 1, 'maybe'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]
>>>trees.calcShannonEnt(myDat)
1.3709505944546687
我们可以看到,在数据集中添加更多的分类,信息熵明显变大了。
知识要点:
①信息熵:是度量样本集合纯度最常用的一种指标,信息的期望值。
《机器学习》(周志华 著):假定当前样本集合D中第k类样本所占的比例为pk(k=1,2,…,∣γ∣),p_{k}(k=1,2,…,|γ|),pk(k=1,2,…,∣γ∣),则D的信息熵定义为Ent(D)=−∑k=1∣γ∣pklog2pkEnt(D)=-\sum _{k=1}^{|γ|}p_{k}log_{2}p_{k}Ent(D)=−k=1∑∣γ∣pklog2pk
信息熵Ent(D)的值越小,则信息的纯度就越高。
(2)划分数据集
1、我们将对每个特征划分数据集的结果计算一次信息熵,然后判断按照哪个特征划分数据集是最好的划分方式,下面我们先定义一个函数,用来实现按照给定的特征划分数据集这一功能:
def splitDataSet(dataSet, axis, value):
retDataSet = []#创建新列表以存放满足要求的样本
for featVec in dataSet:
if featVec[axis] == value:
#下面这两句用来将axis特征去掉,并将符合条件的添加到返回的数据集中
reducedFeatVec = featVec[:axis]
reducedFeatVec.extend(featVec[axis+1:])
retDataSet.append(reducedFeatVec)
return retDataSet
这段代码其实很简单,但是有个地方需要解释一下,就是:
reducedFeatVec = featVec[:axis]
reducedFeatVec.extend(featVec[axis+1:])
虽然我知道这里肯定在剔除axis特征,并输出剩下元素组成的特征向量,但是一开始还是没绕过弯来,一直还以为是自己“切片”没学好了…下面我通过在python交互环境下进行测试操作,来更好地理解这两句话真正干了什么。
******
PyDev console: starting.
Python 3.6.7 |Anaconda, Inc.| (default, Oct 28 2018, 19:44:12) [MSC v.1915 64 bit (AMD64)] on win32
>>>a=[1, 1, 0]
>>>b=a[:0]
>>>b
[]
>>>c=a[:1]
>>>c
[1]
>>>d=a[:2]
>>>d
[1, 1]
>>>d.extend(a[3:])
>>>d
[1, 1]
>>>d.append(a[3:])
>>>d
[1, 1, []]
从上面这一波操作可以看出,通过第一步操作,可以将axis以前的元素存到reduceFeatVec列表中,而通过第二步操作,可以将axis以后的元素也同样存进去,这样就可以剔除axis了。最后extend()和append()的区别就不做赘述了。好吧,归根到底,还是“切片”没学好,哈哈~
2、那么下面我们就跟着书中的例程继续走,在python命令提示符内输入下面命令,执行后得到划分后结果:
******
PyDev console: starting.
Python 3.6.7 |Anaconda, Inc.| (default, Oct 28 2018, 19:44:12) [MSC v.1915 64 bit (AMD64)] on win32
>>>import trees
>>>myDat, labels = trees.createDataSet()
>>>myDat
[[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]
>>>trees.splitDataSet(myDat, 0, 1)
[[1, 'yes'], [1, 'yes'], [0, 'no']]
>>>trees.splitDataSet(myDat, 0, 0)
[[1, 'no'], [1, 'no']]
我们可以很直观看出, 通过最后两条命令,数据集通过“不浮出水面是否可以生存”这一特征被划分。
3、以上无论是用来计算香农信息熵的calcShannonEnt函数,还是用来划分数据集的splitDataSet函数,其实都是我们提前做好的两个“工具包”,因为我们从决策树的原理上理解也很容易看出,这两个函数的计算肯定不止一次,需要根据数据集的需要进行循环计算,并在前后评估信息增益,从而才能找到我们想要的结果——最优的数据集划分方法。
那么下面就进入了这一步,我们在trees.py添加chooseBestFeactureToSplit函数:
def chooseBestFeatureToSplit(dataSet):
numFeatures = len(dataSet[