import torch.nn as nn
from pytorch_forecasting.models.nn import MultiEmbedding
from pytorch_forecasting.models.base_model import BaseModelWithCovariates
from pytorch_forecasting.metrics import NegativeBinomialDistributionLoss
import pytorch_lightning as pl
import torch
class CNNDeepARLightningModule(pl.LightningModule):
def __init__(self, model, learning_rate: float):
super().__init__()
self.save_hyperparameters(ignore=["model"])
self.model = model
self.learning_rate = learning_rate
def forward(self, x):
return self.model(x)
def training_step(self, batch, batch_idx):
x, y = batch
# Assuming the model's forward returns predictions directly for loss calculation
y_hat = self(x)
loss = self.model.loss(y_hat, y) # Use the model's loss function
self.log("train_loss", loss)
return loss
def validation_step(self, batch, batch_idx):
x, y = batch
# Assuming the model's forward returns predictions directly for loss calculation
y_hat = self(x)
# The target y is a tuple (target_values, target_scale)
loss = self.model.loss(y_hat, y[0]) # Use y[0] for target values
self.log("val_loss", loss)
return loss
def configure_optimizers(self):
optimizer = torch.optim.Adam(self.parameters(), lr=self.learning_rate)
return optimizer
class CNNDeepAR(BaseModelWithCovariates):
def __init__(
self,
embedding_sizes,
static_categoricals,
time_varying_categoricals_encoder,
time_varying_categoricals_decoder,
time_varying_reals_encoder,
time_varying_reals_decoder,
x_categoricals,
embedding_paddings,
static_reals,
categorical_groups,
x_reals,
embedding_labels,
hidden_size=64,
cnn_kernel_size=3,
dropout=0.1,
**kwargs
):
super().__init__(**kwargs)
self.save_hyperparameters()
# Access needed parameters from dataset_parameters via self.hparams
embedding_sizes = self.hparams.embedding_sizes
x_categoricals = self.hparams.x_categoricals
# Access the list of real variables from self.hparams.x_reals
x_reals_list = self.hparams.x_reals
class EmbeddingLayer(nn.Module):
def __init__(self, input_dim, embedding_dim):
super(EmbeddingLayer, self).__init__()
self.embeddings = nn.Embedding(input_dim, embedding_dim)
self.output_size = embedding_dim
self.embedding = MultiEmbedding(embedding_sizes, x_categoricals)
# Calculate input_size based on the length of the list of real variables
output_value = int(self.embedding.output_size['value']) if isinstance(self.embedding.output_size, dict) else int(self.embedding.output_size)
self.input_size = len(x_reals_list) + output_value
self.hidden_size = hidden_size
self.cnn = nn.Conv1d(
in_channels=self.input_size,
out_channels=self.hidden_size,
kernel_size=cnn_kernel_size,
padding=cnn_kernel_size // 2
)
self.rnn = nn.LSTM(
input_size=self.hidden_size,
hidden_size=self.hidden_size,
batch_first=True
)
self.output_layer = nn.Linear(self.hidden_size, 1)
from pytorch_forecasting.metrics import NegativeBinomialDistributionLoss
self.loss_fn = NegativeBinomialDistributionLoss()
def forward(self, x):
# Ensure input is float type for CNN
encoder_cont = x["encoder_cont"].float()
decoder_cont = x["decoder_cont"].float()
batch_size = encoder_cont.size(0)
# Handle empty categorical input
if "encoder_cat" in x and x["encoder_cat"].numel() > 0:
x_cat_encoder = self.embedding(x["encoder_cat"])
else:
x_cat_encoder = None
if "decoder_cat" in x and x["decoder_cat"].numel() > 0:
x_cat_decoder = self.embedding(x["decoder_cat"])
else:
x_cat_decoder = None
if x_cat_encoder is not None:
x_input_encoder = torch.cat([x_cat_encoder, encoder_cont], dim=-1)
else:
x_input_encoder = encoder_cont
x_input_encoder = x_input_encoder.permute(0, 2, 1) # [B, C, T]
x_cnn_encoder = self.cnn(x_input_encoder)
x_cnn_encoder = x_cnn_encoder.permute(0, 2, 1) # [B, T, C]
# For simplicity, we'll use the last encoder output as the initial hidden state for the decoder
# A more complex approach would involve passing the encoder's final state to the decoder
# and processing decoder inputs through the CNN as well.
rnn_output, hidden = self.rnn(x_cnn_encoder)
# Predict for the decoder part
# This is a simplified approach for a single step prediction
# For multi-step prediction, you would loop or use a decoder network
decoder_input = decoder_cont[:, :self.hparams.prediction_length, :] # Take only the prediction length part
if x_cat_decoder is not None:
decoder_input = torch.cat([x_cat_decoder[:, :self.hparams.prediction_length, :], decoder_input], dim=-1)
# Apply CNN to decoder input if needed (simplified: using last encoder output)
# A more complete model would process decoder inputs through CNN
# For this fix, we'll directly use the last encoder RNN output for prediction
output = self.output_layer(rnn_output[:, -1, :]) # Use last encoder RNN output
# The NegativeBinomialDistributionLoss expects a single value output (the mean)
# and calculates the distribution parameters internally.
return output.unsqueeze(-1) # Ensure output shape is [B, 1, 1] for prediction length 1
def training_step(self, batch, batch_idx):
x, y = batch
# The loss function expects the raw output from the model's forward pass
y_hat = self(x)
# The target y is a tuple (target_values, target_scale)
loss = self.loss_fn(y_hat, y[0]) # Use y[0] for target values
self.log("train_loss", loss)
return loss
def validation_step(self, batch, batch_idx):
x, y = batch
# The loss function expects the raw output from the model's forward pass
y_hat = self(x)
# The target y is a tuple (target_values, target_scale)
loss = self.loss_fn(y_hat, y[0]) # Use y[0] for target values
self.log("val_loss", loss)
return loss
cnn_deepar_model = CNNDeepAR.from_dataset(
training_dataset,
hidden_size=best_params["hidden_size"],
dropout=best_params["dropout"],
)
lightning_model = CNNDeepARLightningModule(cnn_deepar_model, learning_rate=best_params["learning_rate"])
#Set seed
pl.seed_everything(42)
# Lightning Trainer
trainer = pl.Trainer(
max_epochs=30,
gradient_clip_val=best_params["clip_val"],
logger=False,
enable_checkpointing=True
)
# Train
trainer.fit(lightning_model, train_dataloaders=train_dataloader, val_dataloaders=val_dataloader): /usr/local/lib/python3.11/dist-packages/lightning/pytorch/utilities/parsing.py:209: Attribute 'loss' is an instance of `nn.Module` and is already saved during checkpointing. It is recommended to ignore them using `self.save_hyperparameters(ignore=['loss'])`.
/usr/local/lib/python3.11/dist-packages/lightning/pytorch/utilities/parsing.py:209: Attribute 'logging_metrics' is an instance of `nn.Module` and is already saved during checkpointing. It is recommended to ignore them using `self.save_hyperparameters(ignore=['logging_metrics'])`.
INFO:lightning_fabric.utilities.seed:Seed set to 42
INFO: GPU available: False, used: False
INFO:lightning.pytorch.utilities.rank_zero:GPU available: False, used: False
INFO: TPU available: False, using: 0 TPU cores
INFO:lightning.pytorch.utilities.rank_zero:TPU available: False, using: 0 TPU cores
INFO: IPU available: False, using: 0 IPUs
INFO:lightning.pytorch.utilities.rank_zero:IPU available: False, using: 0 IPUs
INFO: HPU available: False, using: 0 HPUs
INFO:lightning.pytorch.utilities.rank_zero:HPU available: False, using: 0 HPUs
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
/tmp/ipython-input-45-1214955043.py in <cell line: 0>()
187
188 # Train
--> 189 trainer.fit(lightning_model, train_dataloaders=train_dataloader, val_dataloaders=val_dataloader)
10 frames
/usr/local/lib/python3.11/dist-packages/torch/optim/optimizer.py in __init__(self, params, defaults)
259 param_groups = list(params)
260 if len(param_groups) == 0:
--> 261 raise ValueError("optimizer got an empty parameter list")
262 if not isinstance(param_groups[0], dict):
263 param_groups = [{'params': param_groups}]
ValueError: optimizer got an empty parameter list
怎么办
最新发布