Переменная производительность при обучении Resnet на Google Compute Engine с данными из Google Cloud Storage - PullRequest
0 голосов
/ 02 марта 2019

Я пытаюсь обучить классификатор изображений resnet18 на большом количестве культур по помеченным данным Google Streeview.Я следую вместе с этим уроком .У меня есть два набора данных: одно из примерно ~ 20 000 изображений и одно из приблизительно ~ 100 000 изображений.Оба набора данных хранятся в одном и том же формате, и оба были загружены в соответствующие корзины Google Cloud Storage.Затем я смонтировал оба этих блока в домашнем каталоге своей виртуальной машины, используя gcsfuse с флагом --implicit-dirs.

Затем я запускаю файл train.py на моей виртуальной машине Google Compute Engine, которая была создана из Изображение VM для глубокого обучения на облачном рынке Google.Виртуальная машина имеет один виртуальный ЦП, один графический процессор Nvidia Tesla K80, 3,75 ГБ памяти и 100 ГБ постоянного диска.

Когда я запускаю обучающий скрипт, я не изменяю ничего, кроме указания правильной переменной dataset_dirgcsfuse -монтированный каталог на виртуальной машине.

Когда я запускаю train.py в каталоге 100k crop, он запускается относительно быстро, причем одна эпоха занимает ~ 30 минут.Я прыгаю в top, пока он работает, и загрузка ЦП довольно высока, оставаясь на уровне около 90%.

Однако, при использовании той же виртуальной машины, когда я запускаю train.py в каталоге с урожаем 20 КБ, он работает оченьмедленнее, с одной эпохой, занимающей 6-7 часов, несмотря на меньший размер набора данных.В этом случае загрузка ЦП никогда не превышает 5%.

Я не могу понять, что вызывает замедление, поскольку ничто (насколько я могу судить) не отличается между двумя запусками, за исключением наборов данных,которые оба отформатированы одинаково.Я использую тот же pytorch dataloader с тем же количеством потоков.Оба блока GCS находятся в одной и той же области, us-west1, которая является той же областью, что и мой экземпляр виртуальной машины.

Кажется вероятным, что каким-то образом один сегмент ограничен по IO относительно другого блока, но я не могу понять,почему.

Любые мысли приветствуются!

Мой train.py файл находится ниже.

import torch
import torch.nn as nn
import torch.optim as optim
from torch.optim import lr_scheduler
import numpy as np
import torchvision
from torchvision import datasets, models, transforms
import matplotlib.pyplot as plt
import time
import os
import copy
from collections import defaultdict



data_transforms = {
    'Test': transforms.Compose([
        transforms.RandomResizedCrop(224),
        transforms.RandomHorizontalFlip(),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ]),
    'Val': transforms.Compose([
        transforms.Resize(256),
        transforms.CenterCrop(224),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ]),
}


data_dir = 'home/gweld/sliding_window_dataset/'
image_datasets = {x: datasets.ImageFolder(os.path.join(data_dir, x),
                                          data_transforms[x])
                  for x in ['Test', 'Val']}
dataloaders = {x: torch.utils.data.DataLoader(image_datasets[x], batch_size=4,
                                             shuffle=True, num_workers=4)
              for x in ['Test', 'Val']}
dataset_sizes = {x: len(image_datasets[x]) for x in ['Test', 'Val']}
class_names = image_datasets['Test'].classes

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")




def train_model(model, criterion, optimizer, scheduler, num_epochs=25):
    since = time.time()

    best_model_wts = copy.deepcopy(model.state_dict())
    best_acc = 0.0

    for epoch in range(num_epochs):
        print('Epoch {}/{}'.format(epoch, num_epochs - 1))
        print('-' * 10)

        # Each epoch has a training and validation phase
        for phase in ['Test', 'Val']:
            if phase == 'Test':
                scheduler.step()
                model.train()  # Set model to training mode
            else:
                model.eval()   # Set model to evaluate mode

            running_loss = 0.0
            running_corrects = 0

            class_corrects = defaultdict(int)
            class_totals   = defaultdict(int)

            # Iterate over data.
            for inputs, labels in dataloaders[phase]:
                inputs = inputs.to(device)
                labels = labels.to(device)

                # zero the parameter gradients
                optimizer.zero_grad()

                # forward
                # track history if only in train
                with torch.set_grad_enabled(phase == 'Test'):
                    outputs = model(inputs)
                    _, preds = torch.max(outputs, 1)
                    loss = criterion(outputs, labels)

                    # backward + optimize only if in training phase
                    if phase == 'Test':
                        loss.backward()
                        optimizer.step()

                # statistics
                running_loss += loss.item() * inputs.size(0)
                running_corrects += torch.sum(preds == labels.data)

                for index, pred in enumerate(preds):
                    actual = labels.data[index]
                    class_name = class_names[actual]

                    if actual == pred: class_corrects[class_name] += 1
                    class_totals[class_name] += 1

            epoch_loss = running_loss / dataset_sizes[phase]
            epoch_acc = running_corrects.double() / dataset_sizes[phase]

            print('{} Loss: {:.4f} Acc: {:.4f}'.format(
                phase, epoch_loss, epoch_acc))

            if phase == 'Val':
                print("Validation Class Accuracies")

                for class_name in class_totals:
                    class_acc = float(class_corrects[class_name])
                    class_acc = class_acc/class_totals[class_name]

                    print("{:20}{}%".format(class_name, 100*class_acc))
                print("\n")

            # deep copy the model
            if phase == 'Val' and epoch_acc > best_acc:
                best_acc = epoch_acc
                best_model_wts = copy.deepcopy(model.state_dict())

        print()

    time_elapsed = time.time() - since
    print('Training complete in {:.0f}m {:.0f}s'.format(
        time_elapsed // 60, time_elapsed % 60))
    print('Best val Acc: {:4f}'.format(best_acc))

    # load best model weights
    model.load_state_dict(best_model_wts)
    return model




model_ft = models.resnet18(pretrained=True)
num_ftrs = model_ft.fc.in_features
model_ft.fc = nn.Linear(num_ftrs, 5) # last arg here, # classes? -gw

model_ft = model_ft.to(device)

criterion = nn.CrossEntropyLoss()

# Observe that all parameters are being optimized
optimizer_ft = optim.SGD(model_ft.parameters(), lr=0.001, momentum=0.9)

# Decay LR by a factor of 0.1 every 7 epochs
exp_lr_scheduler = lr_scheduler.StepLR(optimizer_ft, step_size=7, gamma=0.1)


# Train and evaluate
# ^^^^^^^^^^^^^^^^^^

print('Beginning Training on {} train and {} val images.'.format(dataset_sizes['Test'], dataset_sizes['Val']))


model_ft = train_model(model_ft, criterion, optimizer_ft, exp_lr_scheduler,
                       num_epochs=25)




torch.save(model_ft.state_dict(), 'models/test_run_resnet18.pt')
...