深度学习Day02 DataSet
前言:作为深度学习小白,总是对"数据是如何喂给模型的"感到困惑直到理解了 DataSet 和 DataLoader 的设计哲学,才恍然大悟——原来数据加载也是一门艺术!
一、为什么要使用DataSet?
1.1 传统方式的痛点
假设要训练一个图像分类模型,最直观的想法是:
# ❌ 朴素做法:一次性加载所有数据
images = []
labels = []
for path in image_paths:
img = cv2.imread(path) # 读取图片
images.append(img)
labels.append(get_label(path))
# 转为 numpy 数组
X = np.array(images) # 假设有 10 万张 224×224 的图片
y = np.array(labels) # 这将占用约 15GB 内存!
# 手动分批
for i in range(0, len(X), batch_size):
batch_X = X[i:i+batch_size]
batch_y = y[i:i+batch_size]
# 训练模型...
可以发现这里存在了很多问题:
1、💥 内存爆炸:大规模数据集根本装不下
2、🐌 效率低下:无法并行读取数据
3、🔧 代码混乱:数据处理和模型训练耦合在一起
4、🔄 难以复用:换个数据集要重写大量代码
1.2 Dataset 的解决方案
为解决上述问题,PyTorch 提出了一个优雅的抽象:将数据封装成可索引的对象。
# ✅ 使用 Dataset
class ImageDataset(Dataset):
def __init__(self, image_paths, labels):
self.paths = image_paths # 只保存路径,不读取数据
self.labels = labels
def __len__(self):
return len(self.paths)
def __getitem__(self, idx):
# 按需加载:只在需要时才读取图片
image = cv2.imread(self.paths[idx])
label = self.labels[idx]
return torch.tensor(image), torch.tensor(label)
# 使用方式
dataset = ImageDataset(paths, labels)
loader = DataLoader(dataset, batch_size=32, shuffle=True, num_workers=4)
for batch_images, batch_labels in loader:
# 直接使用批次数据训练
outputs = model(batch_images)
loss = criterion(outputs, batch_labels)
DataSet所体现出来的优势:
✅ 按需加载:内存占用降低 90% 以上
✅ 自动批处理:DataLoader 自动组装 batch
✅ 并行加速:
num_workers=4开启多进程读取✅ 代码清晰:数据逻辑和训练逻辑完全解耦
二、Dataset 的核心机制
2.1 三大必备方法
由于PyTorch 的DataSet是一个抽象类,任何自定义数据集都必须实现以下三个方法:
from torch.utils.data import Dataset
class MyDataset(Dataset):
def __init__(self, ...):
"""
初始化方法:
- 读取数据索引(文件路径、数据库查询结果等)
- 设置数据增强策略
- 不要在这里加载所有数据!
"""
pass
def __len__(self):
"""
返回数据集大小
DataLoader 需要这个信息来计算有多少个 batch
"""
return len(self.data)
def __getitem__(self, idx):
"""
核心方法:根据索引返回单条数据
- 这里才真正加载数据(按需加载)
- 返回格式:(特征, 标签) 或只有特征(测试集)
"""
return self.data[idx], self.label[idx]
2.2 工作流程图解
完整流程可分为三个阶段:
阶段1:创建 Dataset
dataset = MyDataset(...)
✅ 执行
__init__方法✅ 准备数据索引(如文件路径列表)
❌ 不加载实际数据到内存
阶段2:创建 DataLoader
loader = DataLoader(dataset, batch_size=32, shuffle=True)
✅ 保存 dataset 引用
✅ 设置采样策略(批次大小、是否打乱等)
❌ 仍然不读取数据
阶段3:开始遍历(核心)
for batch_images, batch_labels in loader:
# 使用数据...
DataLoader 在每次迭代时自动执行以下步骤:
采样索引
根据shuffle策略生成本批次的索引列表
例如:[0, 15, 23, 7, 31, ...](共 32 个,假设 batch_size=32)调用
__getitem__(🔥 关键!)
对每个索引调用一次:⚠️ 这里才真正读取数据(按需加载)dataset.__getitem__(0) # 返回 (img0, label0) dataset.__getitem__(15) # 返回 (img15, label15) dataset.__getitem__(23) # 返回 (img23, label23) # ... 共调用 32 次组装 batch
将 32 条数动堆叠成张量:batch_images = torch.stack([img0, img15, img23, ...]) # [32, C, H, W] batch_labels = torch.stack([label0, label15, ...]) # [32]返回循环:你在 for 循环中拿到的就是组装好的
batch_images和batch_labels
三、模式
DataSet里面有一个参数叫“mode”,意为模式,一般分为三种模式:train、val、test。
3.1 为什么需要不同的模式?
在机器学习中,我们通常会将数据集划分成三个部分:
完整数据集
├── 训练集 (Training Set) - 用于训练模型
├── 验证集 (Validation Set) - 用于调参和选择模型
└── 测试集 (Test Set) - 用于评估最终性能
我们可以将训练模型类比为备考:
数据集 对应场景 作用 训练集 平时做的练习题 学习知识,提升能力 验证集 模拟考试 检验学习效果,调整复习策略 测试集 真正的考试 最终评估真实水平
在训练时,只能看训练集(就像学习只能用练习题),验证集用于边学边测(模拟考),测试集只在最后用一次(真正考试)。
3.2 训练集(train)
作用:让模型学习数据中的规律
# 在训练集上:
# - 模型看到数据和标签
# - 根据预测错误来更新参数
for batch_x, batch_y in train_loader:
prediction = model(batch_x)
loss = criterion(prediction, batch_y) # 计算预测和真实标签的差距
loss.backward() # 反向传播
optimizer.step() # 更新参数
3.3 验证集(val)
作用:检查模型是否"学偏了"(过拟合)
# 在验证集上:
# - 模型看到数据和标签
# - 只计算准确率,不更新参数
# - 用于调整超参数(学习率、网络层数等)
with torch.no_grad(): # 关闭梯度计算
for batch_x, batch_y in val_loader:
prediction = model(batch_x)
accuracy = calculate_accuracy(prediction, batch_y)
# 如果验证准确率下降 → 可能过拟合 → 早停
过拟合示例:
Epoch 1: Train Acc=60%, Val Acc=55% ✅ 正常
Epoch 10: Train Acc=90%, Val Acc=85% ✅ 正常
Epoch 20: Train Acc=99%, Val Acc=88% ✅ 还可以
Epoch 30: Train Acc=100%, Val Acc=82% ⚠️ 过拟合!
(训练集记住了,但验证集性能下降)
3.4 测试集 (test)
作用:评估模型在真实场景的表现
# 在测试集上:
# - 模型只能看到数据,看不到标签
# - 用于最终评估(比赛提交、论文实验)
# - 整个训练过程只用一次
for batch_x in test_loader: # 没有 batch_y!
prediction = model(batch_x)
# 保存预测结果,提交到比赛平台
3.5 实际使用场景
举例:
训练流程:
# 步骤1:创建训练集和验证集
train_dataset = covidDataset('data.csv', mode='train', feature_dim=10)
val_dataset = covidDataset('data.csv', mode='val', feature_dim=10)
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=32, shuffle=False)
# 步骤2:训练循环
for epoch in range(100):
# ========== 训练阶段 (使用 train_loader) ==========
model.train() # 设置为训练模式
for batch_x, batch_y in train_loader:
outputs = model(batch_x)
loss = criterion(outputs, batch_y)
# 反向传播,更新参数
optimizer.zero_grad()
loss.backward()
optimizer.step()
# ========== 验证阶段 (使用 val_loader) ==========
model.eval() # 设置为评估模式
with torch.no_grad(): # 不计算梯度
for batch_x, batch_y in val_loader:
outputs = model(batch_x)
val_loss = criterion(outputs, batch_y)
# 只计算损失,不更新参数
print(f'Epoch {epoch}: Train Loss={loss:.4f}, Val Loss={val_loss:.4f}')
测试流程:
# 步骤3:训练完成后,用测试集预测
test_dataset = covidDataset('test.csv', mode='test', feature_dim=10)
test_loader = DataLoader(test_dataset, batch_size=32, shuffle=False)
model.eval()
predictions = []
with torch.no_grad():
for batch_x in test_loader: # 注意:测试集没有标签
outputs = model(batch_x)
predictions.append(outputs)
# 保存预测结果
predictions = torch.cat(predictions)
更多推荐
所有评论(0)