Pregunta Tensorflow: restaurar un gráfico y modelo y luego ejecutar la evaluación en una sola imagen


Creo que sería inmensamente útil para la comunidad de Tensorflow si hubiera una solución bien documentada para la tarea crucial de probar una sola imagen nueva contra el modelo creado por el convnet en el tutorial CIFAR-10.

Puedo estar equivocado, pero este paso crítico que hace que el modelo entrenado sea utilizable en la práctica parece faltar. Hay un "eslabón perdido" en ese tutorial, un script que carga directamente una sola imagen (como matriz o binario), la compara con el modelo entrenado y devuelve una clasificación.

Las respuestas anteriores brindan soluciones parciales que explican el enfoque general, pero ninguna de las cuales he podido implementar con éxito. Otros bits y piezas se pueden encontrar aquí y allá, pero lamentablemente no se han sumado a una solución de trabajo. Considere amablemente la investigación que he hecho, antes de etiquetar esto como duplicado o ya respondido.

Tensorflow: cómo guardar / restaurar un modelo?

Restauración del modelo TensorFlow

No se pueden restaurar los modelos en tensorflow v0.8

https://gist.github.com/nikitakit/6ef3b72be67b86cb7868

La respuesta más popular es la primera, en la que @RyanSepassi y @YaroslavBulatov describen el problema y un enfoque: uno tiene que "construir manualmente un gráfico con nombres de nodo idénticos y utilizar Ahorro para cargar los pesos en él". Aunque ambas respuestas son útiles, no es evidente cómo se podría conectar esto al proyecto CIFAR-10.

Una solución completamente funcional sería altamente deseable para que podamos trasladarla a otros problemas de clasificación de imágenes individuales. Hay varias preguntas sobre SO a este respecto que piden esto, pero todavía no hay una respuesta completa (por ejemplo, Cargar punto de control y evaluar una sola imagen con DNN tensorflow)

Espero que podamos converger en un guión de trabajo que todos puedan usar.

La secuencia de comandos siguiente aún no es funcional, y me alegraría saber de usted cómo se puede mejorar para proporcionar una solución para la clasificación de una sola imagen utilizando el modelo capacitado de tutorial CIFAR-10 TF.

Asuma que todas las variables, nombres de archivo, etc. no han sido tocados desde el tutorial original.

Archivo nuevo: cifar10_eval_single.py

import cv2
import tensorflow as tf

FLAGS = tf.app.flags.FLAGS

tf.app.flags.DEFINE_string('eval_dir', './input/eval',
                           """Directory where to write event logs.""")
tf.app.flags.DEFINE_string('checkpoint_dir', './input/train',
                           """Directory where to read model checkpoints.""")

def get_single_img():
    file_path = './input/data/single/test_image.tif'
    pixels = cv2.imread(file_path, 0)
    return pixels

def eval_single_img():

    # below code adapted from @RyanSepassi, however not functional
    # among other errors, saver throws an error that there are no
    # variables to save
    with tf.Graph().as_default():

        # Get image.
        image = get_single_img()

        # Build a Graph.
        # TODO

        # Create dummy variables.
        x = tf.placeholder(tf.float32)
        w = tf.Variable(tf.zeros([1, 1], dtype=tf.float32))
        b = tf.Variable(tf.ones([1, 1], dtype=tf.float32))
        y_hat = tf.add(b, tf.matmul(x, w))

        saver = tf.train.Saver()

        with tf.Session() as sess:
            sess.run(tf.initialize_all_variables())
            ckpt = tf.train.get_checkpoint_state(FLAGS.checkpoint_dir)

            if ckpt and ckpt.model_checkpoint_path:
                saver.restore(sess, ckpt.model_checkpoint_path)
                print('Checkpoint found')
            else:
                print('No checkpoint found')

            # Run the model to get predictions
            predictions = sess.run(y_hat, feed_dict={x: image})
            print(predictions)

def main(argv=None):
    if tf.gfile.Exists(FLAGS.eval_dir):
        tf.gfile.DeleteRecursively(FLAGS.eval_dir)
    tf.gfile.MakeDirs(FLAGS.eval_dir)
    eval_single_img()

if __name__ == '__main__':
    tf.app.run()

32
2018-06-06 03:14


origen


Respuestas:


Hay dos métodos para alimentar una sola imagen nueva al cifar10 modelo. El primer método es un enfoque más limpio, pero requiere modificaciones en el archivo principal, por lo tanto, requerirá un nuevo entrenamiento. El segundo método es aplicable cuando un usuario no desea modificar los archivos del modelo y, en su lugar, desea usar los archivos de punto de control / metagráficos existentes.

El código para el primer enfoque es el siguiente:

import tensorflow as tf
import numpy as np
import cv2

sess = tf.Session('', tf.Graph())
with sess.graph.as_default():
    # Read meta graph and checkpoint to restore tf session
    saver = tf.train.import_meta_graph("/tmp/cifar10_train/model.ckpt-200.meta")
    saver.restore(sess, "/tmp/cifar10_train/model.ckpt-200")

    # Read a single image from a file.
    img = cv2.imread('tmp.png')
    img = np.expand_dims(img, axis=0)

    # Start the queue runners. If they are not started the program will hang
    # see e.g. https://www.tensorflow.org/programmers_guide/reading_data
    coord = tf.train.Coordinator()
    threads = []
    for qr in sess.graph.get_collection(tf.GraphKeys.QUEUE_RUNNERS):
        threads.extend(qr.create_threads(sess, coord=coord, daemon=True,
                                         start=True))

    # In the graph created above, feed "is_training" and "imgs" placeholders.
    # Feeding them will disconnect the path from queue runners to the graph 
    # and enable a path from the placeholder instead. The "img" placeholder will be 
    # fed with the image that was read above.
    logits = sess.run('softmax_linear/softmax_linear:0', 
                     feed_dict={'is_training:0': False, 'imgs:0': img})

    #Print classifiction results.
    print(logits) 

El script requiere que el usuario cree dos marcadores de posición y una instrucción de ejecución condicional para que funcione.

Los marcadores de posición y la instrucción de ejecución condicional se agregan en cifar10_train.py como se muestra a continuación:

def train():   
"""Train CIFAR-10 for a number of steps."""   
    with tf.Graph().as_default():
        global_step = tf.contrib.framework.get_or_create_global_step()

    with tf.device('/cpu:0'):
        images, labels = cifar10.distorted_inputs()

    is_training = tf.placeholder(dtype=bool,shape=(),name='is_training')
    imgs = tf.placeholder(tf.float32, (1, 32, 32, 3), name='imgs')
    images = tf.cond(is_training, lambda:images, lambda:imgs)
    logits = cifar10.inference(images)

Las entradas en el modelo cifar10 están conectadas al objeto de cola de cola, que es una cola de varias etapas que puede captar previamente los datos de los archivos en paralelo. Vea una buena animación de corredor de cola aquí

Mientras que los corredores de colas son eficientes en la captación previa de grandes conjuntos de datos para el entrenamiento, son una exageración para la inferencia / prueba donde solo se necesita un solo archivo para clasificar, también están un poco más involucrados para modificar / mantener. Por esa razón, he agregado un marcador de posición "is_training", que se establece en False durante el entrenamiento, como se muestra a continuación:

 import numpy as np
 tmp_img = np.ndarray(shape=(1,32,32,3), dtype=float)
 with tf.train.MonitoredTrainingSession(
     checkpoint_dir=FLAGS.train_dir,
     hooks=[tf.train.StopAtStepHook(last_step=FLAGS.max_steps),
            tf.train.NanTensorHook(loss),
            _LoggerHook()],
     config=tf.ConfigProto(
         log_device_placement=FLAGS.log_device_placement)) as mon_sess:
   while not mon_sess.should_stop():
     mon_sess.run(train_op, feed_dict={is_training: True, imgs: tmp_img})

Otro marcador de posición "imgs" contiene un tensor de forma (1,32,32,3) para la imagen que se alimentará durante la inferencia: la primera dimensión es el tamaño del lote, que es uno en este caso. He modificado el modelo cifar para que acepte 32x32 imágenes en lugar de 24x24, ya que las imágenes cifar10 originales son 32x32.

Finalmente, el enunciado condicional alimenta el marcador de posición o el resultado del corredor de cola al gráfico. El marcador de posición "is_training" se establece en False durante la inferencia y el marcador de posición "img" se alimenta con una matriz numpy: la matriz numpy se redimensiona de 3 a 4 vectores dimensionales para ajustarse al tensor de entrada a la función de inferencia en el modelo.

Eso es todo lo que hay que hacer. Se puede inferir cualquier modelo con datos de prueba únicos / definidos por el usuario, como se muestra en el script anterior. Básicamente lea el gráfico, alimente los datos a los nodos del gráfico y ejecute el gráfico para obtener el resultado final.

Ahora el segundo método. El otro enfoque es hackear cifar10.py y cifar10_eval.py para cambiar el tamaño del lote a uno y reemplazar los datos provenientes del corredor de la cola con la lectura de un archivo.

Establezca el tamaño del lote en 1:

tf.app.flags.DEFINE_integer('batch_size', 1,
                             """Number of images to process in a batch.""")

Inferencia de llamada con un archivo de imagen leído.

def evaluate():   with tf.Graph().as_default() as g:
    # Get images and labels for CIFAR-10.
    eval_data = FLAGS.eval_data == 'test'
    images, labels = cifar10.inputs(eval_data=eval_data)
    import cv2
    img = cv2.imread('tmp.png')
    img = np.expand_dims(img, axis=0)
    img = tf.cast(img, tf.float32)

    logits = cifar10.inference(img)

A continuación, pase los logits a eval_once y modifique eval una vez para evaluar los logits:

def eval_once(saver, summary_writer, top_k_op, logits, summary_op): 
    ...
    while step < num_iter and not coord.should_stop():
        predictions = sess.run([top_k_op])
        print(sess.run(logits))

No hay una secuencia de comandos separada para ejecutar este método de inferencia, solo ejecute cifar10_eval.py, que ahora leerá un archivo de la ubicación definida por el usuario con un tamaño de lote de uno.


7
2017-07-10 05:04



Así es como ejecuté una sola imagen a la vez. Debo admitir que parece un poco hacky con la reutilización de obtener el alcance.

Esta es una función auxiliar

def restore_vars(saver, sess, chkpt_dir):
    """ Restore saved net, global score and step, and epsilons OR
    create checkpoint directory for later storage. """
    sess.run(tf.initialize_all_variables())

    checkpoint_dir = chkpt_dir

    if not os.path.exists(checkpoint_dir):
        try:
            os.makedirs(checkpoint_dir)
        except OSError:
            pass

    path = tf.train.get_checkpoint_state(checkpoint_dir)
    #print("path1 = ",path)
    #path = tf.train.latest_checkpoint(checkpoint_dir)
    print(checkpoint_dir,"path = ",path)
    if path is None:
        return False
    else:
        saver.restore(sess, path.model_checkpoint_path)
        return True

Aquí está la parte principal del código que ejecuta una sola imagen a la vez dentro del ciclo for.

to_restore = True
with tf.Session() as sess:

    for i in test_img_idx_set:

            # Gets the image
            images = get_image(i)
            images = np.asarray(images,dtype=np.float32)
            images = tf.convert_to_tensor(images/255.0)
            # resize image to whatever you're model takes in
            images = tf.image.resize_images(images,256,256)
            images = tf.reshape(images,(1,256,256,3))
            images = tf.cast(images, tf.float32)

            saver = tf.train.Saver(max_to_keep=5, keep_checkpoint_every_n_hours=1)

            #print("infer")
            with tf.variable_scope(tf.get_variable_scope()) as scope:
                if to_restore:
                    logits = inference(images)
                else:
                    scope.reuse_variables()
                    logits = inference(images)


            if to_restore:
                restored = restore_vars(saver, sess,FLAGS.train_dir)
                print("restored ",restored)
                to_restore = False

            logit_val = sess.run(logits)
            print(logit_val)

Aquí hay una implementación alternativa a los portadores de lugares anteriores, es un poco más limpio en mi opinión. pero dejaré el ejemplo anterior por razones históricas.

imgs_place = tf.placeholder(tf.float32, shape=[my_img_shape_put_here])
images = tf.reshape(imgs_place,(1,256,256,3))

saver = tf.train.Saver(max_to_keep=5, keep_checkpoint_every_n_hours=1)

#print("infer")
logits = inference(images)

restored = restore_vars(saver, sess,FLAGS.train_dir)
print("restored ",restored)

with tf.Session() as sess:
    for i in test_img_idx_set:
        logit_val = sess.run(logits,feed_dict={imgs_place=i})
        print(logit_val)

5
2018-06-09 23:57



lo conseguí trabajando con esto

softmax = gn.inference(image)
saver = tf.train.Saver()
ckpt = tf.train.get_checkpoint_state(FLAGS.checkpoint_dir)
with tf.Session() as sess:
  saver.restore(sess, ckpt.model_checkpoint_path)
  softmaxval = sess.run(softmax)
  print(softmaxval)

salida

[[  6.73550041e-03   4.44930716e-04   9.92570221e-01   1.00681427e-06
    3.05406687e-08   2.38927707e-04   1.89839399e-12   9.36238484e-06
    1.51646684e-09   3.38977535e-09]]

4
2017-12-27 19:33



No tengo código de trabajo para ti, me temo, pero así es como a menudo abordamos este problema en la producción:

  • Guarde el GraphDef en el disco, usando algo como write_graph.

  • Utilizar freeze_graph cargar el GraphDef y los puntos de control, y guardar un GraphDef con las variables convertidas en constantes.

  • Carga el GraphDef en algo así como label_image o classify_image.

Para su ejemplo, esto es excesivo, pero al menos sugiero serializar el gráfico en el ejemplo original como un GraphDef, y luego cargarlo en su script (para que no tenga que duplicar el código que genera el gráfico). Con el mismo gráfico creado, debería poder poblarlo desde un SaverDef, y el script freeze_graph puede ayudar como un ejemplo.


2
2018-06-07 01:38