Distributed TensorFlow [Async, Between-Graph Replication]: cuál es la

Distributed TensorFlow [Async, Between-Graph Replication]: cuál es la interacción exacta entre trabajadores y servidores con respecto a la actualización de variables

He leído Distributed TensorFlow Doc y esta pregunta sobre StackOverflow pero todavía tengo algunas dudas sobre la dinámica detrás del entrenamiento distribuido que se puede realizar con TensorFlow y su arquitectura de servidor de parámetros. Este es un fragmento de código de Distributed TensorFlow Doc:

if FLAGS.job_name == "ps":
    server.join()
  elif FLAGS.job_name == "worker":

    # Assigns ops to the local worker by default.
    with tf.device(tf.train.replica_device_setter(
        worker_device="/job:worker/task:%d" % FLAGS.task_index,
        cluster=cluster)):

      # Build model...
      loss = ...
      global_step = tf.contrib.framework.get_or_create_global_step()

      train_op = tf.train.AdagradOptimizer(0.01).minimize(
          loss, global_step=global_step)

Y aquí parte de la respuesta de la pregunta de StackOverflow que leí:

El trabajador lee todos los parámetros del modelo compartido en paralelo desde la(s) tarea(s) de PS y las copia en la tarea del trabajador. Estas lecturas son descoordinado con cualquier escritura concurrente, y no se adquieren bloqueos: en particular, el trabajador puede ver actualizaciones parciales de uno o más otros trabajadores (por ejemplo, un subconjunto de las actualizaciones de otro trabajador puede se han aplicado, o un subconjunto de los elementos de una variable puede haber actualizado).

El trabajador calcula los gradientes localmente, en función de un lote de datos de entrada y los valores de los parámetros que leyó en el paso 1.

El el trabajador envía los gradientes para cada variable al PS apropiado tarea, y aplica los gradientes a su respectiva variable, usando un regla de actualización determinada por el algoritmo de optimización (p. SGD, SGD con Momentum, Adagrad, Adam, etc.). Las reglas de actualización suelen utilizar (aproximadamente) operaciones conmutativas, por lo que pueden ser aplicado de forma independiente sobre las actualizaciones de cada trabajador, y el estado de cada variable será un agregado continuo de la secuencia de actualizaciones recibidas.

Tengo que reproducir este tipo de arquitectura de servidor de parámetros en otro entorno y necesito entender profundamente cómo los trabajadores y las tareas de PS interactúan entre sí dentro del marco de TensorFlow. Mi pregunta es, ¿la tarea PS realiza algún tipo de operación de fusión o actualización después de recibir el valor de los trabajadores o simplemente almacena el valor más nuevo? ¿Puede ser algo razonable simplemente almacenar el valor más nuevo? Mirando el código de la documentación de TensorFlow, veo que la tarea de PS solo hace un join() y me pregunto detrás de esta llamada de método cuál es el comportamiento completo de la tarea de PS.

Una pregunta más, ¿cuál es la diferencia entre calcular un degradado y aplicar un degradado?

Mostrar la mejor respuesta

Vayamos en orden inverso y comencemos desde su última pregunta: ¿cuál es la diferencia entre calcular un degradado y aplicar un degradado?

Calcular los gradientes significa ejecutar el paso hacia atrás en la red, después de haber computado la pérdida. Para el descenso de gradiente, esto significa estimar el valor de gradients en la fórmula a continuación (nota: esta es una simplificación enorme de lo que realmente implica la computación de gradientes, busque más información sobre la retropropagación y el descenso de gradiente para una explicación adecuada de cómo funciona esto). Aplicar los gradientes significa actualizar los parámetros de acuerdo con los gradientes que acaba de calcular. Para el descenso de gradiente, esto (más o menos) significa ejecutar lo siguiente:

weights = weights - (learning_step * gradients)

Tenga en cuenta que, según el valor de learning_step, el nuevo valor de weights depende tanto del valor anterior como de los pesos calculados.

Con esto en mente, es más fácil comprender la arquitectura PS/worker. Hagamos la suposición simplificada de que solo hay un PS (veremos más adelante cómo extenderlo a varios PS)

Un PS (servidor de parámetros) guarda en memoria el weights (es decir, los parámetros) y recibe gradients, ejecutando el paso de actualización que escribí en el código de arriba. Hace esto cada vez que recibe gradientes de un trabajador.

Un trabajador, por otro lado, busca cuál es el valor actual de weights en el PS, hace una copia local, ejecuta un pase hacia adelante y hacia atrás de la red en un lote de datos y obtiene un nuevo gradients, que luego se envía de vuelta al PS.

Observe el énfasis en "actual": no hay bloqueo ni sincronización entre procesos entre los trabajadores y el PS. Si un trabajador lee weights en medio de una actualización (y, por ejemplo, la mitad ya tiene el nuevo valor y la mitad aún se está actualizando), esos son los pesos que usará para la siguiente iteración. Esto mantiene las cosas rápidas.

¿Qué pasa si hay más PS? ¡No hay problema! Los parámetros de la red están particionados entre los PS, el trabajador simplemente los contacta a todos para obtener los nuevos valores para cada parte de los parámetros y envía solo los gradientes relevantes para cada PS específico.

Gracias, todo está claro. Solo una cosa, cuando dijiste "... Lo hace cada vez que recibe pesos de un trabajador". quisiste decir "cada vez que recibe gradientes", ¿verdad?

:). Disculpa otra pregunta. Entonces, el trabajador calcula el gradiente, envía el gradiente al PS y el PS aplica el gradiente. Pero al mirar el código de la documentación de TensorFlow dentro del código del trabajador, hay una llamada al método minimizar() y al mirar el código fuente, vi que hay una llamada a compute_gradients() y apply_gradients() dentro de él. ¿Eso significa que en el TF distribuido de bajo nivel hace que el trabajador solo ejecute compute_gradients () y el PS calcule el evento apply_gradients () si minimiza () está dentro del código del trabajador?

No estoy familiarizado con el código de bajo nivel de TF, pero sé que hay mucha magia en el nivel de resolución del dispositivo de operación. Es posible que apply_gradients se comporte de manera diferente en el caso distribuido (por ejemplo, activando el envío de los gradientes en lugar de aplicar las modificaciones) y el comportamiento depende del dispositivo en el que se colocan los gradientes.

Ok, gracias. Haré esta pregunta de bajo nivel en otra pregunta entonces :)