You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用trainer.tune后PyTorch Lightning的ModelCheckpoint找不到val_loss报错

问题:PyTorch Lightning调用trainer.tune后,ModelCheckpoint无法找到val_loss指标

在Kaggle环境下,使用PyTorch Lightning结合wandb logger训练LSTM模型时,遇到如下问题:

  • 不执行trainer.tune()时,trainer.fit()可以正常运行
  • 执行trainer.tune()后,ModelCheckpoint抛出错误,提示找不到val_loss指标,尽管已经在validation_step中明确log了该指标,且通过打印确认val_loss在ModelCheckpoint触发前已计算,但训练指标中仍缺失val_loss。

模型类代码

class Model(pl.LightningModule):
    def __init__(
        self,
        input_size: int,
        hidden_size: int,
        bidirectional: bool = False,
        lstm_layers: int = 1,
        lstm_dropout: float = 0.4,
        fc_dropout: float = 0.4,
        lr: float = 0.01,
        lr_scheduler_patience: int = 2,
    ):
        super().__init__()
        self.lr = lr
        self.save_hyperparameters()

        # LSTM
        self.encoder_lstm = nn.LSTM(
            input_size=input_size,
            hidden_size=hidden_size,
            num_layers=lstm_layers,
            bidirectional=bidirectional,
            dropout=lstm_dropout if lstm_layers > 1 else 0,
            batch_first=True,
        )

        # Fully-connected
        num_directions = 2 if bidirectional else 1
        self.fc = nn.Sequential(
            nn.Linear(
                hidden_size * num_directions, hidden_size * num_directions * 2
            ),
            nn.ReLU(),
            nn.Dropout(fc_dropout),
            nn.Linear(hidden_size * num_directions * 2, input_size),
        )

        self.loss_function = nn.MSELoss()

    def configure_optimizers(self):
        optimizer = torch.optim.AdamW(self.parameters(), lr=self.lr)
        return {
            "optimizer": optimizer,
            "lr_scheduler": {
                "scheduler": torch.optim.lr_scheduler.ReduceLROnPlateau(
                    optimizer, patience=self.hparams.lr_scheduler_patience
                ),
                "monitor": "val_loss",
            },
        }

    def forward(self, x, prev_state):
        ...

    def training_step(self, batch, batch_idx):
        loss, _ = self._step(batch)

        self.log("train_loss", loss)
        return loss

    def validation_step(self, batch, batch_idx):
        loss, embeddings = self._step(batch)

        self.log("val_loss", loss)
        
        return {
            'val_loss': loss,
            'preds': embeddings # 供自定义回调使用
        }

    def test_step(self, batch, batch_idx):
        loss, _ = self._step(batch)

        self.log("test_loss", loss)

训练代码

model = Model(
    bidirectional=False,
    lstm_layers=1,
    lstm_dropout=0.4,
    fc_dropout=0.4,
    lr=0.01,
    lr_scheduler_patience=2
)

...

checkpoint_callback = ModelCheckpoint(
    monitor="val_loss",
    every_n_train_steps=100,
    verbose=True
)

trainer = pl.Trainer(
    accelerator='gpu',
    precision=16,
    max_epochs=100,
    callbacks=[early_stopping, checkpoint_callback, lr_monitor, custom_callback],
    log_every_n_steps=50,
    logger=wandb_logger,
    auto_lr_find=True,
)

trainer.tune(model, train_dataloaders=train_dataloader, val_dataloaders=val_dataloader)

trainer.fit(model, train_dataloader, val_dataloader)

报错信息

MisconfigurationException: `ModelCheckpoint(monitor='val_loss')` could not find the monitored key in the returned metrics: ['train_loss', 'epoch', 'step']. HINT: Did you call `log('val_loss', value)` in the `LightningModule`?

解决方案

1. 临时禁用tune阶段的ModelCheckpoint

trainer.tune()会触发部分训练/验证流程,但该阶段的指标不会持久化到后续fit阶段。可以在tune前移除checkpoint回调,完成后恢复:

# 保存原始回调列表
original_callbacks = trainer.callbacks
# 过滤掉ModelCheckpoint
trainer.callbacks = [cb for cb in original_callbacks if not isinstance(cb, ModelCheckpoint)]
# 执行tune
trainer.tune(model, train_dataloaders=train_dataloader, val_dataloaders=val_dataloader)
# 恢复原始回调
trainer.callbacks = original_callbacks

2. 修改ModelCheckpoint的触发时机

将every_n_train_steps=100改为按epoch触发,确保只有在完成完整验证epoch后才会执行checkpoint:

checkpoint_callback = ModelCheckpoint(
    monitor="val_loss",
    every_n_epochs=1,
    verbose=True
)

3. 在tune后手动执行一次验证

在tune完成后、fit开始前,手动运行一次验证流程,提前记录val_loss:

trainer.tune(model, train_dataloaders=train_dataloader, val_dataloaders=val_dataloader)
# 手动执行验证
trainer.validate(model, val_dataloaders=val_dataloader)
trainer.fit(model, train_dataloader, val_dataloader)

4. 显式设置log参数

在validation_step的log语句中显式指定prog_bar=True和logger=True,确保指标被正确记录:

def validation_step(self, batch, batch_idx):
    loss, embeddings = self._step(batch)
    # 显式设置log参数
    self.log("val_loss", loss, prog_bar=True, logger=True)
    return {
        'val_loss': loss,
        'preds': embeddings
    }

内容的提问来源于stack exchange,提问作者Karol

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 00:07:52