Dieser Artikel ist der Artikel zum 18. Tag von TensorFlow Adventskalender 2016.
Ursprünglich hatte ich vor, PredNet und ConvLSTM zu implementieren, aber da ich den Frame des Videos selbst vorhersagen kann, habe ich diesen Artikel mit der Absicht geschrieben, ihn auszuprobieren. Da ich es auf der vorherigen Veranstaltung der Tensor Flow User Group "NN Thesis Drinking Party" angekündigt habe, interessiert mich der Umriss des ursprünglichen Artikels. Bitte beachten Sie diese Folie.
Ich denke, es ist leicht vorstellbar, wie es aus dem Namen aussieht. Bei der herkömmlichen LSTM war der Zeitübergangszustand der Tensor im 2. Stock (Chargengröße, Anzahl der Einheiten in der mittleren Schicht), jetzt jedoch der Tensor im 4. Stock (Chargengröße, Länge, Breite, Anzahl der Kanäle). Zu diesem Zeitpunkt wurde in der Vergangenheit die Verbindung zwischen den Schichten von der Gesamtverbindung zur Faltung geändert, da der zu behandelnde Zustand Bildinformationen sind. Herkömmliches LSTM ist ↓ Gefaltetes LSTM ist ↓
Es sieht nicht sehr anders aus, aber es ist nur eine Faltung der Matrixmultiplikation. Es sollte jedoch beachtet werden, dass der Teil des Adamal-Produkts, zu dem das Guckloch beiträgt, unverändert bleibt. (Ich denke immer, warum ist der Gucklochteil nicht die Multiplikation der Matrix, sondern das Adamal-Produkt ...?)
Faltungs-LSTM ist natürlich nicht als Standardfunktion vorhanden, daher müssen Sie es implementieren. Lassen Sie uns "ConvLSTMCell" implementieren, indem wir "tf.nn.rnn_cell.RNNCell" erben. Der Quellcode ist unter [hier] angegeben (https://github.com/TakuyaShinmura/conv_lstm). Ich habe auch eine Datensatzverarbeitung und ein DL-Skript erstellt, aber es wird chaotisch, und der ursprüngliche Datensatz ist furchtbar groß. Deshalb verarbeite ich ihn so oft ich ihn verwende und stelle ihn so in das Repository, wie er ist.
Der Referenzcode lautet hier.
Was ich dieses Mal erstellt habe, ist eine Vorhersage der Fahrlandschaft mit KITTI Dataset. Ursprünglich sollten wir vorhersagen, wie viele Frames in der Zukunft aus den letzten Frames stammen. Da jedoch viel Code vorhanden ist und das Erlernen einige Zeit in Anspruch nimmt, werden wir ein Netzwerk aufbauen, das aus den letzten 4 Frames einen Frame in der Zukunft vorhersagt.
Ich habe verschiedene Bildgrößen ausprobiert, aber als ich es mit GFORCE GTX 1070 verschoben habe, hatte ich das Gefühl, dass 128 x 128 die Grenze waren. Also habe ich es mit 64x64 verifiziert. In dem Papier war die LSTM-Ebene mehrschichtig, aber es war mühsam, den Wrapper zu ändern, sodass ich ihn in einer einzelnen Ebene erstellt habe. Aus irgendeinem Grund verwendete die Fehlerfunktion Kreuzentropie im Papier, aber ich fühlte mich unwohl, also werde ich den absoluten Fehler verwenden.
Es gibt mindestens drei Methoden, die in der ursprünglichen tf.nn.RNNCell vererbt werden müssen: state_size
, output_size
und __call__
. Es gibt noch einen "Nullzustand", aber dieses Mal werden alle Anfangswerte des internen Zustands mit 0 erstellt, sodass es nicht erforderlich ist, ihn ursprünglich zu implementieren, aber diesmal ist die Form des internen Zustands der Tensor im 4. Stock Um es zu nehmen, müssen wir es ändern. Die Rolle von jedem ist "output_size", was die Anzahl der Ausgabeeinheiten ist (nicht der interne Zustand). Aufgrund der Art von RNN entspricht es der Anzahl der Einheiten in der mittleren Schicht, die sich im internen Zustand befinden. Wenn Sie die Ausgabe projizieren möchten, um den Rechenaufwand zu verringern, ändern Sie sie entsprechend.
rnn_cell.py
if num_proj:
self._state_size = (
LSTMStateTuple(num_units, num_proj)
if state_is_tuple else num_units + num_proj)
self._output_size = num_proj
else:
self._state_size = (
LSTMStateTuple(num_units, num_units)
if state_is_tuple else 2 * num_units)
self._output_size = num_units
@property
def state_size(self):
return self._state_size
@property
def output_size(self):
return self._output_size
Als nächstes folgt "state_size", dh die Anzahl der Einheiten im internen Status. Im Fall einer allgemeinen RNN oder GRU entspricht sie natürlich der Anzahl der Zwischenschichten, die sich im internen Zustand befinden, aber in LSTM wirken sich sowohl der interne Zustand als auch die Ausgabe auf den nächsten Zustand aus, so dass das obige " Die Größe wird wie bei rnn_cell.pyverdoppelt.
zero_state gibt den mit 0 aufgefüllten Anfangszustand zurück, der mit dieser
state_size` übereinstimmt.
In Bezug auf den "call" des Funktionsaufrufs des Objekts ist die Verarbeitung hier der Teil, der die Verarbeitung zu jedem Zeitschritt beschreibt, indem das Gewicht und die Eingabe tatsächlich multipliziert werden.
Die RNN-bezogenen Operationen von TensorFlow haben eine kleine Anzahl von Zeilen. Wenn Sie also interessiert sind, lesen Sie diese bitte.
tf.nn.ConvLSTMCell Nun die Implementierung des Hauptthemas "ConvLSTMCell". Es gibt zwei Punkte.
Der erste Punkt ist, dass Sie den Eingang mit dem Ausgang der vorherigen Zeit kombinieren müssen, wenn Sie das Eingangs- oder Vergessengatter erstellen. (Verarbeitung im unteren Teil der Abbildung unten)
Zu diesem Zeitpunkt kann bei der herkömmlichen LSTM die Größe zwischen der Ausgabe und der Eingabe der vorherigen Zeit unterschiedlich sein, so dass es nicht möglich ist, einfach hinzuzufügen. Daher werden die Tensoren in den Richtungen Eingangslänge und Ausgangslänge kombiniert. Ich hoffe, Sie können sich PPAP vorstellen. (Abbildung unten)
Diesmal können Eingabe, Ausgabe und Zustand jedoch nicht so kombiniert werden, wie sie sind, da sie einen Tensor 4. Ordnung haben. Um dies zu lösen, wird auch der zweite Punkt behandelt, aber die Eingabe und Ausgabe werden in Kanalrichtung kombiniert, indem nur die vertikalen und horizontalen Größen des Bildes vereinheitlicht werden. Das Bild ist in der folgenden Abbildung dargestellt.
rnn_cell.py
if len(args) == 1:
res = math_ops.matmul(args[0], weights)
else:
res = math_ops.matmul(array_ops.concat(1, args), weights)
conv_lstm_cell.py
#Achten Sie darauf, zu polstern, da es sich um ein gemeinsames Gewicht handelt='SAME'Einklappen
if len(args) == 1:
res = tf.nn.conv2d(args[0],kernel, stride, padding='SAME')
else:
res = tf.nn.conv2d(array_ops.concat(3, args), kernel, stride, padding='SAME')
Die Verzweigung nach if-Minuten wird nur zwischen dem allgemeinen Fall rnn und dem Fall lstm aufgeteilt. Als Unterschied im verschachtelten Teil von else wird "concat" vor dem Falten angewendet. Im Fall der herkömmlichen Methode ist es in der Richtung von Rang 1 verbunden, aber in "conv_lstm" können Sie sehen, dass es in der Richtung von Rang 3 (Kanal) verbunden ist.
Der zweite Punkt ist das oben erwähnte Kopplungsproblem, und vor allem aufgrund der Eigenschaften von RNN und der Eigenschaften der Zeitausbreitung unter Verwendung gemeinsamer Gewichte muss der Tensor im inneren Zustand immer die gleiche Form haben. Es gibt. Daher ist die Faltpolsterung natürlich "GLEICH". Selbstverständlich korrigiert die Faltauffüllung nur die Filtergröße. Wenn Sie also "Schritt" auf 1 oder mehr einstellen, wird das Bild kleiner. Legen Sie daher die Schrittgröße immer auf [1,1,1,1] fest. Aus diesem Grund sind die Berechnungskosten sehr hoch und das Lernen wird nur fortgesetzt, wenn es mit einem etwas kleinen Bild durchgeführt wird.
Nachdem wir das Verhalten jedes Mal mit "Convlstmcell" implementiert haben, werden wir es auf die Zeit von RNN erweitern. Es gibt ungefähr zwei Methoden zum zeitlichen Erweitern von Zellen: Eine ist die Verwendung einer for-Anweisung bei Verwendung von "reuse_variables ()" und die andere die Verwendung von "tf.nn.rnn ()" oder "tf.nn.dynamic_rnn ()". ist. Dieses Mal werde ich die TensorFlow-Funktion verwenden. In diesem Fall wird diesmal tf.nn.rnn () verwendet. Persönlich wollte ich "dynamic_rnn ()" verwenden, was beim Erstellen von Eingabedaten nicht mühsam ist. Da die Zeitachse jedoch mit der Option "time_major" usw. im zweiten Stock des Tensors festgelegt ist, ändere ich diesen Teil Da es nervig war, werde ich rnn ()
verwenden. Daher sind die Eingabedaten eine Liste von Tensoren im 4. Stock (Chargengröße, horizontal, vertikal, Kanal).
train.py
#Eingabedaten(batch, width, height, channel)Tensor-Zeitreihenliste im 4. Stock
images = []
for i in xrange(4):
input_ph = tf.placeholder(tf.float32,[None, IMG_SIZE[0], IMG_SIZE[1], 3])
tf.add_to_collection("input_ph", input_ph)
images.append(input_ph)
#Richtige Antwortdaten(batch, width, height, channel)Tensor im 4. Stock
y = tf.placeholder(tf.float32,[None, IMG_SIZE[0], IMG_SIZE[1], 3])
Mmmm, ich habe feed_dict
ziemlich klobig gemacht, aber gab es keinen besseren Weg?
train.py
feed_dict = {}
#Holen Sie sich den ersten Frame des Bildes, der für das Training der Stapelgröße verwendet wird
target = []
for i in xrange(FLAGS.batch_size):
target.append(random.randint(0,104))
#Feed für Platzhalter des Eingabebildes_Füllen Sie das Diktat aus
for i in xrange(4):
inputs = []
for j in target:
file = FLAGS.data_dir+str(i+j)+'.png'
img = cv2.imread(file)/255.0
inputs.append(img)
feed_dict[tf.get_collection("input_ph")[i]] = inputs
Derzeit kann der Modellaufbau des Zeiterweiterungsteils jedoch sehr einfach geschrieben werden.
train.py
cell = conv_lstm_cell.ConvLSTMCell(FLAGS.conv_channel, img_size=IMG_SIZE, kernel_size=KERNEL_SIZE,
stride= STRIDE, use_peepholes=FLAGS.use_peepholes, cell_clip=FLAGS.cell_clip, initializer=initializer,
forget_bias=FLAGS.forget_bias, state_is_tuple=False, activation=activation)
outputs, state = tf.nn.rnn(cell=cell, inputs=images, dtype=tf.float32)
Ein Bild wird basierend auf der Ausgabe des letzten Zeitpunkts des Faltungs-LSTM erzeugt. Die notwendige Information ist der letzte Tensor der Liste "Ausgänge", der von "tf.nn.rnn ()" zurückgegeben wird. Holen Sie ihn also mit "Ausgängen [-1]" und erhalten Sie ihn (Stapelgröße, Breite, Höhe, Anzahl der Kanäle). Das Bild wird durch Falten des Tensors im 4. Stock erzeugt. Wie ich im Faltungspunkt LSTM erwähnt habe, sind alle Bilddaten, die im Netzwerk angezeigt werden, gleich groß. Mit ihm wird das Bild des erwarteten Rahmens durch Falten mit 1x1 ausgegeben.
train.py
#Holen Sie sich die Ausgabe beim letzten Mal
last_output=outputs[-1]
#Falten Sie das Ergebnis in 1x1 und verarbeiten Sie es auf die gleiche Größe wie das Originalbild
kernel = tf.Variable(tf.truncated_normal([1,1 ,FLAGS.conv_channel, 3],stddev=0.1))
result = tf.nn.conv2d(last_output, kernel,[1,1,1,1], padding='SAME')
result = tf.nn.sigmoid(result)
Da der Pixelwert des Ausgabebildes 0 bis 255 sein muss, ist die Zündfunktion eine Sigmoidfunktion und das Ergebnis wird mit 255 multipliziert. Sie können das Bild sicher ausgeben.
Nun, ich verwende TensorBoard nicht zum Protokollieren oder Generieren einer Prüfpunktdatei, aber ich werde das Ergebnis des entsprechenden Ablaufs einfügen.
Natürlich lerne ich und am Ende scheint die weiße Linie auf der Straße überraschend gut zu sein, aber ich denke, dass die Parameter angemessen sind, also ist es so. In der zweiten Hälfte lag der absolute Fehlerdurchschnitt bei 0,1. Übrigens hat das Trainieren durch Erhöhen der Bildgröße viel Zeit in Anspruch genommen, aber der absolute Fehler wurde noch kleiner und es wurde ziemlich klar.
Ich habe keine Parametereinstellung vorgenommen, daher ist das Ergebnis hmm. Gelegentlich gab es Fälle, in denen es recht gut funktionierte, aber die Bäume auf der Straße waren verschwunden und es war immer noch voller Dinge.
Da wir das Faltungs-LSTM nur auf ein Minimum implementiert haben, müssen Sie, wenn Sie verschiedene Dinge ausprobieren möchten, mit dem Zellen-Wrapper, den Systemmethoden tf.nn und seq2seq spielen. Wenn ich es für die Arbeit benutze, werde ich es wahrscheinlich implementieren und optimieren. Das beste Ergebnis war jedenfalls, dass ich den Code um RNN fest lesen konnte.
Ich wünsche allen ein schönes Jahr.
Recommended Posts