Что происходит
Пожалуйста, исправьте меня, если я ошибаюсь в любом месте
Изменения, на которые вы ссылаетесь, были внесены в 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