GradienTape-Konvergenz viel langsamer als Keras.model.fit

8

Ich versuche gerade, die TF2.0- API zu bekommen, aber als ich das GradientTape mit einem normalen keras.Model.fit verglich , bemerkte ich:

  1. Es lief langsamer (wahrscheinlich aufgrund der eifrigen Ausführung)

  2. Es konvergierte viel langsamer (und ich bin mir nicht sicher warum).

+--------+--------------+--------------+------------------+
|  Epoch | GradientTape | GradientTape | keras.Model.fit  |
|        |              |  shuffling   |                  |
+--------+--------------+--------------+------------------+
|    1   |     0.905    |     0.918    |      0.8793      |
+--------+--------------+--------------+------------------+
|    2   |     0.352    |     0.634    |      0.2226      |
+--------+--------------+--------------+------------------+
|    3   |     0.285    |     0.518    |      0.1192      |
+--------+--------------+--------------+------------------+
|    4   |     0.282    |     0.458    |      0.1029      |
+--------+--------------+--------------+------------------+
|    5   |     0.275    |     0.421    |      0.0940      |
+--------+--------------+--------------+------------------+

Hier ist die Trainingsschleife, die ich mit dem GradientTape verwendet habe :


optimizer = keras.optimizers.Adam()
glove_model = GloveModel(vocab_size=len(labels))
train_loss = keras.metrics.Mean(name='train_loss')

@tf.function
def train_step(examples, labels):
    with tf.GradientTape() as tape:
        predictions = glove_model(examples)
        loss = glove_model.glove_loss(labels, predictions)

    gradients = tape.gradient(loss, glove_model.trainable_variables)
    optimizer.apply_gradients(zip(gradients, glove_model.trainable_variables))

    train_loss(loss)



total_step = 0
for epoch in range(epochs_number):

    pbar = tqdm(train_ds.enumerate(), total=int(len(index_data) / batch_size) + 1)

    for ix, (examples, labels) in pbar:

        train_step(examples, labels)


    print(f"Epoch {epoch + 1}, Loss {train_loss.result()}")

    # Reset the metrics for the next epoch
    train_loss.reset_states()

Und hier ist das Keras.Model.fit- Training:

glove_model.compile(optimizer, glove_model.glove_loss)
glove_model.fit(train_ds, epochs=epochs_number)

Hier ist die Quelle tf.data.Dataset

train_ds = data.Dataset.from_tensor_slices(
    (np.hstack([index_rows.reshape(-1, 1), index_cols.reshape(-1, 1)]), index_data)
).shuffle(100000).batch(batch_size, drop_remainder=True)

Und hier ist das Modell.

class GloveModel(keras.Model):

    def __init__(self, vocab_size, dim=100, a=3/4, x_max=100):
        super(GloveModel, self).__init__()

        self.vocab_size = vocab_size
        self.dim = dim
        self.a = a
        self.x_max = x_max

        self.target_embedding = layers.Embedding(
            input_dim=self.vocab_size, output_dim=self.dim, input_length=1, name="target_embedding"
        )
        self.target_bias = layers.Embedding(
            input_dim=self.vocab_size, output_dim=1, input_length=1, name="target_bias"
        )

        self.context_embedding = layers.Embedding(
            input_dim=self.vocab_size, output_dim=self.dim, input_length=1, name="context_embedding"
        )
        self.context_bias = layers.Embedding(
            input_dim=self.vocab_size, output_dim=1, input_length=1, name="context_bias"
        )

        self.dot_product = layers.Dot(axes=-1, name="dot")

        self.prediction = layers.Add(name="add")
        self.step = 0

    def call(self, inputs):

        target_ix = inputs[:, 0]
        context_ix = inputs[:, 1]

        target_embedding = self.target_embedding(target_ix)
        target_bias = self.target_bias(target_ix)

        context_embedding = self.context_embedding(context_ix)
        context_bias = self.context_bias(context_ix)

        dot_product = self.dot_product([target_embedding, context_embedding])
        prediction = self.prediction([dot_product, target_bias, context_bias])

        return prediction

    def glove_loss(self, y_true, y_pred):

        weight = tf.math.minimum(
            tf.math.pow(y_true/self.x_max, self.a), 1.0
        )
        loss_value = tf.math.reduce_mean(weight * tf.math.pow(y_pred - tf.math.log(y_true), 2.0))

        return loss_value



Ich habe mehrere Konfigurationen und Optimierer ausprobiert, aber nichts scheint die Konvergenzrate zu ändern.

Benjamin Breton
quelle
1
Eine Sache, die Sie sich ansehen sollten, ist das Mischen von Daten vor jeder Epoche.
THN
Ich habe genau das gleiche Mischen zwischen der Anpassungsmethode und GradientTape, weil ich die tf.Data-API verwende.
Benjamin Breton
1
Ich denke, sie sind nicht genau gleich. Können Sie den Code von Ihnen zeigen tfds? Beachten Sie, dass Keras .fitstandardmäßig vor jeder Epoche gemischt werden. Sie können testen, indem Sie das Mischen in Keras deaktivieren und deren Konvergenzrate vergleichen.
THN
@THN Ich werde es dir senden, aber ich führe bereits ein Shuffle mit der tf.Dataset-API durch, damit es nichts ändert, oder?
Benjamin Breton
@THN Ich habe die tf.data.Dataset
Benjamin Breton

Antworten:

2

Dataset.shuffle()mische nur jedes Minibatch, damit jede Epoche die gleiche Reihenfolge hat. Keras .fit()verwendet einige magische Methoden, um den gesamten Datensatz vor jeder Epoche zu mischen. Um dies in TF zu tun, müssen Sie Dataset verwenden .repeat(epochs_number)und .shuffle(..., reshuffle_each_iteration=True):

train_ds = data.Dataset.from_tensor_slices(
    (np.hstack([index_rows.reshape(-1, 1), index_cols.reshape(-1, 1)]), index_data)
    ).shuffle(100000, reshuffle_each_iteration=True
    ).batch(batch_size, drop_remainder=True
    ).repeat(epochs_number)

for ix, (examples, labels) in train_ds.enumerate():
    train_step(examples, labels)
    current_epoch = ix // (len(index_data) // batch_size)

Diese Problemumgehung ist weder schön noch natürlich. Im Moment können Sie sie verwenden, um jede Epoche zu mischen. Es ist ein bekanntes Problem und wird behoben. Sie können es in Zukunft for epoch in range(epochs_number)anstelle von verwenden .repeat().

THN
quelle
Es tut mir leid, ich habe Ihren Code hinzugefügt, aber die Konvergenz ist noch langsamer. Ich habe die Ergebnisse in der Spalte GradientTape shuffle hinzugefügt. Es macht für mich keinen Sinn ...
Benjamin Breton
@BenjaminBreton An dieser Stelle bezweifle ich, dass in Ihrem Code einige andere Fehler lauern. Vielleicht ist es am besten, einen Link zu Ihrem Repo zu erstellen, um den vollständigen Code anzuzeigen. Wenn Sie sicher sind, dass Ihre Experimente korrekt durchgeführt wurden, sollten Sie ein Problem mit Tensorflow Repo eröffnen.
THN
Vielen Dank für Ihre Hilfe @THN Ich habe das Problem auf dem TF2.0-Repo github.com/tensorflow/tensorflow/issues/33898 veröffentlicht . Ich werde versuchen, den Fehler mit einem anderen Modell zu reproduzieren.
Benjamin Breton
1
Es stellte sich heraus, dass Sie Recht hatten @THN Ich mischte mit numpy und es löste das Problem. Ich werde eine umfassende Antwort veröffentlichen
Benjamin Breton
0

Das Problem ergab sich aus dem Mischen mit der Methode tf.Dataset . Es wurde jeweils nur ein Bucket durch den Datensatz gemischt. Die Verwendung von Keras.Model.fit führte zu besseren Ergebnissen, da wahrscheinlich ein weiteres Mischen hinzugefügt wird .

Ich habe ein Mischen mit hinzugefügt numpy.random.shuffleund es hat die Leistung mit beiden Trainingsmethoden verbessert:

Die Generierung des Datensatzes ist jetzt:

numpy_data = np.hstack([index_rows.reshape(-1, 1), index_cols.reshape(-1, 1), index_data.reshape(-1, 1)])

np.random.shuffle(numpy_data)

indexes = np.array(numpy_data[:, :2], dtype=np.uint32)
labels = np.array(numpy_data[:, 2].reshape(-1, 1), dtype=np.float32)

train_ds = data.Dataset.from_tensor_slices(
    (indexes, labels)
).shuffle(100000).batch(batch_size, drop_remainder=True)

Und die Ergebnisse sind:

+--------+--------------+------------------+
|  Epoch | GradientTape |  keras.Model.fit |
+--------+--------------+------------------+
|    1   |     0.294    |      0.294       |
+--------+--------------+------------------+
|    2   |     0.111    |      0.110       |
+--------+--------------+------------------+
|    3   |     0.089    |      0.089       |
+--------+--------------+------------------+
|    4   |     0.074    |      0.075       |
+--------+--------------+------------------+
|    5   |     0.063    |      0.063       |
+--------+--------------+------------------+

Der Trainingstyp pro Epoche ist mit 2 Minuten pro Epoche ungefähr gleich .

Benjamin Breton
quelle