[PYTHON] Remarques sur tf.function et traçage

introduction

Cet article est l'article du 20e jour du Calendrier de l'Avent TensorFlow 2.0 2019. Je pense que le changement majeur dans TensorFlow 2.0 est que EagerExecution est devenu la valeur par défaut, vous permettant d'écrire dans un langage pédagogique et d'écrire plus librement dans un style pythonique. Cependant, d'un autre côté, il y a un problème que les performances et la portabilité sont sacrifiées, mais afin de le résoudre afin que vous puissiez bénéficier à la fois du mode Graph en 1.x et du mode Eager en 2.x. Celui qui est apparu dans est tf.function. Dans cet article, je voudrais vous présenter comment utiliser tf.function et les points à prendre en compte lors de son utilisation. Fondamentalement, il s'agit d'un résumé du Site officiel, donc si vous voulez en savoir plus, veuillez vous y référer également.

Comment utiliser tf.function

Il est facile à utiliser, ajoutez un collator avec @ tf.function à la fonction qui décrit le traitement lourd que vous souhaitez optimiser, ou définissez une fonction et alimentez-la dans la méthode tf.function pour séparer la fonction du mode graphique. C'est une méthode de création.

function_test.py


import tensorflow as tf

@tf.function
def f(x,y):
  return x + y

#Ou

def g(x,y):
  return x * y

h = tf.function(g)

De plus, si vous appelez une autre fonction dans @ tf.function, la portée s'étend à cette fonction, vous n'avez donc pas à vous soucier de vérifier toutes les fonctions et d'ajouter @ tf.function (ou plutôt, nous le recommandons). Sens). Par conséquent, il semble que vous puissiez facilement bénéficier du mode graphique simplement en l'ajoutant à la partie de traitement lourd pour le moment. Cependant, s'il s'agit d'un style d'écriture simple comme celui du tutoriel, ce n'est pas un gros problème, mais si vous essayez de faire quelque chose d'un peu compliqué, vous ne penserez pas que vous ne connaissez pas la spécification de tf.function. Vous devez être prudent parce que vous le faites. Au début du Site officiel, il y a la description suivante.

Il y a certains éléments qui sont difficiles à interpréter, mais je pense que c'est plus facile à comprendre si vous regardez un exemple concret, alors jetons un coup d'œil.

Expérience

Tout d'abord, par souci de simplicité, préparez la fonction simple suivante.

tracing_test.py


import tensorflow as tf

@tf.function
def double(a):
    print("Tracing with, {}".format(a) )
    return a + a

C'est une fonction simple qui double l'argument d'entrée et le renvoie, et inclut également le processus d'impression de l'argument d'entrée. L'argument fonctionne avec des entiers, des nombres réels et des chaînes. Faisons cela avec quelques modèles.

tracing_test.py


print(double(3))
print()
print(double(2))
print()
print(double(tf.constant(1.1)))
print()
print(double(tf.constant('a')))
print()
print(double(tf.constant('b')))

Le résultat est le suivant.

Tracing with, 3
tf.Tensor(6, shape=(), dtype=int32)

Tracing with, 2
tf.Tensor(4, shape=(), dtype=int32)

Tracing with, Tensor("a:0", shape=(), dtype=float32)
tf.Tensor(2.2, shape=(), dtype=float32)

Tracing with, Tensor("a:0", shape=(), dtype=string)
tf.Tensor(b'aa', shape=(), dtype=string)

tf.Tensor(b'bb', shape=(), dtype=string)

Le résultat est un peu étrange. L'instruction print n'est pas exécutée uniquement à la suite de l'exécution du dernier tf.constant ('b') comme argument. Si vous essayez à nouveau d'exécuter le programme ci-dessus, vous obtiendrez des résultats encore plus étranges.

tracing_test.py


print(double(3))
print()
print(double(2))
print()
print(double(tf.constant(1.1)))
print()
print(double(tf.constant('a')))
print()
print(double(tf.constant('b')))

Le résultat est le suivant.

tf.Tensor(6, shape=(), dtype=int32)

tf.Tensor(4, shape=(), dtype=int32)

tf.Tensor(2.2, shape=(), dtype=float32)

tf.Tensor(b'aa', shape=(), dtype=string)

tf.Tensor(b'bb', shape=(), dtype=string)

La valeur correcte sera renvoyée, mais l'instruction d'impression écrite au milieu ne sera pas exécutée du tout. Qu'est-ce que ça veut dire?

Tracing En fait, ce comportement étrange implique un processus appelé Tracing lorsque tf.function construit et optimise une fonction sur un graphe de calcul. tf.function convertit une fonction qui décrit non seulement le traitement dérivé de TensorFlow mais également spécifique à Python en un graphe de calcul. Et il omet le traitement spécifique à Python (instruction d'impression dans ce cas) qui n'est pas réellement lié à l'exécution du graphe de calcul. Mais pourquoi cela a-t-il été fait au début? C'est parce que lorsque tf.function convertit une fonction en un graphe de calcul, un processus appelé Tracing s'exécute. Les fonctions écrites en Python n'ont pas de type explicite dans leurs arguments. Par conséquent, bien qu'il soit pratique de pouvoir entrer diverses valeurs, c'est un problème du côté de la fonction tf.function d'essayer de créer un graphe de calcul optimal. Par conséquent, lorsqu'une valeur ou un type qui n'a pas été entré dans l'argument est entré et que la fonction est appelée pour la première fois, un processus appelé Tracing est exécuté pour exécuter tous les traitements spécifiques à Python dans la fonction. J'ai dit «des valeurs et des types qui n'ont jamais été inclus dans l'argument», mais à proprement parler, les critères sont les suivants.

Par conséquent, le comportement étrange du précédent est le suivant.

tracing_test.py


print(double(3)) #Tracing parce que c'est la valeur que je vois pour la première fois
print()
print(double(2)) #Tracing parce que c'est la valeur que je vois pour la première fois
print()
print(double(tf.constant(1.1))) #Tracing parce que c'est la valeur que je vois pour la première fois
print()
print(double(tf.constant('a'))) #Traçage car c'est la première forme à voir
print()
print(double(tf.constant('b'))) #Exécution de graphique optimisée car c'est la forme de type que nous avons vue auparavant

Une fois que vous exécutez Tracing, TensorFlow enregistre le graphe de calcul résultant en interne. Ensuite, la prochaine fois qu'une valeur précédemment tracée ou un argument type / forme est entré, le graphique de calcul optimisé sera exécuté. Par conséquent, dans le programme ci-dessus, l'instruction d'impression Python n'a pas été exécutée lors du dernier appel, et toutes les instructions d'impression n'ont pas été exécutées lors de sa nouvelle exécution.

Alors, que devrions-nous faire?

Alors je reviendrai au début,

Cela signifie que. C'est Print plus tôt, mais si vous l'écrivez dans tf.print à la place, il sera exécuté à chaque fois. Vous pouvez également éviter un comportement étrange en utilisant pleinement les fonctions dérivées de TensorFlow, telles que l'utilisation de tf.summary ou l'utilisation de tf.Variable si vous souhaitez mettre à jour diverses valeurs dans une fonction. Cela améliore également les performances. Cependant, veuillez noter que nous ne disons pas que nous ne devrions pas inclure de traitement spécifique à Python. L'avantage de pouvoir programmer de manière plus flexible en pouvant l'utiliser avec l'écriture pythonique est grand. Cependant, veuillez noter que si vous définissez une fonction sans penser à quoi que ce soit et que vous ajoutez tf.function, elle se comportera étrangement.

Résumé

C'est bien d'avoir TensorFlow 2.0 et de pouvoir bénéficier à la fois des modes Graph et Eager, mais cela crée des fonctions qui reposent trop sur des fonctionnalités spécifiques à Python autres que les pièges mentionnés ci-dessus. Vous pouvez marcher sur un bogue inattendu. Si vous utilisez TensorFlow, utilisez autant que possible les méthodes dérivées de TensorFlow et, lorsque vous utilisez des fonctions spécifiques à Python, concevez en tenant compte du comportement AutoGraph et du traçage. Le Site officiel a beaucoup d'autres choses à surveiller et comment contrôler ces comportements. Si vous avez besoin d'implémenter votre propre modèle à partir de maintenant et que vous devez utiliser tf.function, veuillez le lire.

Recommended Posts

Remarques sur tf.function et traçage
Remarques sur les * args et ** kargs
Notes sur pyenv et Atom
Notes sur Python et les types de dictionnaire
Remarques sur l'utilisation de la post-réception et de la post-fusion
Notes sur Flask
Remarques sur la construction de Python et pyenv sur Mac
Remarques sur l'installation de Python3 et l'utilisation de pip sous Windows7
Remarques sur les réseaux de neurones
Notes de céleri sur Django
Remarques sur l'installation de PycURL
Remarques sur l'utilisation d'Alembic
Remarques sur les fonctions de la famille SciPy.linalg
Remarques et conseils sur l'assemblage vertical de PySpark DataFrame
Remarques sur le traitement d'images HDR et RAW avec Python
Notes sur la construction de TinyEMU et le démarrage du noyau Linux sur Emscripten
Python sur Ruby et Ruby en colère sur Python
Remarques sur l'installation de dlib sur Mac
Notes sur le module sqlite3 de python
Remarques sur la définition des slots PySide (2)
[Django] Remarques sur l'utilisation de django-debug-toolbar
Enregistrement et lecture sous Linux
Précautions lors de la définition des emplacements pour PySide
[Python] Notes sur l'analyse des données
Remarques sur l'optimisation à l'aide de Pytorch
Remarques sur l'installation de Python sur votre Mac
Remarques sur l'installation de pipenv sur votre Mac
Remarques sur le déploiement de pyenv avec Homebrew et la gestion des versions de Python
Catalina sur Mac et pyenv
Remarques sur l'installation d'Anaconda 3 sur Windows
Remarques sur imshow () d'OpenCV
[Python] Notes sur les instructions while (style d'écriture et boucle infinie)
Remarques sur l'installation de Python sur CentOS
Notes sur la lecture et l'écriture d'images TIFF float32 avec python
Python 3.6 sous Windows ... et vers Xamarin.
Remarques sur la gestion des packages avec conda
MQTT sur Raspberry Pi et Mac
Installez Mecab et mecab-python3 sur Ubuntu 14.04
Installez et exécutez Dropbox sur Ubuntu 20.04
Installez OpenCV et Chainer sur Ubuntu
Installez CUDA 8.0 et Chainer sur Ubuntu 16.04
Remarques sur l'utilisation de MeCab depuis Python
Remarques sur l'utilisation de pywinauto
Construisez et installez OpenCV sur Windows
Enquête sur la construction et le fonctionnement de kivi
Lier Modelica et Python sous Windows
Remarques sur l'utilisation des featuretools
Pour moi: notes sur l'infrastructure et le réseau
Remarques sur l'installation de Python à l'aide de PyEnv
Installez le fabric sur Ubuntu et essayez
Maîtriser le pip et la roue dans Windows
Classification des erreurs (python3.x) et notes de débogage
Notes sur l'utilisation de rstrip avec python.
Remarques sur l'accès à dashDB à partir de python
(Notes personnelles) Métaclasses et métaprogrammation Python
Notes d'instructions d'installation Homebrew et Pycharm
Remarques sur l'utilisation de matplotlib sur le serveur
Remarques sur la rédaction de requirements.txt