Redis
 sql >> Base de Dados >  >> NoSQL >> Redis

Keras prevê não retornar na tarefa de aipo


Eu me deparei com esse mesmo problema, e cara, era uma toca de coelho. Queria postar minha solução aqui, pois pode salvar alguém um dia de trabalho:

Estruturas de dados específicas de thread do TensorFlow


No TensorFlow, há duas estruturas de dados principais que funcionam nos bastidores quando você chama model.predict (ou keras.models.load_model , ou keras.backend.clear_session , ou praticamente qualquer outra função que interage com o back-end do TensorFlow):
  • Um gráfico do TensorFlow, que representa a estrutura do seu modelo Keras
  • Uma sessão do TensorFlow, que é a conexão entre o gráfico atual e o tempo de execução do TensorFlow

Algo que não está explicitamente claro nos documentos sem alguma pesquisa é que tanto a sessão quanto o gráfico são propriedades do thread atual . Veja os documentos da API aqui e aqui.

Usando modelos do TensorFlow em diferentes threads


É natural querer carregar seu modelo uma vez e então chamar .predict() nele várias vezes depois:
from keras.models import load_model

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

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


Em um contexto de servidor web ou pool de trabalhadores como o Celery, o que isso significa é que você carregará o modelo quando importar o módulo que contém o load_model linha, um thread diferente executará some_worker_function , executando a previsão na variável global que contém o modelo Keras. No entanto, tentar executar a previsão em um modelo carregado em um thread diferente produz erros de "tensor não é um elemento deste gráfico". Graças aos vários posts do SO que abordaram este tópico, como ValueError:Tensor Tensor(...) não é um elemento deste gráfico. Ao usar o modelo keras de variável global. Para que isso funcione, você precisa se apegar ao gráfico do TensorFlow que foi usado -- como vimos anteriormente, o gráfico é uma propriedade do thread atual. O código atualizado fica assim:
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)

A reviravolta um tanto surpreendente aqui é:o código acima é suficiente se você estiver usando Thread s, mas trava indefinidamente se você estiver usando Process sim. E, por padrão, o Celery usa processos para gerenciar todos os seus pools de trabalhadores. Então, neste momento, as coisas ainda não está trabalhando no aipo.

Por que isso só funciona em Thread s?


Em Python, Thread s compartilham o mesmo contexto de execução global que o processo pai. Dos documentos _thread do Python:

Este módulo fornece primitivos de baixo nível para trabalhar com vários threads (também chamados de processos ou tarefas leves) — vários threads de controle compartilhando seu espaço de dados global.

Como os threads não são processos separados reais, eles usam o mesmo interpretador python e, portanto, estão sujeitos ao infame Global Interpeter Lock (GIL). Talvez mais importante para esta investigação, eles compartilham espaço de dados global com o pai.

Em contraste com isso, Process es são reais novos processos gerados pelo programa. Isso significa:
  • Nova instância do interpretador Python (e sem GIL)
  • O espaço de endereço global é duplicado

Observe a diferença aqui. Enquanto Thread s têm acesso a uma única variável de sessão global compartilhada (armazenada internamente no tensorflow_backend módulo de Keras), Process es têm duplicatas da variável Session.

Meu melhor entendimento desse problema é que a variável Session deve representar uma conexão exclusiva entre um cliente (processo) e o tempo de execução do TensorFlow, mas ao ser duplicada no processo de bifurcação, essas informações de conexão não são ajustadas corretamente. Isso faz com que o TensorFlow trave ao tentar usar uma sessão criada em um processo diferente. Se alguém tiver mais informações sobre como isso está funcionando nos bastidores do TensorFlow, adoraria ouvir!

A solução / solução alternativa


Eu fui ajustando o aipo para que ele usasse Thread s em vez de Process es para pooling. Existem algumas desvantagens nessa abordagem (veja o comentário GIL acima), mas isso nos permite carregar o modelo apenas uma vez. De qualquer forma, não estamos realmente vinculados à CPU, pois o tempo de execução do TensorFlow maximiza todos os núcleos da CPU (ele pode contornar o GIL, pois não é escrito em Python). Você precisa fornecer ao Celery uma biblioteca separada para fazer o pooling baseado em encadeamento; os documentos sugerem duas opções:gevent ou eventlet . Em seguida, você passa a biblioteca escolhida para o trabalhador por meio do --pool argumento de linha de comando.

Alternativamente, parece (como você já descobriu @pX0r) que outros back-ends Keras, como Theano, não têm esse problema. Isso faz sentido, pois esses problemas estão intimamente relacionados aos detalhes de implementação do TensorFlow. Eu pessoalmente ainda não experimentei o Theano, então sua milhagem pode variar.

Eu sei que esta pergunta foi postada há algum tempo, mas o problema ainda está por aí, então espero que isso ajude alguém!