决策树的构造
- 优点:计算复杂度不高,输出结果易于理解(直观),对中间值的缺失不敏感,可以处理不相关特征数据
- 缺点:可能会产生过度匹配问题
- 适用数据类型:数值型和标称型
常用算法
- ID3:无法处理数值型数据,不适用于存在太多特征划分的标称型数据。
- CART
- C4.5
一般流程
- 收集数据:可以使用任何方法
- 准备数据:树构造算法只适用于标称型数据,因此数值型数据必须离散化
- 分析数据:可以使用任何方法,构造树完成之后,我们应该检查图形是否符合预期
- 训练算法:构造树的数据结构
- 测试算法:使用经验树计算错误率。
- 使用算法:此步骤可以适用于任何监督学习算法,而使用决策树可以更好地理解数据的内在含义
划分数据集
【tree.py】
原则:将无序的数据变得更加有序。
对每个特征划分数据集的结果计算一次信息熵,然后判断按照哪个特征划分数据集是最好的划分方式。
度量集合无序程度
- 信息增益:熵
- 基尼不纯度(Gini impurity)
Python语言不用考虑内存分配问题。Python在函数中传递的是列表的引用,在函数内部对列表对象的修改,将会影响该列表对象的整个生存周期。如何消除这个不良影响?
- 创建一个新的列表对象:为了不修改原始数据集
- 将符合特征的数据抽取出来:遍历数据集中的每个元素,一旦发现符合要求的值,则将其添加到新创建的列表中
如何选择最好的数据划分方式?
- 使用列表推导(List Comprehension)来创建新的列表,将数据集中所有特征值或者可能存在的值写入这个新list中
- 遍历当前特征中的所有唯一属性值,对每个特征划分一次数据集
- 计算数据集的新熵值,并对所有唯一特征值得到的熵求和
- 比较所有特征中的信息增益,返回最好特征划分的索引值
Python语言列表类型自带的extend()和append()功能类似,但是在处理多个列表时,这两个方法的处理结果有什么不同?
假定存在两个列表,a和b:
>>> a=[1,2,3]
>>> b=[4,5,6]
>>> a.append(b)
>>> a
[1,2,3,[4,5,6]]
>>> a=[1,2,3]
>>> b=[4,5,6]
>>> a.extend(b)
>>> a
[1,2,3,4,5,6]
递归构建决策树
工作原理
得到原始数据集,然后基于最好的属性划分数据集,由于特征值可能多于两个,因此可能存在大于两个分支的数据集划分。第一次划分之后,数据将被向下传递到树分支的下一个节点,在这个节点上,我们可以再次划分数据。因此我们可以采用递归的原则处理数据集。
递归结束的条件是:程序遍历完所有划分数据集的属性,或者每个分支下的所有实例都具有相同的分类,则得到一个叶子节点或者终止块。
使用Matplotlib注解绘制决策树
建议按照图形比例绘制树形图,好处是无需关于实际输出图形的大小,一旦图形大小发生了变化,函数会自动按照图形大小重新绘制。具体操作是通过计算树包含的所有叶子节点数,划分图形的宽度,从而计算得到当前节点的中心位置,也就是,按照叶子节点的数目将x軕划分为若干部分。
【treePlotter.py】
-
plotMidText()—计算父节点和子节点的中间位置,并在此处添加简单的文本标签信息。
-
plotTree()
1.计算树的宽和高
2.绘出子节点具有的特征值,或者沿此分支向下的数据实例必须具有的特征值。
3.按比例减少y坐标偏移(因为图形为自顶向下绘制)
存储决策树
构造决策树很耗时,为了节省计算时间,最好能够在每次执行分类时调用已经构造好的决策树。而存储决策树的具体方法是使用Python模块pickle序列化对象,将分类器存储在硬盘上。
- storeTree()
- grabTree()
e.g. 预测隐形眼镜类型
如何减少过度匹配的问题?
裁剪决策树,去掉一些不必要的叶子节点。如果叶子节点只能增加少许信息,则可以删除该节点,将它并入到其他叶子节点中。