写点什么

决策树 - 分类树

  • 2022-11-01
    山东
  • 本文字数:5320 字

    阅读完需:约 17 分钟

关键概念:节点


  • 根节点:没有进边,有出边。包含最初的,针对特征的提问。

  • 中间节点:既有进边也有出边,进边只有一条,出边可以有很多条。都是针对特征的提问。

  • 叶子节点:有进边,没有出边,每个叶子节点都是一个类别标签。

  • 子节点和父节点:在两个相连的节点中,更接近根节点的是父节点,另一个是子节点。

DecisionTreeClassifier 与红酒数据集

tree.DecisionTreeClassifier(    ["criterion='gini'", "splitter='best'", 'max_depth=None', 'min_samples_split=2', 'min_samples_leaf=1', 'min_weight_fraction_leaf=0.0', 'max_features=None', 'random_state=None', 'max_leaf_nodes=None', 'min_impurity_decrease=0.0', 'min_impurity_split=None', 'class_weight=None', 'presort=False'],)
复制代码

建立一棵树

导入需要的算法库和模块


from sklearn import treefrom sklearn.datasets import load_wine # 里面有很多著名的数据集from sklearn.model_selection import train_test_split
复制代码


了解数据


wine = load_wine() # 内置的红酒数据集print(type(wine)) # 类似于字典---<class 'sklearn.utils.Bunch'>
wine.keys()---dict_keys(['data', 'target', 'target_names', 'DESCR', 'feature_names'])
print(wine.DESCR) # 数据集简介# 太多了,这里不写了
wine.feature_names # 特征名,也就是X每个维度代表什么,一会有翻译---['alcohol', 'malic_acid', 'ash', 'alcalinity_of_ash', 'magnesium', 'total_phenols', 'flavanoids', 'nonflavanoid_phenols', 'proanthocyanins', 'color_intensity', 'hue', 'od280/od315_of_diluted_wines', 'proline']
wine.target_names # 目标名,也就是每一类Y的名字。这里有3类Y,y_i in ('class_0', 'class_1', 'class_2')---array(['class_0', 'class_1', 'class_2'], dtype='<U7')
wine.data.shape # 这里说明了数据集X一共有178个数据,13个特征---(178, 13)
wine.target # 即整个Y集---# 就是178个0,1,2这里不写了
import pandas as pdpd.concat([pd.DataFrame(wine.data),pd.DataFrame(wine.target)],axis=1) # 将X和Y连成一个DataFrame---
复制代码


分割训练集和数据集


Xtrain, Xtest, Ytrain, Ytest = train_test_split(wine.data,wine.target,test_size=0.3)# test_size=0.3意味着训练集占70%,测试集占30%# 注意,这里是随机的
print(type(Xtrain))---<class 'numpy.ndarray'>
Xtrain.shape,Xtest.shape,Ytrain.shape,Ytest.shape---((124, 13), (54, 13),(124,), (54,))
复制代码


建立模型


clf = tree.DecisionTreeClassifier(criterion="entropy")clf = clf.fit(Xtrain, Ytrain)score = clf.score(Xtest, Ytest) #返回预测准确度accuracy
score # 每次可能不一样,在后面会解释为什么不一样---0.8888888888888888
复制代码


tree.score 可以对测试集,同样的,也可以对训练集

score_train = clf.score(Xtrain, Ytrain) # 注意这里是在训练集上计算score
score_train
---
1.0


画出一棵树


import graphviz # 画树所需的包feature_name=['酒精','苹果酸','灰','灰的碱性','镁','总酚','类黄酮','非黄烷类酚类','花青素','颜色强度','色调','od280/od315稀释葡萄酒','脯氨酸'] # 和上面的英文特征相对应dot_data = tree.export_graphviz(clf                                ,feature_names=feature_name                                ,class_names=['琴酒','雪莉','贝尔摩德']                                ,filled=True                                ,rounded=True)graph = graphviz.Source(dot_data)# feature_names:X特征名,即X不同维度的名称# class_names:Y类名,即不同类别Y的名称# filled:是否填充颜色,颜色深浅表示纯度,不同颜色表示不同类别# rounded:使用圆框/方框显示
复制代码


逗号写在参数前面,方便注释,尤其是最后一行注释掉,不需要增删逗号


graph# entropy:纯度# samples:样本个数# value:样本比例
复制代码



很遗憾图片并不能显示中文


保存 png 格式的 graphviz 输出的 dot 文件

dot_data = tree.export_graphviz(clf
                                ,feature_names=feature_name
                                ,class_names=['琴酒','雪莉','贝尔摩德']
                                ,filled=True
                                ,rounded=True
                                ,out_file="./tree.dot"
)

注意这里要加,out_file="./tree.dot",然后在与笔记同一目录下会生成一个tree.dot文件找目录的方法

import os
print(os.path.abspath('.'))

看返回结果 cmd 到该目录下,执行

dot -Tpng tree.dot -o tree.png


继续探索决策树


clf.feature_importances_ # 特征重要性---array([0.02512577, 0.        , 0.        , 0.        , 0.035285  , 0.        , 0.        , 0.        , 0.        , 0.09686628, 0.13368571, 0.24884153, 0.46019571])
list(zip(feature_name,clf.feature_importances_))---[('酒精', 0.02512577188063646), ('苹果酸', 0.0), ('灰', 0.0), ('灰的碱性', 0.0), ('镁', 0.0), ('总酚', 0.0), ('类黄酮', 0.0), ('非黄烷类酚类', 0.0), ('花青素', 0.03528500112337327), ('颜色强度', 0.09686627661594578), ('色调', 0.13368570769252183), ('od280/od315稀释葡萄酒', 0.24884153360350494), ('脯氨酸', 0.46019570908401763)]
复制代码

重要参数

criterion 分枝指标

为了通过表格生成一棵决策树,我们需要找到最佳的节点和最佳的分枝方法,对分类树来说,衡量这个最佳的指标可以使用基尼系数,输入"gini",这也是 criterion 默认的值;使用信息熵,输入"entropy"二者的数学表示



代表给定的节点,代表标签的任一分类


这里我们尽量少提数学相关的内容,可以到统计学习方法中看决策树相关知识


基尼系数缺点:对不纯度相对不敏感。信息熵缺点:涉及对数计算,速度相对慢一点;对不纯度更敏感,尤其对于高维数据或者噪声很多的数据,容易过拟合


通常使用基尼系数数据维度很大,噪声很大的时候使用基尼系数维度低,数据比较清晰的时候,信息熵和基尼系数没区别当决策树拟合程度不够好的时候,使用信息熵

random_state 随机生成参数

实际上,无论决策树模型如何进化,在分枝上的本质都是追求某个不纯度相关的指标的优化,也就是说,决策树在建树时,是考优化节点来追求一棵优化的树,但即使我们在每个节点都选择最优分类结点,我们并不能保证整体数最优。集成算法被用来解决这个问题。sklearn 表示,既然一棵树不能保证最优,那就建更多的不同的树,然后从中取最好的。怎样从一组数据集中建不同的树?在每次分枝时,不从使用全部特征,而是随机选取一部分特征,从中选取不纯度相关指标最优的作为分枝用的节点。这样,每次生成的树也就不同了。


clf = tree.DecisionTreeClassifier(criterion="entropy"                                  ,random_state=0                                 )# random_state:用来设置分枝中的随机模式的参数,输入任意非负正整数,会一直长处一棵树,让模型稳定下来。默认None,在高维度时随机性会表现更明显,低维度的数据随机性几乎不会显现。clf = clf.fit(Xtrain, Ytrain)score = clf.score(Xtest, Ytest)
score # 依旧有可能不一样---0.9074074074074074
复制代码


即使我们固定了 random_state=0 或者其他数字,由于训练集和数据集的划分是随机的。当我们重启 jupyter 的时候,数据集将不同。因此,即使固定 random_state,树仍有可能是不同的

splitter 随机生成参数

clf = tree.DecisionTreeClassifier(criterion="entropy"                                  ,random_state=0                                  ,splitter='random'                                 )# splitter:控制决策树分枝的方式# 默认"best",即选择更重要的特征进行分枝(上面有提到"在每次分枝时,不从使用全部特征,而是随机选取一部分特征",因此这里只是对这一部分随机选取的特征选择feature_importances_最大的进行分枝)# 输入"random",在分支的时候会更加随机,注意这里的随机可以认为是最佳随机,即给与一定的随机性,但仍然受到feature_importances_影响,不是随机选一个。clf = clf.fit(Xtrain, Ytrain)score = clf.score(Xtest, Ytest)
score---0.9259259259259259
复制代码


当我们预测到模型有可能过拟合,可以考虑调节 splitter 来降低过拟合的可能性。实际上,当书建成以后,我们也可以通过剪枝来防止过拟合

max_depth 剪枝参数

限制树的最大深度,超过设定深度的树枝会被全部剪掉对于一棵决策树,每多生长一层,对样本的需求量就会增加一倍,所以限制树的深度能够有效的限制过拟合默认五 None。如果为 None,决策树会持续生长直到虽有叶节点不纯度为 0,或者直到每个叶节点所含的样本量都小于参数 min_samples_split 中输入的数字


实际使用时,建议从=3 开始尝试,看看拟合的效果决定是否增加设定深度


生成决策树后我们需要用 score 评判决策树的好坏,如果 score 没有变化或者变化很小,说明剪枝后效果也很好,且计算更少(奥卡姆剃刀原理),所以选择剪枝

min_sample_leaf & min_samples_split 剪枝参数

min_samples_leaf 限定,一个节点在分枝后的每个子节点都必须包含至少 min_samples_leaf 个训练样本,否则分枝就不会发生,或者,分枝会朝着满足每个子节点都包含 min_samples_leaf 个样本的方向去发生如果输入整数,则认为输入的数字是叶节点存在所需的最小样本量;如果输入浮点数,则认为输入的浮点数是比例,即叶节点存在所需的最小样本量为输入模型的样本量浮点数


min_samples_split 就是对于中间结点而言的,同 min_sample_leaf


这里我们可以看到,如果我们限定的 min_sample_leaf 不合适,决策树可能被迫生成与数据集少部分数据相违背的决策树分枝,即

score_train = clf.score(Xtrain, Ytrain) 
score_train

结果不为 1.0


这个参数设置的太小会引起过拟合,设置的太大会阻止模型学习数据,一般来说,建议从=5 开始使用

max_features 剪枝参数

分枝时考虑的最大特征个数,超过限制个数的特征会被舍弃不建议使用该参数,因为其舍弃特征并不考虑特征的重要性。如果数据维度过多可以考虑 PCA 等方式降维来防止过拟合默认 None


允许参数详情查询 api

min_impurity_decrease 剪枝参数

限制不纯度降低的最小大小,不纯度下降小于设定数值,分枝不会发生


在前面说到,不纯度可以是基尼指数,也可以是信息增益


带权重的不纯度下降可以表示为



默认=0,接收浮点数


信息增益小也就意味着,该分枝对于区分该中间节点所对应的数据集作用并不大

class_weight & min_weight_fraction_leaf 目标权重参数

常用于样本不平衡的数据集中。样本不平衡是指在一组数据集中,标签的一类天生占有很大的比例。比如说,在银行要判断“一个办了信用卡的人是否会违约”,就是是 vs 否(1%:99%)的比例。这种分类状况下,即便模型什么也不做,全把结果预测成“否”,正确率也能有 99%。


建议查询官方 API,暂时用不到,用到回来补(其实现在我还不会用,也说不明白)

确认最优剪枝参数

一般我们可以结合 matplotlib 来绘制超参数学习曲线来进行判断超参数学习曲线,是一条以超参数的取值为横坐标,模型的度量指标为纵坐标的曲线,它是用来衡量不同超参数取值下模型的表现的线在我们建好的决策树里,我们的模型度量指标就是 score


import matplotlib.pyplot as plt
test = []for i in range(10): clf = tree.DecisionTreeClassifier(max_depth=i+1 ,criterion="entropy" ,random_state=0 ,splitter="best" ) clf = clf.fit(Xtrain,Ytrain) score = clf.score(Xtest,Ytest) test.append(score)
plt.plot(range(1,11),test,color="red",label="max_depth")plt.legend()plt.show()
复制代码


重要接口和属性

之前提到过


  • feature_importances_,查看各个特征推模型的重要性

  • fit 用于生成决策树

  • score 用于评估决策树准确性


clf.apply(Xtest)# 返回每个测试样本所在叶结点的索引---array([10,  7,  7, 10,  4, 10, 10,  7,  4, 10,  5,  5, 10,  7,  7,  4, 10, 5,  7,  5,  7,  7,  9,  5,  7,  9, 10, 10,  4, 10, 10,  4,  4,  7, 7,  4, 10,  4,  5,  4, 10,  7, 10,  7, 10, 10,  7,  7,  4, 10,  5, 10,  7,  5], dtype=int64)
clf.predict(Xtest)# 返回每个测试样本的分类/回归结果---array([0, 1, 1, 0, 2, 0, 0, 1, 2, 0, 1, 1, 0, 1, 1, 2, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 2, 0, 0, 2, 2, 1, 1, 2, 0, 2, 1, 2, 0, 1, 0, 1, 0, 0, 1, 1, 2, 0, 1, 0, 1, 1])
复制代码


视频作者:菜菜TsaiTsai

链接:【技术干货】菜菜的机器学习sklearn【全85集】Python进阶_哔哩哔哩_bilibili

发布于: 刚刚阅读数: 5
用户头像

还未添加个人签名 2022-09-14 加入

还未添加个人简介

评论

发布
暂无评论
决策树-分类树_机器学习_烧灯续昼2002_InfoQ写作社区