训练指标监控
介绍 PyTorch 中的训练日志保存、指标监控、wandb、TensorBoard、PyTorch Profiler、CUDA Profiling 等内容
日志保存与指标监控
-
训练过程中,通常会跟踪以下指标:
-
train_loss/val_loss -
train_acc/val_acc -
precision/recall、F1
-
学习率(scheduler)
-
-
并按 step 或 epoch 控制:
-
日志记录频率(log_step)
-
模型保存频率(checkpoint_step)
-
-
optimizer.param_groups是 PyTorch 优化器的核心接口,每个param_group是一个字典,存储了优化器参数和超参数,常用字段有字段 含义 可视化意义 params当前组内的参数列表 不可直接可视化,但可用于梯度统计 lr学习率 随时间变化,曲线展示优化进度 momentum动量(如 SGD) 可观察动量策略对训练影响 betasAdam 的 beta 参数 (β1, β2)调试 Adam 收敛行为 weight_decayL2 正则化系数 用于监控正则化影响 dampeningSGD 特有参数 可调试动量衰减 nesterov是否使用 Nesterov 用于策略对比 maximize是否最大化 loss(少用) 对强化学习任务有用 -
示例代码
import torch import torch.nn as nn import torch.optim as optim import os import numpy as np import matplotlib.pyplot as plt # ========== 配置 ========== epochs = 10 log_step = 20 # 每多少 batch 打一次日志 checkpoint_step = 2 # 每多少 epoch 保存一次模型 save_dir = "./checkpoints" os.makedirs(save_dir, exist_ok=True) # ========== 模型 & 数据 ========== model = nn.Sequential( nn.Linear(10, 50), nn.ReLU(), nn.Linear(50, 2) ) criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(model.parameters(), lr=1e-3) scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=5, gamma=0.1) # 假数据 x_train = torch.randn(200, 10) y_train = torch.randint(0, 2, (200,)) train_loader = [(x_train[i:i+20], y_train[i:i+20]) for i in range(0, 200, 20)] # ========== 训练循环 ========== train_losses, train_accs, lrs = [], [], [] for epoch in range(epochs): model.train() batch_losses, batch_accs = [], [] for step, (x, y) in enumerate(train_loader): optimizer.zero_grad() out = model(x) loss = criterion(out, y) loss.backward() optimizer.step() batch_losses.append(loss.item()) preds = out.argmax(dim=1) batch_accs.append((preds == y).float().mean().item()) # 控制日志打印 if (step + 1) % log_step == 0 or (step+1)==len(train_loader): print(f"Epoch {epoch+1}, Step {step+1}, Loss {np.mean(batch_losses):.4f}, Acc {np.mean(batch_accs):.4f}") train_losses.append(np.mean(batch_losses)) train_accs.append(np.mean(batch_accs)) lrs.append(optimizer.param_groups[0]['lr']) scheduler.step() # 控制 checkpoint 保存 if (epoch + 1) % checkpoint_step == 0: torch.save(model.state_dict(), os.path.join(save_dir, f"model_epoch{epoch+1}.pt")) # ========== 绘图 ========== plt.figure(figsize=(8,4)) plt.plot(train_losses, label="Train Loss") plt.plot(train_accs, label="Train Acc") plt.xlabel("Epoch") plt.ylabel("Value") plt.legend() plt.title("Training Metrics") plt.show() plt.figure(figsize=(8,4)) plt.plot(lrs, label="Learning Rate") plt.xlabel("Epoch") plt.ylabel("LR") plt.title("Learning Rate Schedule") plt.show()
wandb
-
wandb 配置
配置 含义 使用场景 entitywandb 账号或团队名 指定归属账户或团队 project项目名 一个 project 下可以有多个实验 group分组 用于组织相关实验,例如不同超参组合属于一个组 tags标签列表 自定义标签,用于快速搜索或对比 name实验名 唯一标识某次运行 -
常用 API
方法 作用 备注 wandb.init()初始化运行,设置 entity、project、name、group、tags 每次训练必须调用一次 wandb.config.update(dict)更新超参数或配置 超参搜索时可以方便记录 wandb.log(dict, step=None)上传指标,可选 step step 默认为 wandb 自增 wandb.watch(model, log="all", log_freq=10)监控模型参数、梯度、直方图、梯度范数 log 可以选 gradients,parameters,allwandb.save("file")保存本地文件到 wandb 云 可保存 checkpoint、配置文件 wandb.finish()结束当前运行 可显式调用,也可自动结束 -
示例代码
import wandb # 初始化 wandb.init(project="my_project", name="exp1") # 配置超参 config = {"lr": 1e-3, "batch_size": 20, "epochs": 10} wandb.config.update(config) # 训练循环 for epoch in range(config["epochs"]): epoch_loss, epoch_acc = 0, 0 for step, (x, y) in enumerate(train_loader): optimizer.zero_grad() out = model(x) loss = criterion(out, y) loss.backward() optimizer.step() preds = out.argmax(dim=1) acc = (preds == y).float().mean().item() epoch_loss += loss.item() epoch_acc += acc # 每个 batch 记录 wandb.log({"batch_loss": loss.item(), "batch_acc": acc}) # 每个 epoch 记录 wandb.log({"epoch_loss": epoch_loss/len(train_loader), "epoch_acc": epoch_acc/len(train_loader)}) # 可视化模型结构 wandb.watch(model, log="all", log_freq=10)
TensorBoard
-
SummaryWriter 参数
torch.utils.tensorboard.SummaryWriter( log_dir=None, # 日志目录 comment='', # 日志注释,会附加到目录名 purge_step=None, # 用于恢复训练时丢弃之前的 step max_queue=10, # 队列缓存数量,异步写入 flush_secs=120, # 每隔多少秒 flush filename_suffix='' # 日志文件后缀 ) -
常用 API
方法 功能 使用示例 add_scalar(tag, scalar_value, global_step)添加单值标量 loss/acc/lr 曲线 add_scalars(main_tag, tag_scalar_dict, global_step)一次添加多条曲线 multi-experiment loss add_histogram(tag, values, global_step)权重 / 梯度分布 monitor layer weights add_image(tag, img_tensor, global_step)上传图片 可视化输入 / 输出 add_images(tag, img_tensor, global_step)多图片 batch 可视化 add_graph(model, input_to_model)可视化计算图 模型结构展示 add_embedding(mat, metadata=None, label_img=None, global_step=None)可视化 embedding 特征空间分析 flush()强制写入磁盘 异步写入时有用 close()关闭 writer 必须在训练结束调用 -
示例代码
from torch.utils.tensorboard import SummaryWriter writer = SummaryWriter(log_dir="./runs/exp1") for epoch in range(epochs): epoch_loss, epoch_acc = 0, 0 for step, (x, y) in enumerate(train_loader): optimizer.zero_grad() out = model(x) loss = criterion(out, y) loss.backward() optimizer.step() preds = out.argmax(dim=1) acc = (preds == y).float().mean().item() writer.add_scalar("batch/loss", loss.item(), epoch*len(train_loader)+step) writer.add_scalar("batch/acc", acc, epoch*len(train_loader)+step) # epoch 级记录 writer.add_scalar("epoch/loss", epoch_loss/len(train_loader), epoch) writer.add_scalar("epoch/acc", epoch_acc/len(train_loader), epoch) # 可视化模型结构 dummy_input = torch.randn(1,10) writer.add_graph(model, dummy_input) writer.close()
PyTorch Profiler
-
构造函数
torch.profiler.profile( activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA], schedule=None, # 训练阶段调度 on_trace_ready=None, # 回调,例如写入 TensorBoard record_shapes=False, # 是否记录张量形状 profile_memory=False,# 是否记录显存占用 with_stack=False, # 是否记录调用栈 use_cuda=True # 是否分析 CUDA ) -
schedule 用于控制采样周期,通常配合训练迭代:
schedule=torch.profiler.schedule(wait=1, warmup=1, active=3, repeat=2)wait: 忽略前几步,避免冷启动warmup: 预热阶段,不统计active: 开始采样的步数repeat: 循环次数 \
-
常用 API
方法 作用 示例 record_function("name")手动打标签 with record_function ("forward") prof.key_averages().table(sort_by="cuda_time_total")按时间排序输出表格 查看 top kernel prof.export_chrome_trace("trace.json")导出 Chrome Trace 可用 chrome://tracing 查看 timeline prof.tensorboard_trace_handler("logdir")导出到 TensorBoard 可直接在 TensorBoard 中查看 timeline -
示例代码
from torch.profiler import profile, record_function, ProfilerActivity with profile( activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA], record_shapes=True, with_stack=True ) as prof: for x, y in train_loader: with record_function("forward_backward"): out = model(x) loss = criterion(out, y) optimizer.zero_grad() loss.backward() optimizer.step() print(prof.key_averages().table(sort_by="cuda_time_total", row_limit=10)) # 导出 TensorBoard Trace prof.export_chrome_trace("trace.json")
CUDA Profiling
-
一般指 nsys(NVIDIA System Profiler),旧的 nvprof 已经废弃,也可以使用 Visual Profiler / Nsight Systems GUI
-
常用命令
# 采样训练脚本 nsys profile -o profile_report python train.py # 输出文件为 profile_report.qdrep -
查看指标
- Kernel 时间:forward/backward 执行时间
- SM Utilization:GPU 利用率
- Memory throughput:显存带宽占用
- Timeline:线程执行顺序和阻塞状态
-
与 PyTorch Profiler 配合
- PyTorch Profiler 导出 Chrome Trace (
trace.json) - NSYS 生成
.qdrep - 在 GUI 中对比哪些 kernel 被 CPU 阻塞、哪些 kernel 计算时间长、数据加载是否成为瓶颈
- PyTorch Profiler 导出 Chrome Trace (
这篇文档有帮助吗?