尽管 TensorBoard 提供丰富的可视化功能,但有效监控的根本在于训练和评估代码中的系统性记录。仅仅运行循环是不够的;你需要记录主要的性能数据,以了解训练是如何进展的,并在问题出现时进行诊断。这里说明如何在 PyTorch 训练和评估例程中直接实现基础的指标记录。为什么要记录指标?记录指标有几个重要目的:性能追踪: 观察损失和准确度(或其他相关指标)在不同训练轮次中的趋势,以判断模型是否有效学习。调试: 及早发现潜在问题。损失是否在下降?是否停滞不前?验证性能是否在改善或变差?记录指标中的异常情况常常指向一些根本问题,比如之前讨论过的学习率问题或过拟合。比较: 记录的指标允许在不同模型架构、超参数或训练运行之间进行客观比较。可视化的根本: 像 TensorBoard 这样的工具依赖这些记录的值来生成图表和仪表板。要记录的指标具体指标取决于你的任务,但常用指标包括:损失: 这是你的优化器试图最小化的值。你几乎总是应该记录损失。追踪训练损失(在训练循环中基于训练数据计算)和验证损失(在评估循环中基于单独的验证数据集计算)很重要。比较这两者对于识别过拟合非常重要。准确度: 对于分类任务,准确度(正确分类样本的比例)是一个标准且易于理解的指标。其他任务特定指标: 根据问题类型,你可能记录精确度、召回率、F1分数(用于分类),平均绝对误差(MAE)或均方根误差(RMSE)(用于回归),交并比(IoU)(用于分割)等。在训练循环中实现记录在训练期间,你通常希望追踪每个训练轮次中的平均损失和准确度。为每个批次记录指标可能产生噪音,并且对整体趋势的指示性较差,尽管有时它对调试不稳定性有用。下面是修改标准训练轮次函数以包含记录的方法:import torch # 假设模型、train_dataloader、loss_fn、optimizer 已定义 def train_one_epoch(model, train_dataloader, loss_fn, optimizer, device): model.train() # 设置模型为训练模式 running_loss = 0.0 correct_predictions = 0 total_samples = 0 for batch_idx, (inputs, labels) in enumerate(train_dataloader): inputs, labels = inputs.to(device), labels.to(device) # 1. 清零梯度 optimizer.zero_grad() # 2. 前向传播 outputs = model(inputs) # 3. 计算损失 loss = loss_fn(outputs, labels) # 4. 反向传播 loss.backward() # 5. 优化器步骤 optimizer.step() # --- 记录步骤 --- # 累加损失(使用 .item() 获取 Python 数字) running_loss += loss.item() * inputs.size(0) # 按批次大小加权 # 累加准确度(分类示例) _, predicted = torch.max(outputs.data, 1) total_samples += labels.size(0) correct_predictions += (predicted == labels).sum().item() # --- 记录步骤结束 --- # 计算当前轮次的平均损失和准确度 epoch_loss = running_loss / total_samples epoch_acc = correct_predictions / total_samples print(f"Training Epoch: Loss: {epoch_loss:.4f}, Accuracy: {epoch_acc:.4f}") # 返回指标以供后续记录或分析 return epoch_loss, epoch_acc记录实现中的要点:在轮次开始时初始化累加器(running_loss、correct_predictions、total_samples)。在批次循环内部,计算损失和预测后,更新累加器。使用 loss.item() 获取当前批次损失张量的标量值,以防止计算图被保留。如果你想在最后平均之前得到总损失,则乘以 inputs.size(0)(批次大小);否则,你可以平均批次损失,但如果最后一个批次较小,按批次大小加权会更准确。计算批次的准确度(或其他指标),并添加到累加总数中。在这里也使用 .item()。循环结束后,通过将累加总数除以样本数量,计算整个轮次的平均损失和准确度。打印或存储这些轮次级别的指标。在评估循环中实现记录在评估循环中进行记录是类似的,但存在重要区别:它在 torch.no_grad() 下运行,以禁用梯度计算。模型应处于评估模式(model.eval()),以禁用 dropout 并使用训练期间学习到的批归一化统计量。没有反向传播或优化器步骤。import torch # 假设模型、val_dataloader、loss_fn 已定义 def evaluate_model(model, val_dataloader, loss_fn, device): model.eval() # 设置模型为评估模式 running_loss = 0.0 correct_predictions = 0 total_samples = 0 with torch.no_grad(): # 禁用梯度计算 for inputs, labels in val_dataloader: inputs, labels = inputs.to(device), labels.to(device) # 前向传播 outputs = model(inputs) # 计算损失 loss = loss_fn(outputs, labels) # --- 记录步骤 --- running_loss += loss.item() * inputs.size(0) _, predicted = torch.max(outputs.data, 1) total_samples += labels.size(0) correct_predictions += (predicted == labels).sum().item() # --- 记录步骤结束 --- epoch_loss = running_loss / total_samples epoch_acc = correct_predictions / total_samples print(f"Validation: Loss: {epoch_loss:.4f}, Accuracy: {epoch_acc:.4f}") return epoch_loss, epoch_acc存储和使用记录的指标将指标打印到控制台对于即时反馈很有用。为了更系统的分析或可视化,你会希望存储它们。简单的 Python 列表或字典效果很好:# --- 在你的主训练脚本中 --- num_epochs = 10 device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # ... 初始化模型、数据、损失函数、优化器 ... train_losses, train_accuracies = [], [] val_losses, val_accuracies = [], [] for epoch in range(num_epochs): print(f"--- Epoch {epoch+1}/{num_epochs} ---") train_loss, train_acc = train_one_epoch(model, train_dataloader, loss_fn, optimizer, device) val_loss, val_acc = evaluate_model(model, val_dataloader, loss_fn, device) # 存储指标 train_losses.append(train_loss) train_accuracies.append(train_acc) val_losses.append(val_loss) val_accuracies.append(val_acc) # 可选:根据验证性能在此处保存模型检查点 # 可选:将指标记录到 TensorBoard(使用上述存储的值) # writer.add_scalar('Loss/train', train_loss, epoch) # writer.add_scalar('Loss/validation', val_loss, epoch) # ... 等等。 print("Training finished.") # 现在你可以分析这些列表:train_losses、val_losses 等。 # 例如,将它们保存到文件或绘制图表。这种结构使你能够收集整个训练过程中的性能数据。这些存储的列表(train_losses、val_losses等)正是你可以输入到 Matplotlib 等绘图库或传递给 TensorBoard SummaryWriter(如前一节所述)以创建如下可视化图表的内容。{"data":[{"type":"scatter","mode":"lines+markers","name":"训练损失","x":[1,2,3,4,5,6,7,8,9,10],"y":[1.85,1.21,0.95,0.78,0.65,0.58,0.51,0.47,0.43,0.40],"line":{"color":"#339af0"},"marker":{"color":"#339af0"}},{"type":"scatter","mode":"lines+markers","name":"验证损失","x":[1,2,3,4,5,6,7,8,9,10],"y":[1.60,1.15,0.98,0.88,0.80,0.75,0.72,0.70,0.69,0.68],"line":{"color":"#ff922b"},"marker":{"color":"#ff922b"}}],"layout":{"title":"训练和验证损失随轮次变化","xaxis":{"title":"轮次"},"yaxis":{"title":"损失"},"legend":{"orientation":"h","yanchor":"bottom","y":1.02,"xanchor":"right","x":1},"margin":{"l":50,"r":20,"t":40,"b":40},"template":"plotly_white"}}训练和验证损失曲线在10个轮次上绘制。观察这些趋势有助于诊断过拟合(验证损失增加而训练损失减少)或欠拟合(两种损失都保持高位)。通过在训练和评估期间持续记录指标,你对模型的行为有很好的了解,从而能够就超参数调整、模型调整和调试策略做出明智的决定。