赞
踩
上一篇文章中主要介绍了以下几方面:
本文以一个新的数据集(隐形眼镜数据集)为基础实现构建决策树、决策树的保存与加载、利用决策树分类、决策树的可视化,前文的知识不在过多概述,着重介绍这四个方面。
先大致了解一下数据集:
这份数据源至UCI数据库,其共有4个特征分别为age(年龄)、prescript(症状)、astigmatic(闪光)、tearRate(泪液产生率)以及一个分类标签class,该分类包含硬材质、软材质和不应配带三种。
为了方便处理,对样本做以下处理:
在构造决策树之前,先回顾一下前几个子模块的工作原理:先获取原始数据集,然后基于最优特征划分数据集,当数据集特征大于两个时,第一次划分之后,数据将被向下传递至树的下一个节点,在这个节点上,在此划分数据,此过程是利用递归原理处理数据集。
什么时候划分结束呢?当程序遍历完所有划分数据集的属性,或者每个分支下所有实例分类一致时代表划分数据集结束。
而构造决策树的过程就是将每一次划分出的数据填入一个字典中,当数据集划分结束时,向字典中填充数据也结束,此过程也是一个递归过程,至此决策树的构造完成。
代码如下:
def CreateTree(DataSet): #获取所有特征标签 index_list = list(DataSet.columns) #获取最后一列(分类标签)的类别 label_series = DataSet.iloc[:,-1].value_counts() #判断类别标签最多一个是否等于数据样本数、或者数据集是否只有一列 if label_series[0]==DataSet.shape[0] or DataSet.shape[1] == 1: return label_series.index[0] #返回类标签 # 获取最优特征列索引 col = ChooseBF(DataSet) # 获取最优特征 BestFeature = index_list[col] #将最优特征依次填入字典中 TheTree = {BestFeature:{}} # 从标签列表中删去该特征标签 del index_list[col] #提取最佳切分列的所有属性值 value_list = set(DataSet.iloc[:,col]) #利用递归方法建树,每次对象为当前最优特征 for value in value_list: TheTree[BeatFeature][value] = CreateTree(splitSet(DataSet,col,value)) return TheTree
递归函数的第一个停止条件是所有的类标签都相同,递归函数第二个停止条件是使用完数据集中所有的特征,即数据集不能继续划分;字典变量TheTree储存了树的所有信息,BestFeature则是当前最优特征。
最后代码遍历当前最优特征的所有属性值,在每个数据集划分上递归调用函数CreateTree(),并且传入的参数是每次划分之后的数据集,得到的返回值都会被插入字典TheTree中,递归结束后,字典中将会嵌套很多代表叶子节点信息的数据。
得到TheTree字典如下:
{'tearRate': {0: 'no lenses', 1: {'astigmatic': {0: {'age': {0: 'soft', 1: 'soft', 2: {'prescript': {0: 'no lenses', 1: 'soft'}}}}, 1: {'prescript': {0: 'hard', 1: {'age': {0: 'hard', 1: 'no lenses', 2: 'no lenses'}}}}}}}}
从左边开始,第一个关键字key为tearRate,这代表在所有特征中,tearRate特征的信息增益最大,在此特征下,数据下降(划分)最快,该关键字的值也是一个字典。第二个关键字是依据tearRate特征划分的数据集,这些关键字的值就是tearRate节点的子节点。
这些值可能是类标签,也可能是另一个字典。如果值是类标签,则该子节点为叶子节点;如果值是另一个字典,则该子节点是一个判断节点,通过这类格式不断重复就构成了一棵决策树。
决策树的保存有很多种方法,但原理都是一样的,即序列化与反序列化,这里介绍以下两种方法。
#第一种方法
np.save('TheTree.npy',TheTree)
read_tree = np.load('TheTree.npy',allow_pickle=True).item()
第一种方法是利用numpy库中的save方法,可以将字典格式的决策树保存为npy文件;当读取树时,需要在方法后加上item(),因为我们存储的数据是字典类型,若是矩阵类型则需删去。
#第二种方法
import pickle
def storeTree(inputTree, filename):
fw = open(filename,'wb')
pickle.dump(inputTree,fw)
fw.close()
def grabTree(filename):
fr = open(filename,'rb')
return pickle.load(fr)
第二种方法是利用pickle库的dump方法对数据序列化,在读取时,用load方法即可加载数据,这里需要注意,不论写入还是读取时,都需要以二进制的格式,不然会发生报错。
构造决策树之后,可以将它用于实际数据的分类,在执行数据分类时,需要传入决策树、特征标签列表和用于分类的测试数据。然后程序会比较测试数据与决策树上的数值,递归执行该过程直到进入叶子节点,最后得到的分类结果就是叶子节点所属类型。
代码如下:
#传入的数据为决策树、数据集特征标签、测试数据 def classify(inputTree,labels,testVec): #获取决策树的第一个节点 FirstStr = list(inputTree.keys())[0] #取第一个节点外下一个字典 SecondDict = inputTree[FirstStr] ''' {'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}} {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}} {0: 'no', 1: 'yes'} ''' #取第一个节点在labels里的索引 feat_index = labels.index(FirstStr) #遍历字典中的key for key in SecondDict.keys(): #比较testVec里的值与树节点的值,如果达到叶子节点,返回类标签 if testVec[feat_index]==key: #如果下一个字典中的仍包含字典,则递归继续比较 if type(SecondDict[key])==dict : classlabel = classify(SecondDict[key],labels,testVec) else: #直到最后取到类标签 classlabel = SecondDict[key] return classlabel
其中传入特征标签列表labels的作用是帮助确定每次最优特征在数据集中的索引,利用index方法查找当前列表中第一个匹配FirstStr变量的元素,然后代码递归遍历整棵树,比较测试数据testVec变量中的值与树节点的值,直到达到叶子节点,返回当前节点的分类标签。
这里利用了上篇文章的数据构造的树做一个SecondDict举例,它的作用就是获取当前字典中最优特征(第一个关键字)的值,以达到与测试数据递归比较的效果。
classlabel = classify(inputTree,labels,[0,1,0,0])
'''
no lenses
'''
执行该函数,可以将传入的数据与原文中的数据进行比对,得到的分类结果是一致的。
决策树的主要优点就是直观易于理解,如果不能将其直观地显示出来,就无法发挥其优势。但通过matplotlib库绘制决策树是一个十分复杂的过程,这里偷懒介绍另一种比较简易的方法。
Graphviz是一种图形绘制工具,可以绘制出很多图形结构,但传入的数据需要的是dot格式,所以这里利用sklearn生成的决策树进行可视化。
Graphviz下载地址中下载graphviz-2.38.msi文件,在安装结束后需要配置环境,将该文件夹的路径添加至系统变量的Path中,在cmd中输入dot -version出现版本信息则代表安装配置成功。
决策树可视化代码如下:
from sklearn import tree from sklearn.tree import DecisionTreeClassifier import pydotplus import graphviz def pic_tree(DataSet): #所有特征数据 feature_train = DataSet.iloc[:, :-1] #类标签数据 the_label = DataSet.iloc[:, -1] #unique将类标签去重 labels = the_label.unique().tolist() #以类标签的在列表中的索引代替该标签——转化成数字 the_label = the_label.apply(lambda x: labels.index(x)) #训练数据 clf = DecisionTreeClassifier() clf = clf.fit(feature_train, the_label) #绘图过程 dot_data = tree.export_graphviz(clf, out_file=None, feature_names=['age','prescript','astigmatic','tearRate'], class_names=['no lenses', 'soft','hard'], filled=True, rounded=True, special_characters=True) # 两种方法 # 1.利用graphviz库生成PDF图片 pic = graphviz.Source(dot_data) pic.render('lense') # 2.利用pydotplus库将Dot格式转成PDF #graph = pydotplus.graph_from_dot_data(dot_data) #return graph.write_pdf("lense.pdf")
这里生成决策树图片时也有两种方法,第一种是利用graphviz库的Source方法生成PDF图片,第二种需要利用pydotplus库将Dot格式转成PDF,最后得到的可视化图片如下:
综上有关决策树的相关知识介绍完毕,总体来说,这个分类算法还是易于理解的,但它是十分重要的,因为它为后面学习随机森林奠定了基础,每一个算法都有各自的适合环境,而决策树也有自己的优缺点。
决策树的优点:
决策树的缺点:
公众号【喵说Python】后台回复“隐形眼镜”即可获取源码和数据供参考,感谢阅读。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。