数据加载与可视化(Pytorch)
数据的加载与可视化
TensorBoard 的使用
● ✅ 监控训练损失与准确率曲线
● ✅ 对比多次实验
● ✅ 观察输入与中间层图像
● ✅ 跟踪参数/梯度的分布变化
● ✅ 高维嵌入向量的投影可视化
● ✅ 超参数影响分析
安装与配置
TensorBoard 是 TensorFlow 官方推出的可视化工具,也已被 PyTorch 等框架原生支持。它能帮我们直观地监控训练过程中的损失曲线、准确率变化、模型结构、参数分布等,是调试和优化深度学习模型的利器。
安装 pip install tensorboard
TensorBoard 是一个本地 Web 服务,通过读取你写入的事件文件(event files)来显示可视化面板。基本启动命令(假设日志目录为 ./runs 或 ./logs):
tensorboard --logdir=./runs
常用参数:
● --logdir:指定日志目录,支持用逗号分隔多个目录进行对比。
● --port:指定端口,默认 6006。
● --bind_all:允许局域网内其他设备访问。
启动后,在浏览器打开 http://localhost:6006 即可看到界面。
PyTorch 通过 torch.utils.tensorboard.SummaryWriter 写入数据。
from torch.utils.tensorboard import SummaryWriter
日志会保存在 runs/experiment_1 目录下
writer = SummaryWriter("runs/experiment_1")
可以给不同实验建立不同子目录,方便在 TensorBoard 中对比。
标量记录
add_scalar 用于记录单个标量,add_scalars 可在同一图表中同时绘制多条曲线。
示例:训练循环中记录损失
for epoch in range(num_epochs):
train_loss = train_one_epoch(...)
val_loss = validate(...)
记录到 TensorBoard,横轴为 global_step
writer.add_scalar("Loss/train", train_loss, epoch)
writer.add_scalar("Loss/val", val_loss, epoch)
用 add_scalars 一次性对比
writer.add_scalars("Loss/compare", {
"train": train_loss,
"val": val_loss
}, epoch)
writer.close() # 训练结束记得关闭
标签中的 / 会在 TensorBoard 界面中形成层级分组,例如 Loss/train 和 Loss/val 会自动放在 “Loss” 组下。
图片记录
训练过程中将输入图像、网络中间层的特征图或生成对抗网络(GAN)的生成结果记录下来,可以直观地观察数据增强效果、图像重建质量或生成器的演化过程。
单个图像使用 add_image,多个图像用小批量展示使用 add_images。图像数据要求是 CHW 或 HWC 格式,数值范围通常为 [0, 1] 或 [0, 255],可以在参数中指定 dataformats。
from torch.utils.tensorboard import SummaryWriter
import torchvision
writer = SummaryWriter("runs/image_example")
# 取出一批数据
images, labels = next(iter(train_loader))
# images shape: (batch, C, H, W),数值范围 [0, 1] 或经归一化后
# 1. 记录一批图像,形成网格(自动排列成网格图)
img_grid = torchvision.utils.make_grid(images)
writer.add_image("batch_images", img_grid, global_step=0)
# 2. 直接记录多张图像(TensorBoard 会分别展示)
writer.add_images("batch_multi", images, global_step=0)
# 如果需要将中间特征图 (N,C,H,W) 记录,可以先取部分通道或取平均
feature_maps = model.get_features(images) # shape: [N, C, H, W]
# 为了展示,只取前8个通道,并转换为单批次图像展示
for i in range(8):
writer.add_image(f"feature_channel_{i}", feature_maps[0, i:i+1, :, :], 0, dataformats='CHW')
writer.close()
注意:如果图像数据已被归一化(例如 ImageNet 的 mean/std),直接可视化会显得很怪异,需要反归一化后再记录。
嵌入向量投影 (Embedding Projector)
将高维数据(如词向量、图像特征、最后一层输出)降维到 2D 或 3D,用于观察聚类效果、类别可分性,是模型调试中非常有用的可视化手段。
Transforms的使用
在深度学习中,Transforms(变换) 是将原始数据(尤其是图像)转换成适合模型训练的形式的重要工具。PyTorch 的 torchvision.transforms 模块提供了一系列常用操作,包括裁剪、翻转、归一化、转张量等,并支持组合和自定义。合理使用 transforms 不仅能统一数据格式,还能通过数据增强有效提升模型泛化能力。
为什么
- 类型转换:原始图片通常是 PIL.Image 或 numpy.ndarray,需要转成 torch.Tensor。
- 尺寸统一:不同图片大小不一,需 resize 或 crop 到固定尺寸。
- 数值归一化:像素值从 [0, 255] 映射到 [0, 1] 或标准化到零均值单位方差。
- 数据增强:随机翻转、旋转、色彩抖动等,增加训练样本多样性,减少过拟合。
基本用法:Compose 与常用变换
torchvision.transforms 中的操作分为两类:作用于 PIL Image 的,以及作用于 Tensor 的。通常用 transforms.Compose 将它们串联起来。
import torchvision.transforms as transforms
transform = transforms.Compose([
transforms.Resize((224, 224)), # 缩放到 224x224
transforms.RandomHorizontalFlip(), # 随机水平翻转(增强)
transforms.ToTensor(), # 转成 Tensor 并归一化到 [0,1]
transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]) # 标准化(ImageNet参数)
])
注意:ToTensor() 会将 PIL Image (0-255) 自动转换为 Tensor (0.0-1.0),之后 Normalize 才能生效。
常用变换速查表
| 类别 | 变换 | 说明 |
|---|---|---|
| 形状变换 | Resize(size) | 缩放至指定尺寸(可能改变宽高比) |
| CenterCrop(size) | 中心裁剪 | |
| RandomCrop(size) | 随机位置裁剪 | |
| RandomResizedCrop(size, scale=(0.08,1.0)) | 随机区域裁剪后缩放,常用作训练增强 | |
| 翻转/旋转 | RandomHorizontalFlip(p=0.5) | 随机水平翻转 |
| RandomVerticalFlip(p=0.5) | 随机垂直翻转 | |
| RandomRotation(degrees) | 随机旋转,degrees 可以是单值或范围 | |
| 颜色/亮度 | ColorJitter(brightness, contrast, saturation, hue) | 随机调整亮度、对比度、饱和度、色调 |
| Grayscale(num_output_channels=1) | 转灰度图 | |
| 张量操作 | ToTensor() | 将 PIL Image 或 ndarray (HWC, 0-255) → Tensor (CHW, 0-1) |
| Normalize(mean, std) | 对 Tensor 标准化,输入需是 [0,1] 的 Tensor | |
| ConvertImageDtype(torch.float) | 转换 Tensor 数据类型 |
内置数据集使用
from torchvision.datasets import ImageFolder
from torch.utils.data import DataLoader
train_transform = transforms.Compose([
transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.ColorJitter(brightness=0.2, contrast=0.2),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
val_transform = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
train_dataset = ImageFolder(root='./data/train', transform=train_transform)
val_dataset = ImageFolder(root='./data/val', transform=val_transform)
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=32, shuffle=False)
自定义数据集使用
from torch.utils.data import Dataset
from PIL import Image
class MyDataset(Dataset):
def __init__(self, img_paths, labels, transform=None):
self.img_paths = img_paths
self.labels = labels
self.transform = transform
def __getitem__(self, idx):
img = Image.open(self.img_paths[idx]).convert('RGB')
label = self.labels[idx]
if self.transform:
img = self.transform(img)
return img, label
def __len__(self):
return len(self.img_paths)
使用时传入写好的 transform 即可。
数据增强
通用分类模型增强
train_transform = transforms.Compose([
transforms.RandomResizedCrop(224, scale=(0.08, 1.0), ratio=(3/4, 4/3)),
transforms.RandomHorizontalFlip(),
transforms.ColorJitter(0.2, 0.2, 0.2, 0.1),
transforms.RandomGrayscale(p=0.1),
transforms.ToTensor(),
transforms.Normalize(mean, std)
])
更简单的增强(小模型/快速实验)
train_transform = transforms.Compose([
transforms.RandomCrop(32, padding=4), # 先在四周补 0 再随机裁剪
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)) # CIFAR-10参数
])
DataLoader 的使用
在深度学习中,DataLoader 是将 Dataset 包装成可迭代、多进程、批量化的数据流的关键工具。它负责在训练循环中自动完成分批、打乱、多线程预加载等繁琐工作,让我们可以专注于模型设计。本文将基于 PyTorch 详细讲解 DataLoader 的核心用法
一个典型的训练循环需要:
● 从数据集中取出一小批(batch)样本;
● 对每个 batch 进行前向传播、计算损失、反向传播;
● 同时希望在下一个 batch 被 GPU 计算时,CPU 已经在提前加载下一批数据(预取)。
DataLoader 将上述流程封装为一个可迭代对象,只需 for batch in train_loader 即可,并且支持多线程/多进程并行加载,极大提升数据吞吐。
任何 DataLoader 都需要一个 torch.utils.data.Dataset 对象,可以是 PyTorch 内置的(如 TensorDataset、ImageFolder),也可以是自定义的。
import torch
from torch.utils.data import DataLoader, TensorDataset
# 创建假数据:100个样本,每个样本10维特征,标签0~1
features = torch.randn(100, 10)
labels = torch.randint(0, 2, (100,))
dataset = TensorDataset(features, labels)
# 构建 DataLoader
loader = DataLoader(dataset, batch_size=16, shuffle=True)
# 迭代
for batch_idx, (data, target) in enumerate(loader):
print(data.shape, target.shape) # 每批 (16,10) 和 (16,)
通常我们会把之前讲的 transforms 放在 Dataset 的 __getitem__ 中,这样 DataLoader 自动调用,两者无缝衔接
from torchvision.datasets import ImageFolder
from torchvision import transforms
transform = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485,0.456,0.406], std=[0.229,0.224,0.225])
])
dataset = ImageFolder(root='./data/train', transform=transform)
loader = DataLoader(dataset, batch_size=32, shuffle=True, num_workers=4)
DataLoader 的签名及常用参数:
DataLoader(
dataset,
batch_size=1,
shuffle=False,
sampler=None,
batch_sampler=None,
num_workers=0,
collate_fn=None,
pin_memory=False,
drop_last=False,
timeout=0,
worker_init_fn=None,
prefetch_factor=2,
persistent_workers=False,
)
| 参数 | 类型 | 默认值 | 作用说明 | 推荐用法 |
|---|---|---|---|---|
| dataset | Dataset | 必填 | 要加载的数据集对象 | 传入自定义或内置的 Dataset 实例 |
| batch_size | int | 1 | 每批样本数 | 根据 GPU 显存调整,通常取 32, 64, 128 |
| shuffle | bool | False | 是否在每个 epoch 开始时打乱数据 | 训练集设 True,验证/测试集必须设 False |
| num_workers | int | 0 | 数据加载子进程数(0 为主进程) | 设为 4~8(不超过 CPU 核数),Windows 需 if name == 'main': 保护 |
| pin_memory | bool | False | 是否将数据锁页到 CPU 内存,加速向 GPU 传输 | 配合 tensor.cuda(non_blocking=True) 使用,显存紧张时可关闭 |
| drop_last | bool | False | 是否丢弃最后一个不完整的 batch | 训练时通常设 True(稳定 BatchNorm),验证时设 False |
| collate_fn | callable | None | 将多个样本组合成 batch 的函数 | 样本结构复杂(如可变长目标检测框)时自定义 |
| sampler | Sampler | None | 定义采样策略(与 shuffle 互斥) | 用于分布式训练或自定义采样,一般不单独设置 |
| batch_sampler | Sampler | None | 直接采样 batch 索引(与 batch_size, shuffle, sampler 等互斥) | 高级用法,一般不直接使用 |
| prefetch_factor | int | 2 | 每个 worker 预取的 batch 数量 | 可适当增大(如 4)以减少 GPU 空闲等待 |
| persistent_workers | bool | False | 是否在 epoch 间保持 worker 进程存活 | 设 True 可避免每个 epoch 重启进程的开销 |
自定义Dataset
最小模板
from torch.utils.data import Dataset
class MyDataset(Dataset):
def __init__(self, ...):
# 初始化:存储文件路径、标签、transform 等
pass
def __len__(self):
# 返回数据集总样本数
return len(self.data)
def __getitem__(self, idx):
# 根据索引 idx 加载并返回一个样本
# 可以返回张量、元组、字典等
return sample
●_len_ 让 len(dataset) 能工作
●_getitem_ 支持索引访问 dataset[i],是 DataLoader 获取样本的入口
从文件中加载
假设你的数据存放在 ./data/images/,标签在一个 CSV 文件中(labels.csv):
images/
img001.jpg
img002.jpg
...
labels.csv:
filename,label
img001.jpg,cat
img002.jpg,dog
...
import pandas as pd
from PIL import Image
from torch.utils.data import Dataset
class ImageCSVDataset(Dataset):
def __init__(self, csv_file, img_dir, transform=None):
self.annotations = pd.read_csv(csv_file)
self.img_dir = img_dir
self.transform = transform
# 将文本标签映射为数字
self.class_to_idx = {'cat': 0, 'dog': 1} # 你可以用 enumerate 自动生成
def __len__(self):
return len(self.annotations)
def __getitem__(self, idx):
row = self.annotations.iloc[idx]
img_name = row['filename']
label_str = row['label']
label = self.class_to_idx[label_str]
img_path = f"{self.img_dir}/{img_name}"
image = Image.open(img_path).convert('RGB')
if self.transform:
image = self.transform(image)
return image, label

浙公网安备 33010602011771号