在深度/机器学习模型训练时,有必要展示或者记录每个batch/epoch的各种损失以及精度信息。除了最简单的print方式,目前有多种库提供了高级的API实现方式。下面就scGPT项目的学习过程,整理三种方式。

1. logging库

  • logging库时python的内置模块,设计用于打印输出多种用户自定义的日志信息
1
2
3
4
5
6
import sys
import logging
# 创建一个日志记录器对象,并设置name
logger = logging.getLogger("test_log")
logger.setLevel(logging.INFO)
# 设置输出级别为INFO及以上

DEBUG: 详细的信息,通常只在诊断问题时使用。 INFO: 确认一切按预期工作的消息。 WARNING: 表示某些不期望的情况或潜在问题。 ERROR: 更严重的问题,程序未能执行某些功能。 CRITICAL: 严重错误,程序可能无法继续运行。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 创建控制台处理器
console_handler = logging.StreamHandler() # 默认sys.stderr (sys.stdout备选)

# 创建格式器并添加到处理器
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)

# 将处理器添加到 logger
logger.addHandler(console_handler)

logger.info("This is one info")
# 2024-10-22 21:05:16,441 - test_log - INFO - This is one info

epoch = 0
batch = 1
total_batch = 100
acc = 0.85

logger.info(
    f"| epoch {epoch:3d} | {batch:3d}/{total_batch:3d} batches | "
    f"Accuray {acc}"
)
# 2024-10-22 21:10:02,747 - test_log - INFO - | epoch   0 |   1/100 batches | Accuray 0.85
  • 设置日志输出到指定文件
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 额外设置一个handler,将log同时保存到本地日志文件中
logger.handlers
# [<StreamHandler stderr (NOTSET)>]

file_handler = logging.FileHandler('test.log')
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)

logger.handlers
# [<StreamHandler stderr (NOTSET)>,
#  <FileHandler /home00/liss/Study/cellxgene/test.log (NOTSET)>]

# 删除刚才新增的handler
# logger.removeHandler(logger.handlers[2])

2. tensorboard库

TensorBoard 是一个用于可视化机器学习实验的工具,可与 PyTorch 等框架结合使用。

参考: https://pytorch.org/docs/stable/tensorboard.html https://pytorch.org/tutorials/recipes/recipes/tensorboard_with_pytorch.html https://pytorch.org/tutorials/intermediate/tensorboard_profiler_tutorial.html?highlight=tensorboard 介绍了如何使用tensorboard记录GPU相关数据,暂不记录。

1
2
3
4
5
import numpy as np
import torch
from torch.utils.data import TensorDataset, DataLoader
from torch.utils.tensorboard import SummaryWriter
# from tensorboardX import SummaryWriter
  • 创建一个SummaryWriter对象,其中设置log文件的保存路径
    • 一级目录runs: 表示本次模型相关的日志组
    • 二级目录experiment_1: 表示本次训练的日志内容
1
writer = SummaryWriter(log_dir='runs/experiment_1')

一级目录(runs)下可以放置多次训练(experiment_2, experiment_3, …)的日志结果,用于比较分析。

  • 然后在每次迭代训练过程,使用writer.add_scalar()记录目标的性能指标、训练参数等信息
    • 参数1 tag :例如Loss/train, 进行命名。若使用/,则会进行分组标注,例如 Loss类、Accuracy类
    • 参数2 scalar_value:记录的标量值
    • 参数3 global_step:当前的step数
  • 当训练完成后,需要显式的关闭tensorboard
 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
x = torch.arange(-5, 5, 0.1).view(-1, 1)
y = -5 * x + 3 * x * x + 0.1 * torch.randn(x.size())

dataloader = DataLoader(TensorDataset(x, y), batch_size=5, shuffle=True)

model = torch.nn.Linear(1, 1)
criterion = torch.nn.MSELoss()
optimizer = torch.optim.SGD(model.parameters(), lr = 0.1)

def train_model(iter):
    for epoch in range(iter):
        for i,  (x_dat, y_dat) in enumerate(dataloader):
            y_hat = model(x_dat)
            loss = criterion(y_hat, y_dat)
            writer.add_scalar("Loss/train", loss.item(), epoch*len(dataloader) + i)
            # random value just for demonstration
            writer.add_scalar("Loss/test", np.random.random(), epoch*len(dataloader) + i)
            writer.add_scalar("Accuracy/train", np.random.random(), epoch*len(dataloader) + i)
            writer.add_scalar("Accuracy/test", np.random.random(), epoch*len(dataloader) + i)
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()

train_model(5)
writer.flush() # make sure that all pending events have been written to disk.
writer.close() # close tensorboard

此外,还有其他记录方式,例如add_histogram等。但觉得还是add_scalar最常用。

  • 最后在linux终端,通过命令打开网页可视化结果。
1
2
3
# linux命令行运行,指定日志组路径
tensorboard --logdir=runs
# http://localhost:6006/
image-20241022222658673

3. wandb库

Weights & Biases (WandB) 是一个用于机器学习实验管理的工具,提供了一系列功能来帮助开发者跟踪、可视化和分享实验结果。

参考:

https://wandb.ai/site

https://docs.wandb.ai/tutorials/pytorch/

  1. 初始化
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import wandb
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset

# 登陆账户,使用用户的API
wandb.login(key="User settings → API keys")

# 初始化 WandB
wandb.init(
    project="pytorch-simulated-data",
    config={
        "epochs": 5,
        "batch_size": 16,
        "learning_rate": 0.01
    }
)

# 获取配置
config = wandb.config
  1. 加载数据和模型
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 模拟数据
X = torch.randn(1000, 20)  # 1000个样本,每个样本有20个特征
y = (torch.sum(X, dim=1) > 0).long()  # 简单的线性可分任务

# 创建数据集和数据加载器
dataset = TensorDataset(X, y)
train_loader = DataLoader(dataset, batch_size=config.batch_size, shuffle=True)

# 定义简单的模型
class SimpleNN(nn.Module):
    def __init__(self):
        super(SimpleNN, self).__init__()
        self.fc1 = nn.Linear(20, 64)
        self.fc2 = nn.Linear(64, 2)

    def forward(self, x):
        x = torch.relu(self.fc1(x))
        x = self.fc2(x)
        return x

model = SimpleNN()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=config.learning_rate)
  1. 记录训练损失以及其它个性化参数
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 训练模型
for epoch in range(config.epochs):
    model.train()
    total_loss = 0
    for batch_idx, (data, target) in enumerate(train_loader):
        optimizer.zero_grad()
        output = model(data)
        loss = criterion(output, target)
        loss.backward()
        optimizer.step()
        total_loss += loss.item()

    # 记录损失
    wandb.log({"epoch": epoch, "loss": total_loss / len(train_loader)})

# 结束 WandB 运行
wandb.finish()

上面只记录了最简单的用法,有时间再学习下其它高级功能。

wandb使用教程(持续更新ing…)_wandb 官网-CSDN博客