注意
跳转至页面底部下载完整示例代码,或通过 JupyterLite 或 Binder 在浏览器中运行此示例。
通过代价复杂度剪枝进行决策树后剪枝#
DecisionTreeClassifier 提供了诸如 min_samples_leaf 和 max_depth 等参数来防止树过拟合。代价复杂度剪枝(Cost complexity pruning)为控制树的大小提供了另一种选择。在 DecisionTreeClassifier 中,该剪枝技术由代价复杂度参数 ccp_alpha 进行参数化。较大的 ccp_alpha 值会增加被剪枝节点的数量。在此,我们仅展示 ccp_alpha 对树进行正则化的效果,以及如何根据验证得分来选择 ccp_alpha。
有关剪枝的详细信息,请参阅 最小代价复杂度剪枝。
# Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause
import matplotlib.pyplot as plt
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
叶节点的总杂质度与剪枝树的有效 alpha 值之关系#
最小代价复杂度剪枝通过递归方式寻找“最弱链接”节点。最弱链接由有效 alpha 值(effective alpha)表征,其中有效 alpha 最小的节点会被优先剪枝。为了了解哪些 ccp_alpha 值是合适的,scikit-learn 提供了 DecisionTreeClassifier.cost_complexity_pruning_path,它返回剪枝过程中每一步的有效 alpha 值以及相应的总叶节点杂质度。随着 alpha 的增加,树被剪掉的部分越多,从而增加了叶节点的总杂质度。
X, y = load_breast_cancer(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
clf = DecisionTreeClassifier(random_state=0)
path = clf.cost_complexity_pruning_path(X_train, y_train)
ccp_alphas, impurities = path.ccp_alphas, path.impurities
在下图中,移除了最大的有效 alpha 值,因为它对应的是仅有一个节点的平凡树。
fig, ax = plt.subplots()
ax.plot(ccp_alphas[:-1], impurities[:-1], marker="o", drawstyle="steps-post")
ax.set_xlabel("effective alpha")
ax.set_ylabel("total impurity of leaves")
ax.set_title("Total Impurity vs effective alpha for training set")

Text(0.5, 1.0, 'Total Impurity vs effective alpha for training set')
接下来,我们使用这些有效 alpha 值训练决策树。ccp_alphas 中的最后一个值是剪掉整棵树的 alpha 值,使树 clfs[-1] 仅剩一个节点。
clfs = []
for ccp_alpha in ccp_alphas:
clf = DecisionTreeClassifier(random_state=0, ccp_alpha=ccp_alpha)
clf.fit(X_train, y_train)
clfs.append(clf)
print(
"Number of nodes in the last tree is: {} with ccp_alpha: {}".format(
clfs[-1].tree_.node_count, ccp_alphas[-1]
)
)
Number of nodes in the last tree is: 1 with ccp_alpha: 0.3272984419327777
在本示例的剩余部分,我们移除了 clfs 和 ccp_alphas 中的最后一个元素,因为它代表的是仅有一个节点的平凡树。在此我们展示了随着 alpha 的增加,树的节点数和树深度是如何减小的。
clfs = clfs[:-1]
ccp_alphas = ccp_alphas[:-1]
node_counts = [clf.tree_.node_count for clf in clfs]
depth = [clf.tree_.max_depth for clf in clfs]
fig, ax = plt.subplots(2, 1)
ax[0].plot(ccp_alphas, node_counts, marker="o", drawstyle="steps-post")
ax[0].set_xlabel("alpha")
ax[0].set_ylabel("number of nodes")
ax[0].set_title("Number of nodes vs alpha")
ax[1].plot(ccp_alphas, depth, marker="o", drawstyle="steps-post")
ax[1].set_xlabel("alpha")
ax[1].set_ylabel("depth of tree")
ax[1].set_title("Depth vs alpha")
fig.tight_layout()

训练集和测试集的准确率与 alpha 的关系#
当 ccp_alpha 设置为 0 并保持 DecisionTreeClassifier 的其他默认参数时,树会发生过拟合,导致 100% 的训练准确率和 88% 的测试准确率。随着 alpha 的增加,树被剪枝的程度提高,从而生成了一棵泛化能力更好的决策树。在此示例中,设置 ccp_alpha=0.015 可使测试准确率最大化。
train_scores = [clf.score(X_train, y_train) for clf in clfs]
test_scores = [clf.score(X_test, y_test) for clf in clfs]
fig, ax = plt.subplots()
ax.set_xlabel("alpha")
ax.set_ylabel("accuracy")
ax.set_title("Accuracy vs alpha for training and testing sets")
ax.plot(ccp_alphas, train_scores, marker="o", label="train", drawstyle="steps-post")
ax.plot(ccp_alphas, test_scores, marker="o", label="test", drawstyle="steps-post")
ax.legend()
plt.show()

脚本总运行时间:(0 分 0.398 秒)
相关示例