tensorflow
Comment déboguer une fuite de mémoire dans TensorFlow
Recherche…
Utilisez Graph.finalize () pour intercepter les nœuds ajoutés au graphique
Le mode d'utilisation de TensorFlow le plus courant consiste à créer d' abord un graphe de flux de données des opérateurs TensorFlow (comme tf.constant()
et tf.matmul()
, puis à exécuter des étapes en appelant la méthode tf.Session.run()
dans une boucle (par exemple, un boucle de formation).
Une source courante de fuites de mémoire est celle où la boucle de formation contient des appels qui ajoutent des nœuds au graphe et qui s'exécutent à chaque itération, ce qui entraîne une croissance du graphe. Celles-ci peuvent être évidentes (par exemple, un appel à un opérateur TensorFlow comme tf.square()
), implicite (par exemple un appel à une fonction de bibliothèque TensorFlow qui crée des opérateurs comme tf.train.Saver()
) ou subtile (par exemple un appel à un opérateur surchargé sur un tf.Tensor
et un tableau NumPy, qui appelle implicitement tf.convert_to_tensor()
et ajoute un nouveau tf.constant()
au graphique).
La méthode tf.Graph.finalize()
peut aider à détecter les fuites comme ceci: elle marque un graphe en lecture seule et déclenche une exception si quelque chose est ajouté au graphe. Par exemple:
loss = ...
train_op = tf.train.GradientDescentOptimizer(0.01).minimize(loss)
init = tf.initialize_all_variables()
with tf.Session() as sess:
sess.run(init)
sess.graph.finalize() # Graph is read-only after this statement.
for _ in range(1000000):
sess.run(train_op)
loss_sq = tf.square(loss) # Exception will be thrown here.
sess.run(loss_sq)
Dans ce cas, l'opérateur surchargé *
tente d'ajouter de nouveaux noeuds au graphique:
loss = ...
# ...
with tf.Session() as sess:
# ...
sess.graph.finalize() # Graph is read-only after this statement.
# ...
dbl_loss = loss * 2.0 # Exception will be thrown here.
Utilisez l'allocateur tcmalloc
Pour améliorer les performances d'allocation de mémoire, de nombreux utilisateurs de TensorFlow utilisent souvent tcmalloc
au lieu de l'implémentation malloc()
par défaut, car tcmalloc
souffre moins de la fragmentation lors de l'allocation et de la désallocation d'objets volumineux (nombreux tenseurs). Certains programmes TensorFlow nécessitant beaucoup de mémoire ont été connus pour faire fuir l' espace d'adressage du tas (tout en libérant tous les objets individuels qu'ils utilisent) avec la valeur par défaut malloc()
, mais ils se sont bien tcmalloc
après le passage à tcmalloc
. De plus, tcmalloc
inclut un profileur de tas , qui permet de tcmalloc
les fuites restantes.
L'installation de tcmalloc
dépendra de votre système d'exploitation, mais les éléments suivants fonctionnent sur Ubuntu 14.04 (trusty) (où script.py
est le nom de votre programme TensorFlow Python):
$ sudo apt-get install google-perftools4
$ LD_PRELOAD=/usr/lib/libtcmalloc.so.4 python script.py ...
Comme indiqué ci-dessus, le simple fait de passer à tcmalloc
peut résoudre de nombreuses fuites apparentes. Toutefois, si l'utilisation de la mémoire continue de croître, vous pouvez utiliser le profileur de tas comme suit:
$ LD_PRELOAD=/usr/lib/libtcmalloc.so.4 HEAPPROFILE=/tmp/profile python script.py ...
Après avoir exécuté la commande ci-dessus, le programme écrit régulièrement des profils sur le système de fichiers. La séquence de profils sera nommée:
-
/tmp/profile.0000.heap
-
/tmp/profile.0001.heap
-
/tmp/profile.0002.heap
- ...
Vous pouvez lire les profils à l'aide de l'outil google-pprof
, qui (par exemple, sur Ubuntu 14.04) peut être installé dans le cadre du package google-perftools
. Par exemple, pour regarder le troisième instantané collecté ci-dessus:
$ google-pprof --gv `which python` /tmp/profile.0002.heap
En exécutant la commande ci-dessus, une fenêtre GraphViz apparaîtra, affichant les informations du profil sous la forme d'un graphique dirigé.