一. 决策树与预备知识
- 基本概述
决策树是一种树型结构,它是以实例为基础的归纳学习,每个内部结点表示在一个属性上的测试,每个分支代表一个测试输出,每个叶结点代表一个类别。决策树采用的是自顶向下的递归方法,其基本思想是以信息熵为度量构造一棵熵值下降最快的树,到叶子节点处的熵值为0,此时每个叶节点中的实例都属于同一类。
- 决策树学习算法的特点
决策树学习算法的最大优点是它可以自学习,只需要对训练实例进行较好的标注,就能够进行学习。
决策树算法属于有监督学习算法。
从一类无序无规则的事物中,推理出决策树表示的分类规则。
- 熵
当熵和条件熵中的概率由数据估计(特别是极大似然估计)得到时,所对应的熵和条件熵分别称为经验熵和经验条件熵。
熵度量了事物的不确定性,越不确定的事物,它的熵就越大。具体的,随机变量X的熵的表达式如下:
推广到多个个变量的联合熵,这里给出两个变量X和Y的联合熵表达式:
- 信息增益
信息增益表示得知特征A的信息而使得类X的信息的不确定性减少的程度。例如:当你要判断一个女生是否适合你,比如 身高/长相/体重这三个因素是决定你是否选择她的关键指标,信息增益就是说当你知道女生的身高后,你就能进一步确定是否是适合你的,那么这种不确定性减少的程度就是信息增益。
- 互信息
定义:特征A对训练数据集D的信息增益为,定义集合D的经验熵与特征A给定条件下D的经验条件熵之差,即
, 这即为训练数据集D和特征A的互信息。
- 信息增益的计算方法
a. 计算数据集D的经验熵
b. 计算特征A对数据集D的经验条件熵
c. 计算信息增益
经验熵计算:
经验条件熵计算:
信息增益:
** 谁的信息增益最大,那个特征就是当前决策树要选择的节点。
- 信息增益率
特征对训练数据集的信息增益定义为其信息增益与训练数据集关于特征的值的熵之比,即
- 基尼指数
- 基尼指数的图像, 熵, 分类误差率三者之间的关系
基尼系数和熵之半的曲线非常接近,仅仅在45度角附近误差稍大。因此,基尼系数可以做为熵模型的一个近似替代。而CART分类树算法就是使用的基尼系数来选择决策树的特征。同时,为了进一步简化,CART分类树算法每次仅仅对某个特征的值进行二分,而不是多分,这样CART分类树算法建立起来的是二叉树,而不是多叉树。这样一可以进一步简化基尼系数的计算,二可以建立一个更加优雅的二叉树模型。
二. 决策树学习算法
特征选择也即选择最优划分属性,从当前数据的特征中选择一个特征作为当前节点的划分标准。我们希望在不断划分的过程中,决策树的分支节点所包含的样本尽可能属于同一类,即节点的“纯度”越来越高。而选择最优划分特征的标准不同,也导致了决策树算法的不同。
- 三种决策树学习算法介绍
ID3算法: 适应信息来进行特征选择的决策树学习过程即为ID3决策。
C4.5算法: 利用信息增益率进行特征选择
CART算法:利用基尼指数进行特征选择
总结:一个属性的信息增益越大,表明属性对样本的熵减少的能力更强,这个属性使得数据由不确定性变成确定性的能力越强。
2. 算法
算法的核心是在决策树各个节点上根据信息增益来选择进行划分的特征,然后递归地构建决策树。具体方法:
- 从根节点开始,对节点计算所有可能的特征的信息增益,选择信息增益值最大的特征作为节点的划分特征;
- 由该特征的不同取值建立子节点;
- 再对子节点递归地调用以上方法,构建决策树;
- 到所有特征的信息增益都很小或者没有特征可以选择为止,得到最终的决策树
的局限:
- 没有剪枝(没有考虑过拟合的问题)
- 采用信息增益作为选择最优划分特征的标准,然而信息增益会偏向那些取值较多的特征(这也是采用信息增益率的原因)。在相同条件下,取值比较多的特征比取值少的特征信息增益大。比如一个变量有个值,各为,另一个变量为个值,各为,其实他们都是完全不确定的变量,但是取个值的比取2个值的信息增益大。
- 没有考虑连续特征,比如长度,密度都是连续值,无法在运用。这大大限制了的用途。
- 算法对于缺失值的情况没有做考虑
举例:
ID3 算法的作者昆兰基于上述不足,对ID3算法做了改进,这就是C4.5算法
3. C4.5算法
参考博客:https://www.cnblogs.com/pinard/p/6050306.html的讲解。
总之,C4.5解决了上述四个问题:
第一 不能处理连续特征
第二个就是用信息增益作为标准容易偏向于取值较多的特征
第三缺失值处理的问
第四过拟合问题
解决不能处理连续特征的问题:
的思路是将连续的特征离散化。比如个样本的连续特征有个,从小到大排列为,则取相邻两样本值的平均数,一共取得个划分点,其中第个划分点表示为:
对于这个点,分别计算以该点作为二元分类点时的信息增益。选择信息增益最大的点作为该连续特征的二元离散分类点。比如取到的增益最大的点为,则小于的值为类别,大于的值为类别,这样我们就做到了连续特征的离散化。
解决信息增益作为标准容易偏向于取值较多的特征
引入一个信息增益比的变量,它是信息增益和特征熵的比值。表达式如下: 其中: 为样本特征输出的集合,为样本特征,对于特征熵,
表达式如下:
其中:为特征A的类别数,为特征的第个取值对应的样本个数。为样本个数。特征数越多的特征对应的特征熵越大,它作为分母,可以校正信息增益容易偏向于取值较多的特征的问题。
对于第三个缺失值处理的问题,主要需要解决的是两个问题,一是在样本某些特征缺失的情况下选择划分的属性,二是选定了划分属性,对于在该属性上缺失特征的样本的处理。
- CART算法
首先,我们要明白,什么是回归树,什么是分类树。两者的区别在于样本输出,如果样本输出是离散值,那么这是一颗分类树。如果果样本输出是连续值,那么这是一颗回归树。
,分类回归树算法,既可用于分类也可用于回归。CART分类树算法使用基尼系数来代替信息增益比,基尼系数代表了模型的不纯度,基尼系数越小,则不纯度越低,特征越好。这和信息增益(比)是相反的。
假设决策树是二叉树,内部节点特征的取值为“是”和“否”,左分支为取值为“是”的分支,右分支为取值为”否“的分支。这样的决策树等价于递归地二分每个特征,将输入空间(即特征空间)划分为有限个单元。的分类树用基尼指数来选择最优特征的最优划 分点,具体过程如:
具体的,在分类问题中,假设有个类别,第个类别的概率为, 则基尼系数的表达式为:
如果是二类分类问题,属于第一个样本输出的概率是,则基尼系数的表达式为:
对于个给定的样本,假设有个类别, 第个类别的数量为,则样本的基尼系数表达式为:
从根节点开始,对节点计算现有特征的基尼指数,对每一个特征,例如,再对其每个可能的取值 如,根据样本点对的结果的”是“与”否“划分为两个部分,利用下式进行计算。
- 在所有可能的特征以及该特征所有的可能取值中,选择基尼指数最小的特征及其对应的取值作为最优特征和最优切分点。然后根据最优特征和最优切分点,将本节点的数据集二分,生成两个子节点。
- 对两个字节点递归地调用上述步骤,直至节点中的样本个数小于阈值,或者样本集的基尼指数小于阈值,或者没有更多特征后停止;
- 生成分类树;
三. 决策树过拟合
(1) 由于决策树算法非常容易过拟合,因此对于生成的决策树必须要进行剪枝。剪枝的算法有非常多,的剪枝方法有优化的空间。思路主要是两种,一种是预剪枝,即在生成决策树的时候就决定是否剪枝。另一个是后剪枝,即先生成决策树,再通过交叉验证来剪枝。
Cart树剪枝
(2) sklearn 提供的剪枝方式:
a. max_depth
限制树的最大深度,超过设定深度的树枝全剪掉
b. min_samples_split
一个节点要包含min_samples_split个训练样本
c. min_samples_leaf
一个节点在分支后的每个节点至少包含多少个叶子节点
d. min_weight_fraction_leaf
这个参数用来限制信息增益的大小
e. max_features
这个参数用做树的精修,限制分枝时考虑的特征个数
# 决策树.py
#
import numpy as np
from sklearn import tree, datasets, preprocessing
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
import graphviz
np.random.RandomState(0)
# 加载数据
iris = datasets.load_iris()
# 划分训练集与测试集
x, y = iris.data, iris.target
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3)
# 数据预处理
scaler = preprocessing.StandardScaler().fit(x_train)
x_train = scaler.transform(x_train)
x_test = scaler.transform(x_test)
# 创建模型
clf = tree.DecisionTreeClassifier('gini')
# 模型拟合
clf.fit(x_train, y_train)
# 预测
y_pred = clf.predict(x_test)
# 评估
print(accuracy_score(y_test, y_pred))
dot_data = tree.export_graphviz(clf, out_file=None,
feature_names=iris.feature_names,
class_names=iris.target_names,
filled=True, rounded=True,
special_characters=True)
graph = graphviz.Source(dot_data)
graph.render("iris")