(十四)Dataset 与 DataLoader
# 项目文件:demos/loader_batches.py
# 五条样本按每批两条读取,最后一批保留一条;固定生成器后核对打乱顺序。
import torch
from torch.utils.data import DataLoader, TensorDataset
x = torch.arange(5, dtype=torch.float32).unsqueeze(1)
y = 2 * x + 1
dataset = TensorDataset(x, y)
for features, target in DataLoader(dataset, batch_size=2):
print("本批", features[:, 0].tolist(), tuple(features.shape), tuple(target.shape))
def order():
generator = torch.Generator().manual_seed(7)
loader = DataLoader(dataset, batch_size=2, shuffle=True, generator=generator)
return [number for features, _ in loader for number in features[:, 0].tolist()]
print("固定生成器顺序相同", order() == order())Dataset 描述“给一个编号,怎样取到一条样本”;DataLoader 负责把样本编号安排成批,再把同批数据叠起来。TensorDataset 适合几份已经对齐的张量,第 i 条会一起取 x[i] 和 y[i]。
五条样本、每批两条,顺序读取时得到 [0,1]、[2,3]、[4],形状分别为 (2,1)、(2,1)、(1,1)。最后一批可以比前面小,统计整轮损失时要记住各批的实际样本数。
shuffle=True 每轮安排打乱的编号,输入和目标仍作为一对读取。给 DataLoader 传入独立的 torch.Generator,可以控制和保存这份打乱状态。例中两次从种子 7 新建生成器,得到相同顺序。
配套项目把同一接口写成自己的数据集,并读取两种本地数据:
# 项目文件:training_project/src/readingtorch/data.py
import csv
from dataclasses import dataclass
import hashlib
from pathlib import Path
import numpy as np
import torch
from torch.utils.data import DataLoader, Dataset, Subset
class ArrayDataset(Dataset):
# 一次索引返回一条输入和对应目标,批量组合交给 DataLoader。
def __init__(self, x, y):
self.x, self.y = x, y
def __len__(self):
return len(self.x)
def __getitem__(self, index):
return self.x[index], self.y[index]
def read_raw(task, path):
if task == "regression":
with path.open(encoding="utf-8", newline="") as stream:
rows = list(csv.DictReader(stream))
x = torch.tensor([[float(r["minutes"]), float(r["sessions"])] for r in rows])
y = torch.tensor([[float(r["pages"])] for r in rows])
elif task == "images":
with np.load(path, allow_pickle=False) as archive:
x = torch.from_numpy(archive["images"].astype(np.float32)).unsqueeze(1)
y = torch.from_numpy(archive["labels"].astype(np.int64))
else:
raise ValueError("请选择 regression 或 images")
if not torch.isfinite(x).all() or not torch.isfinite(y).all():
raise ValueError("数据中出现了 NaN 或无穷值")
return x.float(), yArrayDataset 的 __len__ 和 __getitem__ 接上之前学过的对象协议。CSV 每行得到两个浮点输入与一个浮点目标;图像文件读出 (N,8,8),unsqueeze(1) 补上单通道,得到 (N,1,8,8),目标为长度 N 的整数类别编号。
np.load(..., allow_pickle=False) 读取这里保存的普通数组,isfinite 检查数据是否含有 NaN 或无穷值,发现后把问题指向输入文件。原始数据生成过程见 make_data.py。批次与采样接口可查 PyTorch 数据加载。