[PYTHON] Videorahmenvorhersage unter Verwendung von Faltungs-LSTM mit TensorFlow

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.

Faltungs-LSTM (Faltungs-LSTM)

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 ↓ 従来のLSTM Gefaltetes LSTM ist ↓ スクリーンショット 2016-12-13 18.34.41.png

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

Implementierung in TensorFlow

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 diesmal gemacht habe

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. スクリーンショット 2016-12-15 14.02.42.png

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.

Über tf.nn.RNNCell

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.

  1. Konzentrieren Sie die Ausgabe der vorherigen Zeit und die neue Eingabe, die in einem allgemeinen LSTM erfolgt, auf die Kanalebene des Bildes.
  2. Um den internen Zustand und die Eingabe / Ausgabe auf die gleiche Größe zu bringen (ohne Kanäle), wird beim Falten eine 0-Auffüllung durchgeführt, und der Schritt wird sowohl vertikal als auch horizontal auf 1 Pixel festgelegt.

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) スクリーンショット 2016-12-13 19.42.57.png

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)

スクリーンショット 2016-12-13 19.56.48.png

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. スクリーンショット 2016-12-13 20.13.16.png

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.

Zeitentwicklung

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)

Bilderzeugung

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.

Ergebnis

Nun, ich verwende TensorBoard nicht zum Protokollieren oder Generieren einer Prüfpunktdatei, aber ich werde das Ergebnis des entsprechenden Ablaufs einfügen.

スクリーンショット 2016-12-15 15.59.18.png

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.

Rücksichtnahme und Zukunft

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

Videorahmenvorhersage unter Verwendung von Faltungs-LSTM mit TensorFlow
Speichern Sie Videos Frame für Frame mit Python OpenCV
Aktienprognose mit TensorFlow (LSTM) ~ Aktienprognose Teil 1 ~
Vorhersage von Effekten mit LSTM mithilfe von Keras + Tensorflow Teil 2 (Berechnung mit GPU)
Versuchen Sie, FX mit LSTM mithilfe von Keras + Tensorflow Teil 3 vorherzusagen (versuchen Sie, alle Parameter zu treffen).
Zundokokiyoshi mit TensorFlow
Brechen Sie Blöcke mit Tensorflow