2017-06-29 2 views
4

Wie würde man am besten eine Vorverarbeitungsschicht hinzufügen (z. B. subtrahieren und dividieren) in ein keras (v2.0.5) -Modell, so dass das Modell vollständig eigenständig ist Bereitstellung (möglicherweise in einer C++ - Umgebung). Ich habe versucht:Hinzufügen einer Vorverarbeitungsschicht zum Keras-Modell und Festlegen der Tensorwerte

def getmodel(): 
     model = Sequential() 
     mean_tensor = K.placeholder(shape=(1,1,3), name="mean_tensor") 
     std_tensor = K.placeholder(shape=(1,1,3), name="std_tensor") 

     preproc_layer = Lambda(lambda x: (x - mean_tensor)/(std_tensor + K.epsilon()), 
           input_shape=im_shape) 

     model.add(preproc_layer) 

     # Build the remaining model, perhaps set weights, 
     ... 

     return model 

Dann woanders setzen Sie die mittlere/Std auf dem Modell. Ich fand die set_value Funktion versucht, so die folgenden:

m = getmodel() 
mean, std = get_mean_std(..) 

graph = K.get_session().graph 
mean_tensor = graph.get_tensor_by_name("mean_tensor:0") 
std_tensor = graph.get_tensor_by_name("std_tensor:0") 

K.set_value(mean_tensor, mean) 
K.set_value(std_tensor, std) 

jedoch die set_value nicht mit

AttributeError: 'Tensor' object has no attribute 'assign' 

So set_value nicht funktioniert (die begrenzten) docs vorschlagen würde. Was wäre der richtige Weg, dies zu tun? Holen Sie sich die TF-Sitzung, wickeln Sie den gesamten Trainingscode in eine with (session) und verwenden Sie feed_dict? Ich hätte gedacht, dass es einen nativen keras Weg geben würde, Tensor-Werte zu setzen.

Statt einen Platzhalter verwenden Ich habe versucht, den Mittelwert/std auf Modellbau Einstellung entweder K.variable oder K.constant:

mean_tensor = K.variable(mean, name="mean_tensor") 
std_tensor = K.variable(std, name="std_tensor") 

Dies vermeidet set_value Probleme. Obwohl ich merke, dass es funktioniert, wenn ich versuche, dieses Modell zu trainieren (was ich weiß, ist nicht besonders effizient, da Sie die Normalisierung für jedes Bild wiederholen), aber am Ende der ersten Epoche versagt der ModelCheckpoint Handler mit einem sehr tiefen Stapel Spur:

... 
File "/Users/dgorissen/Library/Python/2.7/lib/python/site-packages/keras/models.py", line 102, in save_model 
    'config': model.get_config() 
File "/Users/dgorissen/Library/Python/2.7/lib/python/site-packages/keras/models.py", line 1193, in get_config 
    return copy.deepcopy(config) 
File "/usr/local/Cellar/python/2.7.12_2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/copy.py", line 163, in deepcopy 
    y = copier(x, memo) 
... 
File "/usr/local/Cellar/python/2.7.12_2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/copy.py", line 190, in deepcopy 
    y = _reconstruct(x, rv, 1, memo) 
File "/usr/local/Cellar/python/2.7.12_2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/copy.py", line 343, in _reconstruct 
    y.__dict__.update(state) 
AttributeError: 'NoneType' object has no attribute 'update' 

Update 1:

ich habe auch versucht, einen anderen Ansatz. Trainieren eines Modells als normal, dann schreibe gerade ein zweites Modell, das die Vorverarbeitung tut:

# Regular model, trained as usual 
model = ... 

# Preprocessing model 
preproc_model = Sequential() 
mean_tensor = K.constant(mean, name="mean_tensor") 
std_tensor = K.constant(std, name="std_tensor") 
preproc_layer = Lambda(lambda x: (x - mean_tensor)/(std_tensor + K.epsilon()), 
         input_shape=im_shape, name="normalisation") 
preproc_model.add(preproc_layer) 

# Prepend the preprocessing model to the regular model  
full_model = Model(inputs=[preproc_model.input], 
       outputs=[model(preproc_model.output)]) 

# Save the complete model to disk 
full_model.save('full_model.hdf5') 

Dies scheint bis zum save() Aufruf zu arbeiten, die wie oben mit der gleichen tiefen Stack-Trace ausfällt. Vielleicht ist die Lambda Schicht das Problem, aber juding von this issue die es scheint, sollte es ordnungsgemäß serialisieren.

Also insgesamt, wie ich eine Normalisierungsschicht an ein Keras-Modell anhängen, ohne die Fähigkeit zu serialisieren (und zu PB exportieren)?

Ich bin sicher, Sie können es funktionieren, indem Sie direkt auf TF (z. B. this thread oder tf.Transform), aber hätte gedacht, dass es in keras direkt möglich wäre.

Update 2:

So fand ich, dass der tiefe Stack-Trace, indem Sie

def foo(x): 
    bar = K.variable(baz, name="baz") 
    return x - bar 

So definieren bar innerhalb der Funktion anstelle von außen Rahmen der Erfassung vermieden werden könnte.

Ich habe dann gefunden, ich könnte auf der Festplatte speichern, konnte aber nicht von der Festplatte geladen werden. Es gibt eine Reihe von GitHub-Problemen.Ich habe die in #5396 angegebene Problemumgehung verwendet, um alle Variablen als Argumente zu übergeben, dies erlaubte mir dann zu speichern und zu laden.

Ich dachte, ich war fast da, ich fuhr mit meinem Ansatz von Update 1 über das Stapeln eines Vorverarbeitungsmodells vor einem trainierten Modell. Dies führte dann zu Model is not compiled Fehlern. Arbeitete um diejenigen, aber am Ende habe ich es nie geschafft, die folgende Arbeit zu bekommen:

  • Erstellen und trainieren ein Modell
  • Speichern auf der Festplatte
  • laden es, prepend eine Vorverarbeitung Modell
  • Export der Modell auf der Festplatte als eine gefrorene pb Datei
  • Legen sie das gefrorene pb von der Festplatte
  • Wenden sie es auf einigen unsichtbaren Daten gestapelt

Ich habe es zu dem Punkt, wo es keine Fehler gab, aber konnte nicht die Normalisierung Tensoren durch die gefrorenen pb zu verbreiten. Nachdem zu viel Zeit auf diese verbrachte ich gab dann auf und wechselte zu dem etwas weniger elegant Ansatz:

  • ein Modell bauen mit der Vorverarbeitung in dem Modell von Anfang an, sondern auf einen No-op (Mittelwert = 0, std = 1)
  • Trainieren Sie das Modell, erstellen Sie ein identisches Modell, aber diesmal mit den richtigen Werten für mean/std.
  • Übertragen Sie die Gewichte
  • Export und das Modell

All diese nun vollständig funktioniert wie erwartet pb einzufrieren. Kleiner Aufwand für das Training, aber für mich vernachlässigbar.

Noch nicht herausgefunden, wie man den Wert einer Tensor-Variable in keras setzen würde (ohne die Ausnahme assign zu erhöhen), aber kann jetzt ohne es tun.

Wird @ Daniels Antwort akzeptieren, da sie mich in die richtige Richtung gebracht hat.

Verwandte Frage:

+0

Wenn die Operation fest ist (nichts zu lernen) und sich vor der ersten Schicht befindet, sollten Sie die Operation möglicherweise direkt in den Eingabedaten ausführen, bevor Sie die Daten Ihrem Modell übergeben. Das würde Ihnen ersparen, wenn Sie während des Trainings zusätzliche unnötige Berechnungen durchführen müssen. –

+0

Haben 'mean_tensor' und' std_tensor' dieselbe "Batchgröße" wie X? Dies kann die Antwort erheblich ändern ... –

+0

@Daniel Dies ist ein leicht vereinfachtes Szenario. Ich ignoriere jetzt den Overhead für das Training, aber Sie könnten sich vorstellen, diese Schicht auf ein Modell zu kleben, das in Produktion geht, und Sie würden auf das gleiche Problem stoßen. Es gibt keine Formabweichungsfehler, wenn ich die Tensoren als Konstanten definiere. Ansonsten bekomme ich den no attribute error wie beschrieben. – dgorissen

Antwort

2

Wenn eine Variable erstellen, müssen Sie es den "Wert" geben, nicht die Form:

mean_tensor = K.variable(mean, name="mean_tensor") 
std_tensor = K.variable(std, name="std_tensor") 

nun in Keras, Sie müssen sich nicht mit Sitzungen, Graphen und dergleichen befassen. Sie arbeiten nur mit Layern, und innerhalb von Lambda Layern (oder Verlustfunktionen) können Sie mit Tensoren arbeiten.

Für unsere Lambda-Ebene benötigen wir eine komplexere Funktion, da die Formen übereinstimmen müssen, bevor Sie eine Berechnung durchführen.Da ich nicht weiß, im_shape, nahm ich an, sie hätte drei Dimensionen:

def myFunc(x): 

    #reshape x in a way it's compatible with the tensors mean and std: 
    x = K.reshape(x,(-1,1,1,3)) 
     #-1 is like a wildcard, it will be the value that matches the rest of the given shape.  
     #I chose (1,1,3) because it's the same shape of mean_tensor and std_tensor 

    result = (x - mean_tensor)/(std_tensor + K.epsilon()) 

    #now shape it back to the same shape it was before (which I don't know)  
    return K.reshape(result,(-1,im_shape[0], im_shape[1], im_shape[2])) 
     #-1 is still necessary, it's the batch size 

Jetzt schaffen wir die Lambda-Schicht, es braucht auch eine Ausgabeform (wegen der benutzerdefinierten Betriebsart, das System nicht unbedingt wissen, dass die Ausgabeform)

Danach, nur kompilieren Sie das Modell und trainieren Sie es. (Oft mit model.compile(...) und model.fit(...))


Wenn Sie wollen alles umfassen, einschließlich der Vorverarbeitung innerhalb der Funktion, auch ok:

def myFunc(x): 

    mean_tensor = K.mean(x,axis=[0,1,2]) #considering shapes of (size,width, heigth,channels)  
    std_tensor = K.std(x,axis=[0,1,2]) 

    x = K.reshape(x, (-1,3)) #shapes of mean and std are (3,) here.  
    result = (x - mean_tensor)/(std_tensor + K.epsilon()) 

    return K.reshape(result,(-1,width,height,3)) 

Nun, all dies ist extra Berechnung in Ihrem Modell und wird Verarbeitung verbrauchen. Es ist besser, einfach alles außerhalb des Modells zu machen. Erstellen Sie zuerst die vorverarbeiteten Daten und speichern Sie sie. Erstellen Sie dann das Modell ohne diese Vorverarbeitungsschicht. Auf diese Weise erhalten Sie ein schnelleres Modell. (Es kann wichtig sein, wenn Ihre Daten oder Ihr Modell zu groß ist).

+0

Danke Daniel. Der Parameter tf.variable war ein Tippfehler an meinem Ende (ich meinte Platzhalter). Ich erkannte auch, dass ich nicht klar darüber war, wie ich meine Frage formulierte, ich habe sie aktualisiert. Alles in allem scheint das zu funktionieren, außer das Modell speichert nicht. – dgorissen

+0

Haben Sie auch den zweiten Ansatz versucht? (Alles, einschließlich der Vorverarbeitung, in der Schicht?) –

+0

kurz ja, siehe Update ich hinzugefügt. Wird deine Antwort akzeptieren, da es mir geholfen hat, in die richtige Richtung zu gehen. – dgorissen

Verwandte Themen