趋近智
训练好 PyTorch 模型后,或者在训练期间为了监控进度,您需要使用模型从未见过的数据集(例如验证集或测试集)来评估其表现。如果您是从 TensorFlow Keras 转过来的,您会熟悉便捷的 model.evaluate() 方法,该方法在您编译模型后只需一次调用即可处理评估过程。
在 PyTorch 中,与训练类似,评估模型需要编写一个明确的循环。这使您能够完全控制该过程,进行自定义指标计算,并详细了解模型的行为。评估循环的结构与训练循环非常相似,但有几个重要区别:您不会计算梯度或更新模型权重 (weight)。
构建评估循环通常包含以下步骤:
DataLoader 提供。让我们更详细地了解这些重要组成部分。
model.eval()在开始评估之前,您必须通过调用 model.eval() 将模型切换到评估模式。这很重要,因为某些层,尤其是 torch.nn.Dropout 和 torch.nn.BatchNorm1d / torch.nn.BatchNorm2d / torch.nn.BatchNorm3d,在训练和评估期间具有不同的行为。
调用 model.eval() 会递归地为模型中的所有模块设置模式。反之,当您切换回训练模式时,您将调用 model.train() 以将这些层恢复到其训练行为。
# 假设 'model' 是您的 PyTorch nn.Module 实例
model.eval()
print("模型处于评估模式。")
# ... 执行评估 ...
# 如果您之后需要切换回训练模式
# model.train()
# print("模型已回到训练模式。")
忘记调用 model.eval() 可能会导致评估结果不一致且具有误导性,因为 dropout 仍将处于活跃状态,并且批归一化层将使用批次统计数据而非学习到的总体统计数据。
torch.no_grad()在评估期间,您只关心模型的输出,而不关心更新其权重 (weight)。因此,计算梯度是不必要且计算成本高昂的。PyTorch 提供了一个上下文 (context)管理器 torch.no_grad(),它在其作用域内禁用梯度计算。
使用 torch.no_grad() 提供两个主要优势:
以下是它的用法:
import torch
# 假设模型、数据和目标已定义并位于正确的设备上
# 在此循环的此部分之前应已调用 model.eval()
with torch.no_grad():
# 前向传播
predictions = model(data_batch)
# 损失计算(评估期间可选,但通常有用)
# loss = criterion(predictions, target_batch)
# 其他指标计算
# accuracy = calculate_accuracy(predictions, target_batch)
在 with torch.no_grad(): 块内执行的任何张量操作都将具有 requires_grad=False,即使其输入在块外部具有 requires_grad=True。
现在,让我们将这些元素组合成一个典型的评估循环。此函数将您的模型、评估集的 DataLoader 以及损失函数 (loss function)(判据)作为输入。
import torch
import torch.nn as nn
# 示例:定义一个简单模型、判据和一个用于说明的虚拟数据加载器
# 在实际场景中,这些将是您实际训练过的模型和数据
class SimpleModel(nn.Module):
def __init__(self):
super(SimpleModel, self).__init__()
self.fc = nn.Linear(10, 2) # 10 个输入特征,2 个输出类别
def forward(self, x):
return self.fc(x)
# 用于说明的虚拟数据
dummy_eval_data = [(torch.randn(32, 10), torch.randint(0, 2, (32,))) for _ in range(5)] # 5 个批次,每个批次 32 个样本
eval_loader = torch.utils.data.DataLoader(dummy_eval_data, batch_size=None) # batch_size=None 是因为数据已经分批
model = SimpleModel() # 假设此模型已训练
criterion = nn.CrossEntropyLoss()
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
def evaluate_model(model, dataloader, criterion, device):
model.eval() # 将模型设置为评估模式
total_loss = 0.0
correct_predictions = 0
total_samples = 0
with torch.no_grad(): # 禁用梯度计算
for inputs, labels in dataloader:
inputs = inputs.to(device)
labels = labels.to(device)
# 前向传播
outputs = model(inputs)
# 计算损失
loss = criterion(outputs, labels)
total_loss += loss.item() * inputs.size(0) # 累积损失,按批次大小加权
# 计算准确率
_, predicted_labels = torch.max(outputs, 1)
correct_predictions += (predicted_labels == labels).sum().item()
total_samples += labels.size(0)
avg_loss = total_loss / total_samples
accuracy = correct_predictions / total_samples
print(f'评估结果:平均损失:{avg_loss:.4f},准确率:{accuracy:.4f} ({correct_predictions}/{total_samples})')
return avg_loss, accuracy
# 执行评估
eval_loss, eval_accuracy = evaluate_model(model, eval_loader, criterion, device)
在此 evaluate_model 函数中:
model.eval()。torch.no_grad() 包裹了整个数据循环,确保不计算梯度。device。outputs = model(inputs)。criterion 计算 loss。我们在求和之前将 loss.item() 乘以 inputs.size(0)(批次大小),因为损失函数通常返回批次的平均损失。为了获得数据集的总损失,我们需要将这些求和,然后除以总样本数。torch.max(outputs, 1) 为每个样本找到得分最高的类别。correct_predictions 和 total_samples 的数量被累积。avg_loss 和总体 accuracy。这个结构具有高度适应性。您可以轻松地从 torchmetrics 等库中添加其他指标,或在循环内实现自定义指标。
tf.keras.evaluate() 的比较如果您习惯使用 TensorFlow,Keras 的 model.evaluate(eval_dataset) 方法会隐式地执行所有这些步骤。它接收您的评估数据集,遍历它,计算配置的损失和指标,然后返回结果。
PyTorch 的方法虽然需要更明确的代码,但提供几个优势:
虽然 Keras 提供了便利,但 PyTorch 的方式提供更细粒度的控制,这在研究或处理复杂评估场景时特别有用。
评估模型后,您将使用这些指标(如平均损失和准确率)来比较不同模型、执行超参数 (parameter) (hyperparameter)调整,或决定您的模型是否可以部署。如果您在训练期间进行评估(例如,在每个 epoch 后对验证集进行评估),这些指标还可以为提前停止决策提供依据,帮助您防止过拟合 (overfitting)并节省训练时间。
这部分内容有帮助吗?
model.eval()和torch.no_grad()在标准工作流中的实际应用。tf.keras.Model.evaluate API, TensorFlow Developers, 2024 - Keras evaluate 方法的官方API文档,提供了与PyTorch显式评估循环进行比较的背景。© 2026 ApX Machine Learning用心打造