Skip to content
import os
import random

import numpy as np
import pandas as pd
from sklearn.metrics import mean_absolute_error, mean_squared_error
from sklearn.model_selection import train_test_split

from pytorch_tabular.utils import make_mixed_dataset, print_metrics

# %加载自动重新加载扩展
# %自动重新加载 2
data, cat_col_names, num_col_names = make_mixed_dataset(
    task="regression", n_samples=10000, n_features=20, n_categories=4
)
train, test = train_test_split(data, random_state=42)
train, val = train_test_split(train, random_state=42)

导入库

from pytorch_tabular import TabularModel
from pytorch_tabular.models import CategoryEmbeddingModelConfig
from pytorch_tabular.config import (
    DataConfig,
    OptimizerConfig,
    TrainerConfig,
    ExperimentConfig,
    ModelConfig,
)
from pytorch_tabular.models import BaseModel
from pytorch_tabular.models.common.layers import Embedding1dLayer
from pytorch_tabular.models.common.heads import LinearHeadConfig

定义自定义模型

import torch
import torch.nn as nn
import torch.nn.functional as F
from omegaconf import DictConfig
from typing import Dict
from dataclasses import dataclass, field

PyTorch Tabular 非常易于扩展且可无限自定义。在 PyTorch Tabular 中实现的所有模型都继承了一个抽象类 BaseModel,它实际上是一个 PyTorchLightning 模型。

它处理所有主要功能,如解码配置参数和设置损失和指标。它还计算损失和指标,并将其反馈给进行反向传播的 PyTorch Lightning Trainer。

如果我们看一下 PyTorch Tabular 模型的结构,主要有三个组件:

  1. 嵌入层
  2. 主干
  3. 头部

嵌入层 从数据加载器中接收输入,该输入是一个包含 categoricalcontinuous 张量的字典。嵌入层将这个字典转换为一个单一的张量,正确处理分类张量。目前已经实现了两个嵌入层,EmbeddingLayer1d 和 EmbeddingLayer2d。

主干 是主要的模型架构。

头部 是线性层,它接受主干的输出并将其转换为我们所期望的输出。

为了封装和强制执行这种结构,BaseModel 要求我们定义三个 property 方法: 1. def embedding_layer(self) 2. def backbone(self) 3. def head(self)

还有另一个必须定义的方法 def _build_network(self)。我们可以使用该方法定义嵌入、主干、头部及我们需要在模型中使用的其他层或组件。

对于标准的前馈层作为头部,我们还可以使用 BaseModel 中一个称为 _get_head_from_config 的便捷方法,该方法将使用您在 ModelConfig 中设置的 headhead_config 来自动为您初始化正确的头部。

使用它的一个示例:

self._head = self._get_head_from_config()

对于标准流程,已经定义的 forward 方法就足够了。

def forward(self, x: Dict):
    x = self.embed_input(x) # 嵌入输入字典并返回一个张量
    x = self.compute_backbone(x) # 接收张量输入并进行表示学习
    return self.compute_head(x) # 将主干输出转换为所需输出,如有必要应用目标范围,并将输出打包为所需格式。
这使我们能够定义任何标准的 PyTorch 模型并将其用作 主干,然后使用 PyTorch Tabular 的其余工具来训练、评估和记录模型。

虽然这是最基本的要求,但您可以重新定义或使用任何 PyTorch Lightning 标准方法来调整您的模型和训练。这个设置的真正美在于,您需要自定义的程度完全取决于您自己。对于标准工作流,您可以最小化更改。而对于非常不寻常的模型,您可以重新定义 BaseModel 中的任何方法(只要接口保持不变)。

除了模型,您还需要定义一个配置。配置是 Python 数据类,应该继承 ModelConfig,并具有所有 ModelConfig 的默认参数。任何额外的参数应在数据类中定义。

要注意的关键事项:

  1. 所有不同配置中不同参数(如 TrainerConfig、OptimizerConfig 等)在调用 super() 之前都可在 config 中获取,在调用 super() 之后在 self.hparams 中获取。
  2. forward 方法中的输入批次是一个包含 continuouscategorical 键的字典。
  3. _build_network 方法中,保存每个您想在 forward 方法中访问的组件到 self
# 定义一个 Config 类,用于保存模型的超参数。
# 我们需要继承ModelConfig,以便使用默认参数,如学习率、head、head_config等。
# 存在于配置文件中
@dataclass
class MyAwesomeModelConfig(ModelConfig):
    use_batch_norm: bool = True


# 将核心模型定义为一个纯 PyTorch 模型
# 前向方法接收一个张量并输出一个张量。


class MyAwesomeRegressionModel(nn.Module):
    def __init__(self, hparams: DictConfig, **kwargs):
        super().__init__()
        self.hparams = (
            hparams  # 保存配置以便在模型的其他地方访问
        )
        self._build_network()

    def _build_network(self):
        # 连续和分类维度是预先计算并存储在配置(推断配置)中的
        inp_dim = self.hparams.embedded_cat_dim + self.hparams.continuous_dim
        self.linear_layer_1 = nn.Linear(inp_dim, 200)
        self.linear_layer_2 = nn.Linear(inp_dim + 200, 70)
        self.linear_layer_3 = nn.Linear(inp_dim + 70, 70)
        self.input_batch_norm = nn.BatchNorm1d(self.hparams.continuous_dim)
        if self.hparams.use_batch_norm:
            self.batch_norm_2 = nn.BatchNorm1d(inp_dim + 200)
            self.batch_norm_3 = nn.BatchNorm1d(inp_dim + 70)
        self.dropout = nn.Dropout(0.3)
        self.output_dim = 70

    def forward(self, x: torch.Tensor):
        inp = x
        x = F.relu(self.linear_layer_1(x))
        x = self.dropout(x)
        x = torch.cat([x, inp], 1)
        if self.hparams.use_batch_norm:
            x = self.batch_norm_1(x)
        x = F.relu(self.linear_layer_2(x))
        x = self.dropout(x)
        x = torch.cat([x, inp], 1)
        if self.hparams.use_batch_norm:
            x = self.batch_norm_3(x)
        x = self.linear_layer_3(x)
        return x

    # 将嵌入方案与核心模型一同打包也是良好的实践。
    # 这是为了让模型封装其关于如何嵌入输入的要求。
    # 分类和连续张量的词典,以及如何将它们组合成单个张量。
    # 我们可以使用 PyTorch Tabular 中的一个预定义嵌入层。
    # 或者定义一个自定义的嵌入层。查看 Embedding1dLayer 的实现
    # 关于如何实现嵌入层
    def _build_embedding_layer(self):
        return Embedding1dLayer(
            continuous_dim=self.hparams.continuous_dim,
            categorical_embedding_dims=self.hparams.embedding_dims,
            embedding_dropout=self.hparams.embedding_dropout,
            batch_norm_continuous_input=self.hparams.batch_norm_continuous_input,
        )


# 通过继承 BaseModel 定义 PyTorch Tabular 模型


class MyAwesomeRegressionPTModel(BaseModel):
    def __init__(self, config: DictConfig, **kwargs):
        # 在调用super()之前,保存您在_build_network中需要的任何属性。
        # 在调用 super() 之后,配置将被保存为 `hparams`
        super().__init__(config, **kwargs)

    def _build_network(self):
        # 骨干网络 - 初始化我们之前定义的 PyTorch 模型
        self._backbone = MyAwesomeRegressionModel(self.hparams)
        # 使用我们在骨干网络中定义的方法初始化嵌入层
        self._embedding_layer = self._backbone._build_embedding_layer()
        # 头部 - 我们可以使用辅助方法来初始化标准线性头部
        self.head = self._get_head_from_config()

    # 现在定义BaseModel要求你重写的方法属性。
    @property
    def backbone(self):
        return self._backbone

    @property
    def embedding_layer(self):
        return self._embedding_layer

    @property
    def head(self):
        return self._head

    # 为了实现更多自定义功能,我们可以重写forward、compute_backbone、compute_head等方法。

定义配置

在使用配置创建TabularModel对象时,有一个偏离正常的地方。之前模型是根据配置推断并自动初始化的。但在这里,我们必须使用TabularModel的model_callable参数,并传入模型类(而不是已经初始化的对象)。

data_config = DataConfig(
    target=[
        "target"
    ],  # 目标应始终是一个列表。仅对回归任务支持多目标。多任务分类功能尚未实现。
    continuous_cols=num_col_names,
    categorical_cols=cat_col_names,
)
trainer_config = TrainerConfig(
    auto_lr_find=True,  # 运行LRFinder以自动推导学习率
    batch_size=1024,
    max_epochs=100,
    accelerator="auto",  # can be 'cpu','gpu', 'tpu', or 'ipu'
    devices=-1,  # -1 表示使用所有可用资源。
)
optimizer_config = OptimizerConfig()

head_config = LinearHeadConfig(
    layers="32",  # 每层节点数
    activation="ReLU",  # 各层之间的激活
    dropout=0.1,
    initialization="kaiming",
).__dict__  # 转换为字典以传递给模型配置(OmegaConf不接受对象)

model_config = MyAwesomeModelConfig(
    task="regression",
    use_batch_norm=False,
    head="LinearHead",  # 线性磁头
    head_config=head_config,  # 线性磁头配置
    learning_rate=1e-3,
)

tabular_model = TabularModel(
    data_config=data_config,
    model_config=model_config,
    optimizer_config=optimizer_config,
    trainer_config=trainer_config,
    model_callable=MyAwesomeRegressionPTModel,  # 在使用自定义模型时,我们需要使用model_callable参数。
)
2023-12-26 16:43:07,659 - {pytorch_tabular.tabular_model:134} - INFO - Experiment Tracking is turned off           

训练模型

其余的过程是照常进行

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

2023-12-26 16:43:10,447 - {pytorch_tabular.tabular_model:506} - INFO - Preparing the DataLoaders                   
2023-12-26 16:43:10,455 - {pytorch_tabular.tabular_datamodule:431} - INFO - Setting up the datamodule for          
regression task                                                                                                    
2023-12-26 16:43:10,490 - {pytorch_tabular.tabular_model:556} - INFO - Preparing the Model: Model                  
2023-12-26 16:43:10,541 - {pytorch_tabular.tabular_model:322} - 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

2023-12-26 16:43:10,722 - {pytorch_tabular.tabular_model:612} - 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/loops/fit_loop.py:293: The number of training batches (6) is smaller than the logging interval Trainer(log_every_n_steps=50). Set a lower value for log_every_n_steps if you want to see logs for the training epoch.
/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]
LR finder stopped early after 99 steps due to diverging loss.
Learning rate set to 0.008317637711026709
Restoring states from the checkpoint path at /home/manujosephv/pytorch_tabular/docs/tutorials/.lr_find_c0a3e124-98c0-4ff0-80c4-0287c2f67b80.ckpt
Restored all states from the checkpoint at /home/manujosephv/pytorch_tabular/docs/tutorials/.lr_find_c0a3e124-98c0-4ff0-80c4-0287c2f67b80.ckpt

2023-12-26 16:43:14,083 - {pytorch_tabular.tabular_model:625} - INFO - Suggested LR: 0.008317637711026709. For plot
and detailed analysis, use `find_learning_rate` method.                                                            
2023-12-26 16:43:14,085 - {pytorch_tabular.tabular_model:634} - INFO - Training Started                            
LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0]

┏━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━┓
┃    Name              Type                      Params ┃
┡━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━┩
│ 0 │ _backbone        │ MyAwesomeRegressionModel │ 28.8 K │
│ 1 │ _embedding_layer │ Embedding1dLayer         │     92 │
│ 2 │ head             │ LinearHead               │  2.3 K │
│ 3 │ loss             │ MSELoss                  │      0 │
└───┴──────────────────┴──────────────────────────┴────────┘
Trainable params: 31.2 K                                                                                           
Non-trainable params: 0                                                                                            
Total params: 31.2 K                                                                                               
Total estimated model params size (MB): 0                                                                          
Output()


2023-12-26 16:43:16,563 - {pytorch_tabular.tabular_model:645} - INFO - Training the model completed                
2023-12-26 16:43:16,564 - {pytorch_tabular.tabular_model:1491} - INFO - Loading the best model                     
<pytorch_lightning.trainer.trainer.Trainer at 0x7f08ebab7e90>
result = tabular_model.evaluate(test)
LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0]

Output()
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃        Test metric               DataLoader 0        ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│         test_loss             509.21295166015625     │
│  test_mean_squared_error      509.21295166015625     │
└───────────────────────────┴───────────────────────────┘
/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.



pred_df = tabular_model.predict(test)
pred_df.head()
target_prediction
6252 -111.886528
4684 -169.181137
1731 272.100281
4742 12.167077
4521 56.998451
print_metrics(
    [(mean_squared_error, "MSE", {}), (mean_absolute_error, "MAE", {})],
    test["target"],
    pred_df["target_prediction"],
    tag="Holdout",
)
Holdout MSE: 509.21296869752325 | Holdout MAE: 17.057807657741225