Как сохранить и загрузить модель из DistributedDataParallel обучения - PullRequest
1 голос
/ 05 апреля 2020

Я новичок в Pytorch DstributedDataParallel (), но я обнаружил, что большинство уроков сохраняют модель локального ранга 0 во время обучения. Это означает, что если я получу 3 машины с 4 графическими процессорами на каждом из них, в финале я получу 3 модели, которые сохраняются с каждой машины.

Например, в pytorch Imag eNet учебное пособие в строке 252:

if not args.multiprocessing_distributed or (args.multiprocessing_distributed
                and args.rank % ngpus_per_node == 0):
            save_checkpoint({...})

Они сохраняют модель, если rank % ngpus_per_node == 0.

Насколько я знаю, DistributedDataParallel () автоматически c все уменьшит до потери на бэкэнде, не выполняя никакой дальнейшей работы, каждый процесс может автоматически синхронизировать c потери. Все модели в каждом процессе будут немного отличаться в конце процесса. Это означает, что нам нужно сохранить только одну модель.

Так почему бы нам просто не сохранить модель на rank == 0, а rank % ngpus_per_node == 0?

И какую модель я должен использовать для если я получу несколько моделей?

Если это правильный способ сохранения модели в распределенном обучении, следует ли мне объединить их, использовать одну из них или сделать вывод о базе результатов по всем трем моделям?

Пожалуйста, дайте мне знать, если Я не прав.

1 Ответ

1 голос
/ 05 апреля 2020

Что происходит

Пожалуйста, исправьте меня, если я ошибаюсь в любом месте

Изменения, на которые вы ссылаетесь, были внесены в 2018 через этот коммит и описывается как:

в многопроцессорном режиме, только один процесс запишет контрольную точку

Ранее они были сохранены без каких-либо if block , поэтому каждый узел в каждом графическом процессоре будет сохранять модель, которая действительно расточительна и, скорее всего, будет перезаписывать сохраненную модель несколько раз на каждом узле.

Теперь мы говорим о распределенной многопроцессорной обработке (возможно, многие работники, каждый из которых может иметь несколько графических процессоров).

args.rank для каждого процесса, таким образом, изменяется внутри скрипта на эту строку :

args.rank = args.rank * ngpus_per_node + gpu

, которая имеет следующее комментарий:

Для многопроцессорного распределенного обучения ранг должен быть глобальным рангом среди всех процессов

Следовательно, args.rank является уникальным идентификатором среди всех графических процессоров среди все узлы (или так кажется).

Если так, и у каждого узла есть ngpus_per_node (в этом обучающем коде предполагается, что каждый имеет одинаковое количество графических процессоров из того, что я собрал), тогда модель сохраняется только для одного (последнего) графического процессора на каждом узле. В вашем примере с 3 машинами и 4 графическими процессорами вы получите 3 сохраненные модели (надеюсь, я правильно понимаю этот код, поскольку он довольно запутанный).

Если вы использовали rank==0 только одну модель за мир (где мир будет определен как n_gpus * n_nodes) будет сохранено.

Вопросы

Первый вопрос

Так почему же Разве мы не просто сохраним модель на ранге == 0, а на ранге% ngpus_per_node == 0?

Начну с вашего предположения, а именно:

В лучшую сторону Насколько я знаю, DistributedDataParallel () будет автоматически делать все, чтобы уменьшить потери на бэкэнде, без дальнейшей работы, каждый процесс может синхронизировать c потери автоматически на основе этого.

Точно, он не имеет ничего общего с потерями, а скорее gradient накопление и внесенные поправки к весам, согласно документации (выделено мое):

Этот контейнер распараллеливает применение данного модуля путем разбиения Inpu т через указанные устройства путем разделения на размер партии. Модуль реплицируется на каждую машину и каждое устройство , и каждая такая реплика обрабатывает часть ввода. Во время обратного прохода градиенты от каждого узла усредняются .

Таким образом, когда модель создается с некоторыми весами, она реплицируется на все устройства (каждый графический процессор для каждого узла) , Теперь каждый графический процессор получает часть входных данных (скажем, для общего размера пакета, равного 1024, 4 узлов, каждый с 4 графическими процессорами, каждый графический процессор получит 64 элементов), вычисляет прямой проход, потери, выполняет backprop по тензорному методу .backward(). Теперь все градиенты усредняются и используются как поправка, которая эффективно гарантирует, что каждая реплика module получает одно и то же обновление каждый раз.

Примечание : я не уверен, как именно происходит это усреднение (и я не вижу, что это явно сказано в документах), хотя я предполагаю, что они сначала усредняются по графическим процессорам, а затем по всем узлам, так как это будет наиболее эффективным, на мой взгляд.

Теперь, зачем вам экономить модель для каждого node в таком случае? В принципе, вы можете сохранить только один (так как все модули будут одинаковыми), но у него есть некоторые недостатки:

  • Скажите, что ваш узел, на котором была сохранена ваша модель, вылетает, а файл теряется. Вы должны переделать все вещи. Сохранение каждой модели не является слишком дорогостоящей операцией (выполняется один раз за эпоху или менее), поэтому ее можно легко сделать для каждого узла / рабочего
  • Вам необходимо возобновить обучение. Это означает, что модель должна быть скопирована для каждого работника (и некоторых необходимых метаданных, хотя я не думаю, что это так)
  • Узлам в любом случае придется ждать каждого прямого прохода до конечного значения sh (поэтому градиенты могут быть усреднены), если сохранение модели занимает много времени, это приведет к потере простоя ГП / ЦП (или какой-либо другой схеме синхронизации) придется применить, я не думаю, что есть один в PyTorch). Это делает его несколько «бесплатным», если вы посмотрите на общую картину.

Вопрос 2 (и 3)

И какую модель мне следует использовать, если я получить несколько моделей?

Неважно, поскольку все они будут точно такими же, как и те же исправления с помощью оптимизатора, применяемые к модели с одинаковыми начальными весами.

Вы можете использовать что-то в этом направлении для загрузки сохраненной модели .pth:

import torch

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

parallel_model = torch.nn.DataParallel(MyModelGoesHere())
parallel_model.load_state_dict(
    torch.load("my_saved_model_state_dict.pth", map_location=str(device))
)

# DataParallel has model as an attribute
usable_model = parallel_model.model
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...