web-dev-qa-db-fra.com

Inférence TensorFlow

Je fouille là-dessus depuis un moment. J'ai trouvé une tonne d'articles; mais aucun ne montre vraiment que l'inférence tensorflow comme une inférence simple. C'est toujours "utiliser le moteur de service" ou en utilisant un graphique qui est pré-codé/défini.

Voici le problème: j'ai un appareil qui vérifie parfois les modèles mis à jour. Il doit ensuite charger ce modèle et exécuter des prédictions d'entrée via le modèle.

En keras, c'était simple: construire un modèle; former le modèle et l'appel model.predict (). En scikit-learn même chose.

Je peux saisir un nouveau modèle et le charger; Je peux imprimer tous les poids; mais comment diable puis-je en tirer des conclusions?

Code pour charger le modèle et imprimer les poids:

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

J'ai imprimé toutes mes collections et j'ai: ['queue_runners', 'variables', 'pertes', 'résumés', 'train_op', 'cond_context', 'trainable_variables']

J'ai essayé d'utiliser sess.run(train_op); cependant, cela vient de commencer une session de formation complète; ce n'est pas ce que je veux faire. Je veux juste exécuter l'inférence sur un ensemble différent d'entrées que je fournis qui ne sont pas des enregistrements TF.

Juste un peu plus de détails:

L'appareil peut utiliser C++ ou Python; tant que je peux produire un .exe. Je peux configurer un dict d'alimentation si je veux alimenter le système. Je me suis entraîné avec TFRecords; mais en production, je ne vais pas utiliser TFRecords; c'est un système en temps réel/presque réel.

Merci pour toute contribution. Je poste un exemple de code dans ce référentiel: https://github.com/drcrook1/CIFAR10/TensorFlow qui fait toute la formation et déduit l'exemple.

Tous les indices sont grandement appréciés!

------------ EDITS ----------------- J'ai reconstruit le modèle pour qu'il soit comme ci-dessous:

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

remarquez que j'ajoute le nom 'prediction' à l'opération TF pour pouvoir le récupérer plus tard.

Lors de la formation, j'ai utilisé le pipeline d'entrée pour tfrecords et les files d'attente d'entrée.

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)

J'essaie d'utiliser feed_dict Sur l'opération chargée dans le graphique; mais maintenant il est simplement suspendu ...

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.restore(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()

Je crois que cela ne fonctionne pas car le réseau d'origine a été formé à l'aide de TFRecords. Dans l'exemple de jeu de données CIFAR, les données sont petites; notre véritable ensemble de données est énorme et je crois comprendre que TFRecords est la meilleure pratique par défaut pour la formation d'un réseau. Le feed_dict Est parfaitement logique du point de vue de la production; nous pouvons faire tourner certains fils et remplir cette chose à partir de nos systèmes d'entrée.

Donc je suppose que j'ai un réseau qui est formé, je peux obtenir l'opération de prédiction; mais comment lui dire d'arrêter d'utiliser les files d'attente d'entrée et de commencer à utiliser le feed_dict? N'oubliez pas que du point de vue de la production, je n'ai pas accès à tout ce que les scientifiques ont fait pour le faire. Ils font leur truc; et nous le collons en production en utilisant ce que nous avons convenu de la norme.

------- OPS D'ENTRÉE --------

tf.Operation '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. Opération 'input/input_producer/input_producer_EnqueueMany' type = QueueEnqueueManyV2, tf.Operation 'input/input_producer/input_producer_Close' type = QueueCloseV2, tf. 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 DES ENTRÉES OPS -----

---- MISE À JOUR 3 ----

Je crois que ce que je dois faire est de tuer la section d'entrée du graphique formé avec TF Records et de recâbler l'entrée de la première couche à une nouvelle entrée. C'est un peu comme effectuer une chirurgie; mais c'est le seul moyen que je puisse trouver pour faire de l'inférence si je me suis entraîné à utiliser des TFRecords aussi fou que cela puisse paraître ...

Graphique complet:

enter image description here

Section à tuer:

enter image description here

Je pense donc que la question devient: comment tuer la section d'entrée du graphique et la remplacer par un feed_dict?

Une suite à cela serait: est-ce vraiment la bonne façon de le faire? Cela semble fou.

---- FIN DE LA MISE À JOUR 3 ----

--- lien vers les fichiers de points de contrôle ---

https://drcdata.blob.core.windows.net/checkpoints/CIFAR_10_VGG3_50neuron_1pool_1e-3lr_adam.model.zip?st=2017-05-01T21%3A56%3A00Z&se=2020-05-02T21%3A56%3A00Z&sp= rl & sv = 2015-12-11 & sr = b & sig = oBCGxlOusB4NOEKnSnD% 2FTlRYa5NKNIwAX1IyuZXAr9o% 3D

--end un lien vers les fichiers de points de contrôle ---

----- MISE À JOUR 4 -----

J'ai cédé et j'ai juste donné un coup de feu à la façon "normale" de faire l'inférence en supposant que je pouvais demander aux scientifiques de simplement décaper leurs modèles et que nous pourrions saisir le modèle de décapage; décompressez-le puis exécutez l'inférence sur celui-ci. Donc, pour tester, j'ai essayé la méthode normale en supposant que nous l'avons déjà déballé ... Cela ne fonctionne pas non plus.

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.restore(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 finit par construire un nouveau graphique avec la fonction d'inférence à partir du modèle chargé; puis il ajoute toutes les autres choses de l'autre graphique à la fin de celui-ci. Alors, quand je remplis un feed_dict S'attendant à récupérer des inférences; Je reçois juste un tas d'ordures aléatoires comme si c'était le premier passage sur le réseau ...

Encore; cela semble fou; ai-je vraiment besoin d'écrire mon propre framework pour sérialiser et désérialiser des réseaux aléatoires? Cela devait être fait avant ...

----- MISE À JOUR 4 -----

Encore; Merci!

19
David Crook

D'accord, cela a pris trop de temps à comprendre; voici donc la réponse pour le reste du monde.

Rappel rapide: Je devais persister un modèle qui peut être chargé dynamiquement et déduit sans connaissance des sous-épingles ou de l'intérieur de son fonctionnement.

Étape 1: Créez un modèle en tant que classe et utilisez idéalement une définition d'interface

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

Étape 2: Entraînez votre réseau avec les entrées que vous voulez; dans mon cas, j'ai utilisé Queue Runners et TF Records. Notez que cette étape est effectuée par une équipe différente qui itère, construit, conçoit et optimise les modèles. Cela peut également changer avec le temps. La sortie qu'ils produisent doit pouvoir être extraite d'un emplacement distant afin que nous puissions charger dynamiquement les modèles mis à jour sur les appareils (le matériel de reflashing est pénible surtout s'il est géographiquement distribué). Dans ce cas; l'équipe supprime les 3 fichiers associés à un économiseur de graphiques; mais aussi un cornichon du modèle utilisé pour cette session de formation

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)

Étape: Exécutez une inférence. Chargez votre modèle mariné; créer un nouveau graphique en redirigeant le nouvel espace réservé vers les logits; puis appelez restauration de session. NE PAS RESTAURER LE GRAPHIQUE ENTIER; JUSTE LES 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.restore(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()

Il y a certainement des moyens d'améliorer cela en utilisant des interfaces et peut-être mieux emballer tout; mais cela fonctionne et ouvre la voie à la façon dont nous allons progresser.

NOTE FINALE Quand nous avons finalement poussé cela à la production, nous avons fini par devoir expédier le stupide fichier `mymodel_model.py avec tout pour construire le graphique. Nous appliquons donc maintenant une convention de dénomination pour tous les modèles et il existe également une norme de codage pour les exécutions de modèles de production afin que nous puissions le faire correctement.

Bonne chance!

17
David Crook

Bien que ce ne soit pas aussi simple et sec que model.predict (), c'est toujours très trivial.

Dans votre modèle, vous devriez avoir un tenseur qui calcule la sortie finale qui vous intéresse, nommons ce tenseur output. Vous pouvez actuellement simplement avoir une fonction de perte. Si c'est le cas, créez un autre tenseur (variable dans le modèle) qui calcule réellement la sortie souhaitée.

Par exemple, si votre fonction de perte est:

tf.nn.sigmoid_cross_entropy_with_logits(last_layer_activation, labels)

Et vous vous attendez à ce que vos sorties soient dans la plage [0,1] par classe, créez une autre variable:

output = tf.sigmoid(last_layer_activation)

Maintenant, lorsque vous appelez sess.run(...), il suffit de demander le tenseur output. Ne demandez pas le PO d'optimisation que vous auriez normalement pour le former. Lorsque vous demandez cette variable tensorflow fera le travail minimum nécessaire pour produire la valeur (par exemple, cela ne dérangera pas avec backprop, les fonctions de perte, et tout cela parce qu'une simple passe de transmission directe est tout ce qui est nécessaire pour calculer output .

Donc, si vous créez un service pour retourner des inférences du modèle, vous voudrez garder le modèle chargé dans la mémoire/gpu, et répéter:

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

Vous n'aurez pas besoin de lui fournir les étiquettes car tensorflow ne prendra pas la peine de calculer des opérations qui ne sont pas nécessaires pour produire la sortie que vous demandez. Vous n'avez pas besoin de changer de modèle ou quoi que ce soit.

Bien que cette approche ne soit pas aussi évidente que model.predict(...) je dirais qu'elle est beaucoup plus flexible. Si vous commencez à jouer avec des modèles plus complexes, vous apprendrez probablement à aimer cette approche. model.predict() est comme "penser à l'intérieur de la boîte".

5
David Parks