深度学习中的数据加载与批处理

深度学习项目中,数据的组织和加载是至关重要的步骤。本文将探讨如何使用PyTorch库来有效地组织和加载数据,以及如何通过自定义数据集类来处理大型数据集。将学习到如何将数据分批处理,并在每个epoch循环中将这些批次数据喂给模型。

早期加载与延迟加载数据

早期加载是指在训练开始前将全部数据加载到内存中。然而,当数据量超过计算机内存大小时,这种方法就不再适用。这就是所谓的延迟加载,将在本文中详细探讨这一解决方案。通过本文,将完全理解如何比以前更有效地使用Torch数据集类。

什么是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张量的形式返回所有数据样本。

Torch DataLoader

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__:当调用这个类时,这个方法只运行一次,在这里传递数据或其引用以及标签数据。
  • __getitem__:这个函数每次返回一个输入和相应的标签。
  • __len__:这个需要定义,以创建数据的上限,或者可以说它只是返回数据集的大小。

在__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
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485