数据加载与可视化(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 不仅能统一数据格式,还能通过数据增强有效提升模型泛化能力。
为什么

  1. 类型转换:原始图片通常是 PIL.Image 或 numpy.ndarray,需要转成 torch.Tensor。
  2. 尺寸统一:不同图片大小不一,需 resize 或 crop 到固定尺寸。
  3. 数值归一化:像素值从 [0, 255] 映射到 [0, 1] 或标准化到零均值单位方差。
  4. 数据增强:随机翻转、旋转、色彩抖动等,增加训练样本多样性,减少过拟合。

基本用法: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
posted @ 2026-06-11 20:28  MaoShine  阅读(5)  评论(0)    收藏  举报