TensorFlow Inference

He estado investigando esto por un tiempo. He encontrado un montón de artículos; pero ninguno realmente muestra la inferencia tensor de flujo como una inferencia simple. Siempre es “usar el motor de servicio” o usar un gráfico precodificado / definido.

Aquí está el problema: tengo un dispositivo que ocasionalmente busca modelos actualizados. Luego necesita cargar ese modelo y ejecutar predicciones de entrada a través del modelo.

En keras esto era simple: construir un modelo; entrenar el modelo y la llamada model.predict (). En scikit-aprender lo mismo.

Soy capaz de agarrar un nuevo modelo y cargarlo; Puedo imprimir todos los pesos; ¿Pero cómo en el mundo hago inferencia contra eso?

Código para cargar el modelo y los pesos de impresión:

with tf.Session() as sess: new_saver = tf.train.import_meta_graph(MODEL_PATH + '.meta', clear_devices=True) new_saver.restre(sess, MODEL_PATH) for var in tf.trainable_variables(): print(sess.run(var)) 

Imprimí todas mis colecciones y tengo: [‘queue_runners’, ‘variables’, ‘loss’, ‘resúmenes’, ‘train_op’, ‘cond_context’, ‘trainable_variables’]

Intenté usar sess.run (train_op); sin embargo, eso acaba de empezar a arrancar una sesión de entrenamiento completa; que no es lo que quiero hacer. Solo quiero hacer inferencia contra un conjunto diferente de entradas que proporciono que no son Registros TF.

Solo un poco más de detalle:

El dispositivo puede usar C ++ o Python; siempre y cuando pueda producir un .exe. Puedo configurar un dictado de alimentación si quiero alimentar el sistema. Entrené con TFRecords; pero en producción no voy a usar TFRecords; Es un sistema de tiempo real / casi real.

Gracias por cualquier entrada. Estoy publicando código de muestra en este repository: https://github.com/drcrook1/CIFAR10/TensorFlow que hace toda la inferencia de capacitación y muestra.

Cualquier consejo es muy apreciado!

———— EDITS —————– Reconstruí el modelo para que sea como sigue:

 def inference(images): ''' Portion of the compute graph that takes an input and converts it into a Y output ''' with tf.variable_scope('Conv1') as scope: C_1_1 = ld.cnn_layer(images, (5, 5, 3, 32), (1, 1, 1, 1), scope, name_postfix='1') C_1_2 = ld.cnn_layer(C_1_1, (5, 5, 32, 32), (1, 1, 1, 1), scope, name_postfix='2') P_1 = ld.pool_layer(C_1_2, (1, 2, 2, 1), (1, 2, 2, 1), scope) with tf.variable_scope('Dense1') as scope: P_1 = tf.reshape(C_1_2, (CONSTANTS.BATCH_SIZE, -1)) dim = P_1.get_shape()[1].value D_1 = ld.mlp_layer(P_1, dim, NUM_DENSE_NEURONS, scope, act_func=tf.nn.relu) with tf.variable_scope('Dense2') as scope: D_2 = ld.mlp_layer(D_1, NUM_DENSE_NEURONS, CONSTANTS.NUM_CLASSES, scope) H = tf.nn.softmax(D_2, name='prediction') return H 

Observe que agrego el nombre ‘predicción’ a la operación TF para poder recuperarlo más tarde.

Cuando entrené, utilicé el canal de entrada para tfrecords y colas de entrada.

 GRAPH = tf.Graph() with GRAPH.as_default(): examples, labels = Inputs.read_inputs(CONSTANTS.RecordPaths, batch_size=CONSTANTS.BATCH_SIZE, img_shape=CONSTANTS.IMAGE_SHAPE, num_threads=CONSTANTS.INPUT_PIPELINE_THREADS) examples = tf.reshape(examples, [CONSTANTS.BATCH_SIZE, CONSTANTS.IMAGE_SHAPE[0], CONSTANTS.IMAGE_SHAPE[1], CONSTANTS.IMAGE_SHAPE[2]]) logits = Vgg3CIFAR10.inference(examples) loss = Vgg3CIFAR10.loss(logits, labels) OPTIMIZER = tf.train.AdamOptimizer(CONSTANTS.LEARNING_RATE) 

Estoy intentando usar feed_dict en la operación cargada en el gráfico; Sin embargo ahora es simplemente colgando …

 MODEL_PATH = 'models/' + CONSTANTS.MODEL_NAME + '.model' images = tf.placeholder(tf.float32, shape=(1, 32, 32, 3)) def run_inference(): '''Runs inference against a loaded model''' with tf.Session() as sess: #sess.run(tf.global_variables_initializer()) new_saver = tf.train.import_meta_graph(MODEL_PATH + '.meta', clear_devices=True) new_saver.restre(sess, MODEL_PATH) pred = tf.get_default_graph().get_operation_by_name('prediction') rand = np.random.rand(1, 32, 32, 3) print(rand) print(pred) print(sess.run(pred, feed_dict={images: rand})) print('done') run_inference() 

Creo que esto no funciona porque la red original fue entrenada usando TFRecords. En el conjunto de datos CIFAR de muestra, los datos son pequeños; nuestro verdadero conjunto de datos es enorme y, según tengo entendido, TFRecords es la mejor práctica predeterminada para entrenar una red. El feed_dict tiene gran sentido perfecto desde una perspectiva de producción; podemos hilar algunos hilos y rellenar esa cosa desde nuestros sistemas de entrada.

Entonces, supongo que tengo una red que está capacitada, puedo obtener la operación de predicción; pero ¿cómo le digo que deje de usar las colas de entrada y comience a usar el feed_dict? Recuerde que desde la perspectiva de la producción no tengo acceso a lo que hicieron los científicos para lograrlo. Ellos hacen lo suyo; y lo pegamos en la producción utilizando cualquier estándar acordado.

——- ENTRADA OPS ——–

Operación ‘input / input_producer / Const’ type = Const, tf.Operation ‘input / input_producer / Size’ type = Const, tf.Operation ‘input / input_producer / Greater / y’ type = Const, tf.Operation ‘input / input_producer / Greater ‘type = Greater, tf.Operation’ input / input_producer / Assert / Const ‘type = Const, tf.Operation’ input / input_producer / Assert / Assert / data_0 ‘type = Const, tf.Operation’ input / input_producer / Assert / Assert ‘type = Assert, tf.Operation’ input / input_producer / Identity ‘type = Identity, tf.Operation’ input / input_producer / RandomShuffle ‘type = RandomShuffle, tf.Operation’ input / input_producer ‘type = FIFOQueueV2, tf. Operación ‘input / input_producer / input_producer_EnqueueMany’ type = QueueEnqueueManyV2, tf.Operation ‘input / input_producer / input_producer_Close’ type QueotesClot v_asperas_asperas_asperas_asperas_asperas_asperas_asperas_asperas_asperas_asperación_parte_asperación_asperación_asperación_asperación_asperación_asperación_asperación_asperación_asperación_asperación_asperación_asperación_asperación_asperación_asperación_asperación_asperación_asperación_asperación_asperación_asperas_asperación_asperación_asperación_asperación_esperas_asperación_asperación_asperación_esperas_asperación_esperas_asperación / entrada_productor / entrada_productor / input_producer_Close type = QueueSizeV2, tf.Operation ‘input / input_producer / Cast’ type = Cast, tf.Operation ‘input / input_produc er / mul / y ‘type = Const, tf.Operation’ input / input_producer / mul ‘type = Mul, tf.Operation’ input / input_producer / fraction_of_32_full / tags ‘type = Const, tf.Operation’ input / input_producer / fraction_of_32_full ‘ type = ScalarSummary, tf.Operation ‘input / TFRecordReaderV2’ type = TFRecordReaderV2, tf.Operation ‘input / ReaderReadV2’ type = ReaderReadV2,

—— FIN DE ENTRADA OPS —–

—- ACTUALIZACIÓN 3 —-

Creo que lo que debo hacer es eliminar la sección de entrada del gráfico entrenado con TF Records y volver a conectar la entrada a la primera capa a una nueva entrada. Es un poco como realizar una cirugía; pero esta es la única manera que puedo encontrar para hacer inferencias si entrené usando TFRecords tan loco como suena …

Gráfica completa:

introduzca la descripción de la imagen aquí

Sección para matar:

introduzca la descripción de la imagen aquí

Así que creo que la pregunta es: ¿cómo se mata la sección de entrada del gráfico y se reemplaza con un feed_dict?

Un seguimiento de esto sería: ¿es realmente la manera correcta de hacerlo? Esto parece una locura.

—- FINALIZA LA ACTUALIZACIÓN 3 —-

— enlace a archivos de punto de control —

https://drcdata.blob.core.windows.net/checkpoints/CIFAR_10_VGG3_50neuron_1pool_1e-3lr_adam.model.zip?st=2017-05-01A21%3A56%3A00Z&se=2020-05-02T21%3A56%3A00ZpcPp.jpg=3A00Z&se=2020-05-02T21%3A56%3A00ZPPPPP.jpg%3A00ZYSE=2020-05-02T21%3A56%3A00ZPPPP.jpg 12-11 & sr = b & sig = oBCGxlOusB4NOEKnSnD% 2FTlRYa5NKNIwAX1IyuZXAr9o% 3D

–enlace enlace a archivos de punto de control —

—– ACTUALIZACIÓN 4 —–

Me rendí y solo probé la forma “normal” de realizar la inferencia, asumiendo que los científicos simplemente podrían decapitar sus modelos y nosotros podríamos agarrar el modelo. desempaquetarlo y luego ejecute inferencia en él. Así que, para probar, lo intenté de la forma habitual, asumiendo que ya lo desempacamos … Tampoco funciona, vale la pena …

 import tensorflow as tf import CONSTANTS import Vgg3CIFAR10 import numpy as np from scipy import misc import time MODEL_PATH = 'models/' + CONSTANTS.MODEL_NAME + '.model' imgs_bsdir = 'C:/data/cifar_10/train/' images = tf.placeholder(tf.float32, shape=(1, 32, 32, 3)) logits = Vgg3CIFAR10.inference(images) def run_inference(): '''Runs inference against a loaded model''' with tf.Session() as sess: sess.run(tf.global_variables_initializer()) new_saver = tf.train.import_meta_graph(MODEL_PATH + '.meta')#, import_scope='1', input_map={'input:0': images}) new_saver.restre(sess, MODEL_PATH) pred = tf.get_default_graph().get_operation_by_name('prediction') enq = sess.graph.get_operation_by_name(enqueue_op) #tf.train.start_queue_runners(sess) print(rand) print(pred) print(enq) for i in range(1, 25): img = misc.imread(imgs_bsdir + str(i) + '.png').astype(np.float32) / 255.0 img = img.reshape(1, 32, 32, 3) print(sess.run(logits, feed_dict={images : img})) time.sleep(3) print('done') run_inference() 

Tensorflow termina construyendo un nuevo gráfico con la función de inferencia del modelo cargado; luego agrega todas las otras cosas de la otra gráfica hasta el final. Entonces, cuando relleno un feed_dict esperando recuperar inferencias; Acabo de recibir un montón de basura aleatoria como si fuera el primer paso a través de la red …

Otra vez; esto parece una locura; ¿Realmente necesito escribir mi propio marco para serializar y deserializar redes aleatorias? Esto tiene que haber sido hecho antes …

—– ACTUALIZACIÓN 4 —–

Otra vez; ¡Gracias!

Muy bien, esto tomó demasiado tiempo para entenderlo; Así que aquí está la respuesta para el rest del mundo.

Recordatorio rápido : Necesitaba persistir en un modelo que se pueda cargar e inferir dinámicamente sin saber en cuanto a los puntos clave o el interior de cómo funciona.

Paso 1 : crear un modelo como clase y, idealmente, utilizar una definición de interfaz

 class Vgg3Model: NUM_DENSE_NEURONS = 50 DENSE_RESHAPE = 32 * (CONSTANTS.IMAGE_SHAPE[0] // 2) * (CONSTANTS.IMAGE_SHAPE[1] // 2) def inference(self, images): ''' Portion of the compute graph that takes an input and converts it into a Y output ''' with tf.variable_scope('Conv1') as scope: C_1_1 = ld.cnn_layer(images, (5, 5, 3, 32), (1, 1, 1, 1), scope, name_postfix='1') C_1_2 = ld.cnn_layer(C_1_1, (5, 5, 32, 32), (1, 1, 1, 1), scope, name_postfix='2') P_1 = ld.pool_layer(C_1_2, (1, 2, 2, 1), (1, 2, 2, 1), scope) with tf.variable_scope('Dense1') as scope: P_1 = tf.reshape(P_1, (-1, self.DENSE_RESHAPE)) dim = P_1.get_shape()[1].value D_1 = ld.mlp_layer(P_1, dim, self.NUM_DENSE_NEURONS, scope, act_func=tf.nn.relu) with tf.variable_scope('Dense2') as scope: D_2 = ld.mlp_layer(D_1, self.NUM_DENSE_NEURONS, CONSTANTS.NUM_CLASSES, scope) H = tf.nn.softmax(D_2, name='prediction') return H def loss(self, logits, labels): ''' Adds Loss to all variables ''' cross_entr = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=logits, labels=labels) cross_entr = tf.reduce_mean(cross_entr) tf.summary.scalar('cost', cross_entr) tf.add_to_collection('losses', cross_entr) return tf.add_n(tf.get_collection('losses'), name='total_loss') 

Paso 2 : Entrena tu red con las entradas que quieras; En mi caso utilicé Queue Runners y TF Records. Tenga en cuenta que este paso lo realiza un equipo diferente que itera, construye, diseña y optimiza los modelos. Esto también puede cambiar con el tiempo. La salida que producen debe poder extraerse de una ubicación remota para que podamos cargar dinámicamente los modelos actualizados en los dispositivos (volver a imprimir el hardware es un problema, especialmente si está distribuido geográficamente). En este caso; el equipo elimina los 3 archivos asociados con un protector de gráficos; Pero también un pickle del modelo utilizado para esa sesión de entrenamiento.

 model = vgg3.Vgg3Model() def create_sess_ops(): ''' Creates and returns operations needed for running a tensorflow training session ''' GRAPH = tf.Graph() with GRAPH.as_default(): examples, labels = Inputs.read_inputs(CONSTANTS.RecordPaths, batch_size=CONSTANTS.BATCH_SIZE, img_shape=CONSTANTS.IMAGE_SHAPE, num_threads=CONSTANTS.INPUT_PIPELINE_THREADS) examples = tf.reshape(examples, [-1, CONSTANTS.IMAGE_SHAPE[0], CONSTANTS.IMAGE_SHAPE[1], CONSTANTS.IMAGE_SHAPE[2]], name='infer/input') logits = model.inference(examples) loss = model.loss(logits, labels) OPTIMIZER = tf.train.AdamOptimizer(CONSTANTS.LEARNING_RATE) gradients = OPTIMIZER.compute_gradients(loss) apply_gradient_op = OPTIMIZER.apply_gradients(gradients) gradients_summary(gradients) summaries_op = tf.summary.merge_all() return [apply_gradient_op, summaries_op, loss, logits], GRAPH def main(): ''' Run and Train CIFAR 10 ''' print('starting...') ops, GRAPH = create_sess_ops() total_duration = 0.0 with tf.Session(graph=GRAPH) as SESSION: COORDINATOR = tf.train.Coordinator() THREADS = tf.train.start_queue_runners(SESSION, COORDINATOR) SESSION.run(tf.global_variables_initializer()) SUMMARY_WRITER = tf.summary.FileWriter('Tensorboard/' + CONSTANTS.MODEL_NAME, graph=GRAPH) GRAPH_SAVER = tf.train.Saver() for EPOCH in range(CONSTANTS.EPOCHS): duration = 0 error = 0.0 start_time = time.time() for batch in range(CONSTANTS.MINI_BATCHES): _, summaries, cost_val, prediction = SESSION.run(ops) error += cost_val duration += time.time() - start_time total_duration += duration SUMMARY_WRITER.add_summary(summaries, EPOCH) print('Epoch %d: loss = %.2f (%.3f sec)' % (EPOCH, error, duration)) if EPOCH == CONSTANTS.EPOCHS - 1 or error < 0.005: print( 'Done training for %d epochs. (%.3f sec)' % (EPOCH, total_duration) ) break GRAPH_SAVER.save(SESSION, 'models/' + CONSTANTS.MODEL_NAME + '.model') with open('models/' + CONSTANTS.MODEL_NAME + '.pkl', 'wb') as output: pickle.dump(model, output) COORDINATOR.request_stop() COORDINATOR.join(THREADS) 

Paso 3 : Ejecutar alguna Inferencia. Cargue su modelo en escabeche; cree un nuevo gráfico canalizando el nuevo marcador de posición a los logits; y luego llamar a la sesión de restauración. NO RESTAURAR TODO EL GRÁFICO; SOLO LAS VARIABLES.

 MODEL_PATH = 'models/' + CONSTANTS.MODEL_NAME + '.model' imgs_bsdir = 'C:/data/cifar_10/train/' images = tf.placeholder(tf.float32, shape=(1, 32, 32, 3)) with open('models/vgg3.pkl', 'rb') as model_in: model = pickle.load(model_in) logits = model.inference(images) def run_inference(): '''Runs inference against a loaded model''' with tf.Session() as sess: sess.run(tf.global_variables_initializer()) new_saver = tf.train.Saver() new_saver.restre(sess, MODEL_PATH) print("Starting...") for i in range(20, 30): print(str(i) + '.png') img = misc.imread(imgs_bsdir + str(i) + '.png').astype(np.float32) / 255.0 img = img.reshape(1, 32, 32, 3) pred = sess.run(logits, feed_dict={images : img}) max_node = np.argmax(pred) print('predicted label: ' + str(max_node)) print('done') run_inference() 

Definitivamente hay maneras de mejorar esto usando interfaces y tal vez empaquetando todo mejor; pero esto está funcionando y establece el escenario de cómo seguiremos avanzando.

NOTA FINAL Cuando finalmente pusimos esto en producción, tuvimos que enviar el estúpido archivo mymodel_model.py con todo para construir el gráfico. Así que ahora aplicamos una convención de nomenclatura para todos los modelos y también hay un estándar de encoding para las ejecuciones del modelo de producción, por lo que podemos hacerlo correctamente.

¡Buena suerte!

Si bien no es tan simple como es model.predict (), sigue siendo realmente trivial.

En su modelo, debe tener un tensor que calcule la salida final que le interesa, nombremos esa output tensor. Actualmente puede tener una función de pérdida. Si es así, cree otro tensor (variable en el modelo) que realmente calcule la salida que desea.

Por ejemplo, si su función de pérdida es:

 tf.nn.sigmoid_cross_entropy_with_logits(last_layer_activation, labels) 

Y espera que sus salidas estén en el rango [0,1] por clase, cree otra variable:

 output = tf.sigmoid(last_layer_activation) 

Ahora, cuando llama a sess.run(...) solo solicite el tensor de output . No solicite el OP de optimización que normalmente lo entrenaría. Cuando solicite esta variable, tensorflow realizará el trabajo mínimo necesario para generar el valor (por ejemplo, no se molestará con backprop, funciones de pérdida, y todo eso porque una simple pasada de avance de alimentación es todo lo que es necesario para calcular la output .

Entonces, si está creando un servicio para devolver inferencias del modelo, querrá mantener el modelo cargado en la memoria / gpu y repetir:

 sess.run(output, feed_dict={X: input_data}) 

No necesitará alimentarlo con las tags porque tensorflow no se molestará en calcular operaciones que no son necesarias para producir la salida que está solicitando. No tienes que cambiar tu modelo ni nada.

Si bien este enfoque podría no ser tan obvio como el model.predict(...) Yo diría que es mucho más flexible. Si comienzas a jugar con modelos más complejos, probablemente aprenderás a amar este enfoque. model.predict() es como “pensar dentro de la caja”.