Please enable Javascript to view the contents

使用 TensorBoard 可视化 PyTorch 训练过程

 ·  ☕ 4 分钟

1. 什么是 TensorBoard

TensorBoard 主要是用来监控模型的各种指标的变化,比如 accuracy、loss、各种层的权重分布等。

TensorBoard 是 TensorFlow 的一个可视化工具,支持标量、文本、图像、音频、视频和 Embedding 等多种数据可视化,但是 PyTorch 也可以使用 TensorBoard。

2. 安装 tensorboard

1
pip install tensorboard

3. 使用 tensorboard

3.1 FileWriter 和 SummaryWriter

torch.utils.tensorboard 中有两个写入器,FileWriterSummaryWriter

  • FileWriter 是一个底层的写入器,直接将事件数据写入到 TensorBoard 的事件文件
  • SummaryWriter 是对 FileWriter 的封装,提供了更高级别的 API,可以更方便地记录标量、图像、音频、文本等数据

一般情况下,我们使用 SummaryWriter 即可。

3.2 设置存储目录

1
writer = SummaryWriter('./log')

Tensorboard 记录的内容会保存在 log 目录下,文件格式是 events.out.tfevents.xxxxx。

3.3 SummaryWriter 包含的方法

  • add_hparams

用于记录超参数及其对应的指标值(如损失、精度等)

1
2
3
hparams = {'lr': 0.01, 'batch_size': 32}
metrics = {'accuracy': 0.98, 'loss': 0.05}
writer.add_hparams(hparams, metrics)
  • add_scalar

记录单一标量值(如损失值、学习率)

1
writer.add_scalar('Loss/train', train_loss, epoch)
  • add_scalars

同时记录多个标量值,通常用于比较(如训练损失和验证损失)

1
writer.add_scalars('Loss', {'train': train_loss, 'val': val_loss}, epoch)
  • add_tensor

添加任意形状的张量,通常用于调试和查看数据。

1
2
tensor = torch.rand(3, 3)
writer.add_tensor('Tensor', tensor)
  • add_histogram

添加张量数据的直方图(例如模型权重分布)

1
writer.add_histogram('Weights', model.fc.weight, epoch)
  • add_histogram_raw

手动添加直方图数据(较少用到),需要构造 minmaxbucket 数据

  • add_image

添加单张图像,支持输入 PyTorch 张量或 NumPy 数组

1
writer.add_image('Image', image_tensor, epoch)
  • add_images

添加多张图像(例如一批数据)。

1
writer.add_images('Images', images_tensor, epoch)
  • add_image_with_boxes

可视化图像上的边界框(如目标检测结果)

1
2
boxes = torch.tensor([[10, 20, 50, 60]])  # (x_min, y_min, x_max, y_max)
writer.add_image_with_boxes('ImageWithBoxes', image_tensor, boxes)
  • add_figure

添加 matplotlib 绘制的图形(如自定义的可视化图)

1
2
3
4
5
import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.plot([0, 1, 2], [3, 4, 5])
writer.add_figure('CustomPlot', fig)
  • add_video

添加视频数据,用于可视化时序任务或生成模型的输出

1
writer.add_video('GeneratedVideo', video_tensor, fps=10)
  • add_audio

添加音频数据,用于语音任务的结果展示

1
writer.add_audio('Speech', audio_tensor, sample_rate=16000)
  • add_text

添加文本数据(如日志、注意力权重的描述)

1
writer.add_text('TrainingLog', 'Epoch: 5, Loss: 0.05', epoch)
  • add_onnx_graph

可视化导出的 ONNX 模型结构

1
writer.add_onnx_graph(onnx_model)
  • add_graph

可视化 PyTorch 模型的计算图

1
writer.add_graph(model, input_tensor)
  • add_embedding

可视化嵌入空间(如词嵌入、特征分布)

1
writer.add_embedding(embeddings, metadata=labels)
  • add_pr_curve

添加 PR 曲线(Precision-Recall),用于评估模型的分类性能

1
writer.add_pr_curve('PRCurve', labels, predictions)
  • add_pr_curve_raw

手动添加 PR 曲线的数据(较少用到)

  • add_custom_scalars_multilinechart

添加多线图,用于自定义多种标量值的可视化

1
2
layout = {'CustomChart': {'Multiline': ['train/loss', 'val/loss']}}
writer.add_custom_scalars_multilinechart(layout)
  • add_custom_scalars_marginchart

添加带有上下限边界的图表。

  • add_custom_scalars

组合多个自定义图表布局。

  • add_mesh

添加 3D 网格数据(如点云、模型生成的 3D 表面)

1
2
3
vertices = torch.rand(100, 3)
colors = torch.rand(100, 3)
writer.add_mesh('Mesh', vertices=vertices, colors=colors)
  • close

将缓冲区的数据写入到事件文件,释放文件资源。

3.4 启动服务

1
tensorboard --logdir=./log --host=0.0.0.0 --port=6006
  • --logdir 指定 TensorBoard 读取的日志文件目录
  • --host 指定主机地址,如果不限制访问,可以设置为 0.0.0.0。当设置为 127.0.0.1 时,只能本地访问
  • --port 指定端口,默认为 6006

4. 完整示例

  • 新增 tensorboard 相关的代码

创建训练脚本 mnist.py,内容如下:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.tensorboard import SummaryWriter

# 定义 TensorBoard 的写入器
writer = SummaryWriter("./log")

# 定义超参数
BATCH_SIZE = 512
EPOCHS = 20
LEARNING_RATE = 1e-3
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# 数据加载和预处理,在 data 目录下载 MNIST 数据集
train_loader = torch.utils.data.DataLoader(
    datasets.MNIST(
        "data",
        train=True,
        download=True,
        transform=transforms.Compose(
            [
                transforms.RandomRotation(10),  # 随机旋转,提高模型的泛化能力
                transforms.RandomAffine(
                    0, shear=10, scale=(0.8, 1.2)
                ),  # 仿射变换,提高模型的泛化能力
                transforms.ToTensor(),
                transforms.Normalize((0.1307,), (0.3081,)),
            ]
        ),
    ),
    batch_size=BATCH_SIZE,
    shuffle=True,
)

test_loader = torch.utils.data.DataLoader(
    datasets.MNIST(
        "data",
        train=False,
        transform=transforms.Compose(
            [transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))]
        ),
    ),
    batch_size=BATCH_SIZE,
    shuffle=False,
)


# 定义模型的结构,这里是一个简单的卷积神经网络
class ConvNet(nn.Module):
    def __init__(self):
        super(ConvNet, self).__init__()
        self.conv1 = nn.Conv2d(1, 32, 3)
        self.conv2 = nn.Conv2d(32, 64, 3)
        self.dropout1 = nn.Dropout(0.25)
        self.fc1 = nn.Linear(64 * 5 * 5, 128)
        self.dropout2 = nn.Dropout(0.5)
        self.fc2 = nn.Linear(128, 10)

    def forward(self, x):
        x = F.relu(self.conv1(x))
        x = F.max_pool2d(x, 2)
        x = F.relu(self.conv2(x))
        x = F.max_pool2d(x, 2)
        x = self.dropout1(x)
        x = x.view(-1, 64 * 5 * 5)
        x = F.relu(self.fc1(x))
        x = self.dropout2(x)
        x = self.fc2(x)
        return F.log_softmax(x, dim=1)


# 初始化模型和优化器
model = ConvNet().to(DEVICE)
optimizer = optim.Adam(model.parameters(), lr=LEARNING_RATE)
scheduler = torch.optim.lr_scheduler.StepLR(
    optimizer, step_size=5, gamma=0.5
)  # 学习率衰减

# 添加模型结构到 TensorBoard
example_input = torch.randn(1, 1, 28, 28).to(DEVICE)  # 创建一个示例输入
writer.add_graph(model, example_input)  # 添加模型图

# 训练函数
def train(model, device, train_loader, optimizer, epoch):
    model.train()
    running_loss = 0.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 = F.nll_loss(output, target)
        loss.backward()
        optimizer.step()

        running_loss += loss.item()

        if (batch_idx + 1) % 30 == 0:
            print(
                f"Train Epoch: {epoch} [{batch_idx * len(data)}/{len(train_loader.dataset)} "
                f"({100. * batch_idx / len(train_loader):.0f}%)]\tLoss: {loss.item():.6f}"
            )

    # 将训练的平均损失写入 TensorBoard
    writer.add_scalar("Loss/train", running_loss / len(train_loader), epoch)


# 测试函数
def test(model, device, test_loader, epoch):
    model.eval()
    test_loss = 0
    correct = 0
    with torch.no_grad():
        for data, target in test_loader:
            data, target = data.to(device), target.to(device)
            output = model(data)
            test_loss += F.nll_loss(output, target, reduction="sum").item()
            pred = output.argmax(dim=1, keepdim=True)
            correct += pred.eq(target.view_as(pred)).sum().item()
    test_loss /= len(test_loader.dataset)
    accuracy = 100.0 * correct / len(test_loader.dataset)
    print(
        f"\nTest set: Average loss: {test_loss:.4f}, Accuracy: {correct}/{len(test_loader.dataset)} ({accuracy:.2f}%)\n"
    )

    # 将测试的损失和准确率写入 TensorBoard
    writer.add_scalar("Loss/test", test_loss, epoch)
    writer.add_scalar("Accuracy/test", accuracy, epoch)


# 训练和测试循环
for epoch in range(1, EPOCHS + 1):
    train(model, DEVICE, train_loader, optimizer, epoch)
    test(model, DEVICE, test_loader, epoch)
    scheduler.step()  # 更新学习率

# 保存模型
torch.save(model.state_dict(), "mnist_cnn.pth")
print("Model saved to mnist_cnn.pth")

# 关闭 TensorBoard 写入器
writer.close()
  • 开始训练
1
python mnist.py

在 log 目录下会生成 events.out.tfevents.xxxxx 文件。

  • 启动 TensorBoard
1
2
3
tensorboard --logdir=./log --host=127.0.0.1 --port=6006

TensorBoard 2.18.0 at http://127.0.0.1:6006/ (Press CTRL+C to quit)
  • 查看 TensorBoard

本地浏览器打开 http://127.0.0.1:6006/ 查看 TensorBoard 的界面。即使没有训练完成,也可以看到训练过程中的损失和准确率的变化,如下图:

graphs 标签页可以查看模型的计算图,如下图:


微信公众号
作者
微信公众号