创建决策树

在本单元,您将使用 YDF (Yggdrasil Decision Forest) 库训练并解读决策树。

本单元的灵感来自 🧭? YDF 使用入门教程。

预备知识

在研究数据集之前,请执行以下操作:

  1. 创建一个新的 Colab 笔记本
  2. 在新的 Colab 笔记本中放置并执行以下代码行,以安装 YDF 库:
    !pip install ydf -U
    
  3. 导入以下库:
    import ydf
    import numpy as np
    import pandas as pd
    

帕默企鹅数据集

此 Colab 使用 帕尔默企鹅数据集,其中包含三种企鹅物种的尺寸测量值:

  • 肩带
  • 巴布亚
  • 阿德利

这是一个分类问题 - 目标是根据 Palmer's Penguins 数据集中的数据预测企鹅的种类。以下是企鹅:

三种不同的企鹅。

图 16. 三种不同的企鹅。图片提供者:@allisonhorst

 

以下代码会调用 Pandas 函数,以将 Palmer Penguins 数据集加载到内存中:

path = "https://storage.googleapis.com/download.tensorflow.org/data/palmer_penguins/penguins.csv"
dataset = pd.read_csv(path)
label = "species"

# Display the first 3 examples.
dataset.head(3)

下表将设置 Palmer Penguins 数据集中前 3 个样本的格式:

表 3. 帕默企鹅的前 3 个样本

物种 海岛 bill_length_mm bill_depth_mm flipper_length_mm body_mass_g 性别
数字 0 阿德利 托格森 39.1 岁 土耳其里拉 181.0 3,750.0 男性 2007 年
1 阿德利 托格森 39.5 岁 17.4 岁 186.0 3,800.0 女性 2007 年
2 阿德利 托格森 土耳其里拉 土耳其里拉 195.0 3,250.0 女性 2007 年

完整数据集包含数值特征(例如 bill_depth_mm)、分类特征(例如 island)和缺失特征。与神经网络不同,决策森林本身支持所有这些功能类型,因此您不必执行独热编码、归一化或额外 is_present 特征。

以下代码单元会将数据集拆分为一个训练集和测试集

# Use the ~20% of the examples as the testing set
# and the remaining ~80% of the examples as the training set.
np.random.seed(1)
is_test = np.random.rand(len(dataset)) < 0.2

train_dataset = dataset[~is_test]
test_dataset = dataset[is_test]

print("Training examples: ", len(train_dataset))
# >> Training examples: 272

print("Testing examples: ", len(test_dataset))
# >> Testing examples: 72

使用默认超参数训练决策树

您可以使用 CART(分类和回归树)学习算法(也称为学习器)训练第一个决策树,而无需指定任何超参数。这是因为 ydf.CartLearner 学习器提供了良好的默认超参数值。在本课程的后面部分,您将详细了解此类模型的工作原理。

model = ydf.CartLearner(label=label).train(train_dataset)

前面的调用未指定要用作输入特征的列。因此,将使用训练集内的每一列。该调用也未指定输入特征的语义(例如数值、分类、文本)。因此,系统会自动推断特征语义。

调用 model.plot_tree() 以显示生成的决策树:

model.plot_tree()

在 Colab 中,您可以使用鼠标显示特定元素的详细信息,例如每个节点中的类分布。

使用默认超参数训练的决策树。

图 17. 使用默认超参数训练的决策树。

Colab 显示,根条件包含 243 个样本。但是,您可能还记得训练数据集包含 272 个样本。剩余的 29 个样本已自动预留,用于验证和树状剪枝。

第一个条件测试 bill_depth_mm 的值。表 4 和表 5 显示了根据第一种条件的结果,不同物种的可能性。

表 4. 如果 bill_depth_mm ≥ 42.3,则不同物种的可能性

种类 可能性
Adelie(红色) 8%
巴布亚风格(蓝色) 58%
肩带(绿色) 36%

 

表 5. 如果 bill_depth_mm < 42.3,则不同物种的可能性

品种 可能性
Adelie(红色) 97%
巴布亚风格(蓝色) 2%
肩带(绿色) 0%

bill_depth_mm 是数值特征。因此,我们使用通过数值特征进行二元分类的精确拆分算法找到了值 42.3。

如果 bill_depth_mm ≥ 42.3 为 True,则进一步测试 flipper_length_mm ≥ 207.5 是否可以几乎完全区分 Gentoos 和 Gentoos+Adelie。

以下代码提供了模型的训练和测试准确率:

train_evaluation = model.evaluate(train_dataset)
print("train accuracy:", train_evaluation.accuracy)
# >> train accuracy:  0.9338

test_evaluation = model.evaluate(test_dataset)
print("test accuracy:", test_evaluation.accuracy)
# >> test accuracy:  0.9167

测试准确率高于训练准确率这种情况很少见,但有可能。在这种情况下,测试集可能与训练集不同。 不过,情况并非如此,因为训练和测试是随机拆分的。更可能的原因是测试数据集非常小(只有 72 个样本),因此准确率估算很嘈杂。

在实践中,对于这种小型数据集,最好使用交叉验证,因为它可以计算出更准确的评估指标值。不过,在此示例中,为简单起见,我们继续进行训练和测试。

改进模型超参数

该模型是使用默认超参数值训练的单个决策树。为了获得更好的预测结果,您可以:

  1. 使用更强大的学习器,如随机森林梯度提升树模型。下一页将介绍这些学习算法。

  2. 使用您的观察和直觉来优化超参数。模型改进指南可能会对您有所帮助。

  3. 使用超参数调节自动测试大量可能的超参数。

由于我们尚未看到随机森林和梯度提升树算法,而且样本数量太少,无法进行自动超参数调节,因此您需要手动改进模型。

上面显示的决策树很小,而包含 61 个样本的叶子又包含 Adelie 和 Chinstrap 标签。为什么算法没有进一步划分这片树叶?造成这种情况的原因可能有两种:

  • 可能已达到每叶样本数下限(默认为 min_examples=5)。
  • 为了防止出现过拟合,为防止出现过拟合,为了避免出现模型过拟合的情况,为避免了过度拟合,树可能进行了拆分,然后再进行剪枝。

将最小示例数减少到 1,看看结果:

model = ydf.CartLearner(label=label, min_examples=1).train(train_dataset)
model.plot_tree()

使用 min_examples=1 进行训练的决策树。

图 18. 使用 min_examples=1 进行训练的决策树。

 

包含 61 个样本的叶节点经过了多次进一步划分。

为了确认进一步划分节点是否有价值,我们会在测试数据集上评估这个新模型的质量:

print(model.evaluate(test_dataset).accuracy)
# >> 0.97222

模型质量有所提高,测试准确率从 0.9167 提高至 0.97222。这种超参数更改是个好主意。

决策森林的上一个

通过继续改进超参数,我们有可能达到完美的准确率。不过,我们可以训练一个更强大的模型(例如随机森林),看看模型是否效果更好,而不是这种手动过程。

model = ydf.RandomForestLearner(label=label).train(pandas_train_dataset)
print("Test accuracy: ", model.evaluate(pandas_test_dataset).accuracy)
# >> Test accuracy: 0.986111

随机森林的准确率优于简单树。在接下来的几页中,您将了解原因。

使用和限制

如前所述,单个决策树的质量通常低于随机森林、梯度提升树和神经网络等现代机器学习方法。不过,决策树在下列情况下仍然有用:

  • 作为评估复杂方法的简单且实惠的基准。
  • 在模型质量与可解释性之间进行权衡取舍时。
  • 用作解释决策森林模型的代理,本课程稍后将进行介绍。