在深度学习项目中,数据的组织和加载是至关重要的步骤。本文将探讨如何使用PyTorch库来有效地组织和加载数据,以及如何通过自定义数据集类来处理大型数据集。将学习到如何将数据分批处理,并在每个epoch循环中将这些批次数据喂给模型。
早期加载是指在训练开始前将全部数据加载到内存中。然而,当数据量超过计算机内存大小时,这种方法就不再适用。这就是所谓的延迟加载,将在本文中详细探讨这一解决方案。通过本文,将完全理解如何比以前更有效地使用Torch数据集类。
在深入自定义数据集类之前,先讨论PyTorch中的简单TensorDataset类。假设有一组输入和一组标签(目标数据),基本上就是输入数据和目标数据之间的一一映射。如果对这种一一映射感到困惑,可以参考有关RNN的文章。在这种情况下,可以直接使用TensorDataset类。
import torch
inp = torch.arange(1,16).reshape(5,3)
label = torch.randint(1,3,size=(5,))
print(inp)
print(label)
输出结果:
tensor([[ 1, 2, 3],
[ 4, 5, 6],
[ 7, 8, 9],
[10, 11, 12],
[13, 14, 15]])
tensor([2, 1, 2, 2, 2])
要创建Torch数据集,只需将输入和标签传递给TensorDataset类,它将以torch张量的形式返回所有数据样本。
DataLoader类将数据集分成小批次。最佳实践是,永远不要按原样排列数据。必须在从数据存储中选取数据样本时应用一些随机化技术(数据采样),这种随机化将真正帮助构建良好的模型。
dl = DataLoader(ds, batch_size=2)
for inp, label in dl:
print('{}:{}'.format(inp, label))
如上代码所示,DataLoader将数据加载到固定大小的批次中(最后一个除外),并以正确的标签顺序加载。DataLoader类中有一些常见的采样方法,例如,如果在函数中传递shuffle参数,那么将生成随机打乱的批次。
现在已经看到了TensorDataset类,可以进一步从基本数据集类中进行。将尝试用CIFAR-10的例子来解释这一部分,但可以得到这个想法,并在应用中解决问题。CIFAR-10有10个不同的类别,如飞机、鸟、猫等。训练目录具有以下结构:
根目录 - 训练 子目录 - 飞机、鸟、猫……(10个文件夹) 每个文件夹包含5000张图像
要创建自定义数据集类:需要制作3个抽象方法,这些是必须的:
在__init__函数中要小心。如果数据非常大,以至于无法适应内存,那么不要在__init__函数中读取整个数据。在__init__函数中存储数据的引用或索引,并仅在实际需要数据时将其加载到内存中,是的,在epoch循环内部。这样就可以克服大型数据的内存问题。
from torch.utils.data import Dataset, DataLoader, TensorDataset
import glob
from PIL import Image
class MyDataset(Dataset):
def __init__(self, path, transform):
self.files = glob.glob(path)
self.transform = transform
self.labels = [filepath.split('/')[-2] for filepath in self.files]
def __getitem__(self, item):
file = self.files[item]
label = self.labels[item]
file = Image.open(file)
file = self.transform(file)
return file, label
def __len__(self):
return len(self.files)
在__init__中,只是将文件路径存储在self.files对象中。训练所需的是所有数据样本和相应的标签,但在这里不存储输入,而是只存储输入的引用,将在getitem函数中使用这些引用将数据加载到内存中,也存储了所有数据样本的标签,为此从引用中分割路径并获取子目录名称,如飞机、鸟等,如上所述代码。应该将其映射到整数值(因为神经网络只对数值数据友好)把这个留给。需要一个转换函数将PIL图像转换为张量。
在__len__函数中,只需要返回整个数据的实际长度,即数据集的实际总大小。
在__getitem__函数中,需要实现逻辑,以希望的方式获取数据。在这里,需要将一张图像文件映射到其相应的标签。为了实现这一点,调用图像数据的open和transform函数,然后将图像转换为torch张量(需要将数据转换为张量以进行训练),最后,返回图像和相应的标签。
这就是可以避免内存溢出的方式。只有在需要特定数据时才将数据转移到内存中。意思是,当在训练时迭代数据加载器,只有在那时数据才会加载到内存中。
from torchvision import transforms
path = '/cifar10/train/*/*'
transform = transforms.Compose([transforms.ToTensor()])
cifar_ds = MyDataset(path, transform)
cifar_dl = DataLoader(cifar_ds, batch_size=100, shuffle=True)
import matplotlib.pyplot as plt
from torchvision.utils import make_grid
for images, labels in cifar_dl:
plt.imshow(make_grid(images, 10).permute(1,2,0)) # 4 batch_size
plt.show()
for epoch in range(num_epochs):
for batch in cifar_dl:
//pass the data to the model