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

PyTorch训练时验证损失低于训练损失,代码实现是否存在问题?

问题排查与解答

核心问题定位

你怀疑的val_loss缩进本身没有语法错误,但是你的训练、验证损失统计逻辑,以及验证阶段的模型配置存在明显缺陷,是导致你观察到验证损失持续低于训练损失的核心原因。

1 现有代码的两处错误

1.1 损失统计逻辑错误

  • 训练损失:你当前存入train_losses的是每个epoch最后一个训练批次的损失,不是整个epoch所有训练批次的平均损失。训练过程中每个batch都会更新模型参数,越靠后的batch模型效果越好、损失越低,仅用最后一个batch的损失无法代表整个epoch的平均训练水平。
  • 验证损失:同理,你存入val_losses的是验证集最后一个批次的损失,不是整个验证集的平均损失。如果验证集最后一个批次刚好是难度较低的样本,计算出来的损失自然会低于训练集最后一个批次的损失,无法反映真实的泛化表现。

1.2 验证阶段未切换模型模式

你在验证前没有调用model.eval(),如果你的模型里有Dropout、BatchNorm等训练和推理阶段行为不同的层,会导致验证阶段的计算逻辑和训练阶段不一致,也会造成损失偏差。

2 验证损失低于训练损失的正常场景说明

排除代码错误的前提下,验证损失低于训练损失也属于正常情况,常见触发原因包括:

  • 正则项(如Dropout、L2正则)只在训练阶段生效,验证阶段关闭后模型表现更好
  • 验证集样本难度整体低于训练集
  • 训练集和验证集划分时存在分布偏移,验证集特征更简单

3 修复后的代码参考

def train_model(model, train_loader,val_loader,lr):
    "Model training"
    epochs=100
    criterion = nn.MSELoss()
    optimizer = torch.optim.Adam(model.parameters(), lr=lr, weight_decay=1e-5)
    # Reduce learning rate if no improvement is observed after 10 Epochs.
    scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, 'min', patience=2, verbose=True)
    train_losses = []
    val_losses = []
    for epoch in range(epochs):
        # 每个epoch开始前切换到训练模式
        model.train()
        epoch_train_loss = 0.0
        train_batch_count = 0
        for data in train_loader:
            y_pred = model.forward(data)
            loss1 = criterion(y_pred[:, 0], data.y[0])
            loss2 = criterion(y_pred[:,1], data.y[1])
            train_loss = 0.8*loss1+0.2*loss2
            optimizer.zero_grad()
            train_loss.backward()
            optimizer.step()
            # 累加所有批次的训练损失
            epoch_train_loss += train_loss.detach().numpy()
            train_batch_count += 1
        # 存储整个epoch的平均训练损失
        train_losses.append(epoch_train_loss / train_batch_count)
                        
        # 验证前切换到推理模式
        model.eval()
        epoch_val_loss = 0.0
        val_batch_count = 0
        with torch.no_grad():
            for data in val_loader:
                y_val = model.forward(data)
                loss1 = criterion(y_val[:,0], data.y[0])
                loss2 = criterion(y_val[:,1], data.y[1])
                val_loss = 0.8*loss1+0.2*loss2
                # 累加所有批次的验证损失
                epoch_val_loss += val_loss.detach().numpy()
                val_batch_count +=1
        # 存储整个验证集的平均损失
        avg_val_loss = epoch_val_loss / val_batch_count
        val_losses.append(avg_val_loss)
        # 启用学习率调度器
        scheduler.step(avg_val_loss)
        
        print(f'Epoch: {epoch}, train_loss: {train_losses[epoch]:.3f} , val_loss: {val_losses[epoch]:.3f}')
            
    return train_losses, val_losses

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 22:45:01