En théorie, la prédiction devrait être constante car les poids ont une taille fixe. Comment récupérer ma vitesse après la compilation (sans avoir besoin de supprimer l'optimiseur)?
Voir l'expérience associée: https://nbviewer.jupyter.org/github/off99555/TensorFlowExperiments/blob/master/test-prediction-speed-after-compile.ipynb?flush_cache=true
fit
sanscompile
; L'optimiseur n'existe même pas pour mettre à jour les poids.predict
peut être utilisé sansfit
oucompile
comme décrit dans ma réponse, mais la différence de performances ne devrait pas être aussi dramatique - d'où le problème.Réponses:
MISE À JOUR - 15/01/2020 : les meilleures pratiques actuelles pour les petites tailles de lots devrait être d'alimenter les entrées au modèle directement - par exemple
preds = model(x)
, et si les couches se comportent différemment en train / inférencemodel(x, training=False)
. Par dernier commit, cela est désormais documenté .Je ne les ai pas comparés, mais d'après la discussion de Git , cela vaut également la peine d'essayer
predict_on_batch()
- en particulier avec les améliorations de TF 2.1.ULTIMATE COUPABLE :
self._experimental_run_tf_function = True
. C'est expérimental . Mais ce n'est pas vraiment mauvais.Pour tous les développeurs TensorFlow qui lisent: nettoyez votre code . C'est le bordel. Et il viole d'importantes pratiques de codage, telles qu'une fonction fait une chose ;
_process_inputs
fait beaucoup plus que les "entrées de processus", pareil pour_standardize_user_data
. « Je ne suis pas assez payé » - mais vous ne payer, dans le temps supplémentaire passé à comprendre vos propres trucs, et les utilisateurs de remplir votre page Problèmes avec des bugs plus facile résolus avec un code plus clair.RÉSUMÉ : c'est seulement un peu plus lent avec
compile()
.compile()
définit un indicateur interne qui attribue une fonction de prédiction différente àpredict
. Cette fonction construit un nouveau graphique à chaque appel, le ralentissant par rapport à non compilé. Cependant, la différence n'est prononcée que lorsque le temps de train est beaucoup plus court que le temps de traitement des données . Si nous augmentons la taille du modèle à au moins de taille moyenne, les deux deviennent égaux. Voir le code en bas.Cette légère augmentation du temps de traitement des données est plus que compensée par la capacité de graphique amplifiée. Puisqu'il est plus efficace de ne conserver qu'un seul graphique de modèle, celui qui précompile est supprimé. Néanmoins : si votre modèle est petit par rapport aux données, il vaut mieux sans
compile()
pour l'inférence du modèle. Voir mon autre réponse pour une solution de contournement.QUE DEVRAIS-JE FAIRE?
Comparez les performances du modèle compilé vs non compilé comme je l'ai dans le code en bas.
predict
sur un modèle compilé.predict
sur un modèle non compilé.Oui, les deux sont possibles et cela dépendra de (1) la taille des données; (2) taille du modèle; (3) matériel. Le code en bas montre en fait que le modèle compilé est plus rapide, mais 10 itérations est un petit échantillon. Voir «contournements» dans mon autre réponse pour le «comment faire».
DÉTAILS :
Cela a mis du temps à déboguer, mais c'était amusant. Ci-dessous, je décris les principaux coupables que j'ai découverts, cite quelques documents pertinents et montre les résultats du profileur qui ont conduit au goulot d'étranglement ultime.
(
FLAG == self.experimental_run_tf_function
, par souci de concision)Model
par défaut instancie avecFLAG=False
.compile()
le définitTrue
.predict()
consiste à acquérir la fonction de prédiction,func = self._select_training_loop(x)
predict
etcompile
, tous les autres indicateurs sont tels que:FLAG==True
->func = training_v2.Loop()
FLAG==False
->func = training_arrays.ArrayLikeTrainingLoop()
Véritable coupable : il
_process_inputs()
représente 81% du temps d'exécution . Sa composante majeure?_create_graph_function()
, 72% du temps d'exécution . Cette méthode n'existe même pas pour (B) . Cependant, l'utilisation d'un modèle de taille moyenne_process_inputs
représente moins de 1% du temps d'exécution . Code en bas, et les résultats du profilage suivent.PROCESSEURS DE DONNÉES :
(A) :,
<class 'tensorflow.python.keras.engine.data_adapter.TensorLikeDataAdapter'>
utilisé dans_process_inputs()
. Code source pertinent(B) :,
numpy.ndarray
retourné parconvert_eager_tensors_to_numpy
. Code source pertinent , et iciFONCTION D'EXÉCUTION DU MODÈLE (par ex. Prédire)
(A) : fonction de distribution , et ici
(B) : fonction de distribution (différente) , et ici
PROFILER : résultats pour le code dans mon autre réponse, "petit modèle", et dans cette réponse, "modèle moyen":
Petit modèle : 1000 itérations,
compile()
Petit modèle : 1000 itérations, non
compile()
Modèle moyen : 10 itérations
DOCUMENTATION (indirecte) sur les effets de
compile()
: sourceCONTRE - EXEMPLE :
Sorties :
la source
compile()
?MISE À JOUR : voir la réponse réelle publiée comme une réponse distincte; cet article contient des informations supplémentaires
.compile()
configure la majorité du graphique TF / Keras, y compris les pertes, les métriques, les gradients, et en partie l'optimiseur et ses poids - ce qui garantit un ralentissement notable.Ce qui est inattendu, c'est l'ampleur du ralentissement - décuplé sur ma propre expérience, et pour
predict()
, qui ne met à jour aucun poids. En examinant le code source de TF2, les éléments du graphique semblent étroitement liés, les ressources n'étant pas nécessairement allouées "équitablement".Les développeurs
predict
peuvent ignorer les performances d'un modèle non compilé, car les modèles sont généralement utilisés compilés - mais en pratique , c'est une différence inacceptable. Il est également possible que ce soit un «mal nécessaire», car il existe une solution de contournement simple (voir ci-dessous).Ce n'est pas une réponse complète, et j'espère que quelqu'un pourra la fournir ici - sinon, je suggère d'ouvrir un problème Github sur TensorFlow. (OP a; ici )
Solution : entraînez un modèle, enregistrez ses poids , reconstruisez le modèle sans le compiler, chargez les poids. Ne sauvegardez pas le modèle entier (par exemple
model.save()
), car il se chargera compilé - utilisez plutôtmodel.save_weights()
etmodel.load_weights()
.Solution de contournement 2 : ci-dessus, mais utilisez
load_model(path, compile=False)
; crédit de suggestion: D. MöllerUPDATE : clarifier, optimiseur est pas entièrement instancié avec
compile
, y compris sesweights
etupdates
tenseurs - cela se fait lorsque le premier appel d'une fonction d' ajustement est effectuée (fit
,train_on_batch
, etc.), par l' intermédiairemodel._make_train_function()
.Le comportement observé est donc encore plus étrange. Pire encore, la construction de l'optimiseur ne pas obtenir d'autres ralentissements (voir ci - dessous) - suggérant « la taille du graphique » n'est pas la principale explication ici.
EDIT : sur certains modèles, un ralentissement de 30x . TensorFlow, qu'avez-vous fait. Exemple ci-dessous:
Sorties :
la source
model.fit()
par rapport à une boucle dynamique avec une exécution impatiente pour voir si la perte de performance est trop importante ...load_model(name, compile=False)
, c'est plus simple que de sauvegarder / charger des poids et de recréer le modèle.