python - Puis-je mesurer le temps d'exécution des opérations individuelles avec TensorFlow?




example github (9)

Pour les commentaires de fat-lobyte sous la réponse d' Olivier Moindrot , si vous voulez rassembler le scénario sur toutes les sessions, vous pouvez changer " open('timeline.json', 'w') " en " open('timeline.json', 'a') ".

Je sais que je peux mesurer le temps d'exécution d'un appel à sess.run() , mais est-il possible d'obtenir une granularité plus fine et de mesurer le temps d'exécution des opérations individuelles?


Il n'y a pas encore moyen de le faire dans la version publique. Nous sommes conscients que c'est une caractéristique importante et nous y travaillons.


Récemment publié par la bibliothèque op personnalisée Uber SBNet ( http://www.github.com/uber/sbnet ) a une implémentation de temporisateurs basés sur les événements cuda, qui peuvent être utilisés de la manière suivante:

with tf.control_dependencies([input1, input2]):
    dt0 = sbnet_module.cuda_timer_start()
with tf.control_dependencies([dt0]):
    input1 = tf.identity(input1)
    input2 = tf.identity(input2)

### portion of subgraph to time goes in here

with tf.control_dependencies([result1, result2, dt0]):
    cuda_time = sbnet_module.cuda_timer_end(dt0)
with tf.control_dependencies([cuda_time]):
    result1 = tf.identity(result1)
    result2 = tf.identity(result2)

py_result1, py_result2, dt = session.run([result1, result2, cuda_time])
print "Milliseconds elapsed=", dt

Notez que n'importe quelle partie du sous-graphe peut être asynchrone, vous devriez être très prudent en spécifiant toutes les dépendances d'entrée et de sortie pour les opérations de temporisation. Sinon, la minuterie pourrait être insérée dans le graphique en panne et vous pouvez obtenir un temps erroné. J'ai trouvé à la fois la chronologie et le time.time () timing d'utilité très limitée pour profiler les graphes Tensorflow. Notez également que les API cuda_timer se synchroniseront sur le flux par défaut, qui est actuellement conçu parce que TF utilise plusieurs flux.

Cela dit, je recommande personnellement de passer à PyTorch :) itération de développement est plus rapide, le code s'exécute plus vite et tout est beaucoup moins douloureux.

Une autre approche un peu hacky et arcane pour soustraire la surcharge de tf.Session (qui peut être énorme) est de répliquer le graphique N fois et l'exécuter pour une variable N, résolvant une équation de surcharge fixe inconnue. C'est-à-dire que vous mesureriez autour de session.run () avec N1 = 10 et N2 = 20 et vous savez que votre temps est t et que le temps système est x. Donc quelque chose comme

N1*x+t = t1
N2*x+t = t2

Résolvez pour x et t. L'inconvénient est que cela peut exiger beaucoup de mémoire et n'est pas nécessairement précis :) Assurez-vous également que vos entrées sont complètement différentes / aléatoires / indépendantes sinon TF pliera tout le sous-graphe et ne l'exécutera pas N fois ... Amusez-vous avec TensorFlow: )


Depuis Tensorflow 1.8, il y a un très bon exemple pour utiliser le tf.profile.Profiler here .


Comme cela est très haut lors de la recherche sur "Tensorflow Profiling", notez que la façon actuelle (fin 2017, TensorFlow 1.4) d'obtenir le scénario utilise un ProfilerHook . Cela fonctionne avec les MonitoredSessions dans tf.Estimator où tf.RunOptions ne sont pas disponibles.

estimator = tf.estimator.Estimator(model_fn=...)
hook = tf.train.ProfilerHook(save_steps=10, output_dir='.')
estimator.train(input_fn=..., steps=..., hooks=[hook])

Vous pouvez extraire cette information en utilisant des statistiques d'exécution . Vous devrez faire quelque chose comme ceci (vérifiez l'exemple complet dans le lien ci-dessus):

run_options = tf.RunOptions(trace_level=tf.RunOptions.FULL_TRACE)
run_metadata = tf.RunMetadata()
sess.run(<values_you_want_to_execute>, options=run_options, run_metadata=run_metadata)
your_writer.add_run_metadata(run_metadata, 'step%d' % i)

Mieux que de l'imprimer, vous pouvez le voir dans tensorboard:

De plus, cliquer sur un nœud affichera la mémoire totale exacte, l'heure de calcul et les tailles de sortie du tensoriel.



J'ai utilisé l' objet Timeline pour obtenir l'heure d'exécution pour chaque noeud dans le graphique:

  • vous utilisez un sess.run() classique sess.run() mais aussi les options optionnelles arguments et run_metadata
  • vous créez ensuite un objet Timeline avec les données run_metadata.step_stats

Voici un exemple de programme qui mesure la performance d'une multiplication matricielle:

import tensorflow as tf
from tensorflow.python.client import timeline

x = tf.random_normal([1000, 1000])
y = tf.random_normal([1000, 1000])
res = tf.matmul(x, y)

# Run the graph with full trace option
with tf.Session() as sess:
    run_options = tf.RunOptions(trace_level=tf.RunOptions.FULL_TRACE)
    run_metadata = tf.RunMetadata()
    sess.run(res, options=run_options, run_metadata=run_metadata)

    # Create the Timeline object, and write it to a json
    tl = timeline.Timeline(run_metadata.step_stats)
    ctf = tl.generate_chrome_trace_format()
    with open('timeline.json', 'w') as f:
        f.write(ctf)

Vous pouvez ensuite ouvrir Google Chrome, aller à la page chrome://tracing et charger le fichier timeline.json . Vous devriez voir quelque chose comme:


La fonction get_variable() crée une nouvelle variable ou en renvoie une créée précédemment par get_variable() . Il ne renverra pas de variable créée avec tf.Variable() . Voici un exemple rapide:

>>> with tf.variable_scope("foo"):
...   bar1 = tf.get_variable("bar", (2,3)) # create
... 
>>> with tf.variable_scope("foo", reuse=True):
...   bar2 = tf.get_variable("bar")  # reuse
... 

>>> with tf.variable_scope("", reuse=True): # root variable scope
...   bar3 = tf.get_variable("foo/bar") # reuse (equivalent to the above)
... 
>>> (bar1 is bar2) and (bar2 is bar3)
True

Si vous n'avez pas créé la variable à l'aide de tf.get_variable() , vous avez deux options. Tout d'abord, vous pouvez utiliser tf.global_variables() (comme le suggère @mrry):

>>> bar1 = tf.Variable(0.0, name="bar")
>>> bar2 = [var for var in tf.global_variables() if var.op.name=="bar"][0]
>>> bar1 is bar2
True

Ou vous pouvez utiliser tf.get_collection() comme ceci:

>>> bar1 = tf.Variable(0.0, name="bar")
>>> bar2 = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope="bar")[0]
>>> bar1 is bar2
True

modifier

Vous pouvez aussi utiliser get_tensor_by_name() :

>>> bar1 = tf.Variable(0.0, name="bar")
>>> graph = tf.get_default_graph()
>>> bar2 = graph.get_tensor_by_name("bar:0")
>>> bar1 is bar2
False, bar2 is a Tensor througn convert_to_tensor on bar1. but bar1 equal 
bar2 in value.

Rappelez-vous qu'un tenseur est la sortie d'une opération. Il porte le même nom que l'opération, plus :0 . Si l'opération comporte plusieurs sorties, elles portent le même nom que l'opération plus :0 :1 :2 , etc.





tensorflow