Em teoria, a previsão deve ser constante, pois os pesos têm um tamanho fixo. Como recupero minha velocidade após a compilação (sem a necessidade de remover o otimizador)?
Consulte a experiência associada: https://nbviewer.jupyter.org/github/off99555/TensorFlowExperiments/blob/master/test-prediction-speed-after-compile.ipynb?flush_cache=true
python
performance
tensorflow
keras
jupyter-notebook
off99555
fonte
fonte
fit
semcompile
; o otimizador nem existe para atualizar pesos.predict
pode ser usado semfit
oucompile
como descrito na minha resposta, mas a diferença de desempenho não deve ser tão dramática - daí o problema.Respostas:
ATUALIZAÇÃO - 15/1/2020 : a melhor prática atual para lotes pequenos deve ser a entrada direta do modelo - isto é
preds = model(x)
, e se as camadas se comportarem de maneira diferente no trem / inferênciamodel(x, training=False)
,. Pela confirmação mais recente, isso agora está documentado .Eu não os comparei, mas de acordo com a discussão do Git , também vale a pena tentar
predict_on_batch()
- especialmente com as melhorias no TF 2.1.ULTIMATE CULPADO :
self._experimental_run_tf_function = True
. É experimental . Mas não é realmente ruim.Para qualquer desenvolvedor de TensorFlow que esteja lendo: limpe seu código . É uma bagunça. E viola importantes práticas de codificação, como uma função faz uma coisa ;
_process_inputs
faz muito mais do que "entradas de processo", o mesmo para_standardize_user_data
. "Eu não sou pago o suficiente" - mas você fazer pagamento, no tempo extra gasto compreender o seu próprio material, e em usuários de encher sua página Problemas com erros resolvidos mais fácil com um código mais claro.RESUMO : é apenas um pouco mais lento com
compile()
.compile()
define um sinalizador interno ao qual atribui uma função de previsão diferentepredict
. Essa função constrói um novo gráfico a cada chamada, diminuindo a velocidade em relação a não compilado. No entanto, a diferença é pronunciada apenas quando o tempo de trem é muito menor que o tempo de processamento de dados . Se aumentarmos o tamanho do modelo para pelo menos o tamanho médio, os dois se tornarão iguais. Veja o código na parte inferior.Esse ligeiro aumento no tempo de processamento de dados é mais do que compensado pela capacidade de gráficos amplificados. Como é mais eficiente manter apenas um gráfico de modelo, o pré-compilado é descartado. No entanto : se o seu modelo for pequeno em relação aos dados, você estará melhor sem
compile()
a inferência do modelo. Veja minha outra resposta para uma solução alternativa.O QUE DEVO FAZER?
Compare o desempenho do modelo compilado versus descompilado como eu tenho no código na parte inferior.
predict
em um modelo compilado.predict
em um modelo não compilado.Sim, ambos são possíveis e dependerão do (1) tamanho dos dados; (2) tamanho do modelo; (3) hardware. O código na parte inferior mostra o modelo compilado sendo mais rápido, mas 10 iterações são uma amostra pequena. Veja "soluções alternativas" na minha outra resposta para o "como fazer".
DETALHES :
Demorou um pouco para depurar, mas foi divertido. Abaixo, descrevo os principais culpados que descobri, cito alguma documentação relevante e mostro os resultados do criador de perfil que levaram ao gargalo final.
(
FLAG == self.experimental_run_tf_function
, por questões de concisão)Model
por padrão instancia comFLAG=False
.compile()
define paraTrue
.predict()
envolve a aquisição da função de previsão,func = self._select_training_loop(x)
predict
ecompile
, todos os outros sinalizadores são tais que:FLAG==True
->func = training_v2.Loop()
FLAG==False
->func = training_arrays.ArrayLikeTrainingLoop()
Verdadeiro culpado :
_process_inputs()
, respondendo por 81% do tempo de execução . Seu principal componente?_create_graph_function()
, 72% do tempo de execução . Este método nem existe para (B) . Usar um modelo de tamanho médio, no entanto,_process_inputs
compreende menos de 1% do tempo de execução . Código na parte inferior e resultados de criação de perfil a seguir.PROCESSADORES DE DADOS :
(A) :,
<class 'tensorflow.python.keras.engine.data_adapter.TensorLikeDataAdapter'>
usado em_process_inputs()
. Código fonte relevante(B) :
numpy.ndarray
, devolvido porconvert_eager_tensors_to_numpy
. Código fonte relevante e aquiFUNÇÃO DE EXECUÇÃO DE MODELO (por exemplo, prever)
(A) : função de distribuição , e aqui
(B) : função de distribuição (diferente) , e aqui
PROFILER : resultados para o código na minha outra resposta, "modelo pequeno", e nesta resposta, "modelo médio":
Modelo minúsculo : 1000 iterações,
compile()
Modelo minúsculo : 1000 iterações, não
compile()
Modelo médio : 10 iterações
DOCUMENTAÇÃO (indiretamente) sobre os efeitos de
compile()
: fonteCOUNTEREXAMPLE :
Saídas :
fonte
compile()
?UPDATE : veja a resposta real postada como uma resposta separada; este post contém informações adicionais
.compile()
configura a maioria do gráfico TF / Keras, incluindo perdas, métricas, gradientes e parcialmente o otimizador e seus pesos - o que garante uma desaceleração notável.O que é inesperado é a extensão da desaceleração - 10 vezes em meu próprio experimento e para
predict()
, que não atualiza nenhum peso. Analisando o código-fonte do TF2, os elementos gráficos aparecem fortemente interligados, com os recursos não sendo necessariamente alocados "de maneira justa".Possível negligência pelos desenvolvedores sobre
predict
o desempenho de um modelo não compilado, pois os modelos geralmente são usados compilados - mas, na prática , essa é uma diferença inaceitável. Também é possível que seja um "mal necessário", pois há uma solução simples (veja abaixo).Esta não é uma resposta completa e espero que alguém possa fornecê-la aqui - caso contrário, sugiro que abra um problema do Github no TensorFlow. (OP tem; aqui )
Solução alternativa : treine um modelo, salve seus pesos , reconstrua o modelo sem compilar, carregue os pesos. Você não salvar o modelo inteiro (por exemplo
model.save()
), como ele irá carregar compilado - em vez disso usarmodel.save_weights()
emodel.load_weights()
.Solução 2 : acima, mas use
load_model(path, compile=False)
; crédito da sugestão: D. MöllerACTUALIZAÇÃO : clarificar, optimizador está não totalmente instanciado com
compile
, incluindo os seusweights
eupdates
tensores - isto é feito quando a primeira chamada de uma função de encaixe é feito (fit
,train_on_batch
, etc.), por meio demodel._make_train_function()
.O comportamento observado é, portanto, ainda mais estranho. Pior ainda, a construção do otimizador não provoca mais lentidões (veja abaixo) - sugerir "tamanho do gráfico" não é a principal explicação aqui.
EDIT : em alguns modelos, uma desaceleração de 30x . TensorFlow, o que você fez. Exemplo abaixo:
Saídas :
fonte
model.fit()
contra um laço dinâmico com a execução ansiosos para ver se a perda de desempenho é muito grande ...load_model(name, compile=False)
, é mais simples do que salvar / carregar pesos e recriar o modelo.