栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

聊聊决策树

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

聊聊决策树

从原理上理解 很容易发现CART算法是一种贪心算法 它会从最顶层开始搜索最优的分裂方式 然后对子集也进行同样的处理 多次分裂后 CART算法不会审视自己目前这样的分裂产出的不纯度是否全局最优的。

通常 贪心算法可以获得一个不错的解 但不能保证该解是最优解 为了便于理解 我再举一个例子 假设你要从广州去上海 你在每个节点上都选择最短的路 但这样选择下来的总路径可能不是最短 这便是贪心算法面临的情况。

决策边界可视化

除了前文中提及的将决策树本身可视化外 还有另一种常见的可视化方式 那便是将决策树的决策边界可视化出来。当然 如果数据量很大 还需要对数据进行采样后在进行可视化处理。

编写一个用于可视化决策边界的函数

# To plot pretty figures
%matplotlib inline
import matplotlib as mpl
import matplotlib.pyplot as plt
mpl.rc( axes , labelsize 14)
mpl.rc( xtick , labelsize 12)
mpl.rc( ytick , labelsize 12)
from matplotlib.colors import ListedColormap
def plot_decision_boundary(clf, X, y, axes [0, 7.5, 0, 3], iris True, legend False, plot_training True):
    # 指定间隔内 返回均匀的数字
    x1s   np.linspace(axes[0], axes[1], 100)
    x2s   np.linspace(axes[2], axes[3], 100)
    # meshgrid函数:用两个坐标轴上的点在平面上画网格 其实返回的是矩阵
    x1, x2   np.meshgrid(x1s, x2s)
    # 按行连接两个矩阵 就是把两矩阵左右相加 要求行数相等
    X_new   np.c_[x1.ravel(), x2.ravel()]
    # 预测
    y_pred   clf.predict(X_new).reshape(x1.shape)
    custom_cmap   ListedColormap([ #fafab0 , #9898ff , #a0faa0 ])
    plt.contourf(x1, x2, y_pred, alpha 0.3, cmap custom_cmap)
    if not iris:
        custom_cmap2   ListedColormap([ #7d7d58 , #4c4c7f , #507d50 ])
        plt.contour(x1, x2, y_pred, cmap custom_cmap2, alpha 0.8)
    if plot_training:
        plt.plot(X[:, 0][y 0], X[:, 1][y 0],  yo , label Iris setosa )
        plt.plot(X[:, 0][y 1], X[:, 1][y 1],  bs , label Iris versicolor )
        plt.plot(X[:, 0][y 2], X[:, 1][y 2],  g^ , label Iris virginica )
        plt.axis(axes)
    if iris:
        plt.xlabel( Petal length , fontsize 14)
        plt.ylabel( Petal width , fontsize 14)
    else:
        plt.xlabel(r $x_1$ , fontsize 18)
        plt.ylabel(r $x_2$ , fontsize 18, rotation 0)
    if legend:
        plt.legend(loc lower right , fontsize 14)

基于鸢尾花数据集进行可视化 代码如下

plt.figure(figsize (8, 4))
X   iris.data[:, 2:]  # 花瓣的长与宽
y   iris.target  # 花的种类
plot_decision_boundary(tree_clf, X, y)
plt.plot([2.45, 2.45], [0, 3],  k- , linewidth 2)
plt.plot([2.45, 7.5], [1.75, 1.75],  k-- , linewidth 2)
plt.plot([4.95, 4.95], [0, 1.75],  k: , linewidth 2)
plt.plot([4.85, 4.85], [1.75, 3],  k: , linewidth 2)
plt.text(1.40, 1.0,  Depth 0 , fontsize 15)
plt.text(3.2, 1.80,  Depth 1 , fontsize 13)
plt.text(4.05, 0.5,  (Depth 2) , fontsize 11)
plt.show()

效果

决策树的问题
容易过拟合

决策树的特点是 它极少对训练数据本身做出假设 对比看线性模型 如果你选择使用线性模型 其实你就假设了训练数据是线性变化的 否则线性模型不可能得出好的结果 而决策树不会有这样的假设 这个特点容易让决策树出现过拟合的问题。

以一个具体的例子来展示决策树过拟合的情况:

首先 我通过sklearn的make_moons方法生成半环形分布式的数据集 直观理解如下

from sklearn.datasets import make_moons 
plt.subplot(122)  
x1,y1 make_moons(n_samples 1000,noise 0.1)  
plt.title( make_moons function example )  
plt.scatter(x1[:,0],x1[:,1],marker o ,c y1)  
plt.show()

上述代码效果如下图

有了半环形分布的数据后 训练分类决策树 代码如下

from sklearn.datasets import make_moons
Xm, ym   make_moons(n_samples 100, noise 0.25, random_state 53)
# 分类决策树
tree_clf   DecisionTreeClassifier(random_state 42)
tree_clf.fit(Xm, ym)
# 可视化
plt.figure(figsize (8, 4))
plot_decision_boundary(tree_clf, Xm, ym, axes [-1.5, 2.4, -1, 1.5], iris False)
plt.title( overfit , fontsize 16)
plt.show()

决策树对半环形分布的数据 其决策边界如下

从图可以看出 决策树对该数据有明显的过拟合情况。

解决方法也很简单 就是使用各种超参数对模型进行正则化调整 即限制模型的拟合能力 从而希望获得具有更好泛化的模型。sklearn对决策树提供了max_depth 最大深度 、min_samples_split 分裂前节点必须有的最小样本数 、min_samples_leaf 叶节点必须要有的最小样本数 等等超参数用于正则化。

这里我使用max_depth和min_samples_leaf对分类决策树做了相应的正则化。

from sklearn.datasets import make_moons
Xm, ym   make_moons(n_samples 100, noise 0.25, random_state 53)
# 分类决策树
tree_clf   DecisionTreeClassifier(random_state 42, max_depth 5, min_samples_leaf 4)
tree_clf.fit(Xm, ym)
# 可视化
plt.figure(figsize (8, 4))
plot_decision_boundary(tree_clf, Xm, ym, axes [-1.5, 2.4, -1, 1.5], iris False)
plt.title( regularization , fontsize 16)
plt.show()

可视化效果如图

过拟合的问题不只是在分类决策树上 在回归决策树上也会有 这里可视化的展示一下 让你有更直观的了解 如下图

上图中 左半部分 无疑是过拟合的 而右半部分 使用了min_smaples_leaf做正则化的限制 效果还可以。

此外 从右半部分的图也可以看出 因为回归决策树使用MSE来做节点分裂标准 所以决策树的预测值都是对应区域内实例的目标平均值。

不稳定性

前文中 我们已经展示 决策树对多种数据类型的处理情况并可视化的展示出其决策边界 仔细观察会发现 无论是分类决策树还是回归决策树 其决策边界都喜欢垂直于X轴或Y轴 这使得他们对训练集数据的旋转产生的变化特别敏感 一个具体的例子

np.random.seed(6)
Xs   np.random.rand(100, 2) - 0.5
ys   (Xs[:, 0]   0).astype(np.float32) * 2
angle   np.pi / 4
rotation_matrix   np.array([[np.cos(angle), -np.sin(angle)], [np.sin(angle), np.cos(angle)]])
Xsr   Xs.dot(rotation_matrix)
tree_clf_s   DecisionTreeClassifier(random_state 42)
tree_clf_s.fit(Xs, ys)
tree_clf_sr   DecisionTreeClassifier(random_state 42)
tree_clf_sr.fit(Xsr, ys)
fig, axes   plt.subplots(ncols 2, figsize (10, 4), sharey True)
plt.sca(axes[0])
plot_decision_boundary(tree_clf_s, Xs, ys, axes [-0.7, 0.7, -0.7, 0.7], iris False)
plt.sca(axes[1])
plot_decision_boundary(tree_clf_sr, Xsr, ys, axes [-0.7, 0.7, -0.7, 0.7], iris False)
plt.ylabel( )
plt.show()

从上图可以看出 左边的图 决策树使用一条线就将数据做好了分类 但我们将数据旋转一下 获得右边的图 再使用决策树去处理 会发现 决策树需要绘制多条线才能将数据做好分类 即右边数据训练出的决策树模型 可能无法很好的泛化。

更概括的说 决策树对训练集中微小的数据变化都非常敏感 一个具体例子 如果我们直接使用鸢尾花数据集进行可视化 效果如图 前面展示过

但我们使用相同的数据、类似的代码 会得到与上图大为不同的效果

iris   load_iris()
X   iris.data[:, 2:] # petal length and width
y   iris.target
# 构建决策树时 使用了不同的random_state
tree_clf_tweaked   DecisionTreeClassifier(max_depth 2, random_state 40)
tree_clf_tweaked.fit(X, y)
plt.figure(figsize (8, 4))
plot_decision_boundary(tree_clf_tweaked, X, y, legend False)
plt.plot([0, 7.5], [0.8, 0.8],  k- , linewidth 2)
plt.plot([0, 7.5], [1.75, 1.75],  k-- , linewidth 2)
plt.text(1.0, 0.9,  Depth 0 , fontsize 15)
plt.text(1.0, 1.80,  Depth 1 , fontsize 13)
plt.show()

上述代码可视化的效果如下

从上图可知 即便是相同的训练数据上 如果random_state不同 sklearn选择特征集的算法是随机的 通过random_state参数控制 获得的决策树模型也完全不同了。

随机森林可以通过对多个树进行平均预测来限制这种不稳定性 关于随机森林的内容 我们后面的文章会讨论。

本文涉及代码已提交到 https://github.com/ayuLiao/machine_learning_interstellar_journey 项目中。

我是二两 下篇文章见。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/267116.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号