【问题标题】:coding patterns for efficient minibatch loop using custom pytorch dataset使用自定义 pytorch 数据集实现高效小批量循环的编码模式
【发布时间】:2018-07-04 03:17:08
【问题描述】:

是否有任何一般性建议可以有效地处理自定义数据集中的数据,以便它与小批量 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 对象,如数字或列表,就像在我的示例实现中一样,但是在最后一个代码 sn-p 的“预测和训练”部分中,我们需要一些特定的数据类型来计算东西,比如一个 torch.FloatTensor(似乎数据加载器可以隐式执行此操作),甚至可能包装为一个 torch.autograd.Variable,并且可能还需要对 .cuda() 进行一些调用。我的问题是关于何时进行这些数据转换和函数调用的一般建议。

例如,一种选择是将所有内容都保存为数据集中的 torch.FloatTensor,在 data_loader 循环中,我们可以添加变量包装器并调用 .cuda()。我们还可以通过在 Dataset 构造函数或 getitem 方法中调用 .cuda() 来获得 GPU 上的全部或部分数据。我认为所有这些方法都可能有利有弊。如果我要为多个 epoch 训练模型,我不想在每个 epoch 或小批量迭代中引入不必要的开销,这些开销可以通过预先计算数据集中的内容来避免。可能对 pytorch 内部有更多了解的人(可能与幕后发生的某些缓存或 jit 编译有关)可能能够指出选择一种方法而不是另一种方法的更具体的原因。

【问题讨论】:

    标签: pytorch


    【解决方案1】:

    通常,数据集以一种对磁盘存储更友好的格式存储在文件中。加载数据集时,您希望数据类型对 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 记录,然后分批划分它们。很遗憾,您现在必须手动执行此操作。

    【讨论】:

      【解决方案2】:

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

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

      • 在数据加载器中使用多个工作器来获取您的数据
      • 使用多个 GPU 进行训练
      • 如果您有多个安装了多个 GPU 的服务器,甚至可以进行分布式训练

      【讨论】:

        猜你喜欢
        • 2019-01-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-06-16
        • 2021-08-25
        • 2020-02-10
        • 2019-06-18
        • 2019-05-14
        相关资源
        最近更新 更多