使用 PyTorch Tabular 解决任意表格问题
级别: 初学者
在本教程中,我们将探讨如何使用PyTorch Tabular解决任何表格机器学习问题(分类或回归)。我们将使用来自UCI存储库的Covertype数据集。该数据集包含581012行和54列。该数据集是一个多类分类问题。我们的目标是仅通过制图变量预测森林覆盖类型(不使用遥感数据)。
在典型的机器学习工作流程中,我们通常会执行以下步骤:
1. 加载数据集
2. 分析数据集
3. 将数据集分为训练集和测试集
4. 预处理数据集
5. 定义模型
6. 训练模型
7. 对新数据进行预测
8. 评估模型
让我们看看如何使用PyTorch Tabular执行这些步骤。
步骤 1:加载数据¶
覆盖类型数据集¶
仅通过制图变量预测森林覆盖类型(不使用遥感数据)。给定观察(30 x 30 米单元格)的实际森林覆盖类型是通过美国森林服务局(USFS)区域2资源信息系统(RIS)数据确定的。自变量来源于最初从美国地质调查局(USGS)和USFS数据中获得的数据。数据是原始形式(未缩放),并包含二进制(0或1)列的数据,用于定性自变量(荒野地区和土壤类型)。
该研究区域包括位于科罗拉多州北部罗斯福国家森林的四个荒野区域。这些区域代表了人类造成的干扰最小的森林,因此现有的森林覆盖类型更多是生态过程的结果,而不是森林管理实践的结果。
在 PyTorch Tabular
中有一个简单的实用方法来加载这个特定的数据集。它从 UCI ML Repository 下载数据。原始数据集有两个分类信息 - 土壤类型和荒野区域 - 但进行了独热编码。这个实用方法将它们转换为分类列,使其更接近于真实生活中的数据集。
步骤 2:分析数据集¶
在这一步中,我们将探索数据以更好地理解数据。探索性数据分析(EDA)可以涉及许多内容,这取决于数据和我们试图解决的问题。这可以帮助我们更好地理解数据,并做出一些关于如何处理数据的决策。但在这里,我们将限制在最基本的数据分析;足以了解哪些是连续列,哪些是分类列,以及是否存在任何缺失值。
但这可能并不总是可靠的。例如,如果我们有一个名为 month
的列,并且它的值从 1 到 12,那么它就是一个分类列。但 select_dtypes
会将其视为连续列。因此,我们需要谨慎并使用我们的判断。
但这也不可靠。例如,我们有一个名为 Soil_Type
的列,它有 40 个唯一值。我们如何决定它是一个分类列还是一个连续列?我们在这里也需要使用我们的判断。
通过阅读数据描述、理解领域知识并使用我们的判断,是决定一个列是分类的还是连续的最佳方法。
在这里,我们将 Wilderness_Area
和 Soil_Type
视为分类特征。我们知道 Cover_Type
是目标列,这使得其余的列都是连续特征。
# This separation have already been done for you while loading this particular dataset from `PyTorch Tabular`. Let's load the dataset in the right way.
data, cat_col_names, num_col_names, target_col = load_covertype_dataset()
# Let's also print out a few details
print(f"Data Shape: {data.shape} | # of cat cols: {len(cat_col_names)} | # of num cols: {len(num_col_names)}")
print(f"[bold dodger_blue2] Features: {num_col_names + cat_col_names}[/bold dodger_blue2]")
print(f"[bold purple4]Target: {target_col}[/bold purple4]")
好消息!数据集中没有缺失值。如果有缺失值,我们需要处理它们。Kaggle 有一个关于如何处理缺失值的好教程。你可以在这里找到它。
步骤 3 - 将数据集分割为训练集和测试集¶
现在,在所有表格问题中,当我们应用机器学习时,需要有一个训练集、验证集和测试集。我们将使用训练集来训练模型,验证集来做建模决策(例如超参数、使用的模型类型等),测试集用来评估最终模型。由于数据集没有提供测试集,我们将把训练集分割为训练集、验证集和测试集。
第4步:预处理数据集¶
在典型的机器学习项目中,这是最耗时的一步,我们在此过程中创建新特征、清理数据、处理缺失值、处理异常值、缩放数据、编码分类特征等等。
在基于scikit-learn的项目中,这一步的伪代码看起来像这样:
data = create_new_features(data)
data = clean_data(data)
data = handle_missing_values(data)
data = handle_outliers(data)
data, cat_encoder = encode_categorical_features(data)
data, scaler = scale_data(data)
X, y = split_features_target(data)
但深度学习的一个吸引力在于我们不需要花时间进行特征工程。我们可以直接使用原始数据,让模型自己找到最佳特征。但是我们仍然需要进行一些数据准备。为此,PyTorch Tabular 处理了一些这些需求:
- 分类特征中的缺失值被原生处理
- 分类特征使用嵌入自动编码
- 连续特征使用 StandardScaler 自动缩放
- 自动提取日期特征,如月份、日期和年份
- 可以通过参数启用目标变换,如对数、幂、分位数、box-cox。这也会自动处理反变换。
- 连续特征可以使用 box-cox、分位数正态等进行变换,通过参数设置
虽然我们有这些功能,但我们也可以选择手动进行其中任何步骤。例如,我们可以选择使用独热编码或目标编码对分类特征进行编码,并将其视为连续特征。我们还可以选择使用 MinMaxScaler 或 RobustScaler 对连续特征进行缩放,并关闭自动缩放。
因此,在这里,我们不会执行这些操作。我们将直接使用数据,并让 PyTorch Tabular 处理其余部分。
第5步:定义模型¶
现在,我们将定义模型。在 scikit-learn
工作流中,我们将执行以下步骤:
这就是 PyTorch Tabular 与其他不同的地方,因为我们需要在定义模型之前定义一些配置。原因之一是 PyTorch Tabular 模型在底层处理了很多事情。因此,我们需要告诉模型我们正在处理什么样的数据。我们还需要定义训练动态,以及模型参数。我们需要定义的配置有:
-
DataConfig
- 在这里我们定义与数据相关的配置,如目标列、类别列、连续列、日期列、类别嵌入维度等。但好消息是大多数都是可选的。如果我们不定义它们,PyTorch Tabular 将尝试从数据中推断这些信息,或者有一些处理的经验规则。我们需要定义的最基本内容是目标列名称、连续列和类别列。类别列默认会嵌入,数值列默认会缩放,日期列默认会提取。 -
TrainerConfig
- 在这里我们定义与训练相关的配置,如批量大小、训练轮数、提前停止等。同样,所有这些都是可选的。如果我们不定义它们,PyTorch Tabular 将使用一些默认值。默认情况下,PyTorch Tabular
以批量大小为 64 运行,使用 3 轮的提前停止和启用检查点保存。这意味着模型将在每个训练周期结束时保存,最好模型将被保存。如果验证损失在 3 轮内没有改善,模型将停止训练。尽管TrainerConfig
的所有内容都是可选的,但它具有无限的可定制性。整个 PyTorch LightningTrainer
都可以通过TrainerConfig
中的显式参数或通过TrainerConfig
中的一个通用trainer_kwargs
参数进行访问。 -
OptimizerConfig
- 在这里我们定义与优化器相关的配置,如优化器类型、权重衰减、学习率调度器等。同样,所有这些都是可选的。如果我们不定义它们,PyTorch Tabular 将使用一些默认值。默认情况下,PyTorch Tabular
使用Adam
优化器。它默认不使用任何学习率衰减。尽管OptimizerConfig
的所有内容都是可选的,但它也是可定制的。 -
ExperimentConfig
- 在这里我们定义如何跟踪实验以进行记录和可重复性。默认情况下,PyTorch Tabular
使用tensorboard
进行日志记录。但我们也可以使用wandb
。我们还可以选择不记录任何内容(虽然不推荐),通过不定义ExperimentConfig
。 -
<modelspecificconfig>
- 在这里我们定义要使用的模型及相应的超参数。在PyTorch Tabular
中,每个实施的模型都有自己的配置类。例如,如果我们想使用TabNet
,我们需要定义TabNetConfig
。如果我们想使用GANDALF
,我们需要定义GANDALFConfig
,依此类推。这些配置类各自有自己的一组模型特定超参数,以及一些公共参数,如损失函数、指标、学习率等。同样,所有这些都是可选的。如果我们不定义它们,PyTorch Tabular 将使用一些默认值。学习率默认设置为 1e-3。分类的损失函数设置为CrossEntropyLoss
,回归的损失函数设置为MSELoss
。分类的指标设置为Accuracy
,回归的指标设置为MSE
。所有模型特定的超参数都被设置为其各自论文中建议的默认值,或在实践中效果良好的默认值。
在这里,我们将使用 GANDALF 模型。我们将按如下方式定义配置:
from pytorch_tabular.models import GANDALFConfig
from pytorch_tabular.config import (
DataConfig,
OptimizerConfig,
TrainerConfig,
)
data_config = DataConfig(
target=[
target_col
], # 目标应始终为一个列表
continuous_cols=num_col_names,
categorical_cols=cat_col_names,
)
trainer_config = TrainerConfig(
batch_size=1024,
max_epochs=100,
)
optimizer_config = OptimizerConfig()
model_config = GANDALFConfig(
task="classification",
gflu_stages=6,
gflu_feature_init_sparsity=0.3,
gflu_dropout=0.0,
learning_rate=1e-3,
)
现在我们已经定义了所有的配置,我们可以定义 TabularModel
。除了配置之外,还有一些额外的参数可以传递给模型,以控制模型的详细程度。
我们可以看到,由于我们传递了 verbose=True
,它已经记录了实验跟踪已被禁用。
第6步:训练模型¶
现在,我们可以训练模型。在 scikit-learn
工作流程中,我们会执行如下操作:
在 PyTorch Tabular 中,我们可以采用两种方式来实现这一点:
- 高级API - 一个 fit
方法,它与 scikit-learn API 非常相似,但 fit
方法有很多额外的参数来控制训练动态。这是推荐的模型训练方式。
- 低级API - 一组方法 - prepare_dataloader
、prepare_model
和 train
。这是为希望对训练过程有更多控制的高级用户准备的。
在本入门教程中,我们将坚持使用高级API。我们将使用 fit
方法来训练模型。fit
方法只有一个必需的参数 - train
数据。我们还可以明确传递 validation
数据。如果没有提供,那么它将使用 20% 的训练数据作为验证数据。除此之外,还有许多其他参数,如自定义损失函数、指标、自定义优化器等,可以用于使训练过程更具可定制性。
步骤 7:对新数据进行预测¶
现在我们已经训练好了模型,可以对新数据进行预测。在 scikit-learn
工作流程中,我们通常会执行以下操作:
在 PyTorch Tabular 中,我们可以做类似的操作。我们可以使用 predict
方法对新数据进行预测。该方法返回一个 pandas 数据框格式的预测结果。对于分类问题,它返回类别概率和基于 0.5 阈值的最终预测类别。我们需要做的就是传入一个至少包含所有用于训练的特征的数据框。
第8步:评估模型¶
现在,我们可以评估模型。在 scikit-learn
工作流程中,我们会执行以下操作:
在 PyTorch Tabular 中,我们可以通过两种方式来完成这项工作:
- 在测试集上获得预测并手动计算指标
- 使用 evaluate
方法,它将返回指标(我们在训练期间定义的相同指标)
我们将在这里看到第二种方法。我们可以使用 evaluate
方法在测试集上评估模型。此方法返回一个指标字典。
第9步:保存和加载模型¶
在模型训练完成后,我们可以保存模型并在稍后加载它以对新数据进行预测。在 scikit-learn
的工作流程中,我们通常会执行以下操作:
在 PyTorch Tabular 中,我们可以做类似的事情。我们可以使用 save_model
方法来保存模型。该方法保存了进行新数据预测所需的所有内容。默认情况下,它还保存了数据模块,其中包含训练数据、验证数据和测试数据。但我们可以通过设置 inference_only=True
选择不保存数据模块。
现在我们可以使用 load_model
方法加载保存的模型。这个方法返回模型和数据模块。我们可以使用模型对新数据进行预测。
- 查看 PyTorch Tabular 文档 以了解更多关于该库的信息
- 使用其他模型,如 TabNet,CategoryEmbedding 等。
- 使用不同的数据集并尝试工作流程。
- 查看文档中的其他教程和操作指南。