Python打卡第46天

PyTorch TensorBoard 超全入门笔记(常用API+插入位置+避坑指南)

本文面向深度学习入门者,基于 PyTorch SummaryWriter 体系,覆盖从基础使用、核心API、代码插入位置到实战避坑的全流程,适合收藏查阅、面试复习。


一、TensorBoard 是什么

TensorBoard 是深度学习训练可视化工具,本质是日志写入 + 网页可视化的组合:

  • 代码中通过 SummaryWriter 向日志文件写入指标、图片、模型结构、分布等数据
  • 启动服务后在浏览器中查看曲线、图表,直观监控训练过程
  • 适用场景:loss/准确率监控、梯度异常排查、多实验对比、样本可视化、消融实验记录

安装

复制代码
pip install tensorboard

基础使用三步法

复制代码
# 1. 导入并初始化写入器
from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter(log_dir="runs/exp1")  # 指定日志保存目录

# 2. 训练过程中写入各类数据
writer.add_scalar("train/loss", loss.item(), global_step)

# 3. 训练结束关闭写入器
writer.close()

启动查看

复制代码
# 终端执行,logdir 指向日志根目录
tensorboard --logdir=runs

浏览器打开 [http://localhost:6006](http://localhost:6006) 即可查看面板。


二、核心API详解(按学习优先级排序)

通用参数规则:writer.xxx(tag, 数据, step)

  • tag:标签名,用 / 分组(如 Train/loss),面板自动归类
  • step:横轴,通常用全局步数 global_step 或 epoch

⭐⭐⭐⭐⭐ 必学:标量曲线 add_scalar

作用 :记录单个数值随训练步数的变化,绘制折线图,是最核心、最高频的功能。 适用场景:训练loss、验证loss、准确率、困惑度(PPL)、学习率、F1值等所有单指标。

复制代码
# 记录训练损失
writer.add_scalar("Train/Loss", loss.item(), global_step)
# 记录验证准确率
writer.add_scalar("Test/Accuracy", test_acc, epoch)
# 记录学习率
writer.add_scalar("Train/Learning_Rate", current_lr, global_step)

对应面板 :SCALARS 命名规范 :分组/指标名,例如 SFT/train_loss,同组曲线会聚合在同一文件夹下。


⭐⭐⭐⭐ 必学:超参数对比 add_hparams

作用 :记录当前实验的超参数 + 最终评估指标,支持多组实验横向表格对比,是消融实验神器。 适用场景:调参、多组对照实验、 ablation study。

复制代码
hparams = {
    "lr": 1e-3,
    "batch_size": 64,
    "dropout": 0.2,
    "model": "MLP"
}
final_metrics = {"best_test_acc": 0.62, "min_loss": 0.35}
writer.add_hparams(hparams, final_metrics)

对应面板 :HPARAMS 最佳实践:每组实验唯一log目录,配合hparams可一次性对比十几组超参效果。


⭐⭐⭐ 进阶:权重/梯度直方图 add_histogram

作用 :记录张量内所有数值的分布,观察分布随训练的变化。 适用场景:排查梯度消失/梯度爆炸、验证参数冻结是否生效、监控权重分布偏移。

复制代码
# 每100步记录一次权重和梯度
if global_step % 100 == 0:
    for name, param in model.named_parameters():
        writer.add_histogram(f"weights/{name}", param, global_step)
        if param.grad is not None:
            writer.add_histogram(f"grads/{name}", param.grad, global_step)

对应面板 :HISTOGRAMS / DISTRIBUTIONS 注意事项:

  • 禁止每一步都记录,会导致日志文件体积爆炸,建议间隔50~200步
  • 大模型不要全量记录所有层,抽样少数层即可
  • 微调冻结实验中,可通过梯度直方图是否全为0,验证冻结代码是否生效

⭐⭐⭐⭐ NLP专属:文本记录 add_text

作用 :写入字符串文本内容。 适用场景:大模型生成样本、错误预测case、prompt模板、实验备注。

复制代码
sample_text = f"Prompt: 1+1等于几?\n模型输出: 2\n标准答案: 2"
writer.add_text("Eval/generate_sample", sample_text, global_step)

对应面板:TEXT

CV任务使用较少,LLM微调、SFT训练高频使用。


⭐⭐⭐ CV专属:图像可视化 add_image

作用 :在面板中展示图片。 适用场景:训练样本可视化、错误预测样本、特征图热力图、分割结果对比。

复制代码
import torchvision
# 将多张图片拼成网格
img_grid = torchvision.utils.make_grid(images[:8].cpu())
writer.add_image("Train/sample_images", img_grid, global_step)

对应面板:IMAGES

注意:输入张量形状需为 (C, H, W),多图拼接建议用 make_grid。


⭐⭐ 选学:模型计算图 add_graph

作用 :可视化模型前向计算结构。 适用场景:新手理解网络结构、排查维度错误。

复制代码
# 喂入一条符合输入shape的样例数据
sample_imgs, _ = next(iter(train_loader))
writer.add_graph(model, sample_imgs.to(device))

对应面板 :GRAPHS 注意:

  • 只在训练开始前执行1次即可,不要放入循环
  • 大模型/Transformer结构的图会极其复杂,可读性差,不推荐使用

其他选学API

  1. add_embedding:高维向量降维可视化(PCA/t-SNE),对应 PROJECTOR 面板,适用于词向量、句子嵌入检索模型。
  2. add_pr_curve:绘制精确率-召回率曲线,对应 PR CURVES 面板,适用于不均衡分类任务。

三、黄金插入位置指南(对应训练循环)

很多新手不知道TensorBoard代码该写在哪,这里按训练脚本执行顺序给出标准插入点位。

3.1 训练循环前(仅执行1次)

位置:模型、数据集定义完成后,外层epoch循环之前

  • 初始化 SummaryWriter

  • add_graph:绘制模型结构

  • add_image:可视化初始训练样本

    writer = SummaryWriter(log_dir="runs/exp1")

    一次性可视化

    sample_imgs, _ = next(iter(train_loader))
    writer.add_graph(model, sample_imgs.to(device))
    writer.add_image("train/samples", torchvision.utils.make_grid(sample_imgs[:8]), 0)

3.2 Batch 训练循环内

位置:optimizer.step() 执行完成后

  • 每步都写 :add_scalar 记录batch loss、学习率

  • 间隔N步写 :add_histogram 权重梯度

    for batch_idx, (data, target) in enumerate(train_loader):
    # 前向、loss、反向、更新参数
    optimizer.zero_grad()
    output = model(data)
    loss = criterion(output, target)
    loss.backward()
    optimizer.step()

    复制代码
      global_step += 1
      # ===== 每步记录标量 =====
      writer.add_scalar("Train/batch_loss", loss.item(), global_step)
      
      # ===== 间隔步数记录直方图 =====
      if global_step % 100 == 0:
          for name, param in model.named_parameters():
              writer.add_histogram(f"weights/{name}", param, global_step)

3.3 Epoch 验证结束后

位置:跑完验证集、计算完验证指标后

  • add_scalar:验证loss、验证准确率

  • add_image:错误预测样本

  • add_text:抽样预测case

    验证阶段

    model.eval()
    with torch.no_grad():
    # 跑验证集,计算 val_loss, val_acc
    ...

    writer.add_scalar("Test/loss", val_loss, epoch)
    writer.add_scalar("Test/acc", val_acc, epoch)

3.4 全部训练结束后

位置:所有epoch跑完,脚本退出前

  • add_hparams:写入超参和最终指标

  • writer.close():关闭写入器,刷新剩余日志

    writer.add_hparams(hparams, {"final_acc": final_acc})
    writer.close()


四、完整可运行实战示例(CIFAR10 + MLP)

整合所有核心功能,可直接运行,适合作为模板修改。

复制代码
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
import torchvision
import os

# ========== 1. 配置与数据 ==========
batch_size = 64
epochs = 10
lr = 1e-3
log_dir = "runs/cifar10_mlp_demo"

transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5,0.5,0.5), (0.5,0.5,0.5))
])
train_dataset = datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
test_dataset = datasets.CIFAR10(root='./data', train=False, transform=transform)
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# ========== 2. 模型定义 ==========
class MLP(nn.Module):
    def __init__(self):
        super().__init__()
        self.flatten = nn.Flatten()
        self.fc1 = nn.Linear(3*32*32, 512)
        self.relu = nn.ReLU()
        self.fc2 = nn.Linear(512, 10)
    def forward(self, x):
        x = self.flatten(x)
        x = self.relu(self.fc1(x))
        x = self.fc2(x)
        return x

model = MLP().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=lr)

# ========== 3. 初始化TensorBoard ==========
if os.path.exists(log_dir):
    i = 1
    while os.path.exists(f"{log_dir}_{i}"):
        i += 1
    log_dir = f"{log_dir}_{i}"
writer = SummaryWriter(log_dir)

# 训练前:可视化模型图 + 样本图
sample_imgs, _ = next(iter(train_loader))
writer.add_graph(model, sample_imgs.to(device))
writer.add_image("train/samples", torchvision.utils.make_grid(sample_imgs[:8]), 0)

# ========== 4. 训练循环 ==========
global_step = 0
best_acc = 0.0

for epoch in range(epochs):
    model.train()
    running_loss = 0.0
    correct = 0
    total = 0

    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)
        optimizer.zero_grad()
        output = model(data)
        loss = criterion(output, target)
        loss.backward()
        optimizer.step()

        global_step += 1
        running_loss += loss.item()
        _, predicted = output.max(1)
        total += target.size(0)
        correct += predicted.eq(target).sum().item()

        # batch级标量
        writer.add_scalar("Train/batch_loss", loss.item(), global_step)
        writer.add_scalar("Train/lr", optimizer.param_groups[0]['lr'], global_step)

        # 间隔记录直方图
        if global_step % 100 == 0:
            for name, param in model.named_parameters():
                writer.add_histogram(f"weights/{name}", param, global_step)
                if param.grad is not None:
                    writer.add_histogram(f"grads/{name}", param.grad, global_step)

    # epoch级训练指标
    epoch_train_loss = running_loss / len(train_loader)
    epoch_train_acc = 100. * correct / total
    writer.add_scalar("Train/epoch_loss", epoch_train_loss, epoch)
    writer.add_scalar("Train/epoch_acc", epoch_train_acc, epoch)

    # ========== 验证 ==========
    model.eval()
    test_loss = 0
    correct_test = 0
    total_test = 0
    with torch.no_grad():
        for data, target in test_loader:
            data, target = data.to(device), target.to(device)
            output = model(data)
            test_loss += criterion(output, target).item()
            _, predicted = output.max(1)
            total_test += target.size(0)
            correct_test += predicted.eq(target).sum().item()

    epoch_test_loss = test_loss / len(test_loader)
    epoch_test_acc = 100. * correct_test / total_test
    best_acc = max(best_acc, epoch_test_acc)

    writer.add_scalar("Test/loss", epoch_test_loss, epoch)
    writer.add_scalar("Test/acc", epoch_test_acc, epoch)

    print(f"Epoch {epoch+1}/{epochs} | Train Acc: {epoch_train_acc:.2f}% | Test Acc: {epoch_test_acc:.2f}%")

# ========== 5. 训练结束 ==========
hparams = {"lr": lr, "batch_size": batch_size, "model": "MLP"}
metrics = {"best_test_acc": best_acc}
writer.add_hparams(hparams, metrics)
writer.close()
print(f"训练完成,最佳测试准确率: {best_acc:.2f}%")
print(f"日志路径: {log_dir}")

五、高频避坑指南

  1. 日志覆盖:不同实验必须用不同log目录,建议自动加序号后缀,避免曲线混在一起
  2. 忘记close :训练结束不调用close(),可能导致最后几条数据无法写入文件
  3. 日志体积爆炸:直方图、图片不要每步都记,间隔步数记录;大模型禁用全量权重直方图
  4. step不统一 :推荐统一用global_step做横轴,方便不同batch_size实验对比
  5. tag命名混乱 :坚持分组/指标格式,否则面板条目杂乱无章
  6. add_graph报错:输入张量shape必须和模型输入匹配,确保数据移到对应device
  7. 大模型慎用全量可视化:LLM参数规模大,全量直方图、计算图会严重拖慢训练、占用巨量磁盘

六、API速查表

API 对应面板 核心用途 学习优先级
add_scalar SCALARS loss、acc、lr等指标曲线 ⭐⭐⭐⭐⭐
add_hparams HPARAMS 超参数记录、多实验对比 ⭐⭐⭐⭐
add_histogram HISTOGRAMS 权重/梯度分布、排查梯度异常 ⭐⭐⭐
add_text TEXT 文本样本、case记录 ⭐⭐⭐⭐(NLP)
add_image IMAGES 图片样本、错分可视化 ⭐⭐⭐(CV)
add_graph GRAPHS 模型结构可视化 ⭐⭐
add_embedding PROJECTOR 嵌入向量降维 ⭐⭐
add_pr_curve PR CURVES 不均衡分类PR曲线 ⭐

七、不同场景使用建议

  • 入门练手 :只用 add_scalar + add_hparams 完全足够
  • CV图像任务 :额外加 add_image 查看样本和错例
  • LLM/NLP微调 :额外加 add_text 监控生成效果
  • 模型不收敛、梯度异常 :加上 add_histogram 排查
  • 消融实验/调参 :必加 add_hparams 做横向对比
相关推荐
Flynt1 小时前
日涨4000星的OSINT工具,我拿Linus的用户名和一个随机字符串实测了一遍
python·安全
Evand J3 小时前
【MATLAB例程介绍】(17)永磁同步电机(PMSM)基于自适应无迹卡尔曼滤波(AUKF)的电流噪声在线估计。测量噪声协方差自适应与转速估计对比仿真
开发语言·matlab·卡尔曼滤波·无迹卡尔曼滤波·pmsm·电机滤波
BaiPeter4 小时前
ZPL文件预览
开发语言·c++
曲径海盗4 小时前
【Python 学习第 53 天】列表基础
python·学习笔记·编程入门
Dumbness4 小时前
群里 @ 机器人发"查订单",指令一条都没命中:微信群机器人在路由前漏掉的几步归一化
python·微信
用户1614840955454 小时前
用 M4 Pro 跑羽毛球视频 AI 分析(二):TrackNet MPS 加速 3.2 倍、卡尔曼滤波补漏与自动回合剪辑
python
用户4019925568615 小时前
基于对话树回溯的社媒评论流买方意图识别与噪音过滤实践
python
用户0332126663675 小时前
使用 Python 加密或解密 PDF 文档
python
小七-七牛开发者5 小时前
让 Go 走得更远:用 LLGo 打通 Go 与 C/C++/Python 生态
python·编程·编译器·llvm·llgo