Многопроцессорная обработка PyTorch: правильно ли перемещаются градиенты в реализациях ACER?

#python #multithreading #pytorch #python-multiprocessing #reinforcement-learning

#python #многопоточность #pytorch #python-многопроцессорная обработка #подкрепление-обучение

Вопрос:

Я реализую архитектуру ACER, которая выполняет асинхронное обновление параметров как A3C. Глядя на существующие реализации PyTorch, я часто вижу, что обновление параметров выполняется следующим образом

 # Transfers gradients from thread-specific model to shared model
def _transfer_grads_to_shared_model(model, shared_model):
  for param, shared_param in zip(model.parameters(), shared_model.parameters()):
    if shared_param.grad is not None:
      return
    shared_param._grad = param.grad
 

Я понимаю, что если shared_param.grad не равен None, это означает, что другой поток добавил градиент в модель, поэтому мы возвращаемся, чтобы не перезаписывать его.

Мои вопросы таковы:

  • Таким образом, мы «отбрасываем» некоторые вычисленные градиенты, я прав?
  • Почему мы не используем механизм блокировки при обновлении общих параметров модели? Это позволило бы избежать одновременного обновления параметров двумя потоками. Поскольку потоки тратят большую часть своего времени на выполнение шагов в среде и обновление своих локальных моделей, добавление блокировки для общего обновления модели может оказать незначительное влияние на производительность. Прав ли я в этом? Если да, то почему это не делается на практике?
  • Что произойдет, если поток вызывает optimizer.step() shared_model, в то время как другой поток копирует в него градиенты?

Другая реализация, которую я нашел, делает это так:

 def copy_gradients_from(model, shared_model):
  for shared_parameter, parameter in zip(shared_model.parameters(), model.parameters()):
    shared_parameter._grad = parameter.grad
 

который не проверяет наличие существующих градиентов, а также вызывается без какого-либо Lock механизма.