Skip to content

使用 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 下载数据。原始数据集有两个分类信息 - 土壤类型和荒野区域 - 但进行了独热编码。这个实用方法将它们转换为分类列,使其更接近于真实生活中的数据集。

from pytorch_tabular.utils import load_covertype_dataset
data, _, _, _ = load_covertype_dataset()

步骤 2:分析数据集

在这一步中,我们将探索数据以更好地理解数据。探索性数据分析(EDA)可以涉及许多内容,这取决于数据和我们试图解决的问题。这可以帮助我们更好地理解数据,并做出一些关于如何处理数据的决策。但在这里,我们将限制在最基本的数据分析;足以了解哪些是连续列,哪些是分类列,以及是否存在任何缺失值。

from rich import print
# 识别分类特征最简单的方法之一是使用 pandas 的 select_dtypes 函数。
categorical_features = data.select_dtypes(include=['object'])
print(categorical_features.columns)
Index(['Wilderness_Area', 'Soil_Type'], dtype='object')

但这可能并不总是可靠的。例如,如果我们有一个名为 month 的列,并且它的值从 1 到 12,那么它就是一个分类列。但 select_dtypes 会将其视为连续列。因此,我们需要谨慎并使用我们的判断。

# 另一种方法则是利用每列中的唯一值。
for col in data.columns:
    print(col, len(data[col].unique()))
Elevation 1978
Aspect 361
Slope 67
Horizontal_Distance_To_Hydrology 551
Vertical_Distance_To_Hydrology 700
Horizontal_Distance_To_Roadways 5785
Hillshade_9am 207
Hillshade_Noon 185
Hillshade_3pm 255
Horizontal_Distance_To_Fire_Points 5827
Cover_Type 7
Wilderness_Area 4
Soil_Type 40

但这也不可靠。例如,我们有一个名为 Soil_Type 的列,它有 40 个唯一值。我们如何决定它是一个分类列还是一个连续列?我们在这里也需要使用我们的判断。

通过阅读数据描述、理解领域知识并使用我们的判断,是决定一个列是分类的还是连续的最佳方法。

在这里,我们将 Wilderness_AreaSoil_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]")
Data Shape: (581012, 13) | # of cat cols: 2 | # of num cols: 10
 Features: ['Elevation', 'Aspect', 'Slope', 'Horizontal_Distance_To_Hydrology', 'Vertical_Distance_To_Hydrology', 
'Horizontal_Distance_To_Roadways', 'Hillshade_9am', 'Hillshade_Noon', 'Hillshade_3pm', 
'Horizontal_Distance_To_Fire_Points', 'Wilderness_Area', 'Soil_Type']
Target: Cover_Type
注意 监督学习归结为寻找一个将输入映射到输出的函数。输入称为特征,输出称为目标。特征可以是连续的或分类的。目标可以是连续的或分类的。在分类中,目标是分类的。在回归中,目标是连续的。
# 我们也来检查一下数据是否有缺失值。
print(data.isna().sum())
Elevation                             0
Aspect                                0
Slope                                 0
Horizontal_Distance_To_Hydrology      0
Vertical_Distance_To_Hydrology        0
Horizontal_Distance_To_Roadways       0
Hillshade_9am                         0
Hillshade_Noon                        0
Hillshade_3pm                         0
Horizontal_Distance_To_Fire_Points    0
Cover_Type                            0
Wilderness_Area                       0
Soil_Type                             0
dtype: int64

好消息!数据集中没有缺失值。如果有缺失值,我们需要处理它们。Kaggle 有一个关于如何处理缺失值的好教程。你可以在这里找到它。

注意 PyTorch Tabular 可以原生处理分类特征中的缺失值,但数值特征中的缺失值需要单独处理。

步骤 3 - 将数据集分割为训练集和测试集

现在,在所有表格问题中,当我们应用机器学习时,需要有一个训练集、验证集和测试集。我们将使用训练集来训练模型,验证集来做建模决策(例如超参数、使用的模型类型等),测试集用来评估最终模型。由于数据集没有提供测试集,我们将把训练集分割为训练集、验证集和测试集。

from sklearn.model_selection import train_test_split
train, test = train_test_split(data, random_state=42, test_size=0.2)
train, val = train_test_split(train, random_state=42, test_size=0.2)
print(f"Train Shape: {train.shape} | Val Shape: {val.shape} | Test Shape: {test.shape}")
Train Shape: (371847, 13) | Val Shape: (92962, 13) | Test Shape: (116203, 13)

第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 工作流中,我们将执行以下步骤:

model = SomeModel(**parameters)

这就是 PyTorch Tabular 与其他不同的地方,因为我们需要在定义模型之前定义一些配置。原因之一是 PyTorch Tabular 模型在底层处理了很多事情。因此,我们需要告诉模型我们正在处理什么样的数据。我们还需要定义训练动态,以及模型参数。我们需要定义的配置有:

  1. DataConfig - 在这里我们定义与数据相关的配置,如目标列、类别列、连续列、日期列、类别嵌入维度等。但好消息是大多数都是可选的。如果我们不定义它们,PyTorch Tabular 将尝试从数据中推断这些信息,或者有一些处理的经验规则。我们需要定义的最基本内容是目标列名称、连续列和类别列。类别列默认会嵌入,数值列默认会缩放,日期列默认会提取。

  2. TrainerConfig - 在这里我们定义与训练相关的配置,如批量大小、训练轮数、提前停止等。同样,所有这些都是可选的。如果我们不定义它们,PyTorch Tabular 将使用一些默认值。默认情况下,PyTorch Tabular 以批量大小为 64 运行,使用 3 轮的提前停止和启用检查点保存。这意味着模型将在每个训练周期结束时保存,最好模型将被保存。如果验证损失在 3 轮内没有改善,模型将停止训练。尽管 TrainerConfig 的所有内容都是可选的,但它具有无限的可定制性。整个 PyTorch Lightning Trainer 都可以通过 TrainerConfig 中的显式参数或通过 TrainerConfig 中的一个通用 trainer_kwargs 参数进行访问。

  3. OptimizerConfig - 在这里我们定义与优化器相关的配置,如优化器类型、权重衰减、学习率调度器等。同样,所有这些都是可选的。如果我们不定义它们,PyTorch Tabular 将使用一些默认值。默认情况下,PyTorch Tabular 使用 Adam 优化器。它默认不使用任何学习率衰减。尽管 OptimizerConfig 的所有内容都是可选的,但它也是可定制的。

  4. ExperimentConfig - 在这里我们定义如何跟踪实验以进行记录和可重复性。默认情况下,PyTorch Tabular 使用 tensorboard 进行日志记录。但我们也可以使用 wandb。我们还可以选择不记录任何内容(虽然不推荐),通过不定义 ExperimentConfig

  5. <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。除了配置之外,还有一些额外的参数可以传递给模型,以控制模型的详细程度。

from pytorch_tabular import TabularModel

tabular_model = TabularModel(
    data_config=data_config,
    model_config=model_config,
    optimizer_config=optimizer_config,
    trainer_config=trainer_config,
    verbose=True
)
2024-01-07 04:39:30,992 - {pytorch_tabular.tabular_model:140} - INFO - Experiment Tracking is turned off           

我们可以看到,由于我们传递了 verbose=True,它已经记录了实验跟踪已被禁用。

第6步:训练模型

现在,我们可以训练模型。在 scikit-learn 工作流程中,我们会执行如下操作:

model.fit(X_train, y_train)

在 PyTorch Tabular 中,我们可以采用两种方式来实现这一点: - 高级API - 一个 fit 方法,它与 scikit-learn API 非常相似,但 fit 方法有很多额外的参数来控制训练动态。这是推荐的模型训练方式。 - 低级API - 一组方法 - prepare_dataloaderprepare_modeltrain。这是为希望对训练过程有更多控制的高级用户准备的。

在本入门教程中,我们将坚持使用高级API。我们将使用 fit 方法来训练模型。fit 方法只有一个必需的参数 - train 数据。我们还可以明确传递 validation 数据。如果没有提供,那么它将使用 20% 的训练数据作为验证数据。除此之外,还有许多其他参数,如自定义损失函数、指标、自定义优化器等,可以用于使训练过程更具可定制性。

tabular_model.fit(train=train, validation=val)
Seed set to 42

2024-01-07 04:39:31,059 - {pytorch_tabular.tabular_model:524} - INFO - Preparing the DataLoaders                   
2024-01-07 04:39:31,500 - {pytorch_tabular.tabular_datamodule:499} - INFO - Setting up the datamodule for          
classification task                                                                                                
2024-01-07 04:39:32,358 - {pytorch_tabular.tabular_model:574} - INFO - Preparing the Model: GANDALFModel           
2024-01-07 04:39:32,591 - {pytorch_tabular.tabular_model:340} - INFO - Preparing the Trainer                       
GPU available: True (cuda), used: True
TPU available: False, using: 0 TPU cores
IPU available: False, using: 0 IPUs
HPU available: False, using: 0 HPUs

2024-01-07 04:39:32,839 - {pytorch_tabular.tabular_model:630} - INFO - Auto LR Find Started                        
You are using a CUDA device ('NVIDIA GeForce RTX 3060 Laptop GPU') that has Tensor Cores. To properly utilize them, you should set `torch.set_float32_matmul_precision('medium' | 'high')` which will trade-off precision for performance. For more details, read https://pytorch.org/docs/stable/generated/torch.set_float32_matmul_precision.html#torch.set_float32_matmul_precision
/home/manujosephv/miniconda3/envs/lightning_upgrade/lib/python3.11/site-packages/pytorch_lightning/callbacks/model_checkpoint.py:639: Checkpoint directory saved_models exists and is not empty.
LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0]
/home/manujosephv/miniconda3/envs/lightning_upgrade/lib/python3.11/site-packages/pytorch_lightning/trainer/connectors/data_connector.py:441: The 'train_dataloader' does not have many workers which may be a bottleneck. Consider increasing the value of the `num_workers` argument` to `num_workers=19` in the `DataLoader` to improve performance.
/home/manujosephv/miniconda3/envs/lightning_upgrade/lib/python3.11/site-packages/pytorch_lightning/trainer/connectors/data_connector.py:441: The 'val_dataloader' does not have many workers which may be a bottleneck. Consider increasing the value of the `num_workers` argument` to `num_workers=19` in the `DataLoader` to improve performance.

Finding best initial lr:   0%|          | 0/100 [00:00<?, ?it/s]
`Trainer.fit` stopped: `max_steps=100` reached.
Learning rate set to 0.02089296130854041
Restoring states from the checkpoint path at /home/manujosephv/pytorch_tabular/docs/tutorials/.lr_find_6d1c6109-882a-4b7f-939c-2d42ecd8ff06.ckpt
Restored all states from the checkpoint at /home/manujosephv/pytorch_tabular/docs/tutorials/.lr_find_6d1c6109-882a-4b7f-939c-2d42ecd8ff06.ckpt

2024-01-07 04:39:37,498 - {pytorch_tabular.tabular_model:643} - INFO - Suggested LR: 0.02089296130854041. For plot 
and detailed analysis, use `find_learning_rate` method.                                                            
2024-01-07 04:39:37,500 - {pytorch_tabular.tabular_model:652} - INFO - Training Started                            
LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0]

┏━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━┓
┃    Name              Type              Params ┃
┡━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━┩
│ 0 │ _backbone        │ GANDALFBackbone  │ 42.4 K │
│ 1 │ _embedding_layer │ Embedding1dLayer │    896 │
│ 2 │ _head            │ Sequential       │    252 │
│ 3 │ loss             │ CrossEntropyLoss │      0 │
└───┴──────────────────┴──────────────────┴────────┘
Trainable params: 43.6 K                                                                                           
Non-trainable params: 0                                                                                            
Total params: 43.6 K                                                                                               
Total estimated model params size (MB): 0                                                                          
Output()


2024-01-07 04:41:25,635 - {pytorch_tabular.tabular_model:663} - INFO - Training the model completed                
2024-01-07 04:41:25,636 - {pytorch_tabular.tabular_model:1487} - INFO - Loading the best model                     
<pytorch_lightning.trainer.trainer.Trainer at 0x7f1dbab82c10>

步骤 7:对新数据进行预测

现在我们已经训练好了模型,可以对新数据进行预测。在 scikit-learn 工作流程中,我们通常会执行以下操作:

y_pred = model.predict(X_test)
y_pred_proba = model.predict_proba(X_test)

在 PyTorch Tabular 中,我们可以做类似的操作。我们可以使用 predict 方法对新数据进行预测。该方法返回一个 pandas 数据框格式的预测结果。对于分类问题,它返回类别概率和基于 0.5 阈值的最终预测类别。我们需要做的就是传入一个至少包含所有用于训练的特征的数据框。

pred_df = tabular_model.predict(test)
pred_df.head()
1_probability 2_probability 3_probability 4_probability 5_probability 6_probability 7_probability prediction
250728 0.901409 0.001267 1.025811e-08 9.070018e-08 0.000040 3.519856e-08 9.728358e-02 1
246788 0.156802 0.843021 8.172029e-07 2.142834e-09 0.000171 2.749175e-07 4.734764e-06 2
407714 0.001035 0.969636 4.896594e-03 4.262948e-06 0.019907 4.521028e-03 8.038983e-07 2
25713 0.289917 0.709881 1.039616e-05 5.966012e-08 0.000152 3.714674e-05 1.749813e-06 2
21820 0.000729 0.870874 2.740137e-05 3.132881e-06 0.128357 9.515504e-06 9.939656e-09 2

第8步:评估模型

现在,我们可以评估模型。在 scikit-learn 工作流程中,我们会执行以下操作:

pred_df = model.predict(X_test)
accuracy = accuracy_score(y_test, pred_df)

在 PyTorch Tabular 中,我们可以通过两种方式来完成这项工作: - 在测试集上获得预测并手动计算指标 - 使用 evaluate 方法,它将返回指标(我们在训练期间定义的相同指标)

我们将在这里看到第二种方法。我们可以使用 evaluate 方法在测试集上评估模型。此方法返回一个指标字典。

result = tabular_model.evaluate(test)
LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0]

Output()
/home/manujosephv/miniconda3/envs/lightning_upgrade/lib/python3.11/site-packages/pytorch_lightning/trainer/connectors/data_connector.py:441: The 'test_dataloader' does not have many workers which may be a bottleneck. Consider increasing the value of the `num_workers` argument` to `num_workers=19` in the `DataLoader` to improve performance.

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃        Test metric               DataLoader 0        ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│       test_accuracy            0.878411054611206     │
│         test_loss             0.2998563051223755     │
└───────────────────────────┴───────────────────────────┘


result
[{'test_loss': 0.2998563051223755, 'test_accuracy': 0.878411054611206}]

第9步:保存和加载模型

在模型训练完成后,我们可以保存模型并在稍后加载它以对新数据进行预测。在 scikit-learn 的工作流程中,我们通常会执行以下操作:

joblib.dump(model, "model.joblib")
model = joblib.load("model.joblib")

在 PyTorch Tabular 中,我们可以做类似的事情。我们可以使用 save_model 方法来保存模型。该方法保存了进行新数据预测所需的所有内容。默认情况下,它还保存了数据模块,其中包含训练数据、验证数据和测试数据。但我们可以通过设置 inference_only=True 选择不保存数据模块。

tabular_model.save_model("examples/basic")
2024-01-07 04:43:51,268 - {pytorch_tabular.tabular_model:1531} - WARNING - Directory is not empty. Overwriting the 
contents.                                                                                                          

现在我们可以使用 load_model 方法加载保存的模型。这个方法返回模型和数据模块。我们可以使用模型对新数据进行预测。

loaded_model = TabularModel.load_model("examples/basic")
2024-01-07 04:43:51,948 - {pytorch_tabular.tabular_model:165} - INFO - Experiment Tracking is turned off           
2024-01-07 04:43:51,953 - {pytorch_tabular.tabular_model:340} - INFO - Preparing the Trainer                       
Trainer already configured with model summary callbacks: [<class 'pytorch_lightning.callbacks.rich_model_summary.RichModelSummary'>]. Skipping setting a default `ModelSummary` callback.
GPU available: True (cuda), used: True
TPU available: False, using: 0 TPU cores
IPU available: False, using: 0 IPUs
HPU available: False, using: 0 HPUs

# 让我们检查一下,在使用加载的模型对测试数据进行预测时,是否能得到相同的结果。
result = loaded_model.evaluate(test)
LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0]

Output()
/home/manujosephv/miniconda3/envs/lightning_upgrade/lib/python3.11/site-packages/pytorch_lightning/trainer/connectors/data_connector.py:441: The 'test_dataloader' does not have many workers which may be a bottleneck. Consider increasing the value of the `num_workers` argument` to `num_workers=19` in the `DataLoader` to improve performance.

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃        Test metric               DataLoader 0        ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│       test_accuracy            0.878411054611206     │
│         test_loss             0.2998563051223755     │
└───────────────────────────┴───────────────────────────┘


恭喜!: 你已经训练了一个最先进的深度学习模型数据。你可以尝试以下事项:
  1. 查看 PyTorch Tabular 文档 以了解更多关于该库的信息
  2. 使用其他模型,如 TabNet,CategoryEmbedding 等。
  3. 使用不同的数据集并尝试工作流程。
  4. 查看文档中的其他教程和操作指南。
现在尝试在你自己的项目和Kaggle竞赛中使用这些功能。如果你有任何问题,请随时在 GitHub 讨论区 提问。