阶段:S2-数据处理 | 预计耗时:60 分钟 | 前置:S1 全部验收通过 代码:本篇代码要你自己敲成
model-learn/s2_custom_dataset.py
学习目标
- 理解
Dataset(提供一条数据)和DataLoader(打包成批、打乱、流水线)的分工 - 亲手写出最小 Dataset 类(两个
__方法),给表格数据和图像数据各写一个 - 能说清
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 分钟)
yb.dtype是什么?为什么不是 float?(分类标签是整数类别)- 把
shuffle=True改成False再跑两次,前 8 条一样吗?
任务 3:小挑战(15 分钟)
给 TabularDataset 加一个 transform 参数(传入一个函数),在 __getitem__ 里对特征做归一化(除以 10)——体会"预处理挂在 Dataset 里"的写法。
验收标准
衔接下一阶段
数据能流进模型了 → S2-02 训练验证测试集划分:数据要分成几份、为什么。