【问题标题】:Why is my DataLoader so much slower than a for loop?为什么我的 DataLoader 比 for 循环慢得多?
【发布时间】:2019-09-29 18:01:14
【问题描述】:

我正在为 MNIST 数据集编写一个基于神经网络的分类器。我首先尝试使用周期和批次的循环和索引手动加载数据。在一个教程中,我看到有人使用 torch.utils.data.DataLoader 来完成这个确切的任务,所以我将代码更改为使用 DataLoader。这导致学习过程的持续时间存在重大差异。

我已尝试通过使用基准测试缩小范围来解决此问题。我总是在 CPU(i7 8700k) 和 GPU(1080ti) 上进行基准测试,数据存储在我的 ssd(970 evo) 上。

我首先尝试比较使用和不使用 DataLoader 的 Batch Gradient Descent,然后比较使用和不使用 DataLoader 的 Mini-Batch Gradient Descent。结果让我很困惑。

|                 | BGD         | BGD with DL | MB-GD       | MB-GD with DL |
|-----------------|-------------|-------------|-------------|---------------|
| Time on CPU     | 00:00:56.70 | 00:05:59.31 | 00:01:31.29 | 00:07:46.56   |
| Accuracy on CPU | 82.47       | 33.44       | 94.84       | 87.67         |
| Time on GPU     | 00:00:15.89 | 00:05:41.79 | 00:00:17.48 | 00:05:37.33   |
| Accuracy on GPU | 82.3        | 30.66       | 94.88       | 87.74         |
| Batch Size      | M           | M           | 500         | 500           |
| Epoch           | 100         | 100         | 100         | 100           |

这是使用 DataLoader 的代码,精简为基本要素。

num_epoch = 100
train_loader = DataLoader(batch_size=500, shuffle=False, dataset=dataset_train)

for epoch in range(num_epoch):
    for i, (images, labels) in enumerate(train_loader):
        images = images.view(-1, 28 * 28)
        optimizer.zero_grad()
        outputs = model(images)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

vs 使用循环的代码

num_epoch = 100
batch_size = 500
num_batch = int(len(dataset_train) / batch_size)

for epoch in range(num_epoch):
    for batch_idx in range(num_batch):
        images = dataset_train.data[batch_idx*batch_size:(batch_idx+1)*batch_size].view(-1, 28 * 28)
        labels = dataset_train.targets[batch_idx*batch_size:(batch_idx+1)*batch_size]
        optimizer.zero_grad()
        outputs = model(images)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

我希望 DataLoader 在时间和性能方面至少在接近循环的某个地方执行,但不会慢 10 倍。我也很困惑为什么 DataLoader 会影响模型的准确性。

我是否使用了错误的 DataLoader,或者这只是它的错误用例,而循环更适合我正在做的事情?

编辑:这里有两个小提琴包含 loopdataloader 变体的完整代码

编辑:我相信我可能已经弄清楚如何解决我的主要问题,即数据加载器和循环之间的性能差异。通过将 loader 的 num_workers 参数设置为 8,我设法将 GPU 上使用 DL 的 mini-batch 的时间缩短到 1 分钟左右。虽然这肯定比 5 分钟要好,但它仍然很糟糕,考虑到 GPU 上带有 DL 的 minibatch 与 CPU 上带有循环的 minibatch 的性能相当。

【问题讨论】:

  • 您的dataset_train 对象中有一些转换?
  • @iacolippo 是的transforms.ToTensor()

标签: python pytorch


【解决方案1】:

transforms.ToTensor()PIL Image 范围内的PIL Imagenp.ndarray 作为输入,并将其转换为[0.0, 1.0] 范围内的torch.FloatTensor,如果np.ndarray 具有dtype=np.uint8PIL Image属于(L, LA, P, I, F, RGB, YCbCr, RGBA, CMYK, 1)docs其中一种模式

重新缩放和更改数据类型会影响模型的准确性。此外,DataLoader 执行的操作比批次循环更多,因此时间不同。

附:在进行 minibatch 训练时,你应该打乱你的训练数据

【讨论】:

  • 我仍然不明白该怎么做。数据加载器完全消除了 gpu 的性能优势。附言改组被禁用以获得更准确的循环比较
  • 此外,对数据集进行的转换应该同样影响循环和加载器变体,不是吗?尽管循环变体是在数据集而不是加载器上进行的,但它们实际上并没有进行转换吗?
  • 您对转换的看法是正确的,那么可能会影响准确性的是,如果一种情况下您会随机播放,而另一种情况下您不会。关于速度,我不知道,当我无法运行相同的代码来重现您的问题时,很难回答。
  • 你可以,我在我的编辑中上传了代码。如果您在检索 nn.py 时遇到问题,它只是一个非常基本的 1 层神经网络,带有 sigmoid 激活函数,没有什么花哨或与众不同的地方。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-01-29
  • 2014-01-21
  • 1970-01-01
  • 1970-01-01
  • 2017-11-09
  • 1970-01-01
  • 2019-10-28
相关资源
最近更新 更多