【机器学习深度学习】数据预处理
目录
一、数据预处理的目的是什么?
二、数据预处理示例
2.1 示例代码
2.2 运行结果
2.3 代码解析
2.3.1 自定义数据集 CustomDataset
2.3.2 创建模拟数据
2.3.3 定义数据增强(transform)
2.3.4 构建数据集 + DataLoader
2.3.5 数据加载
2.3.6 TensorDataset 的简化用法(适合非图像数据)
三、总结口诀
一、数据预处理的目的是什么?
把原始数据 → 变成模型可训练格式:张量形式,标准大小,归一化,增强(如翻转/旋转)。
二、数据预处理示例
2.1 示例代码
import torch
import numpy as np
from torch.utils.data import Dataset, DataLoader, TensorDataset
from torchvision import transforms
# ==============================
# 数据预处理(将数据处理成模型可输入的形状)
# ==============================
print("\n" + "=" * 50)
print("数据预处理示例")
print("=" * 50)# 自定义数据集类
class CustomDataset(Dataset):def __init__(self, data, targets, transform=None):self.data = dataself.targets = targetsself.transform = transformdef __len__(self):return len(self.data)def __getitem__(self, idx):sample = self.data[idx]label = self.targets[idx]if self.transform:sample = self.transform(sample)return sample, label# 创建模拟数据
num_samples = 100
data = np.random.randn(num_samples, 3, 32, 32) # 100张32x32的RGB图像
targets = np.random.randint(0, 10, num_samples) # 0-9的标签# 定义转换管道
transform = transforms.Compose([transforms.ToTensor(), # 转为张量transforms.Normalize((0.5,), (0.5,)), # 标准化transforms.RandomHorizontalFlip(), # 随机水平翻转transforms.RandomRotation(15) # 随机旋转±15度
])# 创建数据集和数据加载器
dataset = CustomDataset(data, targets, transform=transform)
dataloader = DataLoader(dataset, batch_size=16, shuffle=True, num_workers=0)# 演示数据加载
print(f"数据集大小: {len(dataset)}")
batch = next(iter(dataloader))
print(f"批数据形状: 输入={batch[0].shape}, 标签={batch[1].shape}")# 使用TensorDataset的简化方法
tensor_x = torch.randn(100, 5) # 特征
tensor_y = torch.randint(0, 2, (100,)) # 二分类标签
tensor_dataset = TensorDataset(tensor_x, tensor_y)
dataloader_simple = DataLoader(tensor_dataset, batch_size=10, shuffle=True)#print(f"批数据形状: 输入={batch[0].shape}, 标签={batch[1].shape}")
2.2 运行结果
==================================================
数据预处理示例
==================================================
数据集大小: 100
批数据形状: 输入=torch.Size([16, 32, 3, 32]), 标签=torch.Size([16])
2.3 代码解析
2.3.1 自定义数据集 CustomDataset
Dataset
子类,它是 PyTorch 数据加载的标准做法:
# 自定义数据集类
class CustomDataset(Dataset):def __init__(self, data, targets, transform=None):self.data = data # 原始数据(图像等)self.targets = targets # 标签(分类结果)self.transform = transform # 预处理方法(可以为 None)def __len__(self):return len(self.data) # 数据集中样本个数def __getitem__(self, idx):sample = self.data[idx] # 第 idx 个图像label = self.targets[idx] # 对应标签if self.transform:sample = self.transform(sample) # 应用 transformreturn sample, label
它的作用就是:
✔ 把你的数据data
(比如图片)和targets
(比如标签)装进一个可迭代对象
✔ 在训练时,按批(batch)从中抽出样本
▲ def __init__(self, data, targets, transform=None)
定义
▲def __len__(self)
def __len__(self):return len(self.data)
计算数据长度
▲def __getitem__(self, idx)
def __getitem__(self, idx):sample = self.data[idx]label = self.targets[idx]if self.transform:sample = self.transform(sample)return sample, label
sample = self.data[idx]
-
从
self.data
中取出第idx
个样本 -
假设
self.data
是一个形状[100, 3, 32, 32]
的数组(100张图片),那么这个就是取出第idx
张图片 -
类型:通常是
ndarray
或张量
label = self.targets[idx]
-
从
self.targets
中取出对应的标签 -
比如,如果是图像分类,
targets
就是[0, 1, 5, 3, ...]
等类别编号 -
所以你现在得到的是一对:
✔ 一张图片
✔ 一个标签
if self.transform:
-
判断你有没有设置
transform
-
transform
是你传入的转换管道,例如:
transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5,), (0.5,))
])
sample = self.transform(sample)
-
如果设置了
transform
,就用它来“处理样本”,比如:-
转为 PyTorch 张量
-
做归一化
-
数据增强(随机翻转、旋转等)
-
📌 注意:标签 label
通常不做 transform,只处理 sample
return sample, label
-
最后,返回处理后的
(样本, 标签)
对 -
DataLoader
会调用这个方法一次次地取出 batch,例如:
for batch in dataloader:x, y = batch # x 是样本数据,y 是标签
2.3.2 创建模拟数据
data = np.random.randn(100, 3, 32, 32)
targets = np.random.randint(0, 10, 100)
模拟了:
-
100张 32×32 的RGB图像
(3通道)→shape=(100, 3, 32, 32)
-
100个标签
,范围在 0~9(多分类)
2.3.3 定义数据增强(transform)
transforms.Compose([...])
是用来 把多个图像变换步骤“串起来”依次执行 的。
就像你做图像预处理的“流水线”——每个步骤按顺序处理图像。
transform = transforms.Compose([transforms.ToTensor(), # ① 转换为张量transforms.Normalize((0.5,), (0.5,)),# ② 标准化transforms.RandomHorizontalFlip(), # ③ 数据增强transforms.RandomRotation(15) # ④ 数据增强
])
每个步骤含义:
步骤 | 作用 |
---|---|
ToTensor() | 把图片/数组转成 PyTorch 张量 |
Normalize((0.5,), (0.5,)) | 将像素值标准化(减均值除方差) |
RandomHorizontalFlip() | 以 50% 概率将图像左右翻转(数据增强) |
RandomRotation(15) | 随机旋转 ±15 度(模拟不同角度) |
是对图像数据进行预处理和增强的过程,目的是为了:
✅ 把原始图像变成模型可以理解的格式
✅ 增强数据多样性,让模型更强健、泛化更好✅ 最终的作用:
让你的训练模型更健壮:
能接受输入是张量
像素值范围适合网络
不容易被方向、角度小变化干扰
在数据不多时有效“制造更多样本”
📌 为啥图像训练前要 transform?
深度学习模型不能直接处理图像(像素),它只理解张量(tensor)!
而且模型容易“过拟合训练数据”,所以我们要用数据增强让它更健壮。
🔍 逐步解释每一行:
① transforms.ToTensor()
把图像数据(PIL Image 或 NumPy 的
[H, W, C]
)变成 PyTorch 张量[C, H, W]
,并自动把像素值从 [0, 255] 缩放到 [0.0, 1.0]
例子:
原图(HWC): uint8, [32, 32, 3], 像素0-255
→ 转为张量: float32, [3, 32, 32], 像素0.0-1.0
② transforms.Normalize((0.5,), (0.5,))
对每个像素做标准化:$x' = \frac{x - 0.5}{0.5}$
等价于把像素从[0, 1]
映射到[-1, 1]
这么做的好处是:
-
加快模型收敛速度
-
避免某些神经元“激活太强”或“始终为零”
📌 如果是 RGB 图像,应该写成 (0.5, 0.5, 0.5)
,不是一个元素!
③ transforms.RandomHorizontalFlip()
以一定概率把图像水平翻转
举个例子:左手 → 右手
作用:增加训练数据的多样性,避免模型只学到“固定方向”
④ transforms.RandomRotation(15)
把图像随机旋转 ±15 度
例如:
-
原图是“狗正面”
-
旋转后变成“狗歪着头”
作用:提升模型对角度变化的鲁棒性
例图(视觉演示)
如果你要训练猫狗分类器:
原图 | 水平翻转 | 旋转15° |
---|---|---|
🐶 正面 | 🐶 反方向 | 🐶 歪头 |
这些都“看起来像狗”,但模型可能就不容易学到这些变化,如果不做 transform。
🧠 总结口诀
步骤 | 作用 |
---|---|
ToTensor() | 变成模型能吃的格式 |
Normalize() | 提升训练效率 |
RandomFlip/Rotate | 增强数据多样性,防止过拟合 |
2.3.4 构建数据集 + DataLoader
dataset = CustomDataset(data, targets, transform=transform)
dataloader = DataLoader(dataset, batch_size=16, shuffle=True)
DataLoader
会:
-
每次给你一小批数据(batch_size=16)
-
每次打乱顺序(shuffle=True)
-
在内部调用你写的
__getitem__
方法 -
返回
(sample, label)
,也就是图片 + 标签
dataset = CustomDataset(data, targets, transform=transform)
这行代码的意思是:
创建一个“自定义数据集对象”,将原始数据
data
和标签targets
封装起来,
并设置了一个图像预处理管道transform
,让你后续可以直接加载预处理后的数据。
关键词 | 说明 |
---|---|
CustomDataset | 自定义数据集类,封装原始数据 + 标签 |
transform=... | 每次取数据时执行的数据预处理流程 |
__getitem__ | 控制每次加载时具体怎么返回数据 |
dataloader = DataLoader(dataset, batch_size=16, shuffle=True)
这行代码的作用是:
把你刚刚创建好的
dataset
数据集对象,分批次(每批16个样本)加载,
并在每个 epoch 前打乱顺序,返回一个支持迭代的“批数据提供器”。
🔹 DataLoader(...)
是什么?
PyTorch 提供的一个数据加载器,它能自动帮你:
-
把数据集
Dataset
拆分成多个 batch(小批次) -
自动迭代取出每一个 batch(配合
for
或next(iter(...))
使用) -
(可选)打乱数据顺序
-
(可选)多线程提速(
num_workers
参数)
🔹 dataset
就是你传入的数据集对象(比如 CustomDataset(...)
),它必须实现两个方法:
__len__ # 返回数据集长度
__getitem__ # 给定索引,返回 (样本, 标签)
🔹 batch_size=16
告诉 DataLoader:
“每次返回 16 个样本为一个批次(mini-batch)。”
这符合深度学习训练的常规方式——批量训练(不是一条一条,而是一批一批地训练)。
🔹 shuffle=True
告诉 DataLoader:
“在每个 epoch 开始前,把样本顺序打乱。”
为什么?
-
避免模型学到“数据的顺序”而不是规律;
-
提高泛化能力(尤其在数据顺序本身有偏差时);
📊 举个例子:
假设你有 100 个训练样本,写了:
dataloader = DataLoader(dataset, batch_size=16, shuffle=True)
那么:
-
每次
next(iter(dataloader))
就返回 16 条(x, y)
; -
总共返回 7 次(最后一次只返回 4 条);
-
每个 epoch 会重新打乱数据顺序后再分批次。
参数 | 含义 | 举例说明 |
---|---|---|
dataset | 数据源(继承自 Dataset) | 你自己定义的 CustomDataset |
batch_size=16 | 每次加载 16 个样本 | 即一个训练批次 |
shuffle=True | 每个 epoch 开始前打乱 | 防止模型记住样本顺序 |
2.3.5 数据加载
# 演示数据加载
print(f"数据集大小: {len(dataset)}")
batch = next(iter(dataloader))
print(f"批数据形状: 输入={batch[0].shape}, 标签={batch[1].shape}")
🔹 第一句:len(dataset)
print(f"数据集大小: {len(dataset)}")
这会调用你 CustomDataset
类中定义的:
def __len__(self):return len(self.data)
✅ 输出的是:你有多少张图像数据(样本数量)。例如:100
🔹 第二句:batch = next(iter(dataloader))
这句可以分成两步理解:
① iter(dataloader)
把 dataloader
变成一个迭代器对象,就像你写:
for batch in dataloader:
它其实底层就是自动做了 iter(dataloader)
。
② next(...)
从这个迭代器里“取出一个 batch”,也就是第一个小批量的数据。
🔍所以这一句整体意思是:
从 DataLoader 中拿出第一个 mini-batch(批数据),保存在变量 batch
里。
这个 batch
是一个元组,格式如下:
batch = (输入数据张量, 标签张量)
# 等价于
x = batch[0]
y = batch[1]
2.3.6 TensorDataset 的简化用法(适合非图像数据)
tensor_x = torch.randn(100, 5) # 100个样本,每个5维特征
tensor_y = torch.randint(0, 2, (100,)) # 100个二分类标签(0或1)
tensor_dataset = TensorDataset(tensor_x, tensor_y)
dataloader_simple = DataLoader(tensor_dataset, batch_size=10)
这个版本适合:
-
不需要 transform
-
输入特征本身就是张量(如 tabular、嵌入、语音信号等)
✅ 最终输出:
print(f"批数据形状: 输入={batch[0].shape}, 标签={batch[1].shape}")
输出示例:
输入 = torch.Size([16, 3, 32, 32])
标签 = torch.Size([16])
代表:
-
一批 16 张图像,每张是 3×32×32
-
每张图像一个标签,长度为 16
语法 | 作用 |
---|---|
iter(dataloader) | 把数据加载器变成迭代器 |
next(...) | 从中取出一个 batch |
batch = next(iter(...)) | 获取一批样本+标签 |
三、总结口诀
自定义 Dataset = 管理你原始数据结构
transform = 自动处理 & 数据增强
DataLoader = 批量送进模型
TensorDataset = 张量格式数据的快速封装