使用自定义pytorch数据集的高效小批量循环的编码模式

时间:2018-01-24 23:21:18

标签: pytorch

是否有任何一般性建议可以在自定义数据集中有效地处理数据,以便它可以很好地与minibatch eval / train循环一起使用?为了更具体地说明我的意思,假设我定义了将x映射到x + 1的合成玩具数据集:

import torch.utils.data as data

class Dataset(data.Dataset):
    def __init__(self):
        super(Dataset, self).__init__()
        # list of [x, y]
        self.dataset = [
            [1, 2],
            [2, 3],
            [3, 4],
            [4, 5]
        ]

    def __getitem__(self, index):
        item = self.dataset[index]
        return item[0], item[1]

    def __len__(self):
        return len(self.dataset)

实际上,这将被包装在DataLoader中并在eval / train循环中访问,如下所示:

dataset = Dataset()
data_loader = data.DataLoader(dataset=dataset, batch_size=2, shuffle=True)
epochs = 100
for i_epoch in range(epochs):
    for i_minibatch, minibatch in enumerate(data_loader):
        x, y = minibatch
        # predict and train

数据集对象可能返回原始Python对象,如数字或列表,就像在我的示例实现中一样,但在最后一个代码片段的“预测和训练”部分,我们需要一些特定的数据类型来计算内容,比如火炬.FloatTensor(似乎数据加载器可以隐式地执行此操作),甚至可能包装为torch.autograd.Variable,并且可能还需要一些.cuda()调用。我的问题是关于何时进行这些数据转换和函数调用的一般建议。

例如,一个选项是将所有内容保存为数据集内的torch.FloatTensor,在data_loader循环中,我们可以添加Variable包装器并调用.cuda()。我们也可以通过在数据集构造函数或getitem方法中调用.cuda()来获取GPU上的全部或部分数据。我认为所有这些方法可能都有利有弊。如果我正在训练几个时代的模型,我不想在每个时期或小批量迭代中引入不必要的开销,这可以通过预先计算数据集中的内容来避免。可能有更多关于pytorch内部知识的人(可能与某些缓存或jit编译有关)可能会指出更具体的理由选择一种方法而不是另一种方法。

2 个答案:

答案 0 :(得分:2)

通常,数据集以更易于存储在磁盘上的格式存储在文件中。加载数据集时,您希望数据类型对PyTorch更友好。这是通过Torchvision库的transformations interface完成的。例如,对于MNIST,以下是标准转换:

datasets.MNIST('../data', train=True, download=True,
               transform=transforms.Compose([
                   transforms.ToTensor(),
                   transforms.Normalize((0.1307,), (0.3081,))
               ])

此处ToTensor将张量中的所有值除以255,这样,如果数据是RGB图像,则张量中的值将为0.0到1.0。

这里的关键是,磁盘上的数据理想情况下应该与您可能想要处理的内容(培训,可视化,计算统计信息等)无关,并且与所使用的框架无关。在加载与您正在做的事情有关的数据之后,您可以应用转换。

我想提到的另一件事是处理非常大的数据集,例如ImageNet。重要的事情很少:

  1. 您应避免使用单独的图像文件作为数据集,因为这在群集中效果不佳。相反,您可以将所有文件打包为LMDB或未压缩的zip格式(使用Python ZipFile模块),然后仅顺序访问这些文件。大文件中的随机访问会极大地降低您的速度。
  2. 对于大型数据集,应避免在DataLoader类中使用shuffle选项。如果这样做,您将再次访问具有随机访问权限的大文件,性能将下降。相反,您可以做的是依次读取K = C * total_epochs * batch_size记录,其中C是您选择的某个常数> =1。然后将K条记录随机排列在内存中,然后将它们分成几批。不幸的是,您现在必须手动执行此操作。

答案 1 :(得分:1)

您是否阅读了一些官方示例,例如imagenet train?在这些示例中,它们首先获取数据。正如您所说,数据已被隐式转换为火炬张量。然后,如果你有GPU,将cpu张量转换为GPU张量。最后,将GPU上的普通张量转换为火炬Variable以使autograd工作。

我认为这是做这些事情的规范和标准方式。至少,我到目前为止看到的所有pytorch代码都是这样做的。如果您想提高速度,可以考虑

  • 在dataloader中使用多个工作人员来获取数据
  • 使用多个GPU进行培训
  • 如果您有多台安装了多个GPU的服务器,
  • 甚至是分布式培训