Я пишу классификатор на основе нейронных сетей для набора данных MNIST.Сначала я попытался загрузить данные вручную, используя циклы и индексы для эпох и пакетов.В учебном пособии я видел, как кто-то использовал torch.utils.data.DataLoader для этой конкретной задачи, поэтому я изменил свой код, чтобы вместо него использовать DataLoader.Это привело к значительным различиям в продолжительности процесса обучения.
Я пытался решить эту проблему, пытаясь сузить ее с помощью тестов.Я всегда проводил тестирование как на CPU (i7 8700k), так и на GPU (1080ti), а данные хранятся на моем ssd (970 evo).
Сначала я попытался сравнить Batch Gradient Descent с DataLoader и без него, а затем Mini-Пакетный градиентный спуск с и без DataLoader.Результаты меня несколько смутили.
| | 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()
против кода с использованием цикла
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, или это просто неправильный вариант использования, и цикл лучше подходит для того, что я делаю?
РЕДАКТИРОВАТЬ: вот две скрипты, содержащие полный код цикла и варианта dataloader
РЕДАКТИРОВАТЬ: я думаю, что я мог выяснить, как исправитьМоя главная проблема - разница в производительности между загрузчиком данных и циклом.Установив параметр num_workers
загрузчика на 8, мне удалось сократить время для мини-пакета с DL на графическом процессоре примерно до 1 минуты.Хотя это определенно лучше, чем 5 минут, это все же плохо, учитывая, что мини-пакет с DL на GPU находится на одном уровне с производительностью мини-пакета с циклом на процессоре.