sql >> Base de Datos >  >> NoSQL >> Redis

Keras predice no regresar dentro de la tarea de apio

Me encontré exactamente con este mismo problema, y ​​el hombre era un agujero de conejo. Quería publicar mi solución aquí, ya que podría ahorrarle a alguien un día de trabajo:

Estructuras de datos específicas de subprocesos de TensorFlow

En TensorFlow, hay dos estructuras de datos clave que funcionan detrás de escena cuando llamas a model.predict (o keras.models.load_model o keras.backend.clear_session , o prácticamente cualquier otra función que interactúe con el backend de TensorFlow):

  • Un gráfico de TensorFlow, que representa la estructura de su modelo de Keras
  • Una sesión de TensorFlow, que es la conexión entre su gráfico actual y el tiempo de ejecución de TensorFlow

Algo que no está explícitamente claro en los documentos sin investigar un poco es que tanto la sesión como el gráfico son propiedades del hilo actual . Consulte los documentos de la API aquí y aquí.

Uso de modelos de TensorFlow en diferentes subprocesos

Es natural querer cargar su modelo una vez y luego llamar a .predict() en él varias veces más tarde:

from keras.models import load_model

MY_MODEL = load_model('path/to/model/file')

def some_worker_function(inputs):
    return MY_MODEL.predict(inputs)

En un contexto de servidor web o grupo de trabajadores como Celery, lo que esto significa es que cargará el modelo cuando importe el módulo que contiene el load_model línea, entonces un subproceso diferente ejecutará some_worker_function , ejecutando predict en la variable global que contiene el modelo de Keras. Sin embargo, intentar ejecutar la predicción en un modelo cargado en un subproceso diferente produce errores de "el tensor no es un elemento de este gráfico". Gracias a las varias publicaciones de SO que tocaron este tema, como ValueError:Tensor Tensor(...) no es un elemento de este gráfico. Cuando se utiliza el modelo de keras de variable global. Para que esto funcione, debe conservar el gráfico de TensorFlow que se usó; como vimos anteriormente, el gráfico es una propiedad del subproceso actual. El código actualizado se ve así:

from keras.models import load_model
import tensorflow as tf

MY_MODEL = load_model('path/to/model/file')
MY_GRAPH = tf.get_default_graph()

def some_worker_function(inputs):
    with MY_GRAPH.as_default():
        return MY_MODEL.predict(inputs)

El giro un tanto sorprendente aquí es:el código anterior es suficiente si está utilizando Thread s, pero se cuelga indefinidamente si está usando Process es. Y, de forma predeterminada, Celery usa procesos para administrar todos sus grupos de trabajadores. Entonces, en este punto, las cosas están todavía no funciona con Apio.

¿Por qué esto solo funciona en Thread? ¿?

En Python, Thread Los s comparten el mismo contexto de ejecución global que el proceso principal. De los documentos Python _thread:

Este módulo proporciona primitivas de bajo nivel para trabajar con múltiples subprocesos (también llamados procesos o tareas ligeros):múltiples subprocesos de control que comparten su espacio de datos global.

Debido a que los subprocesos no son procesos independientes reales, utilizan el mismo intérprete de Python y, por lo tanto, están sujetos al infame Global Interpeter Lock (GIL). Quizás lo más importante para esta investigación es que comparten espacio de datos global con el padre.

En contraste con esto, Process son actuales nuevos procesos generados por el programa. Esto significa:

  • Nueva instancia de intérprete de Python (y sin GIL)
  • El espacio de direcciones global está duplicado

Tenga en cuenta la diferencia aquí. Mientras que Thread tienen acceso a una única variable de sesión global compartida (almacenada internamente en tensorflow_backend módulo de Keras), Process es tienen duplicados de la variable de sesión.

Mi mejor entendimiento de este problema es que se supone que la variable de sesión representa una conexión única entre un cliente (proceso) y el tiempo de ejecución de TensorFlow, pero al duplicarse en el proceso de bifurcación, esta información de conexión no se ajusta correctamente. Esto hace que TensorFlow se cuelgue cuando intenta usar una sesión creada en un proceso diferente. Si alguien tiene más información sobre cómo funciona esto bajo el capó en TensorFlow, ¡me encantaría escucharlo!

La solución/alternativa

Fui ajustando Celery para que use Thread s en lugar de Process es para la agrupación. Hay algunas desventajas en este enfoque (ver el comentario de GIL arriba), pero esto nos permite cargar el modelo solo una vez. De todos modos, no estamos realmente vinculados a la CPU, ya que el tiempo de ejecución de TensorFlow maximiza todos los núcleos de la CPU (puede eludir el GIL ya que no está escrito en Python). Debe proporcionar Celery con una biblioteca separada para realizar agrupaciones basadas en subprocesos; los documentos sugieren dos opciones:gevent o eventlet . Luego pasa la biblioteca que elija al trabajador a través de --pool argumento de línea de comando.

Alternativamente, parece (como ya descubrió @pX0r) que otros backends de Keras como Theano no tienen este problema. Eso tiene sentido, ya que estos problemas están estrechamente relacionados con los detalles de implementación de TensorFlow. Personalmente, aún no he probado Theano, por lo que su kilometraje puede variar.

Sé que esta pregunta se publicó hace un tiempo, pero el problema sigue ahí, ¡así que espero que esto ayude a alguien!