《Python机器学习基础教程》学习笔记(6) 决策树
决策树是广泛用于分类与回归的模型,表现形式是一棵二叉树,本质上是一系列的if/else问题。一棵决策树示例:
构造
①要构造一棵决策树,需要构造一系列的if/else问题,这些问题称为测试。算法搜遍所有可能的测试,找出对目标变量来说信息量最大的那一个(区分效果最好的),作为一个结点
②根据是否满足该结点的条件分为左右子结点,对当前数据进行划分
③反复递归,直到所有叶结点都只包含单一类别,称为纯的(pure)、
这很像一棵二叉搜索树(BST),只不过BST的结点询问的是“要搜索的值是否比该结点的值小/大?”,而决策树的结点询问的是“该样本是否满足条件xxx?”,然后根据问题的答案选择该结点的左/右子树。而不同之处在于,BST可能在树的中间就停下(找到目标值),而决策树会一直搜索至树的叶子结点。
模型
决策树可用于分类与回归任务,分别通过sklearn.tree.DecisionTreeClassifier与sklearn.tree.DecisionTreeRegressor导入。要用决策树进行分类或回归的预测,方法是相同的,即从决策树的根节点开始,根据每层的测试决定搜索的子树,直到遍历到叶结点,若是分类任务,则取该结点内类别的多者作为预测类别;若是回归任务,则取该结点内所有训练点的平均值作为预测值。而对于回归模型,有它的特殊性质:不能外推(extrapolate),也不能在训练数据范围之外进行预测。
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
cancer = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(cancer.data, cancer.target,
stratify=cancer.target, random_state=42)
tree = DecisionTreeClassifier(random_state=0).fit(X_train, y_train)
print("Accuracy on training set: {:.3f}".format(tree.score(X_train, y_train)))
print("Accuracy on test set: {:.3f}".format(tree.score(X_test, y_test)))
Accuracy on training set: 1.000 Accuracy on test set: 0.937
优化
构造决策树直到所有叶结点都是纯的叶结点,会导致模型非常复杂,且高度过拟合。纯叶结点的存在意味着这棵树在训练集上的精度是100%,而在测试集中,泛化能力往往不好。
为了防止过拟合,有两种方法,分别是预剪枝与后剪枝。预剪枝是在树完全展开之前停止树的构造,可以通过调整模型的max_depth(控制树的最大深度),max_leaf_nodes(控制叶结点的最大个数)和min_samples_leaf(控制叶结点中数据点的最小数目)参数来执行。而后剪枝是先构造树,随后删除或折叠信息量很少的结点,但在sklearn中并未实现。
tree = DecisionTreeClassifier(max_depth=4, random_state=0).fit(X_train, y_train) #为模型设置剪枝参数
Accuracy on training set: 0.988 Accuracy on test set: 0.951
可以看到,在设置剪枝参数max_depth=4后,决策树在训练集的精度降低了,但泛化能力却提高了。
分析
决策树的一大优势就是易可视化。我们可以利用sklearn.tree中的export_graphviz函数来生成一个存有决策树图形的.dot格式文件(一种用于保存图形的文本文件格式),然后利用graphviz模块读取这个文件,将决策树可视化。
graphviz模块需要自行安装,不能直接pip,安装方法:graphviz安装
from sklearn.tree import export_graphviz
import graphviz
export_graphviz(tree, out_file="tree.dot", class_names=["maligant", "benign"],
feature_names=cancer.feature_names, impurity=False, filled=True)
with open("tree.dot") as f:
dot_graph = f.read()
graphviz.Source(dot_graph)
(太大了...)
除了直接观察,还可以通过观察特征重要性(feature impotence)来分析决策树。特征重要性为每个特征对树的决策的重要性进行排序,对单个特征,有取值范围[0, 1];对所有特征,特征重要性的总和为1。sklearn的决策树可直接调用模型的feature_importances_方法来实现。
print("Feature importances:\n{}".format(tree.feature_importances_))
Feature importances: [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.01019737 0.04839825 0. 0. 0.0024156 0. 0. 0. 0. 0. 0.72682851 0.0458159 0. 0. 0.0141577 0. 0.018188 0.1221132 0.01188548 0. ]
可将特征重要性可视化:
import numpy as np
import matplotlib.pyplot as plt
def plot_feature_importances_cancer(model):
n_features = cancer.data.shape[1]
plt.barh(range(n_features), model.feature_importances_)
plt.yticks(np.arange(n_features), cancer.feature_names)
plt.xlabel("feature importance")
plt.ylabel("feature")
plot_feature_importances_cancer(tree)
评价
对决策树来说,易可视化,易理解是其一大优势。除此之外,算法还不受数据缩放的影响,无需对特征进行预处理(归一化或标准化)。这样在特征的尺度完全不一样时或者二元特征和连续特征同时存在时,决策树的效果很好。
决策树的主要缺点在于即使做了预剪枝,也容易过拟合,泛化性能较差。因此,在大多数应用中,往往使用集成方法来替代单棵决策树。