《机器学习实战》花了6页半的篇幅,来表述如何用matplotlib画一棵决策树。虽然这件事也有意义,但当你已经较为充分的理解了代码后,其实意义不算大。所以,这一段就此跳过,只留一张范例图供大家参考:
我们来看一下,如何使用决策树做分类。
def classify(inputTree, featLabels, testVec):
firstStr = list(inputTree)[0]
secondDict = inputTree[firstStr]
featIndex = featLabels.index(firstStr)
key = testVec[featIndex]
valueOfFeat = secondDict[key]
if isinstance(valueOfFeat, dict):
classLabel = classify(valueOfFeat, featLabels, testVec)
else: classLabel = valueOfFeat
return classLabel
代码一共是10行。
由于我没有用到画图的函数,而运行代码的时候书本里调用的是画图的函数生成的树,我就用之前的createTree()来生成树。但是这里引发了一个bug,忽然提示“no surfacing”不存在,查了一会儿后发现,其实是在运行createTree()的时候,改写了labels本身。因此就要在生成树后,再重置一下labels即可。
代码解释如下:
def classify(inputTree, featLabels, testVec):
#定义输入,为输入的树(这里就是之前create的树),标签列表以及测试数据
firstStr = list(inputTree)[0]
#取第一个特征值
secondDict = inputTree[firstStr]
#将这个节点的分支生成一棵树
featIndex = featLabels.index(firstStr)
#获取这个特征描述对应的特征数组下标
key = testVec[featIndex]
#通过下标,找到特征对应的值
valueOfFeat = secondDict[key]
#根据值,查找子树,确认是在哪一个分支
if isinstance(valueOfFeat, dict):
classLabel = classify(valueOfFeat, featLabels, testVec)
#isinstance是判断变量类型,即确认valueOfFeat是否是一个字典,如果是,继续classify递归查找
else: classLabel = valueOfFeat
#如果不是,即已经摸到了没有继续分叉的子节点,则终止并返回valueOfFeat,此时它是一个这个测试数据对应的标签
return classLabel
#返回这个标签
总体还比较好理解,难点在于前几行的代码,要对比生成的树的结构,一个根节点或者还有分叉的子节点,我们用secondDict取到它的分叉数据,然后再通过查找到这是哪一个特征项并对应的是什么数值结果,去找到对应的分叉项,并进一步往下走。我也是依靠反复的打印信息,来确定每一行代码的走向,可以说逻辑不难,但代码实现上会有点绕(连书中也说到,涉及到字典的操作,会比较复杂)。
由于至此为止用到的决策树数据集很简单,运算也很快,我们可以每次都跑一遍createTree()生成树,但是如果树很复杂,这种方式肯定是不合适的。我们在深度学习中也会遇到同样的问题,即跑出来的模型,最好是把它存起来,不然每次都跑一遍,累死了(累死了电脑,除非你是顶配,但即便电脑是顶配,碰上复杂模型也是死路一条)。
保存和读取的代码比较简单,用到了pickle,我们看一下:
def storeTree(inputTree, filename):
import pickle
fw = open(filename, 'wb')
pickle.dump(inputTree, fw)
fw.close()
def grabTree(filename):
import pickle
fr = open(filename, 'rb')
return pickle.load(fr)
这里就不解释了吧,确实挺简单的。生成的pickle文件,虽然你可以用后缀名.txt(书中也是这么干的)来保存,但是事实上它并不是一个utf-8的文本格式,而是一个二进制序列化格式。强行打开的话,你就会看到一堆乱码:
乱码当中还是能看到几个英文词的,哪个就是特征的描述,看来这个还没有被转换,其他的就已经看不出是什么了。