首页 › S 模型训练 › S2 数据处理 › 01

S2-01 · Dataset 与 DataLoader:数据怎么流进模型

进阶阶段约 180 分钟阶段:S2 数据处理

阶段:S2-数据处理 | 预计耗时:60 分钟 | 前置:S1 全部验收通过 代码:本篇代码要你自己敲成 model-learn/s2_custom_dataset.py

学习目标

  1. 理解 Dataset(提供一条数据)和 DataLoader(打包成批、打乱、流水线)的分工
  2. 亲手写出最小 Dataset 类(两个 __ 方法),给表格数据和图像数据各写一个
  3. 能说清 shuffle 在训练集要开、测试集要关的原因

核心知识

1. 两个类的分工

类 职责 你要实现的方法
Dataset "仓库管理员":按索引取一条数据 __len__(有多少条)、__getitem__(取第 i 条)
DataLoader "打包流水线":凑批、打乱、并行读 只要传 batch_size / shuffle

任何数据源(CSV、文件夹里的图片、数据库)只要套上这两个 __ 方法,就能进训练循环——这是 PyTorch 生态统一的地方。

2. 为什么要分批(batch)

  • 逐条更新:太抖;全量更新:太慢。64 条一更新 = 平衡点
  • shuffle=True:每轮数据顺序不同,防止模型"按背诵顺序学"。测试集不 shuffle——顺序不影响结果,还方便对账

3. 预处理放在 __getitem__ 里

数据保持原始形态落盘,读取时才转 float32/归一化——正是 训练数据集.md 里"省存储"原则的工程落地。

实验任务

任务 1:敲出脚本(30 分钟)

# s2_custom_dataset.py —— 手写两个 Dataset:表格 + 图像
import csv, random
from pathlib import Path
import torch
from torch.utils.data import Dataset, DataLoader

# ---------- 1. 表格数据:学习时长/平时分 → 是否及格 ----------
csv_path = Path(__file__).parent / "s2_study.csv"
if not csv_path.exists():                      # 第一次运行自动生成 200 条假数据
    random.seed(42)
    with open(csv_path, "w", newline="", encoding="utf-8") as f:
        w = csv.writer(f); w.writerow(["hours", "score", "pass"])
        for _ in range(200):
            h = random.uniform(0, 10)
            s = min(100.0, max(0.0, h * 8 + random.gauss(0, 8)))
            w.writerow([f"{h:.1f}", f"{s:.1f}", int(s >= 60)])

class TabularDataset(Dataset):
    def __init__(self, path):
        self.rows = []
        with open(path, newline="", encoding="utf-8") as f:
            for r in csv.DictReader(f):
                self.rows.append((float(r["hours"]), float(r["score"]), int(r["pass"])))
    def __len__(self):
        return len(self.rows)
    def __getitem__(self, i):
        h, s, passed = self.rows[i]
        return torch.tensor([h, s]), torch.tensor(passed)   # 特征 + 标签

ds = TabularDataset(csv_path)
x, y = ds[0]
print("表格集:", len(ds), "条;单条:", x, "标签:", y)

for xb, yb in DataLoader(ds, batch_size=8, shuffle=True):
    print("一个 batch:", xb.shape, yb.shape, "标签dtype:", yb.dtype)
    break

# ---------- 2. 给 MNIST 套壳:子集 ----------
from torchvision import datasets, transforms
from torch.utils.data import Subset
mnist = datasets.MNIST("data", train=True, download=False,
                       transform=transforms.ToTensor())
sub = Subset(mnist, range(500))                # torch 自带的 Dataset 壳
xs, ys = next(iter(DataLoader(sub, batch_size=16)))
print("MNIST 子集 batch:", xs.shape, ys.shape)

任务 2:观察与回答(10 分钟)

  1. yb.dtype 是什么?为什么不是 float?(分类标签是整数类别)
  2. 把 shuffle=True 改成 False 再跑两次,前 8 条一样吗?

任务 3:小挑战(15 分钟)

给 TabularDataset 加一个 transform 参数(传入一个函数),在 __getitem__ 里对特征做归一化(除以 10)——体会"预处理挂在 Dataset 里"的写法。

验收标准

衔接下一阶段

数据能流进模型了 → S2-02 训练验证测试集划分:数据要分成几份、为什么。

小测验(2 题)

答完立即判分,错题给解析;得分存本地。实验与实操题不上网测:回对话找老师验收。