决策树
基础理论
是什么
决策树多用于分类任务,本质上就是一棵树形结构,通过树形结构中的节点属性来划分新的数据属于哪个分支。形式其实和流程图一样
决策树通常有三个步骤:特征选择、决策树的生成、决策树的修剪。
作用
根据给定的训练数据集构建一个决策树模型,使它能够对实例进行正确的分类。本质上是从训练集中归纳出一组分类规则,或者说是由训练数据集估计条件概率模型。
优缺点
计算直观简单,输出结果易于理解。缺点容易过拟合。
决策树生成步骤
特征选择
对于数据的众多属性,如何找出当前按照哪个属性进行划分分类效果最好呢? 这就需要借助信息学论的知识。信息论中熵度量了事物的不确定性,越不确定的事物,它的熵就越大。特征选择都是基于此概念进行
- ID3算法就是用信息增益大小来判断当前节点应该用什么特征来构建决策树。
- C4.5在ID3基础上改进,使用信息增益率来判断,容易偏向于取值较多的特征的问题。
- CART分类树算法使用基尼系数,基尼系数代表了模型的不纯度,基尼系数越小,则不纯度越低,特征越好。容易偏向于取值较少的特征的问题。
不作细讲,参考blog或西瓜书。
决策树的生成
有了特征选择的标准,生成树其实就是在样本属性总选出最好的特征,已此特征作为节点进行划分;然后在对节点下的子集数据进行重复步骤,不断找出最好的划分特征,进行划分。
# 创建分支的伪代码, 检测数据集中每个子项是否属于同一类:
If so return 类标签:
Else
寻找划分数据集的最好特征
划分数据集
创建分支节点
for 每个划分的子集
调用函数createBranch()并增加返回结果到分支节点中
return 分支节点
决策树的修剪
决策树修建的目的就是为了防止过拟合,如果不剪枝,树形结构过于庞大复杂,对于训练样本来说准确度很高,但是泛化性差。
剪枝有两种:
- 预剪枝
预剪枝是指在决策树生成过程中,对每个结点在划分前先进行估计,若当前结点的划分不能带来决策树泛化性能提升,则停止划分并将当前结点标记为叶结点 - 后剪枝
后剪枝则是先从训练集生成一棵完整的决策树,然后自底向上地对非叶结点进行考察,若将该结点对应的子树替换为叶结点能带来决策树泛化性能提升,则将该子树替换为叶结点.
代码实现
有了上面理论基础,就可以自己实现决策树算法了,机器学习实战 Ch03 中有ID3的实现,可以参考
实践
结合实际的一个例子讲讲具体如何应用决策树进行分类,使用sklean库,不用重复造轮子。
代码参考
例子使用西瓜书中数据集。
编号 | 色泽 | 根蒂 | 敲声 | 纹理 | 脐部 | 触感 | 密度 | 含糖率 | 好瓜 |
---|---|---|---|---|---|---|---|---|---|
1 | 青绿 | 蜷缩 | 浊响 | 清晰 | 凹陷 | 硬滑 | 0.697 | 0.46 | 是 |
2 | 乌黑 | 蜷缩 | 沉闷 | 清晰 | 凹陷 | 硬滑 | 0.774 | 0.376 | 是 |
3 | 乌黑 | 蜷缩 | 浊响 | 清晰 | 凹陷 | 硬滑 | 0.634 | 0.264 | 是 |
4 | 青绿 | 蜷缩 | 沉闷 | 清晰 | 凹陷 | 硬滑 | 0.608 | 0.318 | 是 |
觉得机器学习流程基本都是 数据预处理 、 模型选择 、 根据测试结果来进行模型调参。
1、数据预处理
因为西瓜书中数据为离散的文本值,一般使用one-hot 编码来进行映射,这里因为是决策树算法,直接将数据转换为数值数据就可以。使用sklearn的LabelEncoder
即可
数据较少,直接划分训练集,测试集。
2、使用sklearn训练
DecisionTreeClassifier 方法
参数很多,列举下程序中使用到的几个参数。
- criterion=’gini’: 特征选择算法Gini impurity,和基尼系数还不一样
- splitter:表示在构造树时,选择结点的原则,默认是splitter=‘best’,即选择最好的特征点分类,比如基于信息增益分类时,则选择信息增益最大的特征点,还可以是’random’
- max_depth:树的最大深度
- min_samples_leaf:表示每个叶结点最小的样本数目
- min_samples_split: 分裂所需的最小数量的节点数.当叶节点的样本数量小于该参数后,则不再生成分支.该分支的标签分类以该分支下标签最多的类别为准
def train(x, y):
X_train, X_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state = 123)
print(len(y_train), len(y_test))
clf = tree.DecisionTreeClassifier(min_samples_split=2, min_samples_leaf=2)
clf.fit(X_train, y_train)
print('train score:', clf.score(X_train, y_train), ' test score:', clf.score(X_test, y_test))
dot_data = tree.export_graphviz(clf, out_file='1.dot',feature_names=X_train.columns.values, class_names=['good', 'bad'])
可以看到min_samples_split=2,表示当节点中样本数量少于2 时不再分裂;
min_samples_leaf=2 表明叶子节点最小样本数量为2。 需要同时满足两个条件
参考
结果可视化
先导出’.dot’文件
dot_data = tree.export_graphviz(clf, out_file='tree.dot')
然后使用 graphviz
工具来进行图片导出:
dot -Tpng tree.dot -o 1.png
可以看出,为了保证叶子节点最小样本数量为2,最下面两个叶子节点样本数量都为3,而且不进行再次分裂,如果再次分裂,就会有叶子几点为1 的情况(比如最下层左叶子节点,value=[1,2],表示正负样本数为1,2,如果再次分裂将会有叶子几点为1的情况)
3、根据测试结果调整模型参数
传统的机器学习算法寻找最优参数,可以使用grid search,像决策树这种可调参数并不多,时间消耗也可以接受。对于多个可变参数,不能先找出其中一个参数最优值,然后再此基础上去找另一个参数最优值,因为多参数直接也会有影响。
代码参考