news 2026/4/29 1:56:04

PyTorch + TensorBoard 超实用笔记:从零开始监控你的模型训练

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PyTorch + TensorBoard 超实用笔记:从零开始监控你的模型训练

TensorBoard 是深度学习训练中必不可少的“仪表盘”。它可以实时展示损失曲线、准确率变化、模型结构、参数分布,甚至图像和音频。 好消息是:PyTorch 原生支持 TensorBoard,装一个包就能用,完全不用写 TensorFlow 代码。

本文会带你从零上手 TensorBoard 的核心 API,每个功能都配有参数表、代码示例和运行效果说明。这是一篇面向初学者的完整笔记


1.SummaryWriter– 创建日志记录器

作用:创建日志目录,TensorBoard 会读取该目录下的所有事件文件。

核心参数

参数类型默认值说明
log_dirstrNone日志保存的文件夹路径。若为None,会自动创建runs/下带时间戳的目录。推荐手动指定,如'logs/exp1'
commentstr''仅当log_dir=None时有效,追加到自动生成的文件夹名后。
flush_secsint120每隔多少秒将缓冲区数据强制写入磁盘。训练长时间任务可设为6030

示例

from torch.utils.tensorboard import SummaryWriter writer = SummaryWriter('logs/my_experiment') # 最常用 writer.close()

启动 TensorBoard,在终端中,切换到存放logs文件夹的目录,执行:

tensorboard --logdir=logs

2.add_scalar– 记录标量

作用:记录单个数值随步数的变化,生成曲线。

使用场景:训练损失、验证准确率、学习率、自定义指标等。

参数

参数类型说明
tagstr曲线的名字。用/可以分组,例如'Loss/train''Loss/val'会被归入同一组。
scalar_valuefloat/int要记录的值(y 轴)。
global_stepint步数(x 轴),通常用 epoch 或 iteration 编号。
walltimefloat可选,实际时间戳(一般不用)。

示例(记录训练损失和验证准确率):

for epoch in range(10): train_loss = ... # 计算得到 val_acc = ... writer.add_scalar('Loss/train', train_loss, epoch) writer.add_scalar('Accuracy/val', val_acc, epoch)

add_scalars– 在一张图上画多条曲线

需求:将训练损失和验证损失放在同一张图中对比。

writer = SummaryWriter('logs/two_curves') for epoch in range(10): train_loss = ... # 计算得到 val_loss = ... writer.add_scalars('Loss', {'train': train_loss, 'validation': val_loss}, epoch) writer.close()

在 SCALARS 面板中,会出现一张名为Loss的图,包含两条不同颜色的曲线。


3.add_image/add_images– 记录图像

作用:将图像(或一批图像拼成的网格)写入 TensorBoard。

使用场景:检查输入样本、生成模型的输出、特征图、分割掩码等。

参数add_image):

参数类型说明
tagstr图像组名称。
img_tensortorch.Tensor / numpy.ndarray图像数据。add_image要求形状(C, H, W)(H, W, C)
global_stepint步数,用于观察训练不同阶段的图像变化。
dataformatsstr指定维度顺序,默认'CHW'。若张量为(H, W, C)则需设为'HWC'

常用技巧:用torchvision.utils.make_grid将一批图像拼成网格,再用add_image记录。

示例(显示一批样本):

import torchvision.utils as vutils images, _ = next(iter(dataloader)) grid = vutils.make_grid(images, nrow=8, normalize=True) writer.add_image('samples', grid, 0)

也使用不同的tag分组展示

writer.add_image('cat', cat_img, global_step=0, dataformats='HWC') writer.add_image('dog', dog_img, global_step=0, dataformats='HWC')

4.add_graph– 记录模型计算图

作用:可视化模型的结构和数据流向。

使用场景:检查模型定义是否正确,调试前向传播形状。

参数

参数类型说明
modeltorch.nn.Module要可视化的模型实例。
input_to_modeltorch.Tensor 或 tuple示例输入,形状与真实输入一致。TensorBoard 会用它执行一次前向传播来追踪图。
verbosebool是否打印详细日志(默认False)。

示例

dummy_input = torch.randn(1, 1, 28, 28) writer.add_graph(model, dummy_input)

打开 GRAPHS 面板,可以像看电路图一样展开每个卷积、全连接、残差块,检查连接是否正确。


5.add_histogram– 记录张量分布(直方图)

作用:显示权重、梯度等张量的数值分布随时间的变化。

使用场景:诊断梯度消失/爆炸,观察参数更新是否健康。

参数

参数类型说明
tagstr直方图名称,通常用参数名,如'fc1.weight'
valuestorch.Tensor要统计分布的张量(任意形状,会自动展平)。
global_stepint步数。
binsstr/int分箱方式,默认'tensorflow'。也可指定整数箱数。
max_binsint最大箱数(当bins为字符串时有效)。

示例(每个 epoch 记录所有参数的权重和梯度):

for name, param in model.named_parameters(): writer.add_histogram(name, param, epoch) if param.grad is not None: writer.add_histogram(name + '.grad', param.grad, epoch)

怎么看结果(HISTOGRAMS 面板):

  • 正常:梯度值集中在 e-2 到 e0 之间,分布对称;权重直方图逐渐展宽。

  • 梯度消失:几轮之后梯度直方图缩成一条细线贴在 0 上。

  • 梯度爆炸:直方图出现很长的尾巴,数值远超其他部分。

掌握这个,你就能很快定位训练失败的根本原因。


6.add_hparams– 记录超参数

作用:系统化地记录一组超参数及其对应的最终指标,生成表格和图表。

使用场景:在你获得该组超参数对应的最终评价指标之后调用,通常有几种常见做法,

  • 单次实验结束后:训练完一个模型,得到最佳验证集准确率,立即调用add_hparams记录这组超参数和最终结果。

  • 多次实验的脚本中:比如用循环或并行搜索超参数时,每次实验跑完就调用一次,这样 TensorBoard 的 HParams 插件里会累积多组实验记录。

  • 注意不要在每个 epoch 都调用add_hparams是为最终结果设计的。如果你想记录训练过程中指标的变化,应该用add_scalar

参数

参数类型说明
hparam_dictdict超参数字典,键为超参数名(字符串),值为数值或字符串。
metric_dictdict指标字典,键为指标名(建议以'hparam/'开头),值为数值。
global_stepint可选,步数。

示例

# 待搜索的超参数组合 learning_rates = [0.001, 0.01] batch_sizes = [32, 64] epochs = 3 # 为了快速演示,只训练3轮;实际调参可以增加 # 根日志目录 base_log_dir = "./runs/mnist_hparam_search" for lr in learning_rates: for batch_size in batch_sizes: # 1) 为每组超参数创建独立的子目录 run_name = f"lr_{lr}_bs_{batch_size}" log_dir = os.path.join(base_log_dir, run_name) # 2) 使用 with 语句自动管理 SummaryWriter with SummaryWriter(log_dir) as writer: print(f"\n=== Running: {run_name} ===") # 3) 准备数据加载器(不同的 batch_size) train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True) val_loader = DataLoader(val_dataset, batch_size=1000, shuffle=False) # 4) 创建模型和优化器(可加入 dropout 等超参数,这里固定 dropout=0.2) model = SimpleCNN(dropout=0.2).to(device) optimizer = optim.Adam(model.parameters(), lr=lr) # 5) 训练多个 epoch,并记录曲线 best_acc = 0.0 for epoch in range(1, epochs + 1): train(model, device, train_loader, optimizer, epoch, writer) acc = validate(model, device, val_loader, writer, epoch) if acc > best_acc: best_acc = acc # 6) 最后记录超参数和最终验证准确率(使用最佳或最终) hparams = {'lr': lr, 'batch_size': batch_size} metrics = {'hparam/val_accuracy': best_acc} writer.add_hparams(hparams, metrics) print(f"Finished {run_name}, best accuracy = {best_acc:.2f}%")
lrbatch_sizeval_accuracy
0.001640.93
0.001320.91
0.01640.88
0.01320.85

运行脚本后,日志会保存在./runs/mnist_hparam_search/下,子目录结构为:

runs/mnist_hparam_search/ ├── lr_0.001_bs_32/ ├── lr_0.001_bs_64/ ├── lr_0.01_bs_32/ └── lr_0.01_bs_64/

启动 TensorBoard

tensorboard --logdir=./runs/mnist_hparam_search

浏览器操作

  • 打开http://localhost:6006

  • 左侧 Runs 列表中,你会看到四个可勾选的 Run(对应四个子目录)。

  • Scalars 面板:勾选多个 Run 后,train/lossval/lossval/accuracy曲线会叠加在同一张图上,颜色不同,可以清晰对比不同超参数下的收敛速度和最终性能。

  • HParams 面板:自动汇总所有 Run 的超参数和最终指标(hparam/val_accuracy),呈现为一个表格,支持排序和交互式图表(平行坐标、散点图)。


8.add_embedding– 高维嵌入可视化

深度学习模型(尤其是分类任务)通常会在最后一层(softmax 之前)输出一个高维特征向量(比如 128 维、512 维)。这个向量就是模型对输入数据的内部表示。add_embedding的作用就是把这些高维向量投影到2D 或 3D 空间(使用 PCA、t-SNE 或 UMAP),然后在 TensorBoard 的PROJECTOR插件中展示出来。你可以:

  • 用鼠标拖拽旋转/缩放三维空间

  • 观察不同类别的样本是否聚集在一起

  • 点击某个点,查看对应的原始图片或标签

  • 搜索特定标签的样本

参数

writer.add_embedding(mat, metadata=None, label_img=None, global_step=None, tag='default')
参数名类型说明
matTensororndarray形状为(N, D)的特征矩阵。N是样本数量,D是特征维度。必须是浮点数
metadatalistofstr可选。长度为N的标签列表,每个元素是该样本的类别名称(字符串)。如果不提供,只显示点坐标。
label_imgTensor可选。形状(N, C, H, W)的图像张量。当鼠标悬浮在投影空间的点上时,会显示该样本对应的图片缩略图。
global_stepint可选。记录当前是第几步/第几个 epoch,可用于观察训练过程中特征的可分性如何演变。
tagstr可选。嵌入向量的名称,默认'default'。如果你多次调用add_embedding(比如不同层或不同时间),可以用不同tag区分。

注意metadatalabel_img可以同时提供,也可以只提供其中一个。

示例(提取测试集特征并可视化):

features = torch.cat(features) # (N, D) labels = [f"class_{l}" for l in labels.tolist()] writer.add_embedding(features, metadata=labels, label_img=images, global_step=0)

打开PROJECTOR面板,选择不同的降维算法(PCA/t-SNE 等),就可以玩起来了。

完整ResNet18 + CIFAR-10代码:

import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader from torch.utils.tensorboard import SummaryWriter import torchvision import torchvision.transforms as transforms import torchvision.utils as vutils ​ # ---------- 模型定义---------- class ResNet18ForCIFAR(nn.Module): def __init__(self, num_classes=10): super().__init__() backbone = torchvision.models.resnet18(weights=torchvision.models.ResNet18_Weights.IMAGENET1K_V1) backbone.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False) backbone.maxpool = nn.Identity() self.features = nn.Sequential(*list(backbone.children())[:-1]) self.fc = nn.Linear(512, num_classes) ​ def forward(self, x): feat = self.features(x) feat = feat.view(feat.size(0), -1) out = self.fc(feat) return out, feat # 分类结果 + 特征 ​ # ---------- 数据 ---------- transform_train = transforms.Compose([ transforms.RandomCrop(32, padding=4), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)), ]) transform_val = transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)), ]) ​ trainset = torchvision.datasets.CIFAR10('./data', train=True, download=True, transform=transform_train) valset = torchvision.datasets.CIFAR10('./data', train=False, download=True, transform=transform_val) ​ train_loader = DataLoader(trainset, batch_size=128, shuffle=True, num_workers=2) val_loader = DataLoader(valset, batch_size=128, shuffle=False, num_workers=2) ​ device = torch.device("cuda" if torch.cuda.is_available() else "cpu") model = ResNet18ForCIFAR(num_classes=10).to(device) ​ # ---------- TensorBoard ---------- writer = SummaryWriter('runs/cifar10_demo') ​ # 记录模型图(注意:dummy input 形状匹配) dummy_input = torch.randn(1, 3, 32, 32).to(device) writer.add_graph(model, dummy_input) ​ # 记录一批训练样本图像 sample_images, _ = next(iter(train_loader)) grid = vutils.make_grid(sample_images[:16], nrow=4, normalize=True) writer.add_image('train_samples', grid, 0) ​ # ---------- 训练 ---------- optimizer = optim.Adam(model.parameters(), lr=0.001) criterion = nn.CrossEntropyLoss() ​ for epoch in range(5): model.train() train_loss = 0.0 for inputs, labels in train_loader: inputs, labels = inputs.to(device), labels.to(device) optimizer.zero_grad() outputs, _ = model(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step() train_loss += loss.item() * inputs.size(0) train_loss /= len(trainset) ​ writer.add_scalar('Loss/train', train_loss, epoch) ​ for name, param in model.named_parameters(): writer.add_histogram(name, param, epoch) if param.grad is not None: writer.add_histogram(name+'.grad', param.grad, epoch) ​ print(f"Epoch {epoch+1}, Loss: {train_loss:.4f}") ​ # ---------- 提取特征并调用 add_embedding ---------- model.eval() feature_list, label_list, image_list = [], [], [] with torch.no_grad(): for inputs, labels in val_loader: inputs, labels = inputs.to(device), labels.to(device) _, feats = model(inputs) feature_list.append(feats.cpu()) label_list.append(labels.cpu()) image_list.append(inputs.cpu()) ​ features = torch.cat(feature_list, dim=0) labels = torch.cat(label_list, dim=0) images = torch.cat(image_list, dim=0) metadata = [f"class_{l}" for l in labels.tolist()] ​ embed_writer = SummaryWriter('runs/cifar10_embedding') embed_writer.add_embedding(features, metadata=metadata, label_img=images, global_step=0) embed_writer.close() ​ writer.close() print("训练完成!运行 tensorboard --logdir=runs 查看结果")

如果本文对你有帮助,欢迎点赞 👍、收藏 ⭐、评论 💬!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/29 1:51:21

亚马逊Fire TV Cube 2022解析:Amlogic POP1-G芯片与流媒体性能

1. 亚马逊Fire TV Cube 2022深度解析:Amlogic POP1-G芯片首秀作为一款定位中高端的流媒体设备,2022款Fire TV Cube最引人注目的就是那颗神秘的Amlogic POP1-G处理器。这颗芯片此前从未在公开资料中出现过,但从架构来看,它与Khadas…

作者头像 李华
网站建设 2026/4/29 1:44:27

全球首个GPU加速5G Open RAN技术解析与应用

1. 全球首个GPU加速的5G Open RAN落地解析当NTT DOCOMO在2023年宣布其商用5G网络中部署了全球首个基于NVIDIA Aerial平台的GPU加速Open RAN解决方案时,整个电信行业都为之震动。这标志着传统专用电信设备向软件化、云化架构转型的关键突破。作为一名长期跟踪无线接入…

作者头像 李华
网站建设 2026/4/29 1:44:22

收藏!2026最新7个低门槛AI岗位|小白普通程序员大模型转型必看

结合脉脉2026最新行业调研数据来看:AI赛道就业热度持续爆发,对比往年数据,AI全品类岗位招聘规模同比暴涨10倍以上,涌入赛道的求职人群更是达到11倍增幅!时至今日,人工智能早已脱离概念化的未来赛道标签&…

作者头像 李华