Как работает асинхронное обучение в распределенном Tensorflow?

Я читал Dred Distributed Tensorflow Doc, и он упоминает, что при асинхронном обучении

каждая реплика графика имеет независимый цикл обучения, который выполняется без согласования.

Из того, что я понимаю, если мы используем параметр-сервер с архитектурой данных parallelism, это означает, что каждый рабочий вычисляет градиенты и обновляет свои собственные веса, не заботясь о других обновлениях работников для распределенной тренировки Neural Network. Поскольку все веса распределяются на сервере параметров (ps), я думаю, что ps все равно должен каким-то образом скоординировать (или суммировать) весовые обновления со всех рабочих. Интересно, как работает агрегация в асинхронном обучении. Или более общие слова, как работает асинхронное обучение в распределенном Tensorflow?

Ответ 1

Когда вы тренируетесь асинхронно в распределенном TensorFlow, конкретный работник делает следующее:

  • Рабочий читает все параметры совместно используемой модели параллельно с задачами (задачами) PS и копирует их в рабочую задачу. Эти чтения не согласованы с какими-либо одновременными записями, и никакие блокировки не получаются: в частности, работник может видеть частичные обновления от одного или нескольких других работников (например, подмножество обновлений от другого рабочего может быть применено или подмножество элементов в переменной, возможно, была обновлена).

  • Рабочий вычисляет градиенты локально, на основе партии входных данных и значений параметров, которые он читает на шаге 1.

  • Работник отправляет градиенты для каждой переменной в соответствующую задачу PS, а применяет градиенты к их соответствующей переменной, используя правило обновления, которое определяется по алгоритму оптимизации (например, SGD, SGD с Momentum, Adagrad, Adam и т.д.). В правилах обновления обычно используются (приблизительно) коммутативные операции, поэтому они могут применяться независимо от обновлений от каждого рабочего, а состояние каждой переменной будет выполняемым агрегатом последовательности полученных обновлений.

При асинхронном обучении каждое обновление от рабочего применяется одновременно, и обновления могут быть несколько скоординированы, если дополнительный флаг use_locking=True был установлен, когда соответствующий оптимизатор (например, tf.train.GradientDescentOptimizer). Обратите внимание, однако, что блокировка здесь обеспечивает взаимное исключение для двух одновременных обновлений, и (как указано выше) чтения не приобретают блокировки; блокировка не обеспечивает атомарность по всему набору обновлений.

(В отличие от этого, при синхронном обучении такая утилита, как tf.train.SyncReplicasOptimizer, гарантирует, что все рабочие будут считывать одинаковые, обновленные значения для каждого параметра модели и что все обновления для синхронного шага агрегированные до того, как они будут применены к основным переменным. Для этого рабочие синхронизируются с помощью барьера, который они вводят после отправки их обновления градиента, и уходят после того, как агрегированное обновление было применено ко всем переменным.)

Ответ 2

В асинхронном обучении отсутствует синхронизация весов среди рабочих. Веса хранятся на сервере параметров. Каждый рабочий загружает и изменяет общие веса независимо друг от друга. Таким образом, если один рабочий закончил итерацию быстрее, чем другие работники, она продолжит следующую итерацию, не дожидаясь ожидания. Рабочие только взаимодействуют с сервером общих параметров и не взаимодействуют друг с другом.

В целом он может (в зависимости от задачи) значительно ускорить вычисление. Однако результаты иногда хуже, чем результаты с медленными синхронными обновлениями.

Ответ 3

Посмотрите на пример в документации, на которую вы ссылаетесь:

with tf.device("/job:ps/task:0"):
  weights_1 = tf.Variable(...)
  biases_1 = tf.Variable(...)

with tf.device("/job:ps/task:1"):
  weights_2 = tf.Variable(...)
  biases_2 = tf.Variable(...)

with tf.device("/job:worker/task:7"):
  input, labels = ...
  layer_1 = tf.nn.relu(tf.matmul(input, weights_1) + biases_1)
  logits = tf.nn.relu(tf.matmul(layer_1, weights_2) + biases_2)
  # ...
  train_op = ...

with tf.Session("grpc://worker7.example.com:2222") as sess:
  for _ in range(10000):
    sess.run(train_op)

Вы можете видеть, что обучение распределяется на трех машинах, все они имеют копию одинаковых весов, но, как упоминается чуть ниже примера:

В приведенном выше примере переменные создаются по двум задачам в задании ps, а часть рабочего процесса, использующая вычислительные ресурсы, создается в рабочем задании. TensorFlow вставляет соответствующие передачи данных между заданиями (от ps до рабочего для прямого прохода и от рабочего до ps для применения градиентов).

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

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