[Анализ исходного кода] pytorch распределенный автоград (1) ---- дизайн

глубокое обучение PyTorch

0x00 сводка

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

Другие статьи о распространении PyTorch:

[Анализ исходного кода] Распространение PyTorch (1) ------ история и обзор

[Анализ исходного кода] Как PyTorch использует GPU

[Анализ исходного кода] Распределенный PyTorch (2) ----- DataParallel (включен)

[Анализ исходного кода] Распределенный PyTorch (3) ----- DataParallel (ниже)

[Анализ исходного кода] Распределенный PyTorch (4) ------ Основная концепция распределенного приложения

[Анализ исходного кода] Распределенный PyTorch (5) ------ Обзор DistributedDataParallel и способы его использования

[Анализ исходного кода] Распределенный PyTorch (6) ---DistributedDataParallel -- инициализация и хранение

[Анализ исходного кода] Распределенный PyTorch (7) ----- Группа процессов DistributedDataParallel

[Анализ исходного кода] Распределенный PyTorch (8) -------- Бумага DistributedDataParallel

[Анализ исходного кода] Распределенный PyTorch (9) ----- Инициализация DistributedDataParallel

[Анализ исходного кода] Распределенный PyTorch (10) ------ Редуктор статической архитектуры DistributedDataParallel

[Анализ исходного кода] Распределенный PyTorch (11) ----- DistributedDataParallel для создания операций Reducer и Join

[Анализ исходного кода] Распределенный PyTorch (12) ----- Прямое распространение до DistributedDataParallel

[Анализ исходного кода] Распределенный PyTorch (13) ----- Обратное распространение DistributedDataParallel

0x01 Распределенная структура RPC

Эта статья в основном посвященаpy torch.org/docs/master…В качестве эталона, но исходный документ требует, чтобы пользователи были знакомы сМеханизм АвтоградиРаспределенная среда RPC, поскольку мы уже разобрали механизм Автограда, давайте сначала рассмотрим егоРаспределенная среда RPC.

1.1 Платформа RPC

RPC (удаленный вызов процедур) — это дизайн или техническая идея, а не протокол или спецификация.

Самое простое понимание RPC состоит в том, что узел запрашивает услуги, предоставляемые другим узлом, но для пользовательского кода ему необходимо поддерживать ощущение «локального вызова», то есть для удаленных вызовов функций удаленный сервис должен быть таким же, как вызов локальные функции, или код выглядит так, как будто он выполняется локально.

RPC должен решить несколько проблем:

  • Как общаться: Как установить соединение между вызывающим абонентом и поставщиком услуг.
  • Как обращаться: то есть, как звонящий находит поставщика услуг, и как он узнает, какие услуги в нем есть.
  • Как отправлять параметры: когда вызывающая сторона инициирует удаленный вызов, параметры метода должны быть переданы на сервер через такие протоколы, как TCP Как сериализовать параметры?
  • Как принимать параметры: как десериализовать и вызвать после того, как сервис-провайдер получит параметры.
  • Как вернуть: как отправить возвращаемое значение вызывающей стороне после того, как поставщик услуг вызовет локально предоставляемую службу.

1.2 Четыре столпа PyTorch RPC

Следующее переведено из официальной документацииpy torch.org/docs/master…

Распределенная среда RPC предоставляет механизм обучения модели на нескольких машинах с помощью набора примитивов, обеспечивающих удаленную связь, и высокоуровневый API для автоматического различения моделей, разделенных на нескольких машинах. Распределенная среда RPC упрощает удаленный запуск функций, поддерживает ссылки на удаленные объекты без копирования реальных данных и предоставляет API-интерфейсы автоградации и оптимизатора для прозрачного запуска в обратном направлении и обновления параметров за пределами границ RPC. Эти функции можно разделить на четыре группы API.

  1. Удаленный вызов процедур (RPC)Поддерживает запуск функции на указанном рабочем потоке с заданными параметрами и получение возвращаемого значения или создание ссылки на возвращаемое значение. Существует три основных API RPC:rpc_sync()(Синхронизировать),rpc_async()(асинхронный) иremote()(асинхронно и возвращает ссылку на удаленное возвращаемое значение). Если пользовательский код не может продолжать работу без возвращаемого значения, используйте синхронный API. В противном случае используйте асинхронный API для получения Future и ждите Future, когда вызывающему объекту потребуется возвращаемое значение.remote()API-интерфейсы полезны, когда что-то нужно создать удаленно, но не нужно передавать вызывающей стороне. Представьте ситуацию, когда процесс-драйвер задает параметры сервера и тренера. Драйвер может создать таблицу внедрения на сервере параметров, а затем поделиться ссылкой на таблицу внедрения с тренером, но сам никогда не будет использовать таблицу внедрения локально. при этих обстоятельствах,rpc_sync()иrpc_async()Больше не применимы, так как они всегда подразумевают, что возвращаемое значение отправляется вызывающей стороне немедленно или в будущем.
  2. Удаленная ссылка (RRef)Используется как распределенный общий указатель на локальный или удаленный объект. Им можно поделиться с другими работниками, и подсчет ссылок будет осуществляться прозрачно. У каждого RRef есть только один владелец, и объект существует только внутри этого владельца. Рабочий процесс, не являющийся владельцем, владеющий RRef, может получить копию объекта от владельца, явно запросив его. Когда работнику необходимо получить доступ к объекту данных, но он не является создателем самого объектаremote()Это полезно, когда вызывающая функция также не является владельцем объекта. Распределенный оптимизатор является примером такого варианта использования.
  3. Distributed AutogradСшивает вместе все локальные механизмы автоградации, участвующие в рабочих процессах прямого прохода, и автоматически связывается с ними для вычисления градиентов во время обратного прохода. Это особенно полезно при выполнении прямых проходов, которые должны охватывать несколько компьютеров, таких как параллельное обучение распределенной модели, обучение сервера параметров и т. д. Благодаря этой функции пользовательскому коду больше не нужно беспокоиться о том, как отправлять градиенты через границы RPC и в каком порядке следует запускать локальный механизм автоградации, что может стать очень сложным, если в прямом проходе есть вложенные и взаимозависимые вызовы RPC.
  4. оптимизатор распределенияДля строительства требуетсяOptimizer()(Например,SGD(),Adagrad()и т.д.) и список аргументов RRefs. т. е. создать по одному поверх каждого владельца ссылки.Optimizer()экземпляр, затем запуститеstep()Обновите параметры соответствующим образом. Когда пользователи выполняют распределенное прямое и обратное распространение, параметры и градиенты будут распределены между несколькими рабочими процессами, поэтому каждый связанный рабочий процесс необходимо оптимизировать. Distributed Optimizer объединяет все эти локальные оптимизаторы в один и предоставляет краткие конструкторы иstep()API.

1.3 RRef

Ниже мы используемpy torch.org/docs/master…Станьте эталоном для изучения основных концепций и некоторых деталей дизайна протокола удаленного цитирования.

RRef — это аббревиатура от Remote REFerence. Это ссылка на объект, расположенный на локальном или удаленном рабочем сервере, и ссылки подсчитываются прозрачно внутри. Концептуально его можно рассматривать как распределенный общий указатель. приложение может позвонитьremote()Создайте RRef. Каждый RRefremote()принадлежит вызывающей стороне (т. е. владельцу) объекта и может использоваться несколькими пользователями. Владелец хранит фактические данные и отслеживает глобальный счетчик ссылок. К каждому RRef может обращаться глобальныйRRefIdуникальный идентификатор, глобальныйRRefIdсозданremote()назначение вызывающего абонента.

Среди рабочих-собственников есть только одинOwnerRRefЭкземпляры содержат реальные данные, а среди пользовательских воркеров могут содержать сколько угодноUserRRefs,UserRRefДанные не сохраняются. При использовании RRP владелец будет использовать глобально уникальный RRefId для получения уникального экземпляра OwnerRRef. существуетrpc_sync(),rpc_async()илиremote()В вызове владелец создаетUserRRefи используйте его как параметр или возвращаемое значение. Владелец будет уведомлен, а количество ссылок обновлено соответствующим образом. Если глобальный неUserRRefэкземпляр, и нет права на владельцаOwnerRRefцитаты, тоOwnerRRefи его данные будут удалены.

1.3.1 Предположения

Дизайн протокола RRef основан на следующих предположениях.

  • Временные сбои сети (Transient Network Failures): RRef предназначен для обработки временных сетевых сбоев путем повторной отправки сообщений. RRef не может обрабатывать сбои узлов или постоянные сетевые разделы, и при возникновении этих событий приложение должно завершить работу всех рабочих процессов, вернуться к предыдущей контрольной точке и возобновить обучение.
  • Неидемпотентная определяемая пользователем функция (Non-idempotent UDFs): мы предполагаем, чтоrpc_sync(),rpc_async()илиremote()Пользовательская функция (UDF) не является идемпотентной, поэтому повторная попытка невозможна. Однако внутренние управляющие сообщения RRef являются идемпотентными и могут повторяться при сбое сообщения.
  • Доставка сообщений вне очереди (Out of Order Message Delivery): Мы не делаем предположений о порядке доставки сообщений между парой узлов, потому что и отправитель, и получатель используют несколько потоков, поэтому нет гарантии, какое сообщение будет обработано первым.

Далее мы лишь кратко объясним, как его использовать.py torch.org/docs/master…

1.3.2 Синхронный вызов

Далее нужно вызвать API синхронно, метод находится в workertoвыполняет блокирующий вызов RPC сверху для запускаfunc. Сообщения RPC отправляются и принимаются параллельно с выполнением кода Python. Этот метод является потокобезопасным.

torch.distributed.rpc.rpc_sync( to , func , args = None , kwargs = None , timeout = - 1.0 )

Конкретные параметры следующие:

  • to– Имя/ранг/WorkerInfo целевого работника.
  • func (callable) — вызываемая функция, такая как вызываемые функции Python, встроенные операторы (например, add() ) и аннотированные функции TorchScript.
  • argsfuncКортеж аргументов для вызова.
  • kwargsfuncСловарь аргументов ключевых слов для вызова.
  • timeout– Тайм-аут (в секундах), используемый для этого RPC.

Возвращаемое значение должно использоватьargs and kwargsбегатьfuncрезультат.

Пример:

удостоверитьсяMASTER_ADDR and MASTER_PORTУже настроен поверх двух рабочих.

export MASTER_ADDR=localhost
export MASTER_PORT=5678

Затем запустите следующий код в двух разных процессах.

>>> # On worker 0:
>>> import torch
>>> import torch.distributed.rpc as rpc
>>> rpc.init_rpc("worker0", rank=0, world_size=2)
>>> ret = rpc.rpc_sync("worker1", torch.add, args=(torch.ones(2), 3))
>>> rpc.shutdown()
>>> # On worker 1:
>>> import torch.distributed.rpc as rpc
>>> rpc.init_rpc("worker1", rank=1, world_size=2)
>>> rpc.shutdown()

1.3.2 Асинхронный вызов

Ниже приведен API асинхронного вызова, метод находится в рабочемtoвыполняет неблокирующий вызов RPC сверху для запускаfunc. Сообщения RPC отправляются и принимаются параллельно с выполнением кода Python. Этот метод является потокобезопасным. Метод немедленно возвращает ожидаемоеFuture.

torch.distributed.rpc.rpc_async(to, func, args=None, kwargs=None, timeout=- 1.0)

Конкретные параметры следующие:

  • to– имя/ранг/ целевого работникаWorkerInfo.
  • func (callable) — вызываемая функция, такая как вызываемые функции Python, встроенные операторы (например, add() ) и аннотированные функции TorchScript.
  • argsfuncКортеж аргументов для вызова.
  • kwargs- ДаfuncСловарь аргументов ключевых слов для вызова.
  • timeout– Тайм-аут (в секундах), используемый для этого RPC.

возвращает ожидаемыйFutureобъект. После этого его можно извлечь из объекта.funcВозвращаемое значение.

Пример:

удостоверитьсяMASTER_ADDR and MASTER_PORTУже настроен поверх двух рабочих.

>>> export MASTER_ADDR=localhost
>>> export MASTER_PORT=5678

Затем запустите следующий код в двух разных процессах.

>>> # On worker 0:
>>> import torch
>>> import torch.distributed.rpc as rpc
>>> rpc.init_rpc("worker0", rank=0, world_size=2)
>>> fut1 = rpc.rpc_async("worker1", torch.add, args=(torch.ones(2), 3))
>>> fut2 = rpc.rpc_async("worker1", min, args=(1, 2))
>>> result = fut1.wait() + fut2.wait()
>>> rpc.shutdown()
>>> # On worker 1:
>>> import torch.distributed.rpc as rpc
>>> rpc.init_rpc("worker1", rank=1, world_size=2)
>>> rpc.shutdown()

пример 0x02

Мы следующиеpy torch.org/docs/master…Учиться на основе.

Предположим, у вас есть два узла и очень простая модель, разделенная на два узла. Это можно использоватьtorch.distributed.rpcРеализовано следующим образом.

Основной мотивацией распределенного автограда является запуск обратного распространения в этой распределенной модели.loss, мы вычислили и записали градиенты всех тензоров, которым требуются градиенты.

import torch
import torch.distributed.rpc as rpc

def my_add(t1, t2):
  return torch.add(t1, t2)

# On worker 0:
t1 = torch.rand((3, 3), requires_grad=True)
t2 = torch.rand((3, 3), requires_grad=True)

# Perform some computation remotely.
t3 = rpc.rpc_sync("worker1", my_add, args=(t1, t2))

# Perform some computation locally based on remote result.
t4 = torch.rand((3, 3), requires_grad=True)
t5 = torch.mul(t3, t4)

# Compute some loss.
loss = t5.sum()

0x03 Записи Autograd во время прямого распространения

PyTorch строит график автоградации во время прямого прохода, который используется для выполнения обратного прохода. Подробнее см.Как автоград кодирует историю.

Для распределенного автограда нам необходимо отслеживать все RPC во время прямого распространения, чтобы обеспечить правильное обратное распространение. Для этого при выполнении RPC ставимsendиrecvфункции привязаны к графу автограда.

  • ДолженsendФункция присоединена к исходному узлу RPC, а ее выходное ребро указывает на функцию автоградации входного тензора RPC. При обратном распространенииsendВход в функцию поступает от цели и является соответствующимrecvвывод функции.
  • ДолженrecvФункция присоединяется к принимающему целевому узлу RPC, и ее входные данные получаются от определенных операторов, которые выполняются на принимающем целевом RPC с использованием входных тензоров. При обратном распространенииrecvВыходной градиент функции будет отправлен через исходный узел и использован какsendввод метода.
  • Каждыйsend-recvприсваивается глобально уникальныйautograd_message_idчтобы однозначно идентифицироватьsend-recvправильно. Это полезно для поиска соответствующих функций на удаленных узлах во время обратного распространения.
  • заRRef, всякий раз, когда мы звонимtorch.distributed.rpc.RRef.to_here(), мы все добавляем соответствующий тензор к задействованным тензорамsend-recvправильно.

Например, вот как выглядит график автоградации в нашем примере выше (t5.sum() опущено для простоты).

Мы видим, что метод отправки является отправителем в прямом проходе, но получателем в обратном проходе.

../_images/send_recv_functions.png

0x04 Распределенный контекст Autograd

Каждому прямому и обратному проходу с использованием распределенного автограда назначается уникальныйtorch.distributed.autograd.context, и этот контекст имеет глобально уникальныйautograd_context_id. Контекст создается на каждом узле, если это необходимо.

Контекст работает следующим образом:

  1. Несколько узлов с распределенным обратным распространением могут накапливать градиенты на одном и том же тензоре и сохранять их в памяти тензора..gradвыше. Прежде чем мы запустим оптимизатор, тензор.gradГрадиенты от различных распределенных обратных распространений могут накапливаться. Это похоже на то, чтобы поставитьtorch.autograd.backward()Сделайте несколько звонков локально. Чтобы обеспечить способ разделения каждого градиента обратного распространения, в каждом проходе обратного распространения градиент будет накапливаться вtorch.distributed.autograd.contextсреди.
  2. Во время прямого прохода мы сохраняем в контексте значение каждого прохода автоградаsendиrecvфункция. Это гарантирует, что мы сохраним ссылку на соответствующий узел в графе autograd, чтобы поддерживать его в рабочем состоянии. Среди прочего, это также упрощает поиск соответствующихsendиrecvфункция.
  3. В общем, мы также используем этот контекст для хранения некоторых метаданных для каждого распределенного распространения autograd.

С точки зрения пользователя контекст автограда задается следующим образом:

import torch.distributed.autograd as dist_autograd
with dist_autograd.context() as context_id:
  loss = model.forward()
  dist_autograd.backward(context_id, loss)

Важно отметить, что прямое распространение модели должно вызываться в диспетчере распределенного контекста autograd, потому что для обеспечения того, чтобы:sendиrecvМетоды сохраняются и распространяются обратно на все участвующие узлы.

0x05 Распределенное обратное распространение

В этом разделе мы описываем проблемы точного вычисления зависимостей во время распределенного обратного распространения, а также описываем несколько алгоритмов выполнения распределенного обратного распространения (в алгоритме есть компромиссы).

5.1 Вычисление зависимостей

Во-первых, рассмотрите возможность запуска следующего кода на одной машине.

import torch
a = torch.rand((3, 3), requires_grad=True)
b = torch.rand((3, 3), requires_grad=True)
c = torch.rand((3, 3), requires_grad=True)
d = a + b
e = b * c
d.sum.().backward()

На следующем рисунке показан график автоградации, соответствующий приведенному выше коду.

../_images/local_dependencies.png

В рамках обратного распространения первый шаг, выполняемый механизмом autograd, — подсчет количества зависимостей для каждого узла в графе autograd. Это помогает движку autograd узнать, когда узлы в графе готовы к выполнению. цифры в скобкахadd(1)иmul(0)Представляет количество зависимостей. Как видите, это означает, что при обратном распространенииaddУзел требует 1 вход,mulУзлы не требуют никаких входных данных (другими словами, их не нужно выполнять). Локальный механизм автоградации запускается с корневого узла (в данном случаеd) пройти по графу, чтобы вычислить эти зависимости.

На практике некоторые узлы в графе Autograd могут не выполняться при обратном проходе. Этот факт представляет собой проблему для распределенного автограда. Рассмотрим этот код, который использует RPC.

import torch
import torch.distributed.rpc as rpc

a = torch.rand((3, 3), requires_grad=True)
b = torch.rand((3, 3), requires_grad=True)
c = torch.rand((3, 3), requires_grad=True)

d = rpc.rpc_sync("worker1", torch.add, args=(a, b))
e = rpc.rpc_sync("worker1", torch.mul, args=(b, c))
loss = d.sum()

Связанный график autograd для приведенного выше кода будет выглядеть так:

../_images/distributed_dependencies.png

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

Для приложений, чувствительных к производительности, мы можем сделать это, предполагая, что каждыйsendиrecvВсе функции являются эффективными компонентами обратного распространения, чтобы избежать больших накладных расходов (большинство приложений не выполняют неиспользуемые RPC). Это упрощает распределенный алгоритм автоградации и делает его более эффективным за счет приложения, которое должно знать об этих ограничениях. Этот алгоритм называетсяАлгоритм режима FAST, подробно описано ниже.

В общем случае в рамках обратного распространения может не понадобиться для каждогоsendиrecvфункции действительны. Для решения этой проблемы мы предлагаемАлгоритм режима SMART, этот алгоритм будет описан в следующем разделе. Обратите внимание, что в настоящее время реализован только алгоритм режима FAST.

5.2 Алгоритм режима FAST

Ключевое предположение этого алгоритма состоит в том, что когда мы запускаем обратное распространение, каждыйsendФункция имеет зависимость 1. Другими словами, мы предполагаем, что будем получать градиенты через RPC от другого узла.

Алгоритм следующий:

  1. Начнем с воркеров с корнями обратного распространения (все корни должны быть локальными).
  2. Найти весь текущий распределенный контекст Autograd.sendфункция .
  3. из предоставленного корня и все, что мы получилиsendФункция запускается, и мы вычисляем зависимости локально.
  4. После вычисления зависимостей используйте предоставленный корень для запуска локального механизма автоградации.
  5. Когда движок автограда выполняетrecvфункция,recvФункция отправляет входной градиент соответствующему воркеру через RPC. каждыйrecvФункция знает идентификатор целевого рабочего, поскольку он записывается как часть прямого прохода. пройти черезautograd_context_idиautograd_message_idДолженrecvФункция отправляется на удаленный хост.
  6. Когда удаленный хост получает этот запрос, мы используемautograd_context_idиautograd_message_idнайти подходящееsendфункция.
  7. Если это первый раз, когда работник получилautograd_context_idзапросы, он будет вычислять зависимости локально, как описано в пунктах 1-3 выше.
  8. то получит в точке 6sendМетоды вставляются в очередь для выполнения на локальном механизме автоградации этого рабочего.
  9. Наконец, мы не в Тензоре..gradвместо того, чтобы накапливать градиенты поверх каждого распределенного контекста Autograd отдельно. Градиенты хранятся вDict[Tensor, Tensor]среди ,Dict[Tensor, Tensor]По сути, это карта от Tensor к связанным с ними градиентам, и эту карту можно получить с помощью API get_gradients().

Например, полный код распределенного автограда выглядит следующим образом:

import torch
import torch.distributed.autograd as dist_autograd
import torch.distributed.rpc as rpc

def my_add(t1, t2):
  return torch.add(t1, t2)

# On worker 0:

# Setup the autograd context. Computations that take
# part in the distributed backward pass must be within
# the distributed autograd context manager.
with dist_autograd.context() as context_id:
  t1 = torch.rand((3, 3), requires_grad=True)
  t2 = torch.rand((3, 3), requires_grad=True)

  # Perform some computation remotely.
  t3 = rpc.rpc_sync("worker1", my_add, args=(t1, t2))

  # Perform some computation locally based on remote result.
  t4 = torch.rand((3, 3), requires_grad=True)
  t5 = torch.mul(t3, t4)

  # Compute some loss.
  loss = t5.sum()

  # Run the backward pass.
  dist_autograd.backward(context_id, [loss])

  # Retrieve the gradients from the context.
  dist_autograd.get_gradients(context_id)

Распределенный граф autograd с зависимостями выглядит следующим образом (t5.sum() исключен для простоты):

../_images/distributed_dependencies_computed.png

Алгоритм режима FAST, примененный к приведенному выше примеру, выглядит следующим образом:

  1. существуетWorker 0вверх, мы начинаем с корняlossиsend1Начните вычислять зависимости. результат,send1правильноWorker 0Количество зависимостей равно 1,mulправильноWorker 0Количество зависимостей равно 1.
  2. Теперь мыWorker 0Запустите локальный движок автограда на . Сначала мы выполняемmulфункция с выводом в видеt4Градиент , накопленный в контексте автограда. Затем мы выполняемrecv2, который отправляет эти градиенты вWorker 1.
  3. Так как этоWorker 1Зная об этом обратном распространении в первый раз, он выполнит вычисление зависимости и соответствующим образом пометит его.send2,addиrecv1зависимость.
  4. Далее, вWorker 1местныйautogradадмирал двигателяsend2Вставить в очередь, движок будет выполняться последовательноaddиrecv1.
  5. при исполненииrecv1, он отправляет градиент вWorker 0.
  6. так какWorker 0Эта зависимость с обратным распространением уже рассчитана, поэтому она может применяться только локально.send1Вставить в очередь и выполнить.
  7. Наконец,t1,t2иt4Градиенты накапливаются в распределенном контексте Autograd.

5.3 Алгоритм режима SMART

Полная информация об алгоритме все еще находится в разработке, но для общего представления вы можете обратиться кRFCсерединаУмный режим распределенного алгоритма Autogradчасть .

0x06 Распределенный оптимизатор

ДолженDistributedOptimizerОперация выглядит следующим образом:

  1. Получить удаленные параметры для оптимизации (RRef) список. Эти параметры также могут быть включены локальноRRefлокальные параметры.
  2. положитьOptimizerclass действует как локальный оптимизатор, которыйRRefбеги от хозяина.
  3. Распределенный оптимизатор создает локальныйOptimizerэкземпляр и для каждогоOptimizerсохранитьRRef.
  4. при звонкеtorch.distributed.optim.DistributedOptimizer.step(), распределенный оптимизатор выполняет все локальные оптимизаторы удаленно на соответствующем удаленном рабочем месте с помощью RPC. должно бытьtorch.distributed.optim.DistributedOptimizer.step()Предоставляет распределенный автоградcontext_id. использование локального оптимизатораcontext_idХраните градиенты в соответствующем контексте.
  5. Если несколько параллельных распределенных оптимизаторов обновляют один и тот же пакет параметров рабочего процесса, эти обновления будут выполняться через блокировки.

0x07 Простой сквозной пример

С учетом всего сказанного, вот простой сквозной пример с использованием распределенного автограда и распределенного оптимизатора. Если вы поместите свой код в файл с именем «dist_autograd_simple.py», вы можете запустить его с помощью:MASTER_ADDR="localhost" MASTER_PORT=29500 python dist_autograd_simple.py

import torch
import torch.multiprocessing as mp
import torch.distributed.autograd as dist_autograd
from torch.distributed import rpc
from torch import optim
from torch.distributed.optim import DistributedOptimizer

def random_tensor():
    return torch.rand((3, 3), requires_grad=True)

def _run_process(rank, dst_rank, world_size):
    name = "worker{}".format(rank)
    dst_name = "worker{}".format(dst_rank)

    # Initialize RPC.
    rpc.init_rpc(
        name=name,
        rank=rank,
        world_size=world_size
    )

    # Use a distributed autograd context.
    with dist_autograd.context() as context_id:
        # Forward pass (create references on remote nodes).
        rref1 = rpc.remote(dst_name, random_tensor)
        rref2 = rpc.remote(dst_name, random_tensor)
        loss = rref1.to_here() + rref2.to_here()

        # Backward pass (run distributed autograd).
        dist_autograd.backward(context_id, [loss.sum()])

        # Build DistributedOptimizer.
        dist_optim = DistributedOptimizer(
        optim.SGD,
        [rref1, rref2],
        lr=0.05,
        )

        # Run the distributed optimizer step.
        dist_optim.step(context_id)

def run_process(rank, world_size):
    dst_rank = (rank + 1) % world_size
    _run_process(rank, dst_rank, world_size)
    rpc.shutdown()

if __name__ == '__main__':
  # Run world_size workers
  world_size = 2
  mp.spawn(run_process, args=(world_size,), nprocs=world_size)

0xEE Личная информация

★★★★★★Думая о жизни и технологиях★★★★★★

Публичный аккаунт WeChat:мысли Росси

ссылка 0xFF

py torch.org/docs/master…

py torch.org/docs/master…

py torch.org/docs/master…

py torch.org/docs/master…