¿Cómo manejar el no determinismo cuando se entrena en una GPU?

Al ajustar los hiperparámetros para que mi modelo tenga un mejor desempeño, noté que la puntuación que obtengo (y por lo tanto el modelo que se crea) es diferente cada vez que ejecuto el código a pesar de haber corregido todas las semillas para operaciones aleatorias. Este problema no ocurre si ejecuto en la CPU.

Busqué en Google y descubrí que este es un problema común al usar una GPU para entrenar. Aquí hay un ejemplo muy bueno / detallado con fragmentos de código cortos para verificar la existencia de ese problema.

Señalaron el no determinismo de la función “tf.reduce_sum”. Sin embargo, ese no es el caso para mí. podría ser porque estoy usando un hardware diferente (1080 TI) o una versión diferente de las bibliotecas CUDA o Tensorflow. Parece que hay muchas partes diferentes de las bibliotecas CUDA que no son deterministas y no parece fácil averiguar exactamente qué parte y cómo deshacerse de ella. Además, esto debe haber sido por diseño, por lo que es probable que haya un aumento de la eficiencia suficiente a cambio del no determinismo.

Entonces, mi pregunta es:

Dado que las GPU son populares para entrenar a las NN, las personas en este campo deben tener una manera de lidiar con el no determinismo, porque no puedo ver de qué otra manera podría sintonizar los hiperparámetros de manera confiable. ¿Cuál es la forma estándar de manejar el no determinismo cuando se utiliza una GPU?

TL; DR

  • El no determinismo para operaciones deterministas a priori proviene de implementaciones concurrentes (subprocesos múltiples).
  • A pesar del progreso constante en eso, tensorflow no garantiza actualmente determinismo para todas sus operaciones. Después de una búsqueda rápida en Internet, parece que la situación es similar con las otras herramientas principales.
  • Durante el entrenamiento, a menos que esté depurando un problema, está bien tener fluctuaciones entre ejecuciones. La incertidumbre está en la naturaleza de la capacitación, y es sabio medirla y tenerla en cuenta al comparar los resultados, incluso cuando los juegos de herramientas eventualmente alcanzan un determinismo perfecto en la capacitación .

Eso, pero mucho mas largo

Cuando vea las operaciones de la neural network como operaciones matemáticas, esperaría que todo fuera determinista. Convoluciones, activaciones, entropía cruzada: todo aquí son ecuaciones matemáticas y deberían ser deterministas. Incluso las operaciones pseudoaleatorias, como barajar, abandonar, ruido y similares, están completamente determinadas por una semilla.

Cuando ves esas operaciones desde su implementación computacional, por otro lado, las ves como cálculos masivamente paralelizados, que pueden ser fuente de aleatoriedad a menos que seas muy cuidadoso.

El núcleo del problema es que, cuando ejecuta operaciones en varios subprocesos paralelos, por lo general no sabe qué subproceso terminará primero. No es importante cuando los hilos operan con sus propios datos, por lo que, por ejemplo, aplicar una función de activación a un tensor debería ser determinista. Pero cuando esos subprocesos necesitan sincronizarse, como cuando se calcula una sum, el resultado puede depender del orden de la sum y, a su vez, del orden de qué subproceso terminó primero.

A partir de ahí, tienes en términos generales dos opciones:

  • Mantener el no determinismo asociado a implementaciones más simples.

  • Tenga especial cuidado en el diseño de su algoritmo paralelo para reducir o eliminar el no determinismo en su cálculo. La restricción agregada generalmente resulta en algoritmos más lentos.

¿Qué ruta toma CuDNN? Bueno, sobre todo el determinista. En lanzamientos recientes, las operaciones deterministas son la norma y no la excepción. Pero solía ofrecer muchas operaciones no deterministas y, lo que es más importante, no ofrecía algunas operaciones como la reducción, que las personas necesitaban para implementarse en CUDA con un grado variable de consideración al determinismo.

Algunas bibliotecas como theano fueron más avanzadas con respecto a ese tema, al exponer desde el principio una bandera deterministic que el usuario podría activar o desactivar, pero como puede ver en su descripción, está lejos de ofrecer alguna garantía.

Si es more , a veces seleccionaremos alguna implementación que sea más determinista, pero más lenta. En particular, en la GPU, evitaremos el uso de AtomicAdd. En ocasiones, seguiremos utilizando implementaciones no deterministas, por ejemplo, cuando no tenemos una implementación de GPU que sea determinista. También vea las banderas dnn.conv.algo * para cubrir más casos.

En tensorflow, la comprensión de la necesidad del determinismo ha sido bastante tardía, pero se está demorando en llegar allí, ayudado por el avance de CuDNN en ese frente también. Durante mucho tiempo, las reducciones han sido no deterministas, pero ahora parecen ser deterministas. El hecho de que CuDNN introdujo reducciones deterministas en la versión 6.0 puede haber ayudado por supuesto.

Parece que en la actualidad, el principal obstáculo para el flujo tensorial hacia el determinismo es el paso hacia atrás de la convolución . De hecho, es una de las pocas operaciones para las cuales CuDNN propone un algoritmo no determinista, etiquetado CUDNN_CONVOLUTION_BWD_FILTER_ALGO_0 . Este algoritmo aún se encuentra en la lista de opciones posibles para el filtro hacia atrás en tensorflow. Y dado que la elección del filtro parece basarse en el rendimiento , podría elegirse si es más eficiente. (No estoy tan familiarizado con el código C ++ de tensorflow, así que tome esto con un grano de sal).

¿Es esto importante?

Si está depurando un problema, el determinismo no es importante: es obligatorio. Necesitas reproducir los pasos que llevaron a un problema. Este es actualmente un problema real con kits de herramientas como tensorflow. Para mitigar este problema, su única opción es depurar en vivo, agregando cheques y puntos de interrupción en las ubicaciones correctas, no muy bien.

El despliegue es otro aspecto de las cosas, donde a menudo es deseable tener un comportamiento determinista, en parte para la aceptación humana. Si bien nadie esperaría razonablemente que un algoritmo de diagnóstico médico nunca fallara, sería incómodo que una computadora pudiera dar a un mismo paciente un diagnóstico diferente dependiendo de la ejecución. (Aunque los médicos mismos no son inmunes a este tipo de variabilidad).

Esas razones son motivaciones legítimas para corregir el no determinismo en las redes neuronales.

Para todos los demás aspectos, diría que debemos aceptar, si no abrazar, la naturaleza no determinista del entrenamiento de la neural network. Para todos los efectos, la formación es estocástica. Utilizamos el descenso de gradiente estocástico, barajamos los datos, utilizamos la inicialización y el abandono aleatorios, y lo que es más importante, los datos de entrenamiento son en sí mismos, pero son una muestra aleatoria de datos. Desde ese punto de vista, el hecho de que las computadoras solo puedan generar números pseudoaleatorios con una semilla es un artefacto. Cuando entrenas, tu pérdida es un valor que también viene con un intervalo de confianza debido a esta naturaleza estocástica. La comparación de esos valores para optimizar los parámetros hiperactivos e ignorar esos intervalos de confianza no tiene mucho sentido; por lo tanto, es vano, en mi opinión, esforzarse demasiado para solucionar el no determinismo en ese y muchos otros casos.