2017-04-30 8 views
11

Ich habe mich schon eine Weile damit beschäftigt. Ich habe eine Menge Artikel gefunden; aber keine zeigt nur Tensorfluss-Inferenz als eine einfache Schlussfolgerung. Es verwendet immer die Serving-Engine oder eine Grafik, die vor-codiert/definiert ist.TensorFlow Inference

Hier ist das Problem: Ich habe ein Gerät, das gelegentlich nach aktualisierten Modellen sucht. Es muss dann dieses Modell laden und Eingabevorhersagen durch das Modell ausführen.

In Keras war das einfach: ein Modell bauen; Trainiere das Modell und den Aufruf model.predict(). In scikit - lerne dasselbe.

Ich bin in der Lage, ein neues Modell zu greifen und es zu laden; Ich kann alle Gewichte ausdrucken; aber wie um alles in der Welt laufe ich dagegen?

-Code-Modell und Druck Gewichte zu laden:

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

ich alle meine Sammlungen ausgedruckt und ich habe: [ 'queue_runners', 'Variablen', 'Verluste', 'Zusammenfassungen', ‚train_op ',' cond_context ',' trainable_variables ']

Ich habe versucht mit sess.run (train_op); Jedoch begann gerade eine volle Trainingseinheit zu beginnen; was ich nicht machen möchte. Ich möchte nur Inferenz gegen eine andere Reihe von Eingaben ausführen, die ich zur Verfügung stelle und die nicht TF Records sind.

Nur ein wenig ausführlicher:

Das Gerät kann C++ verwenden oder Python; solange ich eine .exe erzeugen kann. Ich kann ein Feed-Diktat einrichten, wenn ich das System füttern möchte. Ich habe mit TFRecords trainiert; aber in der Produktion werde ich keine TFRecords verwenden; es ist ein Real/Near-Real-Time-System.

Danke für jede Eingabe. Ich poste Beispielcode zu diesem Repo: https://github.com/drcrook1/CIFAR10/TensorFlow, das das ganze Training und die Beispielschlußfolgerung tut.

Alle Hinweise werden sehr geschätzt!

------------ EDITS ----------------- ich das Modell wieder aufgebaut, wie unten zu sein:

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 

Hinweis Ich füge der TF-Operation den Namen "Vorhersage" hinzu, damit ich sie später abrufen kann.

Beim Training habe ich die Eingabe-Pipeline für tfrecords und Eingabewarteschlangen verwendet.

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) 

Ich versuche feed_dict für die geladene Operation in der Grafik zu verwenden; aber jetzt ist es einfach nur hängen ....

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

Ich glaube, dies nicht funktioniert, weil das ursprüngliche Netzwerk trainiert wurde TFRecords verwenden. Im Beispiel-CIFAR-Datensatz sind die Daten klein; Unser realer Datensatz ist riesig und es ist mein Verständnis, dass TFRecords die Standard-Best Practice für das Training eines Netzwerks ist. Das feed_dict ist aus produktionstechnischer Sicht sehr sinnvoll. Wir können einige Threads aufdrehen und dieses Ding aus unseren Eingabesystemen bestücken.

Also ich denke, ich habe ein Netzwerk, das trainiert ist, kann ich die Vorhersage Operation erhalten; aber wie sage ich es, um die Eingangswarteschlangen zu stoppen und das feed_dict zu benutzen?Denken Sie daran, dass ich aus Sicht der Produktion keinen Zugang zu dem habe, was die Wissenschaftler getan haben, um es zu machen. Sie machen ihr Ding; und wir halten es in der Produktion mit dem vereinbarten Standard.

------- INPUT OPS --------

tf.Operation 'input/input_producer/Konst' type = Konst, tf.Operation 'input/input_producer/Size' Art = Const, tf.Operation 'Input/Input_Producer/Greater/y' Typ = Const, tf.Operation 'Input/Input_Producer/Greater' Typ = Greater, tf.Operation 'Input/Input_Producer/Assert/Const' Typ = Const, tf .Operation 'Input/Input_Producer/Assert/Assert/data_0' Typ = Const, tf.Operation 'Input/Input_Producer/Assert/Assert' Typ = Assert, tf.Operation 'Input/Input_Producer/Identity' Typ = Identität, tf.Operation 'input/input_producer/RandomShuffle' type = RandomShuffle, tf.Operation 'input/input_producer' type = FIFOQueueV2, tf.Operation 'input/input_producer/input_producer_EnqueueMany' type = QueueEnqueueManyV2, tf.Operation 'input/input_producer/input_pr oducer_Close 'type = QueueCloseV2, tf.Operation' input/input_producer/input_producer_Close_1 'type = QueueCloseV2, tf.Operation' input/input_producer/input_producer_Size 'type = QueueSizeV2, tf.Operation' input/input_producer/Cast 'type = Cast, tf. Operation 'Input/Input_producer/mul/y' Typ = Const, tf.Operation 'Input/Input_producer/mul' Typ = Mul, tf.Operation 'Input/Input_Producer/Bruch_von_32_Full/Tags' Typ = Const, tf.Operation 'Eingabe/input_producer/fraction_of_32_full‘type = ScalarSummary, tf.Operation 'input/TFRecordReaderV2' type = TFRecordReaderV2, tf.Operation 'input/ReaderReadV2' type = ReaderReadV2,

------ END INPUT OPS -----

---- UPDATE 3 ----

Ich glaube, was ich tun muss, ist, den Eingabeabschnitt des Graphen, der mit TF Records trainiert wurde, zu beenden und den Eingang der ersten Schicht auf einen neuen Eingang umzuverdrahten. Es ist wie eine Operation; aber das ist der einzige Weg, die ich finden kann Inferenz tun, wenn ich mit TFRecords wie verrückt trainiert, wie es klingt ...

Voll Graph:

enter image description here

Abschnitt zu töten:

enter image description here

Also ich denke, die Frage wird: Wie tötet man den Eingabeabschnitt des Graphen und ersetzt es durch ein feed_dict?

Ein Follow-up zu diesem wäre: Ist das wirklich der richtige Weg, es zu tun? Das scheint verrückt zu sein.

---- END UPDATE 3 ----

--- Link-Dateien Checkpoint ---

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 Link-Dateien Checkpoint ---

----- UPDATE 4 -----

Ich gab nach und gab nur einen Schuß auf die 'normale' Art der Inferenz, vorausgesetzt, ich könnte die Wissenschaftler einfach nur ihre beizen lassen Modelle und wir könnten die Modellbeize greifen; entpacke es und führe dann Rückschlüsse darauf. Also zum Testen habe ich den normalen Weg ausprobiert, vorausgesetzt, wir haben es schon ausgepackt ... Es funktioniert auch keine Bohnen wert ...

Tensorflow endet mit dem Erstellen eines neuen Graphen mit der Inferenzfunktion aus dem geladenen Modell; dann hängt es alle anderen Sachen von der anderen Grafik an das Ende davon an. Also dann, wenn ich ein feed_dict bevölkere, in der Erwartung, Inferenzen zurück zu bekommen; Bekomme ich nur ein paar zufälligen Müll, als wäre es der erste Durchgang durch das Netzwerk war ...

wieder; das scheint verrückt zu sein; Muss ich wirklich ein eigenes Framework zum Serialisieren und Deserialisieren von Zufallsnetzwerken schreiben? Dieser hatte zuvor getan worden sein ...

----- UPDATE 4 -----

wieder; Vielen Dank!

Antwort

8

Okay, dies geschah viel zu viel Zeit, um herauszufinden; Also hier ist die Antwort für den Rest der Welt.

Kurzerinnerung: Ich musste ein Modell persistieren, das dynamisch geladen und abgeleitet werden kann, ohne zu wissen, wie es funktioniert.

Schritt 1: ein Modell als eine Klasse erstellen und idealerweise eine Schnittstellendefinition

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

Schritt 2 verwenden: mit Trainieren Sie Ihr Netzwerk, was auch immer Sie wollen Eingänge; In meinem Fall habe ich Queue Runners und TF Records benutzt. Beachten Sie, dass dieser Schritt von einem anderen Team ausgeführt wird, das Modelle iteriert, erstellt, entwirft und optimiert. Dies kann sich auch im Laufe der Zeit ändern. Die Ausgabe, die sie produzieren, muss von einem entfernten Standort abgerufen werden können, damit wir die aktualisierten Modelle auf Geräten dynamisch laden können (das Reflashing von Hardware ist ein Problem, besonders wenn es geografisch verteilt ist). In diesem Fall; Das Team löscht die 3 Dateien, die mit einem Grafikschoner verknüpft sind. sondern auch eine Gurke des verwendeten Modells für diese Trainingseinheit

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) 

Schritt 3: einige Inference starten. Laden Sie Ihr eingelegtes Modell; Erstellen Sie ein neues Diagramm, indem Sie den neuen Platzhalter mit den Logits verknüpfen. und rufen Sie dann die Sitzungswiederherstellung auf. WIEDERHERSTELLEN SIE NICHT DAS GESAMTE GRAPH; GERADE DIE VARIABLEN.

Es gibt definitiv Möglichkeiten, dies zu verbessern mit Schnittstellen und vielleicht alles besser verpacken; aber das funktioniert und schafft die Voraussetzungen dafür, wie wir uns weiterentwickeln werden.

FINAL HINWEIS Wenn wir dies schließlich zur Produktion geschoben, am Ende haben wir die dumme mymodel_model.py mit allem, was die Grafik aufzubauen Datei herunter zu versenden Atomen. Daher erzwingen wir nun eine Namenskonvention für alle Modelle und es gibt auch einen Codierungsstandard für Produktionsmodell-Läufe, damit wir das richtig machen können.

Viel Glück!

+1

Für die Interessierten war das zu viel Gesang und Tanz. Wir verwenden CNTK jetzt stattdessen ... Es war einfacher, alles für alle Teams zu standardisieren und etwas in die Produktion konsistent zu bekommen. –

2

Es ist zwar nicht so geschnitten und trocken wie model.predict() ist, ist es immer noch sehr trivial.

In Ihrem Modell sollten Sie einen Tensor haben, der die endgültige Ausgabe berechnet, die Sie interessiert, nennen wir diesen Tensor output. Möglicherweise haben Sie gerade eine Verlustfunktion. Wenn dies der Fall ist, erstellen Sie einen anderen Tensor (Variable im Modell), der die gewünschte Ausgabe tatsächlich berechnet.

Zum Beispiel, wenn Ihre Verlustfunktion ist:

tf.nn.sigmoid_cross_entropy_with_logits(last_layer_activation, labels) 

Und Sie erwarten, dass Ihre Ausgaben pro Klasse im Bereich [0,1] sein, erstellen Sie eine weitere Variable:

output = tf.sigmoid(last_layer_activation) 

Jetzt Wenn Sie sess.run(...) anrufen, fordern Sie einfach den output Tensor an. Fordern Sie nicht das Optimierungs-OP an, das Sie normalerweise trainieren würden. Wenn Sie diese Variable tensorflow fordern die minimale Arbeit tun notwendig, den Wert zu erzeugen (zB es wird nicht mit Backprop, Verlustfunktionen stören, und das alles, weil ein einfacher Feed-Forward-Pass ist alles, was notwendig ist, output zu berechnen.

Also, wenn Sie einen Service Schlussfolgerungen des Modells zurückkehren möchten Sie das Modell im Speicher/gpu geladen zu halten, und wiederholen:

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

Sie nicht brauchen es um die Etiketten zu füttern, weil tensorflow stören nicht ops zu berechnen, die nicht benötigt werden, um die Ausgabe zu erzeugen, die Sie anfordern. Sie müssen nicht Ihr Modell oder etwas ändern.

Während dieser Ansatz nicht so offensichtlich wie model.predict(...) ich behaupten würde sein könnte, dass es wesentlich flexibler ist. Wenn Sie anfangen, mit komplexeren Modellen zu spielen, werden Sie wahrscheinlich diesen Ansatz lieben lernen. model.predict() ist wie "Denken in der Box."

+0

Meine einzige Folgefrage ist; Wie extrahiere ich "Ausgabe" aus der Grafik? Ich bin absolut glücklich darüber, den endgültigen Tensor 'output' immer als "interface" -Definition zu bezeichnen. Das Problem ist, dass das Gerät nur Zugriff auf die Modell-/Prüfpunktdateien hat. Es hat nicht den Graphen-Konstruktionscode. –

+0

habe ich einen Namen für die softmax Schicht, in der Hoffnung, dass ich Tensor retreive kann und geben einen Schuss: H = tf.nn.softmax (D_2, name = ‚Vorhersage‘), die zuvor, dass Tensor explizit genannt hatte nicht. –

+0

Geben Sie einen Schuss; und es scheint einfach zu sperren: mit tf.Session() als sess: new_saver = tf.train.import_meta_graph (MODELL_PATH + '.meta', clear_devices = True) new_saver.restore (sess, MODEL_PATH) pred = tf .get_default_graph() get_operation_by_name ('Vorhersage') Druck. (sess.run (pred, feed_dict = {Bilder: np.random.rand (1, 32, 32, 3)})) –