目录
1. TensorBoard 的发展历史与原理
2. TensorBoard 的常见操作
3. CIFAR-10 实战总结:MLP vs CNN
MLP (多层感知机) 实战
CNN (卷积神经网络) 实战
附例:
1. TensorBoard 的发展历史与原理
-
发展历史:
-
诞生:2015年随 TensorFlow 发布,初衷是为深度学习提供可视化监控“屏幕”。
-
演进:2016-2018年新增图像/音频、直方图及多运行对比功能;2019年后兼容 PyTorch,成为通用的可视化工具。
-
-
核心原理:
-
写日志(存):在训练过程中,将损失值、准确率、模型结构等数据写入特定的 .tfevents 日志文件。
-
网页展示(看):启动本地网页服务(通常为 http://localhost:6006),自动读取并渲染日志文件,实现训练过程的实时监控。
-
2. TensorBoard 的常见操作
TensorBoard 通过 SummaryWriter 对象来记录不同类型的数据:
| 功能模块 | 代码接口 | 说明 |
| 日志管理 | SummaryWriter(log_dir) | 建议通过代码自动管理目录(如添加 _1, _2 后缀),方便对比不同实验。 |
| 标量 (Scalars) | writer.add_scalar() | 记录 Loss、Accuracy、学习率等随时间变化的单值曲线。 |
| 模型图 (Graphs) | writer.add_graph() | 可视化模型计算图,查看卷积层、全连接层等层次结构。 |
| 图像 (Images) | writer.add_image() | 展示训练样本或预测错误的图像,直观分析模型表现。 |
| 直方图 (Histograms) | writer.add_histogram() | 监控权重 (Weights) 和梯度 (Gradients) 的分布,诊断梯度消失/爆炸。 |
| 文本 (Text) | writer.add_text() | 记录预测标签、超参数说明等文本信息。 |
启动方式:在终端执行 tensorboard –logdir=runs。
3. CIFAR-10 实战总结:MLP vs CNN
在 CIFAR-10 数据集上的实战展示了 TensorBoard 如何辅助模型优化:
MLP (多层感知机) 实战
-
特点:使用 nn.Flatten 将 32x32x3 的图像展平为 3072 维向量。
-
可视化重点:通过 SCALARS 查看 Loss 下降是否平稳;使用 IMAGES 查看原始图像是否正确加载。
-
表现:由于 MLP 丢失了图像的空间结构信息,其测试准确率通常低于 CNN。
CNN (卷积神经网络) 实战
-
特点:引入卷积层 (Conv2d)、批量归一化 (BatchNorm2d) 和池化层 (MaxPool2d),通过特征图提取空间特征。
-
TensorBoard 增强功能:
-
学习率动态监控:配合 ReduceLROnPlateau 调度器,可在 SCALARS 中看到学习率阶梯式下降的过程。
-
Badcase 分析:通过 add_image 记录测试集中预测错误的样本及其真实标签,帮助开发者定位模型弱点(如难以分辨猫和狗)。
-
层权重监控:通过 HISTOGRAMS 观察不同卷积层的权重变化,确保模型在正常学习。
-
-
表现:CNN 在图像任务中表现更优,通过 20 个 Epoch 的训练,准确率可稳步提升(实战中可达 80% 以上)。
附例:
对 resnet18 在 cifar10 上采用微调策略下,用 tensorboard 监控训练过程
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
from torchvision import models, transforms
from torch.utils.tensorboard import SummaryWriter
import os
# ==========================================
# 1. 环境配置与 SummaryWriter 初始化
# ==========================================
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# 日志存放于 runs 文件夹下,包含两个阶段的记录
writer = SummaryWriter('runs/resnet18_stage_finetune')
# ==========================================
# 2. 数据准备:ResNet 预训练模型期望输入为 224×224
# ==========================================
transform = transforms.Compose([
transforms.Resize(224),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
# 使用 ImageNet 的均值和方差进行标准化
transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))
])
trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=True)
# ==========================================
# 3. 模型构建与微调设置
# ==========================================
# 加载预训练权重
model = models.resnet18(weights=models.ResNet18_Weights.IMAGENET1K_V1)
# 修改分类头(fc层):CIFAR-10 有 10 个类别
num_ftrs = model.fc.in_features
model.fc = nn.Linear(num_ftrs, 10)
model = model.to(device)
# 记录模型计算图 (Graphs)
dummy_input = torch.randn(1, 3, 224, 224).to(device)
writer.add_graph(model, dummy_input)
# ==========================================
# 4. 训练核心函数 (集成详细监控)
# ==========================================
def train_model(epochs, start_step, stage_name, current_optimizer):
global_step = start_step
criterion = nn.CrossEntropyLoss()
for epoch in range(epochs):
model.train()
running_loss = 0.0
for i, (inputs, labels) in enumerate(trainloader):
inputs, labels = inputs.to(device), labels.to(device)
current_optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
current_optimizer.step()
# 每 100 步记录一次数据
if i % 100 == 99:
avg_loss = (running_loss + loss.item()) / 100
# 记录标量信息
writer.add_scalar(f'Loss/{stage_name}', avg_loss, global_step)
# 记录最后几层的梯度直方图 (Histograms)
for name, param in model.named_parameters():
if 'fc' in name or 'layer4' in name:
if param.requires_grad and param.grad is not None:
writer.add_histogram(f'Gradients_{stage_name}/{name}', param.grad, global_step)
print(f"[{stage_name}] Epoch {epoch+1}, Step {i+1} – Loss: {avg_loss:.4f}")
running_loss = 0.0
global_step += 1
return global_step
# ==========================================
# 阶段 1:冻结特征提取层,仅训练分类头
# ==========================================
print("\\n>>> 开始阶段 1:冻结底层,训练 FC 层…")
for param in model.parameters():
param.requires_grad = False
# 解冻最后的全连接层
for param in model.fc.parameters():
param.requires_grad = True
# 优化器仅包含 FC 层参数
optimizer_stage1 = optim.Adam(model.fc.parameters(), lr=0.001)
last_step = train_model(epochs=1, start_step=0, stage_name="Freeze", current_optimizer=optimizer_stage1)
# ==========================================
# 阶段 2:全量解冻,精细微调
# ==========================================
print("\\n>>> 开始阶段 2:全量解冻,全局微调…")
for param in model.parameters():
param.requires_grad = True
# 使用更小的学习率以保护预训练权重
optimizer_stage2 = optim.Adam(model.parameters(), lr=0.0001)
train_model(epochs=2, start_step=last_step, stage_name="Unfreeze", current_optimizer=optimizer_stage2)
# ==========================================
# 5. 资源清理
# ==========================================
writer.close()
print("\\n训练流程全部结束。")
print("查看监控:tensorboard –logdir=runs")


