[PYTHON] [TensorFlow 2 / Keras] Ausführen des Lernens mit CTC Loss in Keras

Einführung

Im vorherigen Artikel habe ich geschrieben, wie man CTC-Verlust (Connectionist Temporal Classification) verwendet, um ein Modell (RNN) zu lernen, das Daten variabler Länge für die Eingabe und Ausgabe mit TensorFlow 2.x verwendet. [\ TensorFlow 2 ] Lernen Sie RNN mit CTC Loss-Qiita

Es war jedoch noch eine entladen. ** Wie man mit CTC-Verlust gut mit Keras umgeht **. Ich habe es im vorherigen Artikel versucht, aber das Ergebnis war unbemerkt, mit vielen zweifelhaften Hacks und langsamer Verarbeitung. Dieses Mal habe ich die Lösung gefunden, notieren Sie sie sich.

Ich denke, dass die hier beschriebene Methode nicht nur auf CTC-Verluste angewendet werden kann, sondern auch, wenn Sie eine spezielle Verlustfunktion definieren und erlernen möchten.

Überprüfungsumgebung

Compile () ist nicht die einzige Möglichkeit, eine Verlustfunktion zu definieren

Ich denke, der Grund für die letzte Niederlage war schließlich, dass ich beim Versuch, CTC-Verlust mit Model.compile () von Keras zu definieren, nicht weiterkommen konnte. Tatsächlich gab es jedoch eine andere Möglichkeit, eine Verlustfunktion und eine Bewertungsskala (korrekte Antwortrate usw.) als "Model.compile ()" hinzuzufügen.

Train and evaluate with Keras | TensorFlow Core

The overwhelming majority of losses and metrics can be computed from y_true and y_pred, where y_pred is an output of your model. But not all of them. For instance, a regularization loss may only require the activation of a layer (there are no targets in this case), and this activation may not be a model output.

In such cases, you can call self.add_loss(loss_value) from inside the call method of a custom layer. Here's a simple example that adds activity regularization (note that activity regularization is built-in in all Keras layers -- this layer is just for the sake of providing a concrete example): (Weggelassen) You can do the same for logging metric values: (Weggelassen)

** Wenn Sie Ihre eigene Ebene definieren und add_loss () verwenden, können Sie die Verlustfunktion unabhängig vom Prototyp von (y_true, y_pred) definieren! ** **. Nein, ich muss das Tutorial richtig lesen ... orz

Die API-Beschreibung von add_loss (), die die Verlustfunktion definiert, und add_metric (), die die Bewertungsskala definiert, finden Sie auf der folgenden Seite. tf.keras.layers.Layer | TensorFlow Core v2.1.0

Wie Sie dem Beispielcode im Tutorial entnehmen können, ** sind die Verlustfunktion und die Bewertungsskala "Tensor" und werden durch die Operation "Tensor" zusammengesetzt. ** Das im Beispielcode enthaltene "x1" ist ein "Tensor", der die Ausgabe der Schicht darstellt und zur Darstellung der Verlustfunktion verwendet werden kann.

inputs = keras.Input(shape=(784,), name='digits')
x1 = layers.Dense(64, activation='relu', name='dense_1')(inputs)
x2 = layers.Dense(64, activation='relu', name='dense_2')(x1)
outputs = layers.Dense(10, name='predictions')(x2)
model = keras.Model(inputs=inputs, outputs=outputs)

model.add_loss(tf.reduce_sum(x1) * 0.1)

model.add_metric(keras.backend.std(x1),
                 name='std_of_activation',
                 aggregation='mean')

In diesem Fall sind die Informationen zur Länge der Merkmalsmengenreihen / Etikettenreihen sowie die Etikettenreihen selbst notwendige Informationen für die Berechnung des CTC-Verlusts, daher müssen sie als "Tensor" gespeichert werden. Mit anderen Worten, diese müssen auch als Eingaben in das Modell angegeben werden (als x, nicht als y). Mit anderen Worten, ** erstellen Sie ein Modell mit mehreren Eingaben **.

Beispielcode, der gut funktioniert hat

Der ursprüngliche Quellcode ist GitHub - igormq/ctc_tensorflow_example: CTC + Tensorflow Example for ASR ist.

ctc_tensorflow_example_tf2_keras.py


#  Compatibility imports
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import time

import tensorflow as tf
import scipy.io.wavfile as wav
import numpy as np

from six.moves import xrange as range

try:
    from python_speech_features import mfcc
except ImportError:
    print("Failed to import python_speech_features.\n Try pip install python_speech_features.")
    raise ImportError

from utils import maybe_download as maybe_download
from utils import sparse_tuple_from as sparse_tuple_from

# Constants
SPACE_TOKEN = '<space>'
SPACE_INDEX = 0
FIRST_INDEX = ord('a') - 1  # 0 is reserved to space
FEAT_MASK_VALUE = 1e+10

# Some configs
num_features = 13
num_units = 50 # Number of units in the LSTM cell
# Accounting the 0th indice +  space + blank label = 28 characters
num_classes = ord('z') - ord('a') + 1 + 1 + 1

# Hyper-parameters
num_epochs = 400
num_layers = 1
batch_size = 2
initial_learning_rate = 0.005
momentum = 0.9

# Loading the data

audio_filename = maybe_download('LDC93S1.wav', 93638)
target_filename = maybe_download('LDC93S1.txt', 62)

fs, audio = wav.read(audio_filename)

# create a dataset composed of data with variable lengths
inputs = mfcc(audio, samplerate=fs)
inputs = (inputs - np.mean(inputs))/np.std(inputs)
inputs_short = mfcc(audio[fs*8//10:fs*20//10], samplerate=fs)
inputs_short = (inputs_short - np.mean(inputs_short))/np.std(inputs_short)
# Transform in 3D array
train_inputs = tf.ragged.constant([inputs, inputs_short], dtype=np.float32)
train_seq_len = tf.cast(train_inputs.row_lengths(), tf.int32)
train_inputs = train_inputs.to_tensor(default_value=FEAT_MASK_VALUE)

# Reading targets
with open(target_filename, 'r') as f:

    #Only the last line is necessary
    line = f.readlines()[-1]

    # Get only the words between [a-z] and replace period for none
    original = ' '.join(line.strip().lower().split(' ')[2:]).replace('.', '')
    targets = original.replace(' ', '  ')
    targets = targets.split(' ')

# Adding blank label
targets = np.hstack([SPACE_TOKEN if x == '' else list(x) for x in targets])

# Transform char into index
targets = np.asarray([SPACE_INDEX if x == SPACE_TOKEN else ord(x) - FIRST_INDEX
                      for x in targets])
# Creating sparse representation to feed the placeholder
train_targets = tf.ragged.constant([targets, targets[13:32]], dtype=np.int32) 
train_targets_len = tf.cast(train_targets.row_lengths(), tf.int32)
train_targets = train_targets.to_sparse() 

# We don't have a validation dataset :(
val_inputs, val_targets, val_seq_len, val_targets_len = train_inputs, train_targets, \
                                                        train_seq_len, train_targets_len

# THE MAIN CODE!

# add loss and metrics with a custom layer
class CTCLossLayer(tf.keras.layers.Layer):
    def call(self, inputs):
        labels = inputs[0]
        logits = inputs[1]
        label_len = inputs[2]
        logit_len = inputs[3]

        logits_trans = tf.transpose(logits, (1, 0, 2))
        label_len = tf.reshape(label_len, (-1,))
        logit_len = tf.reshape(logit_len, (-1,))
        loss = tf.reduce_mean(tf.nn.ctc_loss(labels, logits_trans, label_len, logit_len, blank_index=-1))
        # define loss here instead of compile()
        self.add_loss(loss)

        # decode
        decoded, _ = tf.nn.ctc_greedy_decoder(logits_trans, logit_len)

        # Inaccuracy: label error rate
        ler = tf.reduce_mean(tf.edit_distance(tf.cast(decoded[0], tf.int32),
                                          labels))
        self.add_metric(ler, name="ler", aggregation="mean")

        return logits  # Pass-through layer.

# Defining the cell
# Can be:
#   tf.nn.rnn_cell.RNNCell
#   tf.nn.rnn_cell.GRUCell 
cells = []
for _ in range(num_layers):
    cell = tf.keras.layers.LSTMCell(num_units)  # Or LSTMCell(num_units)
    cells.append(cell)
stack = tf.keras.layers.StackedRNNCells(cells)

input_feature     = tf.keras.layers.Input((None, num_features), name="input_feature")
input_label       = tf.keras.layers.Input((None,), dtype=tf.int32, sparse=True, name="input_label")
input_feature_len = tf.keras.layers.Input((1,), dtype=tf.int32, name="input_feature_len")
input_label_len   = tf.keras.layers.Input((1,), dtype=tf.int32, name="input_label_len")

layer_masking = tf.keras.layers.Masking(FEAT_MASK_VALUE)(input_feature)
layer_rnn     = tf.keras.layers.RNN(stack, return_sequences=True)(layer_masking)
layer_output  = tf.keras.layers.Dense(
                   num_classes,
                   kernel_initializer=tf.keras.initializers.TruncatedNormal(0.0, 0.1),
                   bias_initializer="zeros",
                   name="logit")(layer_rnn)
layer_loss = CTCLossLayer()([input_label, layer_output, input_label_len, input_feature_len])

# create models for training and prediction (sharing weights)
model_train = tf.keras.models.Model(
            inputs=[input_feature, input_label, input_feature_len, input_label_len],
            outputs=layer_loss)

model_predict = tf.keras.models.Model(inputs=input_feature, outputs=layer_output)

optimizer = tf.keras.optimizers.SGD(initial_learning_rate, momentum)
# adding no loss: we have already defined with a custom layer
model_train.compile(optimizer=optimizer)

# training: y is dummy!
model_train.fit(x=[train_inputs, train_targets, train_seq_len, train_targets_len], y=None,
                validation_data=([val_inputs, val_targets, val_seq_len, val_targets_len], None),
                epochs=num_epochs)

# Decoding
print('Original:')
print(original)
print(original[13:32])
print('Decoded:')
decoded, _ = tf.nn.ctc_greedy_decoder(tf.transpose(model_predict.predict(train_inputs), (1, 0, 2)), train_seq_len)
d = tf.sparse.to_dense(decoded[0], default_value=-1).numpy()
str_decoded = [''.join([chr(x + FIRST_INDEX) for x in np.asarray(row) if x != -1]) for row in d]
for s in str_decoded:
    # Replacing blank label to none
    s = s.replace(chr(ord('z') + 1), '')
    # Replacing space label to space
    s = s.replace(chr(ord('a') - 1), ' ')
    print(s)

Das Ausführungsergebnis ist wie folgt.

Train on 2 samples, validate on 2 samples
Epoch 1/400
2/2 [==============================] - 2s 991ms/sample - loss: 546.3565 - ler: 1.0668 - val_loss: 464.2611 - val_ler: 0.8801
Epoch 2/400
2/2 [==============================] - 0s 136ms/sample - loss: 464.2611 - ler: 0.8801 - val_loss: 179.9780 - val_ler: 1.0000
(Weggelassen)
Epoch 400/400
2/2 [==============================] - 0s 135ms/sample - loss: 1.6670 - ler: 0.0000e+00 - val_loss: 1.6565 - val_ler: 0.0000e+00
Original:
she had your dark suit in greasy wash water all year
dark suit in greasy
Decoded:
she had your dark suit in greasy wash water all year
dark suit in greasy

Es scheint kein Problem mit der Verarbeitungszeit und dem Wert der Fehlerrate zu geben, und es scheint, dass es endlich richtig funktioniert hat ...! (Da die Verarbeitungszeit der Wert geteilt durch die Anzahl der Abtastwerte ist, ist die tatsächliche Zeit doppelt so groß wie der angezeigte Wert. Wenn sie jedoch für 2 Abtastwerte 300 ms oder weniger beträgt, kann gesagt werden, dass sie mit der vorherigen Zeit identisch ist.)

Kommentar

Addition von Verlust, Metriken

Wie eingangs erwähnt, können Sie den modellbezogenen Tensor verwenden, um die Verlustfunktion mithilfe von Layer.add_loss () zu definieren. Der obige Code definiert eine Ebene namens "CTCLossLayer" und in "call ()" die Version TensorFlow 2.x (vorheriger Artikel). Siehe #% E3% 82% B5% E3% 83% B3% E3% 83% 97% E3% 83% AB% E3% 82% B3% E3% 83% BC% E3% 83% 892))) Ich schreibe fast den gleichen Prozess. Schließlich wird die Eingabe "logits" so ausgegeben, wie sie ist.

Hier hat call () vier Argumente außer self. Mit diesen vier Informationen können Sie CTC-Verluste verursachen und dekodieren. Wenn Sie ein Modell erstellen, benötigen Sie außerdem vier Eingaben für die Ebene, wie unten gezeigt.

layer_loss = CTCLossLayer()(input_label, layer_output, input_label_len, input_feature_len)

Eingabeebene

Die im vorherigen Argument angegebenen Informationen müssen "Tensor" sein. layer_output ist dasselbe wie das normale Keras-Modell, aber input_label, input_label_len, input_feature_len werden durch Hinzufügen einer Eingabeebene unterstützt.

input_feature     = tf.keras.layers.Input((None, num_features), name="input_feature")
input_label       = tf.keras.layers.Input((None,), dtype=tf.int32, sparse=True, name="input_label")
input_feature_len = tf.keras.layers.Input((1,), dtype=tf.int32, name="input_feature_len")
input_label_len   = tf.keras.layers.Input((1,), dtype=tf.int32, name="input_label_len")

Wie Sie sehen können, erstellen wir eine Input-Ebene mit der entsprechenden Form und dtype. Der dtype des Layers außer dem Feature-Betrag sollte int32 sein. In Anbetracht der Spezifikation des Arguments von "tf.nn.ctc_loss" möchte ich die Form von "input_feature_len" und "input_label_len" wirklich in "()" ändern, erhalte aber später eine Fehlermeldung. Ich konnte es nicht gut bewegen. Daher wird die Form als "(1,)" geschrieben und "Umformen" in "CTCLossLayer" ausgeführt.

Eine andere Sache, die ich hinzugefügt habe, ist "sparse = True" beim Erstellen von "input_label". Wenn dies angegeben ist, wird der Tensor, der dem input_label entspricht, zu SparseTensor. tf.keras.Input | TensorFlow Core v2.1.0

Dieses sparse = True ist ein Maß dafür, dass bei der Berechnung der Fehlerrate des Decodierungsergebnisses (tf.nn.ctc_loss, das bei der Berechnung des CTC-Verlusts verwendet wird) das richtige Antwortetikett mit SparseTensorübergeben werden muss. kann auch SparseTensor) empfangen. Die von "Model.fit ()" usw. angegebenen Daten werden ebenfalls von "SparseTensor" erstellt. tf.nn.ctc_loss | TensorFlow Core v2.1.0 tf.edit_distance | TensorFlow Core v2.1.0

In ähnlicher Weise scheint es, wenn man die Eingabe von "RaggedTensor" annimmt, "ragged = True" zu geben.

Wie erstelle ich ein Modell?

Die Modelle für Training und Vorhersage (Inferenz) werden wie unten gezeigt separat erstellt.

model_train = tf.keras.models.Model(
            inputs=[input_feature, input_label, input_feature_len, input_label_len],
            outputs=layer_loss)

model_predict = tf.keras.models.Model(inputs=input_feature, outputs=layer_output)

Während des Trainings waren vier Eingaben erforderlich, aber nur Logits (dh die Ausgabe von "Dense") sind für die Vorhersage (Decodierung) erforderlich, sodass es ausreicht, Funktionen als Eingaben zu haben. Daher arbeitet das Vorhersagemodell mit nur einer Eingabe. Natürlich kann der Verlust nicht berechnet werden, aber er ist nicht nur für Decodierungszwecke erforderlich. Geben Sie daher "layer_output" an, bevor Sie "CTCLossLayer" als Ausgabe durchlaufen.

Wenn Sie es in einem Diagramm zeichnen, wird es so sein. image.png

Da die Ebenen mit Gewichten erstellt werden, um gemeinsam genutzt zu werden, ist es möglich, mit dem Vorhersagemodell wie nach dem Training mit dem Trainingsmodell zu schließen.

Modell kompilieren

Da CTC Loss in einer eigenen Ebene definiert wurde, muss in compile () keine Verlustfunktion definiert werden. In diesem Fall müssen Sie nicht einfach das Argument "Verlust" angeben.

model_train.compile(optimizer=optimizer)

Ausführung des Lernens

Die korrekten Beschriftungs- und Längeninformationen, die zur Berechnung der Verlustfunktion verwendet werden, sind die Informationen, die an die Eingabeebene gesendet werden sollen. Sie müssen daher auf der x-Seite des Arguments von Model.fit () angegeben werden. Für y gibt es nichts zu spezifizieren, also schreibe None.

In ähnlicher Weise schreiben Sie für "validation_data" "None" als zweiten Teil des Tapples.

model_train.fit(x=[train_inputs, train_targets, train_seq_len, train_targets_len], y=None,
                validation_data=([val_inputs, val_targets, val_seq_len, val_targets_len], None),
                epochs=num_epochs)

Um ehrlich zu sein, ich weiß nicht, ob dies die Verwendung ist, die die Formel erwartet, aber wenn Sie in compile () nicht loss angeben, funktioniert y = None einwandfrei ( Wenn "Verlust" angegeben wird, ist eine Bezeichnung erforderlich, die an das Argument "y_true" der Verlustfunktion übergeben werden soll. Daher tritt natürlich ein Fehler auf, sofern nicht einige Daten an "y" übergeben werden.

Ausgabe des Decodierungsergebnisses

Wie oben erwähnt, verwenden Sie model_predict, wenn Sie darauf schließen. Es ist in Ordnung, dem Argument von "vorhersagen ()" nur die Merkmalsmengenreihen zuzuweisen.

decoded, _ = tf.nn.ctc_greedy_decoder(tf.transpose(model_predict.predict(train_inputs), (1, 0, 2)), train_seq_len)

Andere Dinge, über die man sich Sorgen machen muss

―― Masking und input_feature_len haben ähnliche Funktionen, daher fühlt es sich etwas überflüssig an ...

Zusammenfassung

Nachdem Keras das Tutorial richtig gelesen hatte, konnte es mit CTC Loss lernen. Überraschenderweise hat Keras auch eine kleine Wendung. Es tut mir Leid.

Recommended Posts

[TensorFlow 2 / Keras] Ausführen des Lernens mit CTC Loss in Keras
So führen Sie TensorFlow 1.0-Code in 2.0 aus
So führen Sie CNN in 1 Systemnotation mit Tensorflow 2 aus
So führen Sie Tests zusammen mit Python unittest aus
[TensorFlow 2] Lernen Sie RNN mit CTC-Verlust
Für Anfänger, wie man mit häufigen Fehlern in Keras umgeht
So arbeiten Sie mit BigQuery in Python
Umgang mit Speicherlecks in matplotlib.pyplot
So reduzieren Sie die GPU-Speichernutzung mit Keras
[REAPER] Wie man Reascript mit Python spielt
Ich habe versucht, Keras in TFv1.1 zu integrieren
Umgang mit Laufzeitfehlern in subprocess.call
Wie man tkinter mit Python in Pyenv benutzt
So führen Sie LeapMotion mit Nicht-Apple Python aus
[TF] So erstellen Sie Tensorflow in einer Proxy-Umgebung
So installieren Sie das Deep Learning Framework Tensorflow 1.0 in der Windows Anaconda-Umgebung
So konvertieren / wiederherstellen Sie einen String mit [] in Python
Ausführen des in Ansible Tower hinzugefügten Ansible-Moduls
So führen Sie AutoGluon in einer Google Colab-GPU-Umgebung aus
So führen Sie eine Hash-Berechnung mit Salt in Python durch
Erklären Sie ausführlich, wie Sie mit Python einen Sound erzeugen
So führen Sie Python im virtuellen Raum aus (für MacOS)
Umgang mit Pyenv-Initialisierungsfehlern bei Fischen 3.1.0
So führen Sie mit OpenCV ein Null-Padding in einer Zeile durch
Ein Memorandum zur Verwendung von Keras 'keras.preprocessing.image
So laden Sie Dateien in Google Drive mit Google Colaboratory
Freigeben von Ordnern für Docker und Windows mit Tensorflow
Zugriff mit dem Cache beim Lesen von_json mit Pandas
So führen Sie das Lernen mit SageMaker ohne Sitzungszeitlimit durch
Wie man setUp nur einmal in Python unittest ausführt
Ich habe versucht, Grad-CAM mit Keras und Tensorflow zu implementieren
Einstellung zum Ausführen der Anwendung im Unterverzeichnis mit nginx + uwsgi
Vorgehensweise beim Ausführen von Transaktionen: In Anaconda fehlgeschlagen
[How to!] Lerne und spiele Super Mario mit Tensorflow !!
[TF] Speichern und Laden von Tensorflow-Trainingsparametern
Erstellen einer virtuellen Anaconda-Umgebung für die Verwendung mit Azure Machine Learning und Verknüpfen mit Jupyter
Versuchen Sie es mit TensorFlow
So geben Sie ein Dokument im PDF-Format mit Sphinx aus
Verstärkungslernen in kürzester Zeit mit Keras mit OpenAI Gym
So extrahieren Sie einen Termin in Google Kalender mit Python
So überprüfen Sie das Verhalten von ORM mit einer Datei mit django
So zeichnen Sie interaktiv eine Pipeline für maschinelles Lernen mit scikit-learn und speichern sie in HTML
Wie aktualisiere ich mit SQLAlchemy?
[Django] Wie man Eingabewerte im Voraus mit ModelForm angibt
So manipulieren Sie das DOM im Iframe mit Selen
Für diejenigen, die mit TensorFlow2 maschinelles Lernen beginnen möchten
Um gym_torcs mit ubutnu16 auszuführen
Wie man mit Theano besetzt
[AWS] Umgang mit dem Fehler "Ungültiger Codepunkt" in CloudSearch
So führen Sie eine mit Python + py2app erstellte App aus, die mit Anaconda erstellt wurde
So führen Sie Notepad ++ Python aus
Wie mit SQLAlchemy ändern?
So trennen Sie Zeichenfolgen mit ','
[TF] Laden / Speichern von Modell und Parameter in Keras
Wie man RDP auf Fedora31 macht
Wie man in Python entwickelt
So erstellen Sie einen Datenrahmen und spielen mit Elementen mit Pandas
Wie lösche ich mit SQLAlchemy?