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
- add_embedding:高维向量降维可视化(PCA/t-SNE),对应 PROJECTOR 面板,适用于词向量、句子嵌入检索模型。
- 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}")
五、高频避坑指南
- 日志覆盖:不同实验必须用不同log目录,建议自动加序号后缀,避免曲线混在一起
- 忘记close :训练结束不调用
close(),可能导致最后几条数据无法写入文件 - 日志体积爆炸:直方图、图片不要每步都记,间隔步数记录;大模型禁用全量权重直方图
- step不统一 :推荐统一用
global_step做横轴,方便不同batch_size实验对比 - tag命名混乱 :坚持
分组/指标格式,否则面板条目杂乱无章 - add_graph报错:输入张量shape必须和模型输入匹配,确保数据移到对应device
- 大模型慎用全量可视化: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做横向对比