导入并初始化数据集
一、数据集和数据导入器
-
处理数据样本的代码可能会变得杂乱和难以维护; 我们理应希望数据集代码与模型训练代码解耦,以获得更好的可读性和模块性。PyTorch 提供了两个数据原语: torch.utils.data.DataLoader和 torch.utils.data.DataSet,这两个原语允许您使用预加载的数据集以及您自己的数据。Dataset 存储样本及各自对应的标签,DataLoader 将 Dataset 包装成一个迭代器,以便能够轻松访问样本。
-
PyTorch 域库提供了许多预加载的数据集样本(例如 FashionMNIST) ,这些数据集是torch.utils.data.DataSet的子类,并且实现特定于特定数据的函数。它们可用于原型设计和基准测试模型的样本:
- Image Datasets —— 图像数据集
- Text Datasets —— 文本数据集
- Audio Datas —— 音频数据集
1. 导入数据集
我们将从TorchVision加载 Fashion-MNIST 数据集。Fashion-MNIST 是 Zalando 的文章图像的数据集,包括60,000个训练样本和10,000个测试样本。每个例子包含一个28 × 28的灰度图像和一个来自10个种类的相关标签。
- 每个图像高为28像素,宽为28像素,合计784像素。
- 这10个种类说明了它是什么类型的图像。例如: t 恤/上衣,裤子,套头衫,连衣裙,手袋,脚踝靴等。
- 灰度值介于0到255之间,用于测量黑白图像的强度。强度值从白色增加到黑色。例如: 白色为0,而黑色为255。 [怎么恰好相反?]
我们用以下参数导入 FashionMNIST 数据集:
- root 是存储训练/测试数据的路径,
- train 指定训练或测试数据集,
- download = True ,若root指定的路径无效,则会从互联网下载数据。
- Transform 和 target_transform 指定特性和标签转换
%matplotlib inline
import torch
import numpy as np
# 数据集模块,用于处理单个样本
from torch.utils.data import Dataset
# 导入TorchVision的数据集
from torchvision import datasets
# 导入标准化函数,进行预处理
from torchvision.transforms import ToTensor, Lambda
import matplotlib.pyplot as plt
# 导入内置数据集,并传入各参数,用于训练模型
training_data = datasets.FashionMNIST(
root="data",
train=True,
download=True,
transform=ToTensor()
)
# 导入测试数据集,用于测试模型
test_data = datasets.FashionMNIST(
root="data",
train=False,
download=True,
transform=ToTensor()
)
2. 迭代数据集并进行可视化
我们可以像列表一样手动对数据集进行索引访问: training_data [index]。同时也可使用matplotlib来对训练集中的样本进行可视化
labels_map = {
0: "T-Shirt",
1: "Trouser",
2: "Pullover",
3: "Dress",
4: "Coat",
5: "Sandal",
6: "Shirt",
7: "Sneaker",
8: "Bag",
9: "Ankle Boot",
}
figure = plt.figure(figsize=(10, 10))
cols, rows = 5, 5
for i in range(1, cols * rows + 1):
# 下面这句怎么理解?
# sample_idx = torch.randint(len(training_data), size=(1,)).item()
My_in = np.random.randint(len(training_data))
# randint 和 numpy一致,就是随机生成整数。第一个可省略默认下限为 0,第二个参数就是上限 + 1,
# 第三个参数是生成张量的shape,要传入元组
# 后面的 item()是将 张良类型转化为 Python的基本类型
# 读取单项样本的图片以及标签
img, label = training_data[My_in]
# 利用matplot显示各项信息
figure.add_subplot(rows, cols, i)
plt.title(labels_map[label])
plt.axis("off")
plt.imshow(img.squeeze(), cmap="gray")
plt.show()
3. 嘻嘻嘻
- sample_idx = torch.randint(len(training_data), size=(1,)).item()
这一通操作和我自定义的:
- My_in = np.random.randint(len(training_data))
效果等价
sample_idx = torch.randint(len(training_data), size=(1,)).item()
sample_idx
17623
4. 使用 DataLoaders 为训练准备数据
数据集一次检索一个样本的数据集特性和标签。在训练模型时,我们通常希望在“小批量”中传递样本,在每个历元重新分配数据以减少模型过拟合,并使用 Python 的多道处理来加速数据检索。 在机器学习中,你需要指定数据集中的特性和标签。输入Features,输出Labels。我们训练使用features,训练模型来预测label。
- 标签是10种类型: t 恤衫,凉鞋,衣服等
- 特征是图像像素中的陈列模式。
DataLoader 拥有可迭代属性,它在一个简单的 API 中为我们抽象出这种复杂性。为了使用 Dataloader,我们需要设置以下参数:
- 数据:将用于训练模型的训练数据; 或用于评估模型的测试数据
- 批量大小:每批处理的记录数量
- 随机样本数据是否会按索引打乱顺序
from torch.utils.data import DataLoader
# 传入数据集,将其包装为DataLoader便于之后进行迭代
train_dataloader = DataLoader(training_data, batch_size=64, shuffle=True)
test_dataloader = DataLoader(test_data, batch_size=64, shuffle=True)
5. 使用DataLoader迭代数据集
我们已经将数据集加载到 Dataloader 中,并且可以根据需要迭代数据集。下面的每个迭代返回一批【不像DataSet只能一个】 train_features 和 train_labels (分别包含 batch_size = 64个features和labels)。因为我们指定了 shuffle = True,所以在对所有批次进行迭代之后,数据将被混合【有放回】(用于对数据加载顺序进行更细粒度的控制)。
# Display image and label.
# 由于 train_features, train_labels 都只是拥有iterable属性,而非 iterator 所以要使用
# iter函数对其进行转化
train_features, train_labels = next(iter(train_dataloader)) # 一次性读出batch_size = 64个数据
print(f"Feature batch shape: {train_features.size()}")
print(f"Labels batch shape: {train_labels.size()}")
img = train_features[63].squeeze() # 这里的下标范围为 [0, 63]
label = train_labels[63]
# img = train_features[64].squeeze() # 64 就报错啦!
# label = train_labels[64]
plt.imshow(img, cmap="gray")
plt.show()
print(f"Label: {label}")
Feature batch shape: torch.Size([64, 1, 28, 28])
Labels batch shape: torch.Size([64])
Label: 8
A = torch.ones((3, 1, 2))
A.shape
torch.Size([3, 1, 2])
print(A.squeeze().shape)
print(A.squeeze(0).shape)
print(A.squeeze(1).shape)
torch.Size([3, 2])
torch.Size([3, 1, 2])
torch.Size([3, 2])
注意:
- torch.squeeze() : 第一个参数为Tensor对象,用于压缩,第二个参数可选设置为 第几维
- 压缩对应Tensor的为 1 的维度:如 A×1×B×C×1×D 变为 A×B×C×D
- 若 shape(A) = (2, 1,3)
- 调用方式为A.squeeze() 则 shape(A) = (2, 3) ,
- 调用方式为A.squeeze(0) 则 shape(A) = (2,1, 3) , # 若第一维大小为 1 ,则压缩
- 调用方式为A.squeeze(1) 则 shape(A) = (2, 3) , # 压缩第二维
二、标准化
标准化是一种常用的数据预处理技术,用于对数据进行缩放或转换,以确保每个特征对数据的学习贡献相等。例如,灰度图像中的每个像素的值在0到255之间,这是特征。假如一个像素值为17,另一个像素值也会位于 [0,255], 为197。像素重要性分布不均匀,因为像素体积越大,学习效果越差。标准化可以改变数据的范围,而不会影响数据在外部特性之间的区别【讲了这么多其实就是类归一化】。这种预处理是为了避免:
- 预测精度的降低
- 模型学习困难
- 不利于特征数据范围的分布
1. transforms
输入数据常常不是训练机器学习算法所需的最终处理形式。我们使用转换来执行一些数据操作,并使其适合于训练。 所有 TorchVision 数据集都有两个参数(transforms用于修改特征target_transform 用于修改标签)接受包含转换逻辑的调用。torchvision.transforms 模块提供了几种常用且易上手的变换。FashionMNIST功能是基于 PIL 图像格式,标签是整数。对于训练,我们需要将特性作为规范化张量,并将标签作为一热编码张量。为了完成这些转换,我们使用了 ToTensor 和 Lambda。
from torchvision import datasets
from torchvision.transforms import ToTensor, Lambda
ds = datasets.FashionMNIST(
root="data",
train=True,
download=True,
transform=ToTensor(),
# 使用匿名函数进行数据预处理
target_transform=Lambda(lambda y: torch.zeros(10, dtype=torch.float).scatter_(0, torch.tensor(y), value=1))
)
- lambda y: torch.zeros(10, dtype=torch.float).scatter_(0, torch.tensor(y), value=1)
scatter_的用法:第一个参数是维度,第二个参数是待分隔张量,第三个参数是?
2. ToTensors()
ToTensor 将一个 PIL 图像或 NumPy ndarray 转换为 FloatTensor,并将图像像素取值范围限定在[0.,1. ],也就是对灰度值进行归一化
3. Lambda transforms
Lambda transforms 适用于任何用户定义的 Lambda 函数。这样的话,我们定义一个函数将整数转换为一个热编码的张量。它首先创建一个大小为10的零张量(我们数据集中的标签数量) ,然后调用 scatter,它将标签 y 给出的索引值分配为1。你也可以使用 torch.nn.functional.one_hot 作为另一个可选参数。
target_transform = Lambda(lambda y: torch.zeros(
10, dtype=torch.float).scatter_(dim=0, index=torch.tensor(y), value=1))
注意:
- 数据集和数据载入器之间的关系: DataSet 设计用于检索单个数据项,而 DataLoader 设计用于处理批量数据。
- PyTorch内的transforms的作用: 对数据进行一些处理,使其适合训练