TensorFlow: ¿Hay una manera de medir FLOPS para un modelo?

El ejemplo más cercano que puedo obtener se encuentra en este problema: https://github.com/tensorflow/tensorflow/issues/899

Con este código mínimo reproducible:

import tensorflow as tf import tensorflow.python.framework.ops as ops g = tf.Graph() with g.as_default(): A = tf.Variable(tf.random_normal( [25,16] )) B = tf.Variable(tf.random_normal( [16,9] )) C = tf.matmul(A,B) # shape=[25,9] for op in g.get_operations(): flops = ops.get_stats_for_node_def(g, op.node_def, 'flops').value if flops is not None: print 'Flops should be ~',2*25*16*9 print '25 x 25 x 9 would be',2*25*25*9 # ignores internal dim, repeats first print 'TF stats gives',flops 

Sin embargo, el FLOPS devuelto siempre es Ninguno. ¿Hay una forma concreta de medir FLOPS, especialmente con un archivo PB?

Un poco tarde pero quizás ayude a algunos visitantes en el futuro. Para su ejemplo he probado con éxito el siguiente fragmento de código:

 g = tf.Graph() run_meta = tf.RunMetadata() with g.as_default(): A = tf.Variable(tf.random_normal( [25,16] )) B = tf.Variable(tf.random_normal( [16,9] )) C = tf.matmul(A,B) # shape=[25,9] opts = tf.profiler.ProfileOptionBuilder.float_operation() flops = tf.profiler.profile(g, run_meta=run_meta, cmd='op', options=opts) if flops is not None: print('Flops should be ~',2*25*16*9) print('25 x 25 x 9 would be',2*25*25*9) # ignores internal dim, repeats first print('TF stats gives',flops.total_float_ops) 

También es posible usar el generador de perfiles en combinación con Keras como el siguiente fragmento de código:

 import tensorflow as tf import keras.backend as K from keras.applications.mobilenet import MobileNet run_meta = tf.RunMetadata() with tf.Session(graph=tf.Graph()) as sess: K.set_session(sess) net = MobileNet(alpha=.75, input_tensor=tf.placeholder('float32', shape=(1,32,32,3))) opts = tf.profiler.ProfileOptionBuilder.float_operation() flops = tf.profiler.profile(sess.graph, run_meta=run_meta, cmd='op', options=opts) opts = tf.profiler.ProfileOptionBuilder.trainable_variables_parameter() params = tf.profiler.profile(sess.graph, run_meta=run_meta, cmd='op', options=opts) print("{:,} --- {:,}".format(flops.total_float_ops, params.total_parameters)) 

Espero poder ayudar!

Me gustaría basarme en la respuesta de Tobias Schnek, así como en responder a la pregunta original: cómo obtener FLOP de un archivo pb .

Ejecutar el primer fragmento de código de la respuesta de Tobias con TensorFlow 1.6.0

 g = tf.Graph() run_meta = tf.RunMetadata() with g.as_default(): A = tf.Variable(tf.random_normal([25,16])) B = tf.Variable(tf.random_normal([16,9])) C = tf.matmul(A,B) opts = tf.profiler.ProfileOptionBuilder.float_operation() flops = tf.profiler.profile(g, run_meta=run_meta, cmd='op', options=opts) if flops is not None: print('Flops should be ~',2*25*16*9) print('TF stats gives',flops.total_float_ops) 

Obtenemos la siguiente salida:

 Flops should be ~ 7200 TF stats gives 8288 

Entonces, ¿por qué obtenemos 8288 lugar del resultado esperado 7200=2*25*16*9 [a] ? La respuesta está en la forma en que se inicializan los tensores A y B Inicializar con una distribución gaussiana cuesta algo de FLOP. Cambiando la definición de A y B por

  A = tf.Variable(initial_value=tf.zeros([25, 16])) B = tf.Variable(initial_value=tf.zeros([16, 9])) 

Da la salida esperada de 7200 .

Por lo general, las variables de una red se inicializan con distribuciones gaussianas entre otros esquemas. La mayoría de las veces, no estamos interesados ​​en la inicialización de FLOP, ya que se realizan una vez durante la inicialización y no ocurren durante la capacitación ni la inferencia. Entonces, ¿cómo se puede obtener el número exacto de FLOP sin tener en cuenta la inicialización de FLOP ?

Congela la gráfica con un pb . El cálculo del FLOP a partir de un archivo pb fue, en realidad, el caso de uso del OP.

El siguiente fragmento ilustra esto:

 import tensorflow as tf from tensorflow.python.framework import graph_util def load_pb(pb): with tf.gfile.GFile(pb, "rb") as f: graph_def = tf.GraphDef() graph_def.ParseFromString(f.read()) with tf.Graph().as_default() as graph: tf.import_graph_def(graph_def, name='') return graph # ***** (1) Create Graph ***** g = tf.Graph() sess = tf.Session(graph=g) with g.as_default(): A = tf.Variable(initial_value=tf.random_normal([25, 16])) B = tf.Variable(initial_value=tf.random_normal([16, 9])) C = tf.matmul(A, B, name='output') sess.run(tf.global_variables_initializer()) flops = tf.profiler.profile(g, options = tf.profiler.ProfileOptionBuilder.float_operation()) print('FLOP before freezing', flops.total_float_ops) # ***************************** # ***** (2) freeze graph ***** output_graph_def = graph_util.convert_variables_to_constants(sess, g.as_graph_def(), ['output']) with tf.gfile.GFile('graph.pb', "wb") as f: f.write(output_graph_def.SerializeToString()) # ***************************** # ***** (3) Load frozen graph ***** g2 = load_pb('./graph.pb') with g2.as_default(): flops = tf.profiler.profile(g2, options = tf.profiler.ProfileOptionBuilder.float_operation()) print('FLOP after freezing', flops.total_float_ops) 

salidas

 FLOP before freezing 8288 FLOP after freezing 7200 

[a] Por lo general, el FLOP de una multiplicación de matrices es mq (2p -1) para el producto AB donde A[m, p] y B[p, q] pero TensorFlow devuelve 2mpq por alguna razón. Se ha abierto un problema para entender por qué.