训练指标监控

介绍 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)上传指标,可选 stepstep 默认为 wandb 自增
    wandb.watch(model, log="all", log_freq=10)监控模型参数、梯度、直方图、梯度范数log 可以选 gradients, parameters, all
    wandb.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 计算时间长、数据加载是否成为瓶颈
这篇文档有帮助吗?

On this page

页面信息