web-dev-qa-db-fra.com

Tensorflow: restaurer un graphique et un modèle, puis exécuter une évaluation sur une seule image

Je pense qu'il serait extrêmement utile pour la communauté Tensorflow de disposer d'une solution bien documentée à la tâche cruciale consistant à tester une nouvelle image par rapport au modèle créé par = convnet dans le didacticiel CIFAR-1 .

Je me trompe peut-être, mais cette étape critique qui rend le modèle formé utilisable en pratique semble faire défaut. Ce tutoriel contient un "lien manquant": un script qui chargerait directement une seule image (sous forme de tableau ou binaire), le comparerait au modèle formé et renverrait une classification.

Les réponses précédentes donnent des solutions partielles qui expliquent l'approche globale, mais aucune d'entre elles n'a été mise en œuvre avec succès. D'autres morceaux peuvent être trouvés ici et là, mais n'ont malheureusement pas abouti à une solution efficace. Veuillez considérer les recherches que j'ai effectuées avant de marquer ceci en double ou déjà répondu.

Tensorflow: comment sauvegarder/restaurer un modèle?

Restauration du modèle TensorFlow

Impossible de restaurer les modèles dans tensorflow v0.8

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

La réponse la plus populaire est la première, dans laquelle @RyanSepassi et @YaroslavBulatov décrivent le problème et une approche: il faut "construire manuellement un graphe avec des noms de nœud identiques et utiliser Saver pour y charger les poids". Bien que les deux réponses soient utiles, on ne voit pas comment on pourrait intégrer cela au projet CIFAR-10.

Une solution entièrement fonctionnelle serait hautement souhaitable pour pouvoir la porter à d'autres problèmes de classification d'images uniques. Il existe plusieurs questions sur SO à cet égard, qui le demandent, mais toujours pas de réponse complète (par exemple Charger un point de contrôle et évaluer une seule image avec DNN tensorflow ).

J'espère que nous pourrons converger vers un script de travail que tout le monde pourrait utiliser.

Le script ci-dessous n'est pas encore opérationnel et je serais ravi de vous entendre dire que cette solution peut être améliorée pour fournir une solution de classification pour une seule image à l'aide du modèle de didacticiel CIFAR-10 TF.

Supposons que toutes les variables, noms de fichiers, etc. ne sont pas modifiés par rapport au tutoriel d'origine.

Nouveau fichier: 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 __== '__main__':
    tf.app.run()
37
pepe

Il existe deux méthodes pour insérer une nouvelle image dans le modèle cifar1 . La première méthode est une approche plus propre mais nécessite une modification dans le fichier principal, ce qui nécessitera donc une nouvelle formation. La deuxième méthode est applicable lorsqu'un utilisateur ne souhaite pas modifier les fichiers de modèle mais souhaite utiliser les fichiers de point de contrôle/méta-graphique existants.

Le code de la première approche est le suivant:

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) 

Le script nécessite qu'un utilisateur crée deux espaces réservés et une instruction d'exécution conditionnelle pour que cela fonctionne.

Les espaces réservés et l'instruction d'exécution conditionnelle sont ajoutés dans cifar10_train.py comme indiqué ci-dessous:

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)

Les entrées du modèle cifar10 sont connectées à un objet coureur de file d'attente, qui est une file d'attente à plusieurs étages pouvant extraire en parallèle des données à partir de fichiers. Voir une belle animation du coureur de la file d'attente ici

Bien que les coureurs de file d'attente soient efficaces pour pré-extraire de grands ensembles de données pour la formation, ils constituent une surcharge pour l'inférence/le test lorsqu'un seul fichier doit être classifié, mais ils sont aussi un peu plus impliqués dans la modification/la maintenance. Pour cette raison, j'ai ajouté un paramètre de substitution "is_training", défini sur False lors de la formation, comme indiqué ci-dessous:

 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})

Un autre paramètre fictif "imgs" contient un tenseur de forme (1,32,32,3) pour l'image qui sera alimentée lors de l'inférence - la première dimension est la taille du lot, qui dans ce cas. J'ai modifié le modèle cifar pour accepter les images 32x32 au lieu de 24x24 car les images cifar10 d'origine sont au format 32x32.

Enfin, l'instruction conditionnelle alimente la sortie du graphique avec l'espace réservé ou le coureur de la file d'attente. L'espace réservé "is_training" est défini sur False lors de l'inférence et "img" est alimenté par un tableau numpy - le tableau numpy est remodelé de 3 à 4 dimensions vectorielles afin de se conformer à la fonction tenseur à inférence d'entrée dans le modèle.

C'est tout ce qu'il y a à faire. Tout modèle peut être déduit avec une seule donnée de test définie par l'utilisateur, comme indiqué dans le script ci-dessus. Pour l’essentiel, lisez le graphique, alimentez les nœuds du graphique en données et exécutez-le pour obtenir le résultat final.

Maintenant la deuxième méthode. L’autre approche consiste à pirater cifar10.py et cifar10_eval.py pour remplacer la taille du lot par une unité et remplacer les données provenant du gestionnaire de file d’attente par celles lues dans un fichier.

Définissez la taille du lot sur 1:

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

Inférence d'appel avec lecture d'un fichier image.

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)

Puis passez les logits à eval_once et modifiez-les une fois pour évaluer les 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))

Il n'y a pas de script séparé pour exécuter cette méthode d'inférence, il suffit d'exécuter cifar10_eval.py qui lira maintenant un fichier à partir de l'emplacement défini par l'utilisateur avec une taille de lot égale à un.

10
bigdata2

Voici comment j'ai couru une seule image à la fois. Je dois admettre que cela semble un peu compliqué de réutiliser l’objectif.

Ceci est une fonction d'assistance

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

Voici la partie principale du code qui exécute une seule image à la fois dans la boucle 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)

Voici une implémentation alternative à celle ci-dessus en utilisant des espaces réservés, c'est un peu plus propre à mon avis. mais je vais laisser l'exemple ci-dessus pour des raisons historiques.

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)
6
Steven

ça marche avec ça

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)

sortie

[[  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
Noah Smith

Je crains que je n'ai pas de code de travail pour vous, mais voici comment nous abordons souvent ce problème en production:

  • Enregistrez le GraphDef sur le disque, en utilisant quelque chose comme write_graph .

  • Utilisez freeze_graph pour charger le GraphDef et les points de contrôle, puis enregistrez un GraphDef avec les variables converties en constantes.

  • Chargez le GraphDef dans quelque chose comme label_image ou classify_image .

Pour votre exemple, cela est excessif, mais je suggérerais au moins de sérialiser le graphique dans l'exemple d'origine en tant que GraphDef, puis de le charger dans votre script (pour éviter de dupliquer le code qui génère le graphique). Avec le même graphique créé, vous devriez pouvoir le renseigner à partir d'un SaverDef. Le script freeze_graph peut vous aider, par exemple.

2
Pete Warden