PyTorch/Torchvision训练验证指标与损失计算的最佳实践咨询
PyTorch模型模式差异与最佳实践
情况是否属实?
你提到的困扰确实存在,但本质是对模型__call__方法的封装逻辑出了问题——PyTorch并没有强制要求模型在训练模式返回损失、评估模式返回输出,这是新手封装模型时容易踩的坑。只要调整封装方式,就能轻松解决这两个问题。
问题1:计算验证损失并绘制训练历史的最佳实践
别让模型的__call__同时干“算输出”和“算损失”两件事,把逻辑拆分开:
- 模型的
forward(也就是__call__实际调用的方法)只负责计算原始输出(比如分类任务的logits)。 - 损失函数单独定义,训练和验证阶段分别调用:
- 训练时:先拿模型输出,再丢给损失函数算损失,接着做反向传播。
- 验证时:把模型切到
eval()模式(自动关闭dropout、固定batch norm的统计量),同样先拿输出再算损失——这样既不会有统计偏差,代码也简洁得很,完全不用写百行代码。
示例代码:
import torch import torch.nn as nn # 模型只负责前向传播输出 class MyModel(nn.Module): def __init__(self): super().__init__() self.backbone = nn.Sequential( nn.Conv2d(3, 16, kernel_size=3), nn.BatchNorm2d(16), nn.ReLU(), nn.Dropout(0.5), nn.AdaptiveAvgPool2d(1), nn.Flatten(), nn.Linear(16, 10) ) def forward(self, x): return self.backbone(x) # 训练/验证流程 model = MyModel() criterion = nn.CrossEntropyLoss() optimizer = torch.optim.Adam(model.parameters()) # 训练阶段 model.train() for x, y in train_dataloader: outputs = model(x) loss = criterion(outputs, y) optimizer.zero_grad() loss.backward() optimizer.step() # 验证阶段计算损失 model.eval() val_loss = 0.0 with torch.no_grad(): # 验证时不需要计算梯度,节省内存 for x, y in val_dataloader: outputs = model(x) loss = criterion(outputs, y) val_loss += loss.item() * x.size(0) val_loss /= len(val_dataloader.dataset)
用这种方式,你可以轻松记录每一轮的训练/验证损失,直接用来绘制曲线,和TensorFlow的体验一致。
问题2:用TorchMetrics处理训练数据的最佳实践
完全不需要切到评估模式重新跑训练数据!TorchMetrics支持在训练模式下直接计算指标,注意两点就行:
- 训练时模型处于
train()模式,但TorchMetrics的指标(比如准确率)是基于模型输出计算的,和dropout、batch norm的状态无关,不会影响结果。 - 对于需要累积统计的指标(比如
Accuracy),每轮训练前重置指标,每一步更新,轮末计算结果即可。
示例代码:
from torchmetrics import Accuracy model = MyModel() criterion = nn.CrossEntropyLoss() optimizer = torch.optim.Adam(model.parameters()) train_acc = Accuracy(task="multiclass", num_classes=10) model.train() for epoch in range(10): train_acc.reset() # 每轮开始重置指标 epoch_loss = 0.0 for x, y in train_dataloader: outputs = model(x) loss = criterion(outputs, y) # 更新训练准确率 train_acc(outputs.argmax(dim=1), y) optimizer.zero_grad() loss.backward() optimizer.step() epoch_loss += loss.item() * x.size(0) # 获取本轮训练的准确率 epoch_acc = train_acc.compute() epoch_loss /= len(train_dataloader.dataset) print(f"第{epoch+1}轮: 损失={epoch_loss:.4f}, 准确率={epoch_acc:.4f}")
这样训练时就能同步计算指标,不用重复遍历训练数据,完全不浪费计算资源。
内容的提问来源于stack exchange,提问作者J Agustin Barrachina
相关产品推荐
相关产品推荐

