847 lecturi
847 lecturi

Păstrați Keras fit() și antrenați-vă modelul

Prea lung; A citi

Puteți să renunțați la train_step() de la Keras pentru a utiliza un algoritm de instruire particularizat, în timp ce profitați în continuare de caracteristicile fit() cum ar fi apelurile, metrica și instruirea distribuită.
featured image - Păstrați Keras fit() și antrenați-vă modelul
Tensor Flow - [Technical Documentation] HackerNoon profile picture
0-item

Conținut Overview

  • Introducere
  • setare
  • Primul exemplu simplu
  • Mergând la nivel inferior
  • Suport pentru sample_weight & class_weight
  • Fă-ți propriul pas de evaluare
  • Înfășurarea: un exemplu de GAN de la capăt la capăt

Introducere

Când faceți învățare supravegheată, puteți utilizafit()Și totul funcționează fără probleme.

Atunci când aveți nevoie pentru a scrie propriul ciclu de formare de la zero, puteți utilizaGradientTapeIa-ți controlul asupra fiecărui detaliu.

Dar dacă aveți nevoie de un algoritm de formare particularizat, dar doriți totuși să beneficiați de caracteristicile convenabile alefit(), cum ar fi apelurile, suportul de distribuție încorporat sau fuziunea pas cu pas?

Un principiu de bază al Keras esteprogressive disclosure of complexityAr trebui să puteți intra întotdeauna în fluxurile de lucru de nivel inferior într-un mod treptat.Nu ar trebui să cădeți dintr-o stâncă dacă funcționalitatea de nivel înalt nu se potrivește exact cu cazul dvs. de utilizare.Ar trebui să puteți obține mai mult control asupra detaliilor mici, păstrând în același timp o cantitate proporțională de confort de nivel înalt.

Când trebuie să personalizezi ceea cefit()Da, ar trebui săoverride the training step function of the ModelAceasta este funcţia care se numeştefit()pentru fiecare batch de date. veţi putea apoi să apelaţifit()ca de obicei - și va rula propriul algoritm de învățare.

Rețineți că acest model nu vă împiedică să construiți modele cu API-ul funcțional.SequentialModele API funcționale sau modele subclasate.

Să vedem cum funcționează asta.

setare

Necesită TensorFlow 2.8 sau mai recent.


import tensorflow as tf
from tensorflow import keras


Primul exemplu simplu

Să începem cu un exemplu simplu:

  • Creăm o nouă clasă care subclases keras.Model.
  • Trebuie doar să suprapunem metoda train_step (auto, date).
  • Returnează un dicționar care cartografiere nume metrice (inclusiv pierderea) la valoarea lor curentă.

Argumentul de intraredataeste ceea ce este trecut pentru a se potrivi ca date de formare:

  • Dacă treceți aria Numpy, apelând fit(x, y, ...), atunci datele vor fi tuple (x, y)
  • Dacă treceți un set tf.data.Dataset, apelând fit(dataset, ...), atunci datele vor fi ceea ce se obține de dataset la fiecare lot.

În corpul detrain_stepmetodă, implementăm o actualizare de formare regulată, similară cu cea cu care sunteți deja familiarizați.we compute the loss via self.compute_loss()care se încadrează în funcţia (funcţiile) care au fost transmisecompile().

În mod similar, se cheamămetric.update_state(y, y_pred)În metrică de laself.metrics, pentru a actualiza starea metricilor care au fost transmise încompile()Noi căutăm rezultate dinself.metricsîn cele din urmă pentru a recupera valoarea lor actuală.



class CustomModel(keras.Model):
    def train_step(self, data):
        # Unpack the data. Its structure depends on your model and
        # on what you pass to `fit()`.
        x, y = data

        with tf.GradientTape() as tape:
            y_pred = self(x, training=True)  # Forward pass
            # Compute the loss value
            # (the loss function is configured in `compile()`)
            loss = self.compute_loss(y=y, y_pred=y_pred)

        # Compute gradients
        trainable_vars = self.trainable_variables
        gradients = tape.gradient(loss, trainable_vars)
        # Update weights
        self.optimizer.apply_gradients(zip(gradients, trainable_vars))
        # Update metrics (includes the metric that tracks the loss)
        for metric in self.metrics:
            if metric.name == "loss":
                metric.update_state(loss)
            else:
                metric.update_state(y, y_pred)
        # Return a dict mapping metric names to current value
        return {m.name: m.result() for m in self.metrics}

Să încercăm acest lucru:


import numpy as np

# Construct and compile an instance of CustomModel
inputs = keras.Input(shape=(32,))
outputs = keras.layers.Dense(1)(inputs)
model = CustomModel(inputs, outputs)
model.compile(optimizer="adam", loss="mse", metrics=["mae"])

# Just use `fit` as usual
x = np.random.random((1000, 32))
y = np.random.random((1000, 1))
model.fit(x, y, epochs=3)


Epoch 1/3
32/32 [==============================] - 3s 2ms/step - loss: 1.6446
Epoch 2/3
32/32 [==============================] - 0s 2ms/step - loss: 0.7554
Epoch 3/3
32/32 [==============================] - 0s 2ms/step - loss: 0.3924
<keras.src.callbacks.History at 0x7fef5c11ba30>


Mergând la nivel inferior

Desigur, puteți sări doar trecerea unei funcții de pierdere încompile()În loc să faci totulmanualăîntrain_stepLa fel și cu metrologia.

Iată un exemplu de nivel inferior, care utilizează numaicompile()Pentru a configura optimizatorul:

  • Începem prin crearea de instanțe metrice pentru a urmări pierderea noastră și un scor MAE (în __init__()).
  • Implementăm un train_step() personalizat care actualizează starea acestor metrici (prin apelarea update_state() pe ele), apoi le interogăm (prin rezultat()) pentru a le returna valoarea medie curentă, pentru a fi afișată de bara de progres și pentru a fi trecută la orice apelare.
  • Rețineți că ar trebui să apelăm reset_states() pe metricile noastre între fiecare epocă! În caz contrar, apelarea rezultatului() ar returna o medie de la începutul instruirii, în timp ce de obicei lucrăm cu medii per epocă. Din fericire, cadrul poate face acest lucru pentru noi: doar listați orice metrică pe care doriți să o resetați în proprietatea metrică a modelului. Modelul va apela reset_states() pe orice obiect listat aici la începutul fiecărei epoci fit() sau la începutul unui apel pentru evaluare().



class CustomModel(keras.Model):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.loss_tracker = keras.metrics.Mean(name="loss")
        self.mae_metric = keras.metrics.MeanAbsoluteError(name="mae")

    def train_step(self, data):
        x, y = data

        with tf.GradientTape() as tape:
            y_pred = self(x, training=True)  # Forward pass
            # Compute our own loss
            loss = keras.losses.mean_squared_error(y, y_pred)

        # Compute gradients
        trainable_vars = self.trainable_variables
        gradients = tape.gradient(loss, trainable_vars)

        # Update weights
        self.optimizer.apply_gradients(zip(gradients, trainable_vars))

        # Compute our own metrics
        self.loss_tracker.update_state(loss)
        self.mae_metric.update_state(y, y_pred)
        return {"loss": self.loss_tracker.result(), "mae": self.mae_metric.result()}

    @property
    def metrics(self):
        # We list our `Metric` objects here so that `reset_states()` can be
        # called automatically at the start of each epoch
        # or at the start of `evaluate()`.
        # If you don't implement this property, you have to call
        # `reset_states()` yourself at the time of your choosing.
        return [self.loss_tracker, self.mae_metric]


# Construct an instance of CustomModel
inputs = keras.Input(shape=(32,))
outputs = keras.layers.Dense(1)(inputs)
model = CustomModel(inputs, outputs)

# We don't pass a loss or metrics here.
model.compile(optimizer="adam")

# Just use `fit` as usual -- you can use callbacks, etc.
x = np.random.random((1000, 32))
y = np.random.random((1000, 1))
model.fit(x, y, epochs=5)


Epoch 1/5
32/32 [==============================] - 0s 2ms/step - loss: 0.3240 - mae: 0.4583
Epoch 2/5
32/32 [==============================] - 0s 2ms/step - loss: 0.2416 - mae: 0.3984
Epoch 3/5
32/32 [==============================] - 0s 2ms/step - loss: 0.2340 - mae: 0.3919
Epoch 4/5
32/32 [==============================] - 0s 2ms/step - loss: 0.2274 - mae: 0.3870
Epoch 5/5
32/32 [==============================] - 0s 2ms/step - loss: 0.2197 - mae: 0.3808
<keras.src.callbacks.History at 0x7fef3c130b20>


SprijinEșantionare - greutateşiclass_weight

Eșantionare - greutateClasă - greutate

S-ar putea să fi observat că primul nostru exemplu de bază nu a făcut nicio mențiune despre cântărirea eșantionului.fit()Argumentelesample_weightşiclass_weightAți face pur și simplu următoarele:

  • Unpack sample_weight din argumentul de date
  • Transmiteți-l la compute_loss & update_state (desigur, puteți să-l aplicați și manual dacă nu vă bazați pe compile() pentru pierderi și metrici)
  • Asta este.



class CustomModel(keras.Model):
    def train_step(self, data):
        # Unpack the data. Its structure depends on your model and
        # on what you pass to `fit()`.
        if len(data) == 3:
            x, y, sample_weight = data
        else:
            sample_weight = None
            x, y = data

        with tf.GradientTape() as tape:
            y_pred = self(x, training=True)  # Forward pass
            # Compute the loss value.
            # The loss function is configured in `compile()`.
            loss = self.compute_loss(
                y=y,
                y_pred=y_pred,
                sample_weight=sample_weight,
            )

        # Compute gradients
        trainable_vars = self.trainable_variables
        gradients = tape.gradient(loss, trainable_vars)

        # Update weights
        self.optimizer.apply_gradients(zip(gradients, trainable_vars))

        # Update the metrics.
        # Metrics are configured in `compile()`.
        for metric in self.metrics:
            if metric.name == "loss":
                metric.update_state(loss)
            else:
                metric.update_state(y, y_pred, sample_weight=sample_weight)

        # Return a dict mapping metric names to current value.
        # Note that it will include the loss (tracked in self.metrics).
        return {m.name: m.result() for m in self.metrics}


# Construct and compile an instance of CustomModel
inputs = keras.Input(shape=(32,))
outputs = keras.layers.Dense(1)(inputs)
model = CustomModel(inputs, outputs)
model.compile(optimizer="adam", loss="mse", metrics=["mae"])

# You can now use sample_weight argument
x = np.random.random((1000, 32))
y = np.random.random((1000, 1))
sw = np.random.random((1000, 1))
model.fit(x, y, sample_weight=sw, epochs=3)


Epoch 1/3
32/32 [==============================] - 0s 2ms/step - loss: 0.1298
Epoch 2/3
32/32 [==============================] - 0s 2ms/step - loss: 0.1179
Epoch 3/3
32/32 [==============================] - 0s 2ms/step - loss: 0.1121
<keras.src.callbacks.History at 0x7fef3c168100>


Fă-ți propriul pas de evaluare

Ce se întâmplă dacă doriți să faceți același lucru pentru apelurimodel.evaluate()Atunci ai supravietuitest_stepIată cum arată, iată cum arată:



class CustomModel(keras.Model):
    def test_step(self, data):
        # Unpack the data
        x, y = data
        # Compute predictions
        y_pred = self(x, training=False)
        # Updates the metrics tracking the loss
        self.compute_loss(y=y, y_pred=y_pred)
        # Update the metrics.
        for metric in self.metrics:
            if metric.name != "loss":
                metric.update_state(y, y_pred)
        # Return a dict mapping metric names to current value.
        # Note that it will include the loss (tracked in self.metrics).
        return {m.name: m.result() for m in self.metrics}


# Construct an instance of CustomModel
inputs = keras.Input(shape=(32,))
outputs = keras.layers.Dense(1)(inputs)
model = CustomModel(inputs, outputs)
model.compile(loss="mse", metrics=["mae"])

# Evaluate with our custom test_step
x = np.random.random((1000, 32))
y = np.random.random((1000, 1))
model.evaluate(x, y)


32/32 [==============================] - 0s 1ms/step - loss: 0.9028
0.9028095006942749


Înfășurarea: un exemplu de GAN de la capăt la capăt

Să trecem printr-un exemplu de la capăt la capăt care profită de tot ceea ce tocmai ați învățat.

Să ne gândim:

  • O rețea de generatoare a fost concepută pentru a genera imagini 28x28x1.
  • O rețea de discriminatori a vrut să clasifice imaginile 28x28x1 în două clase ("fals" și "real").
  • Un optimizator pentru fiecare.
  • O funcție de pierdere pentru a instrui discriminatorul.



from tensorflow.keras import layers

# Create the discriminator
discriminator = keras.Sequential(
    [
        keras.Input(shape=(28, 28, 1)),
        layers.Conv2D(64, (3, 3), strides=(2, 2), padding="same"),
        layers.LeakyReLU(alpha=0.2),
        layers.Conv2D(128, (3, 3), strides=(2, 2), padding="same"),
        layers.LeakyReLU(alpha=0.2),
        layers.GlobalMaxPooling2D(),
        layers.Dense(1),
    ],
    name="discriminator",
)

# Create the generator
latent_dim = 128
generator = keras.Sequential(
    [
        keras.Input(shape=(latent_dim,)),
        # We want to generate 128 coefficients to reshape into a 7x7x128 map
        layers.Dense(7 * 7 * 128),
        layers.LeakyReLU(alpha=0.2),
        layers.Reshape((7, 7, 128)),
        layers.Conv2DTranspose(128, (4, 4), strides=(2, 2), padding="same"),
        layers.LeakyReLU(alpha=0.2),
        layers.Conv2DTranspose(128, (4, 4), strides=(2, 2), padding="same"),
        layers.LeakyReLU(alpha=0.2),
        layers.Conv2D(1, (7, 7), padding="same", activation="sigmoid"),
    ],
    name="generator",
)

Iată o clasă completă de caracteristici GAN, overridingcompile()să-şi folosească propria semnătură şi să implementeze întregul algoritm GAN în 17 rânduritrain_step:



class GAN(keras.Model):
    def __init__(self, discriminator, generator, latent_dim):
        super().__init__()
        self.discriminator = discriminator
        self.generator = generator
        self.latent_dim = latent_dim
        self.d_loss_tracker = keras.metrics.Mean(name="d_loss")
        self.g_loss_tracker = keras.metrics.Mean(name="g_loss")

    def compile(self, d_optimizer, g_optimizer, loss_fn):
        super().compile()
        self.d_optimizer = d_optimizer
        self.g_optimizer = g_optimizer
        self.loss_fn = loss_fn

    def train_step(self, real_images):
        if isinstance(real_images, tuple):
            real_images = real_images[0]
        # Sample random points in the latent space
        batch_size = tf.shape(real_images)[0]
        random_latent_vectors = tf.random.normal(shape=(batch_size, self.latent_dim))

        # Decode them to fake images
        generated_images = self.generator(random_latent_vectors)

        # Combine them with real images
        combined_images = tf.concat([generated_images, real_images], axis=0)

        # Assemble labels discriminating real from fake images
        labels = tf.concat(
            [tf.ones((batch_size, 1)), tf.zeros((batch_size, 1))], axis=0
        )
        # Add random noise to the labels - important trick!
        labels += 0.05 * tf.random.uniform(tf.shape(labels))

        # Train the discriminator
        with tf.GradientTape() as tape:
            predictions = self.discriminator(combined_images)
            d_loss = self.loss_fn(labels, predictions)
        grads = tape.gradient(d_loss, self.discriminator.trainable_weights)
        self.d_optimizer.apply_gradients(
            zip(grads, self.discriminator.trainable_weights)
        )

        # Sample random points in the latent space
        random_latent_vectors = tf.random.normal(shape=(batch_size, self.latent_dim))

        # Assemble labels that say "all real images"
        misleading_labels = tf.zeros((batch_size, 1))

        # Train the generator (note that we should *not* update the weights
        # of the discriminator)!
        with tf.GradientTape() as tape:
            predictions = self.discriminator(self.generator(random_latent_vectors))
            g_loss = self.loss_fn(misleading_labels, predictions)
        grads = tape.gradient(g_loss, self.generator.trainable_weights)
        self.g_optimizer.apply_gradients(zip(grads, self.generator.trainable_weights))

        # Update metrics and return their value.
        self.d_loss_tracker.update_state(d_loss)
        self.g_loss_tracker.update_state(g_loss)
        return {
            "d_loss": self.d_loss_tracker.result(),
            "g_loss": self.g_loss_tracker.result(),
        }

Haideți să o testăm:



# Prepare the dataset. We use both the training & test MNIST digits.
batch_size = 64
(x_train, _), (x_test, _) = keras.datasets.mnist.load_data()
all_digits = np.concatenate([x_train, x_test])
all_digits = all_digits.astype("float32") / 255.0
all_digits = np.reshape(all_digits, (-1, 28, 28, 1))
dataset = tf.data.Dataset.from_tensor_slices(all_digits)
dataset = dataset.shuffle(buffer_size=1024).batch(batch_size)

gan = GAN(discriminator=discriminator, generator=generator, latent_dim=latent_dim)
gan.compile(
    d_optimizer=keras.optimizers.Adam(learning_rate=0.0003),
    g_optimizer=keras.optimizers.Adam(learning_rate=0.0003),
    loss_fn=keras.losses.BinaryCrossentropy(from_logits=True),
)

# To limit the execution time, we only train on 100 batches. You can train on
# the entire dataset. You will need about 20 epochs to get nice results.
gan.fit(dataset.take(100), epochs=1)


Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11490434/11490434 [==============================] - 0s 0us/step
100/100 [==============================] - 8s 15ms/step - d_loss: 0.4372 - g_loss: 0.8775
<keras.src.callbacks.History at 0x7feee42ff190>

Ideile din spatele învățării profunde sunt simple, deci de ce implementarea lor ar trebui să fie dureroasă?


Publicat inițial pe site-ul TensorFlow, acest articol apare aici sub un titlu nou și este licențiat sub CC BY 4.0.

Publicat inițial pe site-ul TensorFlow, acest articol apare aici sub un titlu nou și este licențiat sub CC BY 4.0.

TensorFlow


Trending Topics

blockchaincryptocurrencyhackernoon-top-storyprogrammingsoftware-developmenttechnologystartuphackernoon-booksBitcoinbooks