如何在训练期间调整 gpu 批量大小?

Vic*_*zzi 5 python gpu neural-network tensorflow pytorch

我感到惊讶的是,我在网上找不到任何关于如何在不停止训练的情况下动态调整 GPU 批量大小的资源。

想法如下:

1) 有一个(几乎)与使用中的 GPU 无关的训练脚本。批量大小将动态调整而不会受到用户的干扰或不需要调整。

2) 仍然能够指定所需的训练批次大小,即使太大而无法装入已知的最大 GPU。

例如,假设我想使用批量大小为 4096 张图像(每张图像 1024x1024)来训练模型。假设我可以访问具有不同 NVidea GPU 的服务器,但我不知道会提前分配给我哪一个。(或者每个人都想使用最大的 GPU,而在我的任期到来之前我已经等了很长时间)。

我希望我的训练脚本找到最大批量大小(假设它是每个 GPU 批次 32 个图像),并且仅在处理完所有 4096 个图像后才更新优化器(一个训练批次 = 128 个 GPU 批次)。

Vic*_*zzi 4

解决这个问题有不同的方法。但是,如果无法指定可以完成该工作的 GPU 或使用多个 GPU,那么动态调整 GPU 批量大小会很方便。

我在 pytorch 中准备了一个示例性的训练示例(它在 TensorFlow 中的工作方式应该类似)

在下面的代码中,try/ except 用于在不停止训练的情况下尝试不同的 GPU 批量大小。当批量变得太大时,会缩小规模并关闭适配。请检查存储库以了解实施细节和可能的错误修复。

它还实现了一种称为“批量欺骗”的技术,该技术在进行反向传播之前执行多次前向传播。在 PyTorch 中,它只需要替换 optimizationr.zero_grad()。

import torch
import torchvision
import torch.optim as optim
import torch.nn as nn

# Example of how to use it with Pytorch
if __name__ == "__main__":

    # #############################################################
    # 1) Initialize the dataset, model, optimizer and loss as usual.
    # Initialize a fake dataset

    trainset = torchvision.datasets.FakeData(size=1_000_000,
                                             image_size=(3, 224, 224),
                                             num_classes=1000)

    # initialize the model, loss and SGD-based optimizer
    resnet = torchvision.models.resnet152(pretrained=True,
                                          progress=True)
    criterion = nn.CrossEntropyLoss()
    optimizer = optim.SGD(resnet.parameters(), lr=0.01)

    continue_training = True  # criteria to stop the training

    # #############################################################
    # 2) Set parameters for the adaptive batch size
    adapt = True  # while this is true, the algorithm will perform batch adaptation
    gpu_batch_size = 2  # initial gpu batch_size, it can be super small
    train_batch_size = 2048  # the train batch size of desire

    # Modified training loop to allow for adaptive batch size
    while continue_training:

        # #############################################################
        # 3) Initialize dataloader and batch spoofing parameter
        # Dataloader has to be reinicialized for each new batch size.
        trainloader = torch.utils.data.DataLoader(trainset,
                                                  batch_size=int(gpu_batch_size),
                                                  shuffle=True)

        # Number of repetitions for batch spoofing
        repeat = max(1, int(train_batch_size / gpu_batch_size))

        try:  # This will make sure that training is not halted when the batch size is too large

            # #############################################################
            # 4) Epoch loop with batch spoofing
            optimizer.zero_grad()  # done before training because of batch spoofing.

            for i, (x, y) in enumerate(trainloader):

                y_pred = resnet(x)
                loss = criterion(y_pred, y)
                loss.backward()

                # batch spoofing
                if not i % repeat:
                    optimizer.step()
                    optimizer.zero_grad()

                # #############################################################
                # 5) Adapt batch size while no RuntimeError is rased.
                # Increase batch size and get out of the loop
                if adapt:
                    gpu_batch_size *= 2
                    break

                # Stopping criteria for training
                if i > 100:
                    continue_training = False

        # #############################################################
        # 6) After the largest batch size is found, the training progresses with the fixed batch size.
        # CUDA out of memory is a RuntimeError, the moment we will get to it when our batch size is too large.
        except RuntimeError as run_error:
            gpu_batch_size /= 2  # resize the batch size for the biggest that works in memory
            adapt = False  # turn off the batch adaptation

            # Number of repetitions for batch spoofing
            repeat = max(1, int(train_batch_size / gpu_batch_size))

            # Manual check if the RuntimeError was caused by the CUDA or something else.
            print(f"---\nRuntimeError: \n{run_error}\n---\n Is it a cuda error?")
Run Code Online (Sandbox Code Playgroud)

如果您有可以在 Tensorflow、Caffe 或其他中执行类似操作的代码,请分享!