Soluciona problemas

En esta guía se proporciona ayuda para la solución de problemas. Está destinada a usuarios que deseen ejecutar sus propios modelos de TensorFlow en Cloud TPU. Si quieres consultar una guía más general a fin de comenzar a usar Cloud TPU, consulta la guía de inicio rápido o el instructivo de MNIST.

Descripción general

La estrategia recomendada para ejecutar modelos de TensorFlow en Cloud TPU es usar la API de TPUEstimator. Si estás usando la API de Estimator de TensorFlow, por lo general solo debes cambiar algunas líneas de código para pasar a TPUEstimator. La forma recomendada para cargar datos en TPUEstimator es con la API de tf.data. Consulta el ejemplo de MNIST en la página sobre cómo usar la API de TPUEstimator con Cloud TPU para ver un ejemplo de la vida real en el que se implementan TPUEstimator y tf.data. Consulta también la página sobre el estimador de MNIST a TPUEstimator.

Después de convertir el modelo a TPUEstimator, asegúrate de que este funcione con la marca use_tpu=False. Si estableces esta marca como “falso”, TensorFlow recurre a la API de Estimator y no usa ningún código relacionado con la TPU. Cualquier problema que tengas mientras ejecutas tu modelo con use_tpu=False no está relacionado con la TPU y está fuera del alcance de esta guía. En cambio, consulta la guía para programadores de TensorFlow.

Se supone que una vez que un modelo se puede ejecutar con TPUEstimator y use_tpu=False, ejecutarlo en la TPU es nada más una cuestión de configurar use_tpu=True y apuntar master a una URL de servidor de TPU (en general, con un agente de resolución de clústeres). Sin embargo, debido a que los modelos de TensorFlow pueden ser muy complejos y la TPU usa su propio motor de ejecución, pueden surgir problemas específicos de la TPU. Estos problemas se incluyen en las siguientes categorías generales:

  1. La secuencia de comandos del entrenamiento no se puede conectar de ninguna manera con el servidor de TPU.

  2. La TPU muestra un error cuando se intenta ejecutar el modelo.

  3. El modelo no se ajusta a la memoria de la TPU.

  4. El modelo puede ejecutarse en la TPU, pero la velocidad de entrenamiento no es tan rápida como se espera.

  5. El modelo puede ejecutarse en la TPU, pero la exactitud del modelo entrenado por la TPU es peor que la de un modelo de referencia entrenado por una CPU/GPU.

Además, en esta guía se incluyen Preguntas frecuentes sobre la funcionalidad general disponible en las TPU.

Para obtener una ayuda más especializada sobre la portabilidad de ciertos tipos de redes neuronales a la TPU, consulta los instructivos de Cloud TPU.

Problemas para establecer la conexión al servidor de TPU

Cuando ejecutas un modelo en la TPU, debes pasar la URL de un servidor de TPU remoto al parámetro master en RunConfig. De forma interna, TensorFlow crea un tf.Session remoto con este servidor. En esta sección, se brinda asistencia en la solución de problemas para situaciones en las que TensorFlow deja de responder o imprime un error cuando se conecta al servidor de TPU. Ten en cuenta que el paso de compilación de grafos de TPU puede llevar más tiempo con modelos grandes. No creas que la secuencia de comandos se interrumpió si tarda hasta 5 minutos en ejecutarse.

El primer paso es verificar si el problema es con el servidor o con la canalización de entrenamiento de TensorFlow. Para ello, ejecuta el instructivo de MNIST con la URL del servidor de TPU y verifica que funcione correctamente. Si todavía hay problemas de conexión con el instructivo de MNIST, esto confirma que el problema está relacionado con el servidor de TPU. En este caso, realiza los siguientes pasos:

  1. Ejecuta el siguiente comando para mostrar las TPU disponibles:

    (vm)$ gcloud compute tpus list
    

    Es posible que también debas configurar tu zone y project, como se muestra en el instructivo de MNIST. Esto muestra una salida como la siguiente:

    NAME       ZONE           ACCELERATOR_TYPE  NETWORK_ENDPOINT   NETWORK  RANGE          STATUS
    demo-tpu   us-central1-b  v2-8              10.240.1.2:8470    default  10.240.1.0  READY

  2. Verifica que estés pasando el valor correcto a --tpu (demo-tpu en el ejemplo anterior) y que esta TPU aparezca como READY. También asegúrate de que tu zone y project se hayan configurado de la siguiente manera:

    (vm)$ gcloud config set project your-project-name
    
    (vm)$ gcloud config set compute/zone us-central1-b
    
  3. Si tu TPU no aparece como READY o si aún tienes problemas para conectarte, reinicia el servidor de forma manual con gcloud compute tpus stop $TPU_SERVER_NAME && gcloud compute tpus start $TPU_SERVER_NAME. En el ejemplo anterior, $TPU_NAME es demo-tpu. Esto puede tardar varios minutos.

  4. Vuelve a ejecutar el comando ... tpus list anterior y espera a que la TPU esté en estado READY. Esto puede tardar varios minutos.

  5. Vuelve a ejecutar el instructivo de MNIST.

  6. Si todavía tienes problemas para ejecutar el instructivo de MNIST, pide ayuda mediante uno de los mecanismos que se describen en Obtén asistencia.

Si el ejemplo de MNIST se ejecuta de manera correcta, pero tu modelo todavía deja de responder, es probable que el problema esté relacionado con la canalización de entrenamiento. Primero, asegúrate de que tu modelo esté usando la API de TPUEstimator, ya que esta no solo maneja la canalización de procesamiento compleja, sino que también permite el cambio sencillo entre la ejecución de TPU y la que no sea TPU con la marca use_tpu. Consulta los instructivos de TPU para ver varios ejemplos de cómo usar TPUEstimator. Una vez que tu modelo esté usando la API de TPUEstimator, verifica que se ejecute de forma correcta cuando se establezca use_tpu=False. Si tu modelo no se ejecuta de forma correcta cuando se configura use_tpu=False, el problema no está relacionado con la TPU.

Depura errores comunes

No se puede usar el sistema de archivos local

Mensaje de error

InvalidArgumentError: Unimplemented: File system scheme '[local]' not implemented

Detalles

Todos los archivos de entrada y el directorio del modelo deben usar una ruta de depósito de almacenamiento en la nube (gs://bucket-name/...), y este depósito debe ser accesible desde el servidor de TPU. Ten en cuenta que el punto de control de modelos y el procesamiento de datos se realizan en el servidor de TPU, no en la máquina local. Si deseas obtener información sobre cómo configurar el almacenamiento en la nube de forma correcta para su uso con la TPU, consulta la guía Conéctate a depósitos de Cloud Storage.

tf.data.Dataset.cache() no se puede almacenar en caché en el sistema de archivos local

Mensaje de error

tensorflow.python.framework.errors_impl.UnimplementedError: File system scheme '[local]' not implemented (file: '[filename].lockfile')

Detalles

Un tf.data.Dataset se puede almacenar en caché. La llamada .cache() tiene dos implementaciones:

  1. En la memoria, si no se pasa ningún argumento.

  2. En un sistema de archivos, si una ruta de acceso al archivo se pasa como un argumento.

En Cloud TPU, (1) funciona (siempre y cuando se ajuste a la memoria disponible), pero (2) no funciona cuando se guarda en el sistema de archivos local y genera el error mencionado anteriormente.

Los siguientes fragmentos de código ilustran ambas situaciones:

(1)
 import tensorflow as tf

def main():
  print('Hello world!')
  ds = tf.data.Dataset.range(10)
  ds = ds.cache()

runs to completion.

(2)
 import tensorflow as tf

def main():
  print('Hello world!')
  ds = tf.data.Dataset.range(10)
  ds = ds.cache('/tmp/foo')

generates the error.

La guía de API contiene información más detallada sobre tf.data.Dataset.cache().

Tipo de datos no compatible

Mensaje de error

TypeError: DataType is not a supported TPU infeed type.

Detalles

Hoy en día, solo los tipos de datos tf.float32, tf.int32, tf.bfloat16 y tf.bool son compatibles con la TPU. Otros tipos de datos comunes, como tf.uint8, tf.string, y tf.int64, deben convertirse en uno de los tipos de datos compatibles durante el procesamiento previo de los datos (es decir, en input_fn de TPUEstimator). Consulta el instructivo de MNIST para ver otro ejemplo. A modo de ejemplo, este fragmento de código de MNIST convierte un tensor image almacenado como una secuencia de bytes tf.uint8 en un tensor tf.float32:

image = tf.decode_raw(image, tf.uint8)
image = tf.cast(image, tf.float32)
image = tf.reshape(image, [784])

Este fragmento convierte un tensor label almacenado como tf.int64 en un tensor tf.int32:

label = tf.cast(label, tf.int32)

Formas dinámicas no compatibles

Mensaje de error

ValueError: shape [Shape] must have a fixed size for dimension d that is known at graph construction time.

Detalles

Para ejecutar un modelo en la TPU, TensorFlow compila el modelo con el marco de trabajo de XLA. Mientras que el paso de compilación mejora de forma significativa la velocidad de entrenamiento y el uso de la memoria, las formas (tamaños de dimensiones) de todos los tensores en el grafo deben ser estáticas, es decir, sus valores deben conocerse al momento de la compilación del grafo. Si no se pueden determinar algunas formas en el momento de la compilación, falla la compilación de la TPU con un error como el anterior.

Una operación común que muestra una forma dinámica es dataset.batch(batch_size), ya que el número de muestras restantes en una transmisión puede ser menor que el tamaño del lote. Por lo tanto, cuando entrenes en la TPU, usa tf.contrib.data.batch_and_drop_remainder(batch_size). Esto eliminaría las últimas muestras de un archivo para garantizar que cada lote tenga una forma estática de batch_size. Por ejemplo:

dataset = ...
dataset = dataset.apply(tf.contrib.data.batch_and_drop_remainder(batch_size))

Operación de TensorFlow no disponible

Mensaje de error

NotFoundError: No registered 'OpName' OpKernel for XLA_TPU_JIT devices compatible with node

Detalles

El modelo usa una operación de TensorFlow que, por el momento, no se encuentra disponible en la TPU.

Para obtener una lista de operaciones disponibles en la TPU, junto con los planes de asistencia futura y sugerencias para una solución alternativa, consulta la guía de operaciones de TensorFlow disponibles.

Mensaje de error de memoria insuficiente

Mensaje de error

ResourceExhaustedError: Ran out of memory in memory space hbm; used: YYY; limit: 7.48G.

Detalles

Cada Cloud TPU está formada por ocho núcleos de TPU y cada uno tiene 8 GB de RAM (o memoria de ancho de banda alto, HBM). Esta memoria se utiliza para almacenar los tensores de peso (variable), así como los tensores de resultado intermedio necesarios en el cálculo de gradientes. Si el modelo es demasiado grande para la RAM de la TPU, falla la inicialización y se imprime el mensaje de error anterior. Consulta la sección sobre cómo reducir el uso de memoria para obtener más ayuda.

No se usa CrossShardOptimizer

Mensaje de error

ValueError: CrossShardOptimizer must be used for model training on TPUs.

Detalles

Cuando defines un modelo con la API de TensorFlow para Python, la gran mayoría del código que escribe el usuario no necesita estar especializado para la TPU. La excepción más importante a esto es el optimizador, que debe incorporarse en tf.contrib.tpu.CrossShardOptimizer() como se muestra a continuación:

optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)
if FLAGS.use_tpu:
  optimizer = tf.contrib.tpu.CrossShardOptimizer(optimizer)
train_op=optimizer.minimize(loss, tf.train.get_global_step())

Cada Cloud TPU está formada por 8 núcleos de TPU, que son unidades de procesamiento independientes. Para cada paso de entrenamiento (es decir, actualización del peso), cada núcleo de TPU ejecuta el cálculo de la propagación y de los gradientes en un minilote de datos independiente y, luego, todos los núcleos intercambian gradientes entre sí. En la mayoría de los casos, es equivalente de forma matemática a calcular las gradientes en un lote grande, aunque hay algunas advertencias que se explican en Información sobre la fragmentación de datos.

CrossShardOptimizer es la operación responsable de este intercambio de gradientes. De forma predeterminada, CrossShardOptimizer calcula los gradientes de la pérdida promedio entre los núcleos, pero puede configurarse para calcular la pérdida total si pasas reduction=losses.Reduction.SUM.

No se puede establecer la conexión con el servidor de TPU

Mensaje de error

An error was raised while a session was being created. This may be due to a preemption of a connected worker or parameter server. A new session is created.

Detalles

Se muestra este error cuando TensorFlow no puede conectarse a la URL del servidor de TPU que se pasa a master. Si deseas obtener ayuda, consulta la sección sobre problemas para establecer la conexión al servidor de TPU.

Errores en el medio del entrenamiento

Si no se puede ejecutar un modelo en la TPU correctamente, cualquier error relacionado con este problema se detecta durante la inicialización. Por ello, es raro que un modelo falle en el medio del entrenamiento. Si esto sucede, la causa más probable es un problema en la función de procesamiento previo de datos. Por ejemplo, cuando usas la API de Dataset, en general debes hacer una llamada a dataset = dataset.repeat(); de lo contrario, el entrenamiento falla después de pasar una vez por los datos. Las operaciones de ejecución dinámica como tf.while_loop() también pueden fallar a causa de alguna situación relacionada a los datos de entrada. También hay una mínima posibilidad de hardware falso o errores en la red.

Problemas para detener la ejecución

Si TensorFlow encuentra un error durante la ejecución de la TPU, la secuencia de comandos a veces parece dejar de responder en lugar de salir del shell. Si esto sucede, presiona CTRL+\ en el teclado para activar un SIGQUIT, lo que hace que Python se cierre de inmediato.

Del mismo modo, si presionas CTRL+C durante la ejecución de la TPU, TensorFlow no se desactiva de inmediato, sino que espera hasta el final del bucle de iteración actual para cerrarse de forma correcta. Si presionas CTRL+\, Python se cierra de inmediato.

Si surge algún error nuevo como DeadlineExceededError cuando vuelves a conectarte al servidor de TPU después de este cierre, restablece el servidor de TPU de forma manual con el comando gcloud compute tpus stop $TPU_SERVER_NAME && gcloud compute tpus start $TPU_SERVER_NAME, en el que $TPU_SERVER_NAME se toma de la primera columna del comando gcloud compute tpus list.

Reduce el uso de la memoria

Si encuentras un error de memoria insuficiente cuando ejecutas tu modelo en la TPU, debes tomar medidas para reducir el uso de memoria del modelo. En esta sección se describen varias causas raíz de problemas de memoria y se proporcionan lineamientos para su resolución.

Gran cantidad de pesos de modelos

Causa posible de problema de la memoria

Cada peso del modelo float32 requiere 4 bytes. Estos pesos se replican en cada núcleo de la TPU. Por ello, es probable que un modelo con cientos de millones de pesos sea demasiado grande para ajustarse a la TPU.

Cómo reducir el uso de la memoria

  1. Ciertos optimizadores requieren de memoria adicional por peso para almacenar las estadísticas de actualización. En particular, AdamOptimizer y AdadeltaOptimizer requieren 8 bytes adicionales por peso. AdagradOptimizer y MomentumOptimizer requieren 4 bytes adicionales por peso. El GradientDescentOptimizer estándar no requiere almacenamiento adicional, aunque puede no funcionar tan bien como otros optimizadores en términos de precisión del modelo final. El AdafactorOptimizer experimental casi no requiere memoria adicional y funciona tan bien como el optimizador Adam de modelo de referencia cuando se entrenan modelos de transformador.
  2. Si la mayoría de los pesos son incorporaciones de palabras, está demostrado que técnicas como WordPiece causan una reducción importante del tamaño del vocabulario y, al mismo tiempo, aumentan la exactitud en una variedad de tareas.
  3. Una próxima versión de TensorFlow brindará compatibilidad experimental para los gradientes y los pesos de comas flotantes de 16 bits, lo que reducirá los requisitos de memoria a la mitad.

Relleno excesivo del tensor

Causa posible de problema de la memoria

Se rellenan los tensores en la memoria de la TPU, es decir, la TPU redondea los tamaños de los tensores almacenados en la memoria para realizar cálculos de forma más eficaz. Este relleno sucede de manera transparente en el nivel del hardware y no afecta los resultados. No obstante, en ciertos casos el relleno puede provocar un aumento significativo del uso de la memoria y del tiempo de ejecución.

Cómo reducir el uso de la memoria

El software de la TPU intenta distribuir los tensores en la memoria para maximizar la eficiencia del cálculo y minimizar el relleno. El proceso de distribución de la memoria es complejo; sin embargo, para obtener mejores resultados, el modelo debe obedecer la siguiente regla general. Para minimizar la sobrecarga de la memoria y maximizar la eficiencia del cálculo, una de las siguientes condiciones debe ser verdadera:

  • El tamaño total del lote debe ser un múltiplo de 64 (8 por núcleo de la TPU) y las dimensiones de las funciones deben ser un múltiplo de 128.

    o

  • El tamaño total del lote debe ser un múltiplo de 1,024 (128 por núcleo de la TPU) y las dimensiones de las funciones deben ser un múltiplo de 8.

Se logra una mejor eficiencia con un tamaño de lote de 1,024 y dimensiones de las funciones que sean múltiplos de 128, aunque esto puede no ser posible para todos los modelos. Para evitar confusiones, “dimensión de las funciones” se refiere al tamaño oculto de una capa completamente conectada o a la cantidad de canales de salida en una convolución. No todas las capas pueden cumplir con esta regla, especialmente la primera y la última capa de la red. Esto es correcto y se espera que la mayoría de los modelos requieran de cierta cantidad de relleno.

Tamaño de lote demasiado grande

Causa posible de problema de la memoria

Cuando entrenamos una red neuronal en una CPU, GPU o TPU, el uso de la memoria proviene de las siguientes dos fuentes:

  1. Almacenamiento de los pesos, de los gradientes de los pesos y de las estadísticas específicas del optimizador como un momentum. El uso de la memoria es directamente proporcional a la cantidad de pesos del modelo, pero no al tamaño del lote.
  2. Almacenamiento de activaciones intermedias de la propagación necesaria para calcular la retropropagación. El uso de la memoria es directamente proporcional al tamaño del lote, a los tamaños de las capas y a las cantidades de capas.

Por lo tanto, la memoria requerida por un modelo depende en gran medida del tamaño del lote.

Cómo reducir el uso de la memoria

Intenta reducir de a poco el tamaño del lote hasta que se ajuste a la memoria y asegúrate de que el tamaño total del lote sea un múltiplo de 64 (el tamaño del lote por núcleo debe ser un múltiplo de 8). Ten en cuenta que los tamaños de lotes más grandes son más eficaces en la TPU. Por lo general, un buen punto de partida es un tamaño total de lote de 1,024 (128 por núcleo).

Modelo demasiado grande

Causa posible de problema de la memoria

La memoria requerida por un modelo depende en gran parte de la cantidad de operadores en el grafo (es decir, las capas en la red). Este requisito de almacenamiento es independiente de la cantidad de pesos. Por ejemplo, calcular el gradiente de un operador como tf.nn.conv2d() puede aumentar el uso de la memoria, además de cualquier memoria que se use para almacenar pesos.

El motor de la TPU intenta recalcular ciertos operadores de forma estratégica para ajustar el modelo a la memoria (lo que se llama rematerialización, similar al punto de control de las gradientes), pero no siempre puede hacerlo.

Cómo reducir el uso de la memoria

Si no se puede ejecutar el modelo en la TPU incluso con un tamaño de lote pequeño (por ejemplo, 64), intenta reducir la cantidad de capas o sus tamaños. Una próxima versión de TensorFlow admitirá el “paralelismo del modelo” en la TPU, lo que permitirá que se ejecuten modelos considerablemente más grandes en Cloud TPU mediante la ejecución de diferentes partes del modelo en núcleos de la TPU diferentes.

Mejora la velocidad de entrenamiento

Si se puede ejecutar correctamente el modelo en la TPU, pero la velocidad de entrenamiento es inferior a la esperada, en esta sección se describen varios métodos que pueden mejorar la velocidad.

Muy pocas iteraciones por bucle

Descripción del problema de rendimiento

El parámetro iterations_per_loop para TPUConfig controla cuántos lotes de datos se envían a la TPU en un solo “bucle de entrenamiento”. Cada bucle de entrenamiento requiere una comunicación significativa entre la máquina local y el servidor de TPU, por lo que si iterations_per_loop es demasiado pequeño, puede ralentizar el entrenamiento de forma considerable.

Cómo saber si tu modelo se ve afectado

Si se muestra el mensaje de registro Enqueue next (X) batch(es) of data to infeed con mucha frecuencia (por ejemplo, cada 3 segundos), entonces tu entrenamiento puede tener una sobrecarga significativa del bucle de entrenamiento.

Cómo mitigar el problema

Establece iterations_per_loop en un valor mayor. En el instructivo de MNIST, esto se controla con la marca --iterations. Mientras el mensaje Enqueue next (X) batch(es) of data to infeed no se muestre más de unas pocas veces por minuto, el valor actual debería ser suficiente. Ten en cuenta que iterations_per_loop se puede establecer en un valor muy grande, con el único inconveniente de que los mensajes de registro y los puntos de control solo pueden tener lugar al final de un ciclo.

Cuello de botella del procesamiento de entrada

Descripción del problema de rendimiento

Mientras la TPU se entrena con un grupo específico de datos, la función de procesamiento de entrada prepara el siguiente grupo de datos en la CPU. Por lo tanto, si la función de entrada toma menos tiempo que la función de modelo, el costo del procesamiento de entrada es, en efecto, cero. No obstante, una función de entrada más lenta que la de modelo crea un cuello de botella.

Cómo saber si tu modelo se ve afectado

Sigue las instrucciones de las Herramientas de Cloud TPU: analizador de canalización de entrada para visualizar el análisis de canalización de entrada en TensorBoard:

image

La página del análisis de canalización de entrada muestra un resumen claro que indica si tu modelo presenta un cuello de botella en el nivel del procesamiento de entrada. La misma página también muestra el tiempo de ejecución por operación, que te permite identificar las operaciones problemáticas.

Cómo mitigar el problema

Existen varias mitigaciones posibles cuando se cargan datos con la API de Dataset:

  1. Almacena tus datos como un conjunto de estructuras tf.train.Example en archivos TFRecord y cárgalos con TFRecordDataset. Consulta el instructivo de la API de Dataset o el instructivo de ResNet para ver ejemplos.
  2. Usa dataset.cache() o dataset.prefetch() para almacenar en búfer los datos de entrada. Esto evita que las demoras esporádicas en el acceso a archivos generen un cuello de botella.
  3. Especifica el parámetro num_parallel_calls de la función dataset.map() para habilitar operaciones map() de subprocesos múltiples.
  4. Realiza el procesamiento previo de los datos costosos sin conexión como un costo único, en vez de que se aplique el costo en cada ciclo de cada entrenamiento.

Todo el procesamiento de entrada se realiza en las CPU ubicadas en el servidor de TPU, no en la máquina local; por lo que la velocidad de la máquina local no es un factor.

Demasiadas operaciones de multiplicación no matricial

Descripción del problema de rendimiento

Cloud TPU puede realizar convoluciones y multiplicaciones de matrices a velocidades increíblemente rápidas. La mayoría de las otras operaciones de TensorFlow tienen implementaciones eficientes en la TPU, pero no representan el valor principal de la TPU en comparación con otro hardware. En consecuencia, se debería dominar un modelo mediante las convoluciones o multiplicaciones de matrices para aprovechar la TPU al máximo.

Cómo saber si tu modelo se ve afectado

En la guía Herramientas de Cloud TPU: perfil de operaciones, se describe cómo generar un perfil de rendimiento para tu modelo desglosado por tipo de operación. En general, la gran mayoría de las arquitecturas de redes neuronales modernas están dominadas por las convoluciones y multiplicaciones de matrices.

Cómo mitigar el problema

Si la ausencia de multiplicaciones de matrices en tu modelo estuvo principalmente motivada por los problemas de velocidad del entrenamiento en otro hardware, te recomendamos volver a evaluar esos modelos en la TPU para un mejor rendimiento de la velocidad. Si la ausencia de multiplicaciones de matrices es una propiedad fundamental del modelo, es posible que la TPU no sea la elección óptima de hardware.

Relleno excesivo del tensor

Descripción del problema de rendimiento

La TPU rellena los tensores en la memoria para poder usar sus unidades de procesamiento de manera eficiente. El relleno puede aumentar el uso de la memoria y del ancho de banda de la memoria. Consulta la sección sobre el relleno del tensor para comprender y solucionar los problemas de relleno del tensor.

Tamaño de lote demasiado pequeño

Descripción del problema de rendimiento

Como regla general, el uso de tamaños de lote más grandes provoca una velocidad de entrenamiento mayor en la TPU, en términos de muestras por segundo.

Cómo saber si tu modelo se ve afectado

El tamaño del lote de cualquier modelo siempre debe ser al menos de 64 (8 por núcleo de la TPU), ya que la TPU siempre rellena los tensores hasta este tamaño. El tamaño de lote ideal durante el entrenamiento en la TPU es de 1,024 (128 por núcleo de la TPU), ya que esto elimina las ineficiencias relacionadas con el relleno y la transferencia de la memoria.

Cómo mitigar el problema

Se recomienda usar el mayor tamaño de lote que se ajuste a la memoria y sea un múltiplo de 64. La manera más sencilla de lograrlo es comenzar con 1,024 y, si esto causa un error de memoria insuficiente, intenta reducir el tamaño del lote hasta que el modelo se ejecute correctamente. Cambiar el tamaño del lote de un modelo puede requerir el ajuste de otros hiperparámetros para lograr la misma exactitud del modelo, como la tasa de aprendizaje; pero esto se debe evaluar caso por caso.

Capas demasiado pequeñas

Descripción del problema de rendimiento

Incluso cuando un modelo está dominado por convoluciones o multiplicaciones de matrices, es posible que la TPU no se ejecute con la eficiencia máxima si los tensores de entrada son pequeños. En comparación con otro hardware, la TPU se ejecuta con mayor eficiencia cuando tanto los lotes como las capas son grandes (por ejemplo, de dimensión >= 512).

Cómo saber si tu modelo se ve afectado

Como regla general, los tamaños de capa menores a 128 son poco eficientes en la TPU, ya que esa es la dimensión nativa de la unidad de multiplicación de matrices de la TPU. Se recomienda un tamaño oculto mínimo de 512 para capas completamente conectadas con el fin de obtener una eficiencia mayor. Ten en cuenta que las capas convolucionales en general no necesitan ser tan grandes como las capas completamente conectadas para lograr un nivel de eficacia equivalente. Por ejemplo, una convolución de 3 x 3 con un tamaño de 256 da como resultado una eficacia similar (alta) a una capa completamente conectada con un tamaño de 2,048, ya que 3 x 3 x 256 = 2,304.

Cómo mitigar el problema

Si el motivo principal de las capas pequeñas en tu modelo es la velocidad de entrenamiento, te recomendamos volver a evaluar tus modelos con capas más grandes en la TPU. Por ejemplo, es posible que el aumento del tamaño de salida de una capa de 256 a 512 solo incremente el tiempo de entrenamiento en un 20%, aunque el modelo tenga un rendimiento de procesamiento 2 veces mayor.

Perfilado del modelo en el nivel de las operaciones

Por lo general, es útil medir el tiempo de ejecución y el uso de memoria en el nivel de las operaciones para identificar los cuellos de botella del rendimiento. Para obtener instrucciones sobre cómo hacerlo,
consulta la guía Herramientas de Cloud TPU: lector de seguimiento.

Depura degradaciones en la exactitud del modelo

Uno de los objetivos del ecosistema de Cloud TPU es que cualquier modelo que esté en entrenamiento en una CPU o en una GPU obtenga una exactitud muy similar a la que se logra con el entrenamiento en la TPU, tal vez con menos ajustes a los hiperparámetros como el tamaño del lote y la tasa de aprendizaje. En ciertas ocasiones, sin embargo, los usuarios pueden notar una degradación en la precisión cuando entrenan modelos en la TPU. La depuración de estos problemas puede ser muy frustrante debido a la naturaleza aleatoria del entrenamiento de la red neuronal. En esta sección, se proporciona orientación sobre cómo identificar la causa raíz de cualquier degradación en la precisión del modelo cuando se porta un modelo a la TPU.

Información sobre la fragmentación de datos (paralelismo de datos)

Uno de los objetivos principales de TensorFlow es que cada operación produzca resultados casi idénticos, ya sea que se ejecute en la CPU, GPU o TPU. Existen ciertas excepciones, como las operaciones aleatorias. En general, si encuentras una diferencia significativa entre el resultado de las operaciones no aleatorias en la TPU y en la CPU, infórmalo como un error.

Sin embargo, para la canalización de entrenamiento en conjunto, hay una diferencia significativa entre el entrenamiento en la CPU/GPU y en TPU: cuando se usan TPUEstimator y use_tpu=False, TensorFlow recurre a su motor de ejecución estándar. Este motor entrena con un lote por paso. No obstante, durante el entrenamiento en la TPU, TensorFlow realiza una fragmentación de datos, también conocida como “paralelismo de datos con SGD síncrono”. El motivo es que cada Cloud TPU está formada por 8 núcleos de TPU, que operan como unidades de procesamiento independientes. Por lo tanto, para cada paso del entrenamiento, cada núcleo de la TPU recibe un lote de datos, calcula los gradientes de peso, intercambia los gradientes con los otros núcleos y, luego, calcula la actualización del peso. De manera predeterminada, la pérdida se promedia de todos núcleos, pero se puede sumar si cambias el parámetro CrossShardOptimizer.

Si la pérdida total del modelo se puede calcular como el promedio (o el total) de las pérdidas independientes por muestra, este procedimiento equivale de manera matemática al entrenamiento en un lote grande único. La operación más común que no es independiente por muestra es la normalización por lotes, que se ejecuta en cada lote por núcleo de forma individual. Por ejemplo, si el tamaño total del lote es 128, el tamaño del lote por núcleo es 16, y cada uno de los 8 núcleos realiza la normalización por lotes de sus propias 16 muestras. En algunos casos, se descubrió que la normalización de lotes pequeños (por ejemplo, inferiores a 32) provoca degradaciones en la exactitud. En una situación ideal, el tamaño total del lote durante el entrenamiento en la TPU puede ser grande (por ejemplo, de 256 a 1,024); por lo que los lotes de ese tamaño no representan un problema mayor. No obstante, si el tamaño del lote es demasiado grande para ajustarse a la memoria, se debe evaluar el efecto de la fragmentación caso por caso.

Debido a las complejidades que introduce la fragmentación, el primer paso de la depuración en caso de degradaciones en la exactitud del modelo consiste en ejecutar un entrenamiento de la TPU determinista y de núcleo único, y compararlo con un modelo entrenado en la CPU/GPU. Generalmente, se puede realizar con rapidez, ya que no es necesario entrenar un modelo hasta la convergencia.

Entrenamiento determinista

Un motivo por el que es difícil depurar las diferencias en la exactitud del modelo es que TensorFlow usa una inicialización de peso y una redistribución de datos diferentes cada vez que se entrena un modelo. Es conveniente modificar el procedimiento del entrenamiento para que sea determinista y, así, varias ejecuciones generen modelos casi idénticos. En esta sección se demuestra cómo ejecutar el instructivo de MNIST de manera determinista:

  1. Genera un archivo de punto de control inicial con la ejecución de un solo paso en la CPU. Este paso se usa para lograr una inicialización de peso determinista. Esto también puede lograrse mediante la propagación de los inicializadores de variables, pero es más difícil.
# Run training for 1 step to create an initial checkpoint.
python mnist_tpu.py \
  --use_tpu=False \
  --data_dir=${STORAGE_BUCKET}/data/ \
  --model_dir=${STORAGE_BUCKET}/init_output \
  --random_seed=12345 \
  --iterations=1
  --train_steps=1
  1. Modifica cualquier función de redistribución de datos en tu función de entrada para usar un valor de origen aleatorio. Esto ya se realizó en el instructivo de MNIST. Funciona para las operaciones de procesamiento de datos de entrada porque siempre se ejecutan en la CPU. Las operaciones aleatorias en la función de modelo pueden no ser deterministas entre la TPU y la CPU. Por ejemplo:
# In the flag definitions
tf.flags.DEFINE_integer("batch_size", None, "Random seed for training")

# In the input_fn
if FLAGS.random_seed is not None:
dataset = dataset.shuffle(seed=FLAGS.random_seed)
  1. Para verificar que el entrenamiento sea determinista, ejecuta el mismo modelo dos veces en la CPU. Ten en cuenta que el entrenamiento se debe ejecutar por una cantidad razonable de pasos (por ejemplo, 1,000), pero no es necesario hacerlo hasta llegar a la convergencia, ya que puede ser muy lento en la CPU.

    Debido a que el entrenamiento en la CPU se compara con uno de núcleo único en la TPU, usa un tamaño de lote que pueda ajustarse a un núcleo de la TPU único (en general, el tamaño de lote completo dividido por 8). TensorFlow no garantiza un determinismo bit a bit entre las ejecuciones, pero la pérdida debe ser muy similar:
# Copy the initial weights
gsutil mkdir ${STORAGE_BUCKET}/cpu_output_1
gsutil cp -f ${STORAGE_BUCKET}/init_output/* ${STORAGE_BUCKET}/cpu_output_1
gsutil mkdir ${STORAGE_BUCKET}/cpu_output_2
gsutil cp -f ${STORAGE_BUCKET}/init_output/* ${STORAGE_BUCKET}/cpu_output_2

# Run 1
python mnist_tpu.py \
  --use_tpu=False \
  --data_dir=${STORAGE_BUCKET}/data/ \
  --model_dir=${STORAGE_BUCKET}/cpu_output_1 \
  --batch_size=128 \
  --random_seed=12345 \
  --train_steps=2000 \
  --eval_steps=10

# Output 1
accuracy = 0.9910644, global_step = 1000, loss = 0.025323588

# Run 2
python mnist_tpu.py \
  --use_tpu=False \
  --data_dir=${STORAGE_BUCKET}/data/ \
  --model_dir=${STORAGE_BUCKET}/cpu_output_1 \
  --batch_size=128 \
  --random_seed=12345 \
  --train_steps=2000 \
  --eval_steps=10

# Output 2
accuracy = 0.9910644, global_step = 1000, loss = 0.025323414

Entrenamiento de la TPU de núcleo único

Cuando puedas ejecutar el instructivo de MNIST de forma determinista, el paso siguiente consiste en replicar en la TPU los resultados entrenados en la CPU, con un núcleo único de TPU para identificar si el problema se relaciona con la fragmentación de datos o con el motor de ejecución de la TPU.

Estos son los pasos para ejecutar el entrenamiento de núcleo único y su evaluación con el instructivo de MNIST:

# Use the same weight initialization as the CPU
gsutil cp -f ${STORAGE_BUCKET}/init_output/* ${STORAGE_BUCKET}/tpu_output

# Run training for 1000 steps
python mnist.py \
    --use_tpu=True \
    --master=$GRPC_SERVER \
    --train_file=${STORAGE_BUCKET}/data/train.tfrecords \
    --model_dir=${STORAGE_BUCKET}/tpu_output \
    --random_seed=12345 \
    --batch_size=128 \
    --train_steps=1000 \
    --eval_steps=10

  accuracy = 0.9910644, global_step = 1000, loss = 0.02514153

La pérdida no coincidirá de manera exacta con el modelo entrenado en la CPU, pero debe ser similar. Si no es el caso para tu modelo, puede indicar un error en el motor de ejecución de la TPU. Antes de enviar un informe de errores, debes verificar los siguientes puntos:

  1. Estás pasando num_shards=1 a TPUConfig.

  2. No tienes operaciones aleatorias en la función de modelo y cualquier operación aleatoria en tu función de entrada se inicializa de forma correcta.

  3. Estás usando el mismo archivo de punto de control inicial para el entrenamiento de la TPU y de la CPU.

Depura el entrenamiento de la TPU de varios núcleos

Si tu modelo alcanza efectivamente la misma pérdida en la CPU y en la TPU de núcleo único, es probable que el problema sea uno de los siguientes:

(a) La degradación se debe a la variación aleatoria natural cuando se entrenan modelos neuronales con diferentes inicializaciones.

(b) La degradación se debe a un problema relacionado con la fragmentación de datos en la TPU.

Para determinar si (a) es el problema, puede ser útil volver a entrenar el modelo completo en la CPU/GPU y en la TPU de varios núcleos con la misma inicialización de peso, como se mostró anteriormente.

Si estás seguro de que la degradación en la precisión tiene importancia estadística, los problemas más probables relacionados con la fragmentación de datos son los siguientes:

  1. Si tu modelo calcula la pérdida como el total de errores por muestra, es probable que desees pasar reduction=losses.Reduction.SUM a CrossShardOptimizer. De forma predeterminada, CrossShardOptimizer calcula el promedio de pérdidas, en lugar del total.
  2. Si tu modelo usa la normalización por lotes, un tamaño de lote total inferior a 256 (por ejemplo, inferior a 32 por núcleo) puede reducir la precisión.
  3. Si tu modelo posee una función de pérdida de lotes, se verá afectado por la fragmentación. Estas funciones de pérdida suelen ser bastante especializadas. Por ejemplo, Karras et al. 2017 usa un discriminante por lotes cuando se entrena una red generativa adversaria.