scikit-learn决策树类库函数介绍


前言

本文是参考了刘建平老师的关于scikit-learn对应决策树算法类库函数的介绍以及《机器学习实战》中对决策树可视化代码的学习。有兴趣的可以多去仔细阅读。刘建平-scikit-learn决策树算法类库介绍

因此在文章中,将学习如何使用决策树进行训练、可视化和做出预测开始。然后,外面将了解scikit-learn使用的CART训练算法。scikit-learn决策树算法类库内部实现是使用了调优过的CART树算法,即可以做分类,又可以做回归。分类决策树对应的是DecisionTreeClassifier,而回归决策树的类对应的是DecisionTreeRegressor。并将学习如何对树进行正则化并将其用于回归任务。


一、DecisionTreeClassifier和DecisionTreeRegressor重要参数调参注意点

这两者的参数定义几乎完全相同,但是意义不全相同。下面就先对DecisionTreeClassifier和DecisionTreeRegressor的重要参数做一个总结,重点比较两者参数使用的不同点和调参的注意点。

参数DecisionTreeClassifierDecisionTreeRegressor
criterion(特征选择标准)可以使用‘gini’或‘entropy’前者代表基尼系数,后者代表信息增益。一般说使用默认的基尼系数‘gini’就可以了,即CART算法。除非你更喜欢类似ID3,C4.5的最优特征选择方法。可以使用"mse"或者"mae",前者是均方差,后者是和均值之差的绝对值之和。推荐使用默认的"mse"。一般来说"mse"比"mae"更加精确。除非你想比较二个参数的效果的不同之处。
splitter(特征划分点选择标准)可以使用"best"或者"random"。前者在特征的所有划分点中找出最优的划分点。后者是随机的在部分划分点中找局部最优的划分点。默认的"best"适合样本量不大的时候,而如果样本数据量非常大,此时决策树构建推荐"random"同左
max_features(划分时考虑的最大特征数)可以使用很多种类型的值,默认是"None",意味着划分时考虑所有的特征数;如果是"log2"意味着划分时最多考虑log2N个特征;如果是"sqrt"或者"auto"意味着划分时最多考虑N−−√个特征。如果是整数,代表考虑的特征绝对数。如果是浮点数,代表考虑特征百分比,即考虑(百分比xN)取整后的特征数。其中N为样本总特征数。一般来说,如果样本特征数不多,比如小于50,我们用默认的"None"就可以了,如果特征数非常多,我们可以灵活使用刚才描述的其他取值来控制划分时考虑的最大特征数,以控制决策树的生成时间。同左
max_depth(决策树最大深度)决策树的最大深度,默认可以不输入,如果不输入的话,决策树在建立子树的时候不会限制子树的深度。一般来说,数据少或者特征少的时候可以不管这个值。如果模型样本量多,特征也多的情况下,推荐限制这个最大深度,具体的取值取决于数据的分布。常用的可以取值10-100之间。同左
min_samples_split(内部节点再划分所需最小样本数)这个值限制了子树继续划分的条件,如果某节点的样本数少于min_samples_split,则不会继续再尝试选择最优特征来进行划分。 默认是2.如果样本量不大,不需要管这个值。如果样本量数量级非常大,则推荐增大这个值。一个项目例子,有大概10万样本,建立决策树时,选择了min_samples_split=10,可以作为参考。同左
min_samples_leaf(叶子节点最少样本数)这个值限制了叶子节点最少的样本数,如果某叶子节点数目小于样本数,则会和兄弟节点一起被剪枝。 默认是1,可以输入最少的样本数的整数,或者最少样本数占样本总数的百分比。如果样本量不大,不需要管这个值。如果样本量数量级非常大,则推荐增大这个值。之前的10万样本项目使用min_samples_leaf的值为5,仅供参考。同左
min_weight_fraction_leaf(叶子节点最小的样本权重和)这个值限制了叶子节点所有样本权重和的最小值,如果小于这个值,则会和兄弟节点一起被剪枝。 默认是0,就是不考虑权重问题。一般来说,如果我们有较多样本有缺失值,或者分类树样本的分布类别偏差很大,就会引入样本权重,这时我们就要注意这个值了。同左
max_leaf_nodes(最大叶子节点数)通过限制最大叶子节点数,可以防止过拟合,默认是"None”,即不限制最大的叶子节点数。如果加了限制,算法会建立在最大叶子节点数内最优的决策树。如果特征不多,可以不考虑这个值,但是如果特征分成多的话,可以加以限制,具体的值可以通过交叉验证得到。同左
class_weight(类别权重)指定样本各类别的的权重,主要是为了防止训练集某些类别的样本过多,导致训练的决策树过于偏向这些类别。这里可以自己指定各个样本的权重,或者用“balanced”,如果使用“balanced”,则算法会自己计算权重,样本量少的类别所对应的样本权重会高。当然,如果你的样本类别分布没有明显的偏倚,则可以不管这个参数,选择默认的"None"不适用于回归树
min_impurity_split(节点划分最小不纯度)这个值限制了决策树的增长,如果某节点的不纯度(基尼系数,信息增益,均方差,绝对差)小于这个阈值,则该节点不再生成子节点。即为叶子节点 。同左
presort(数据是否预排序)这个值是布尔值,默认是False不排序。一般来说,如果样本量少或者限制了一个深度很小的决策树,设置为true可以让划分点选择更加快,决策树建立的更加快。如果样本量太大的话,反而没有什么好处。问题是样本量少的时候,我速度本来就不慢。所以这个值一般懒得理它就可以了。同左

除了这些参数要注意以外,其他在调参时的注意点有:

1)当样本少数量但是样本特征非常多的时候,决策树很容易过拟合,一般来说,样本数比特征数多一些会比较容易建立健壮的模型

2)如果样本数量少但是样本特征非常多,在拟合决策树模型前,推荐先做维度规约,比如主成分分析(PCA),特征选择(Losso)或者独立成分分析(ICA)。这样特征的维度会大大减小。再来拟合决策树模型效果会好。

3)推荐多用决策树的可视化(下节会讲),同时先限制决策树的深度(比如最多3层),这样可以先观察下生成的决策树里数据的初步拟合情况,然后再决定是否要增加深度。

4)在训练模型先,注意观察样本的类别情况(主要指分类树),如果类别分布非常不均匀,就要考虑用class_weight来限制模型过于偏向样本多的类别。

5)决策树的数组使用的是numpy的float32类型,如果训练数据不是这样的格式,算法会先做copy再运行。

6)如果输入的样本矩阵是稀疏的,推荐在拟合前调用csc_matrix稀疏化,在预测前调用csr_matrix稀疏化。

二、训练和可视化决策树

1.导入库

首先是载入类库:

from sklearn.datasets import load_iris
from sklearn import tree

其中iris数据集是鸢尾花数据集。该数据集包含了鸢尾花的三个品种(Iris setosa,Iris virginica 和 Iris versicolor)各50个样本,而每个样本还有4个特征参数(分别是萼片的长宽和花瓣的长宽,以厘米为单位)。具体可以参考:https://www.cda.cn/view/3074.html

由于iris数据集样本量和维度都很小,所以可以方便进行可视化和操作。

然后导入可视化工具库:

import pydotplus
import os
os.environ['PATH']+= os.pathsep+'D:\graphviz\bin/'

在scikit-learn中的决策树的可视化一般都需要安装graphviz,安装完之后就要设值环境变量,将graphviz的bin目录加到PATH。详细可以参考这篇文章:GraphViz’s executables not found解决方法(安装Graphviz)
之后就是安装python插件graphviz以及pydotplus,分别是pip install graphviz以及pip install pydotplus。这样环境就搭好了,有时候python会很笨,仍然找不到graphviz,这时,可以在代码里面加入这一行:

os.environ['PATH']+= os.pathsep+'D:\graphviz\bin/'

注意后面的路径是你自己的graphviz的bin目录。这些工具完成之后我们可以进行下一步。


2、训练决策树

接着载入scikit-learn自带的数据,有决策树拟合,得到模型:

iris = load_iris()
clf = tree.DecisionTreeClassifier()
clf = clf.fit(iris.data,iris.target)

该iris数据集是一种字典结构,数据存储在.data成员中,输出标签存储在.target成员中。
请添加图片描述
我们可以试着画出这个数据的任意两维的数据散点图来加深对该iris数据集一些特征参数的理解:

from sklearn import datasets
import matplotlib.pyplot as plt
iris = datasets.load_iris()
irisFeatures = iris['data']  # iris的特征参数数据
irisFeaturesName = iris['feature_names']  # iris的特征参数的命名
irislabels = iris['target']  # 输出标签
print(irisFeaturesName)
print(irislabels)

# zip()接受任意多个序列参数,返回一个元组tuple列表
# 用不同的标记和颜色画出每种品种iris花朵的前两维数据
# 我们单独绘制每个类以获得不同颜色的标记
def scatter_plot(dim1,dim2):
    for t,marker,color in zip(range(3),'>ox','rgb'):
        # plt.scatter(x,y,marker=,c=)函数用于生成一个散点图
        # x,y:表示的是shape大小为(n,)的数组,也就是我们即将绘制散点图的数据点,输入数据.
        # c:表示色彩或颜色序列,可选,默认蓝色’b’。
        # marker:表示的是标记的样式,可选,默认’o’。
        plt.scatter(irisFeatures[irislabels==t,dim1],irisFeatures[irislabels==t,dim2],marker=marker,c=color)
        dim_meaning = {0:'setal length',1:'setal width',
                       2:'petal length',3:'petal width'}
        plt.xlabel(dim_meaning.get(dim1))
        plt.ylabel(dim_meaning.get(dim2))
plt.subplot(2,3,1)
scatter_plot(0,1)
plt.subplot(232)
scatter_plot(0,2)
plt.subplot(233)
scatter_plot(0,3)
plt.subplot(234)
scatter_plot(1,2)
plt.subplot(235)
scatter_plot(1,3)
plt.subplot(236)
scatter_plot(2,3)
plt.show()
------------------------------------------------------------
['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2
 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 2 2]

请添加图片描述
了解数据集的一些参数后,开始构建分类模型。

首先是利用DecisionTreeClassifier()函数创建一个决策树模型:

clf = tree.DecisionTreeClassifier()

然后用训练集数据传入到模型以达到训练模型的效果,所使用的接口是fit(x,y),英文中fit的意思是“拟合”:

clf = clf.fit(iris.data,iris.target)

3、可视化决策树

要将决策树可视化,首先,使用export_graphviz()方法输出一个图形定义文件,命名为’iris.dot‘。现在可以将模型存入dot文件iris.dot:

with open('iris.dot','w') as f:
    f = tree.export_graphviz(clf,out_file=f)
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)
dot_data = dot_data.replace('\n','')

该函数的具体参数含义可以参考这篇文章:决策树tree.export_graphviz的参数

而我所用的Python解释器版本是3.9,代码运行时图像会出现一个黑色的方块,加上了’dot_data = dot_data.replace(‘\n’,‘’)‘问题就解决了。

最后我们可以用pydotplus生成iris.pdf:

import pydotplus
graph = pydotplus.graph_from_dot_data(dot_data)
graph.write_pdf("iris.pdf")

完整代码如下:

from sklearn.datasets import load_iris
from sklearn import tree
import pydotplus
import os
os.environ['PATH']+= os.pathsep+'D:\graphviz\bin/'

iris = load_iris()
clf = tree.DecisionTreeClassifier()
clf = clf.fit(iris.data,iris.target)

with open('iris.dot','w') as f:
    f = tree.export_graphviz(clf,out_file=f)

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)

dot_data = dot_data.replace('\n','')
graph = pydotplus.graph_from_dot_data(dot_data)
graph.write_pdf("iris.pdf")

就可以得到决策树的生成图:
请添加图片描述
首先分析一下这个生成的决策树,假设你找到一个鸢尾花,要对其进行分类。从根节点出发;该节点询问花的花瓣长度是否小于0.8cm。如果是,则向下移动到根的左子节点。在这种情况下,它是一片叶子节点(即它没有任何子节点),因此它不会提出任何问题:只需查看该节点的预测类,然后决策树就可以预测花朵是山鸢尾花(class= setosa)。

假设你发现了另一朵花,这次花瓣的长度大于0.8cm,你必须向下移动到根的右子节点,该子节点不是叶子节点,因此该节点会问另一个问题:花瓣宽度是否小于1.75cm?如果是,则你的花朵很可能是变色鸢尾花(class= versicolor)。如果不是
,则可能是维吉鸢尾花(class=virginica)。然后往下依次类推。

其中节点的sample属性是统计它应用的训练实例数量。例如,有100个训练实例的花瓣长度大于0.8cm,其中54个花瓣宽度小于1.75cm。节点value属性说明了该节点上每个类别的训练实例数量。例如深度2右下角的节点应用在0个山鸢尾、1个变色鸢尾和45个维吉尼亚鸢尾实例上。最后,节点的gini属性衡量其不纯度:如果应用的所有训练实例都属于同一个类别,那么节点就是“纯”的(gini值为0)。

三、做出预测

下面我们可以选用iris的数据集中的萼片长和花瓣长进行数据拟合,决策树的深度限制最大层数第4层:

import numpy as np
import matplotlib.pyplot as plt

from sklearn import datasets
from sklearn.tree import DecisionTreeClassifier

iris = datasets.load_iris()
# 取萼片长和花瓣长进行数据拟合
X = iris.data[:,[0,2]]
y = iris.target

clf = DecisionTreeClassifier(max_depth=4)
clf.fit(X,y)

然后我们可以用下面的方法绘制决策树决策边界:

x_min,x_max = X[:,0].min()-1,X[:,0].max()+1
y_min,y_max = X[:,1].min()-1,X[:,1].max()+1
# np.meshgrid(): 从坐标向量中返回坐标矩阵
xx,yy = np.meshgrid(np.arange(x_min,x_max,0.1),
                    np.arange(y_min,y_max,0.1))
# ravel()将多维数据展平为一维数据;np.c_是按行连接两个矩阵,就是把两矩阵左右相加,要求行数相等;predict: 输入测试集返回每个测试样本的标签。
z = clf.predict(np.c_[xx.ravel(),yy.ravel()])
z = z.reshape(xx.shape)
# plt.contourf([X, Y,] Z,[levels], **kwargs):绘制等高线,这个函数中输入的参数是x,y对应的网格数据以及此网格对应的高度值
plt.contourf(xx,yy,z,alpha=0.4)
plt.scatter(X[:,0],X[:,1],c=y,alpha=0.8)
plt.xlabel("setal length")
plt.ylabel("petal length")
plt.show()

在这里插入图片描述
决策树可以估算某个实例属于特定类k的概率:首先,跟随决策树找到该实例的叶节点,然后返回该节点中类k的训练实例占比。假如我们有一个萼片长为7cm,花瓣长为2cm,我们使用predict()方法去返回相应的标签进行预测:

a = clf.predict([[7,2]])
print(a)
a = clf.predict_proba([[6,4.7]])
print(a)
----------------------------------------------------------
[0]
[[0. 1. 0.]]

再根据上面的图来看再最下方紫色的矩形,任意点的估计概率都是相同的。

四、正则化超参数

由于决策树极少对训练数据集做出假设。如果不加以限制,树的结构将跟随训练集变化,严密拟合,并且很有可能过拟合。这种模型通常被称为非参数模型,这不是说它不包含任何参数,而是指在训练之前没有确定参数的数量,导致模型结构自由而紧密地贴近数据。相反,参数模型则有预先设定好的一部分参数,因此其自由度受限,从而降低了过拟合的风险(但是增加了欠拟合的风险)。

为避免过拟合,需要在训练过程中降低决策树的自由度,这个过程也被称为正则化。正则化超参数的选择取决于模型,但是通常来说,至少可以限制决策树的最大深度。在Scikit-learn中,这由超参数max_depth控制(默认值为None,意味着无限制)。减小max_depth可使模型正则化,从而降低过拟合的风险。

DecisionTreeClassifier类还有一些其他的参数,同样可以限制决策树的形状:min_samples_split(分裂前节点必须有的最小样本树)、min_samples_leaf、min_weight_fraction_leaf、max_leaf_nodes以及max_features。增大超参数min_*或减小max_*将使模型正则化。


总结

以上就是今天要讲的内容,本文仅仅简单介绍了scikit-learn决策树类库函数的使用,并且如何可视化和训练决策树,并且做出预测。虽然本文仅仅介绍了分类树,但回归树其实也是类似的,只是预测的不是一个类别而是一个值,之后有机会也会详细介绍。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值