Bir sorunuz mu var? TensorFlow Forum Ziyaret Forumunda toplulukla bağlantı kurun

Sıfırdan bir eğitim döngüsü yazmak

TensorFlow.org'da görüntüleyin Google Colab'de çalıştırın Kaynağı GitHub'da görüntüleyin Defteri indirin

Kurulum

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
import numpy as np

Giriş

Keras varsayılan eğitim ve değerlendirme döngüleri, fit() ve evaluate() . Bunların kullanımı, yerleşik yöntemlerle Eğitim ve değerlendirme kılavuzunda ele alınmıştır.

Modelinizin öğrenme algoritmasını özelleştirirken fit() kolaylığından yararlanmaya devam etmek istiyorsanız (örneğin, fit() kullanarak bir GAN'ı eğitmek için), Model sınıfını alt sınıflara ayırabilir ve kendi train_step() yönteminizi uygulayabilirsiniz. fit() sırasında tekrar tekrar çağrılır. Bu, fit() durumda olanları özelleştirme fit() kılavuzunda ele alınmıştır.

Şimdi, eğitim ve değerlendirme üzerinde çok düşük düzeyde kontrol istiyorsanız, kendi eğitim ve değerlendirme döngülerinizi sıfırdan yazmalısınız. Bu kılavuzun konusu budur.

GradientTape kullanma: ilk uçtan uca bir örnek

GradientTape kapsamındaki bir modeli çağırmak, bir kayıp değerine göre katmanın eğitilebilir ağırlıklarının gradyanlarını almanızı sağlar. Bir optimize edici örneği kullanarak, bu degradeleri bu değişkenleri ( model.trainable_weights kullanarak model.trainable_weights ) güncellemek için kullanabilirsiniz.

Basit bir MNIST modelini düşünelim:

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

Özel bir eğitim döngüsü ile mini toplu gradyan kullanarak eğitelim.

İlk olarak, bir optimize ediciye, bir kayıp fonksiyonuna ve bir veri setine ihtiyacımız olacak:

# Instantiate an optimizer.
optimizer = keras.optimizers.SGD(learning_rate=1e-3)
# Instantiate a loss function.
loss_fn = keras.losses.SparseCategoricalCrossentropy(from_logits=True)

# Prepare the training dataset.
batch_size = 64
(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
x_train = np.reshape(x_train, (-1, 784))
x_test = np.reshape(x_test, (-1, 784))

# Reserve 10,000 samples for validation.
x_val = x_train[-10000:]
y_val = y_train[-10000:]
x_train = x_train[:-10000]
y_train = y_train[:-10000]

# Prepare the training dataset.
train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))
train_dataset = train_dataset.shuffle(buffer_size=1024).batch(batch_size)

# Prepare the validation dataset.
val_dataset = tf.data.Dataset.from_tensor_slices((x_val, y_val))
val_dataset = val_dataset.batch(batch_size)

İşte eğitim döngümüz:

  • Çağlar boyunca yinelenen bir for döngüsü açıyoruz
  • Her dönem for , veri kümesi üzerinde gruplar halinde yinelenen bir for döngüsü açıyoruz.
  • Her parti için bir GradientTape() kapsamı açıyoruz
  • Bu kapsamda modeli (ileri geçiş) diyoruz ve kaybı hesaplıyoruz
  • Kapsam dışında, modelin ağırlıklarının kayıpla ilgili gradyanlarını alıyoruz.
  • Son olarak, modelin ağırlıklarını gradyanlara göre güncellemek için optimize ediciyi kullanıyoruz.
epochs = 2
for epoch in range(epochs):
    print("\nStart of epoch %d" % (epoch,))

    # Iterate over the batches of the dataset.
    for step, (x_batch_train, y_batch_train) in enumerate(train_dataset):

        # Open a GradientTape to record the operations run
        # during the forward pass, which enables auto-differentiation.
        with tf.GradientTape() as tape:

            # Run the forward pass of the layer.
            # The operations that the layer applies
            # to its inputs are going to be recorded
            # on the GradientTape.
            logits = model(x_batch_train, training=True)  # Logits for this minibatch

            # Compute the loss value for this minibatch.
            loss_value = loss_fn(y_batch_train, logits)

        # Use the gradient tape to automatically retrieve
        # the gradients of the trainable variables with respect to the loss.
        grads = tape.gradient(loss_value, model.trainable_weights)

        # Run one step of gradient descent by updating
        # the value of the variables to minimize the loss.
        optimizer.apply_gradients(zip(grads, model.trainable_weights))

        # Log every 200 batches.
        if step % 200 == 0:
            print(
                "Training loss (for one batch) at step %d: %.4f"
                % (step, float(loss_value))
            )
            print("Seen so far: %s samples" % ((step + 1) * batch_size))
Start of epoch 0
Training loss (for one batch) at step 0: 153.8545
Seen so far: 64 samples
Training loss (for one batch) at step 200: 1.4767
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 1.4645
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 0.7049
Seen so far: 38464 samples

Start of epoch 1
Training loss (for one batch) at step 0: 0.9202
Seen so far: 64 samples
Training loss (for one batch) at step 200: 0.8473
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 0.6632
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 0.8758
Seen so far: 38464 samples

Metriklerin düşük düzeyde ele alınması

Bu temel döngüye metrik izleme ekleyelim.

Sıfırdan yazılmış bu tür eğitim döngülerinde yerleşik ölçümleri (veya yazdığınız özel ölçümleri) kolayca yeniden kullanabilirsiniz. İşte akış:

  • Döngünün başlangıcında metriği somutlaştırın
  • Her partiden sonra metric.update_state() çağırın
  • metric.result() mevcut değerini görüntülemeniz gerektiğinde metric.result() çağırın
  • metric.reset_states() durumunu temizlemeniz gerektiğinde (genellikle bir dönemin sonunda metric.reset_states() çağırın

Bu bilgiyi, her dönemin sonunda doğrulama verilerinde SparseCategoricalAccuracy hesaplamak için SparseCategoricalAccuracy :

# Get model
inputs = keras.Input(shape=(784,), name="digits")
x = layers.Dense(64, activation="relu", name="dense_1")(inputs)
x = layers.Dense(64, activation="relu", name="dense_2")(x)
outputs = layers.Dense(10, name="predictions")(x)
model = keras.Model(inputs=inputs, outputs=outputs)

# Instantiate an optimizer to train the model.
optimizer = keras.optimizers.SGD(learning_rate=1e-3)
# Instantiate a loss function.
loss_fn = keras.losses.SparseCategoricalCrossentropy(from_logits=True)

# Prepare the metrics.
train_acc_metric = keras.metrics.SparseCategoricalAccuracy()
val_acc_metric = keras.metrics.SparseCategoricalAccuracy()

Eğitim ve değerlendirme döngümüz şu şekildedir:

import time

epochs = 2
for epoch in range(epochs):
    print("\nStart of epoch %d" % (epoch,))
    start_time = time.time()

    # Iterate over the batches of the dataset.
    for step, (x_batch_train, y_batch_train) in enumerate(train_dataset):
        with tf.GradientTape() as tape:
            logits = model(x_batch_train, training=True)
            loss_value = loss_fn(y_batch_train, logits)
        grads = tape.gradient(loss_value, model.trainable_weights)
        optimizer.apply_gradients(zip(grads, model.trainable_weights))

        # Update training metric.
        train_acc_metric.update_state(y_batch_train, logits)

        # Log every 200 batches.
        if step % 200 == 0:
            print(
                "Training loss (for one batch) at step %d: %.4f"
                % (step, float(loss_value))
            )
            print("Seen so far: %d samples" % ((step + 1) * batch_size))

    # Display metrics at the end of each epoch.
    train_acc = train_acc_metric.result()
    print("Training acc over epoch: %.4f" % (float(train_acc),))

    # Reset training metrics at the end of each epoch
    train_acc_metric.reset_states()

    # Run a validation loop at the end of each epoch.
    for x_batch_val, y_batch_val in val_dataset:
        val_logits = model(x_batch_val, training=False)
        # Update val metrics
        val_acc_metric.update_state(y_batch_val, val_logits)
    val_acc = val_acc_metric.result()
    val_acc_metric.reset_states()
    print("Validation acc: %.4f" % (float(val_acc),))
    print("Time taken: %.2fs" % (time.time() - start_time))
Start of epoch 0
Training loss (for one batch) at step 0: 114.3453
Seen so far: 64 samples
Training loss (for one batch) at step 200: 2.2635
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 0.5206
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 1.0906
Seen so far: 38464 samples
Training acc over epoch: 0.7022
Validation acc: 0.7853
Time taken: 5.38s

Start of epoch 1
Training loss (for one batch) at step 0: 0.5879
Seen so far: 64 samples
Training loss (for one batch) at step 200: 0.9477
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 0.4649
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 0.6874
Seen so far: 38464 samples
Training acc over epoch: 0.8114
Validation acc: 0.8291
Time taken: 5.46s

tf.function ile egzersiz adımınızı tf.function

TensorFlow 2.0'daki varsayılan çalışma zamanı, istekli yürütmedir . Bu nedenle, yukarıdaki eğitim döngümüz hevesle yürütülüyor.

Bu, hata ayıklama için harikadır, ancak grafik derlemenin kesin bir performans avantajı vardır. Hesaplamanızı statik bir grafik olarak tanımlamak, çerçevenin küresel performans optimizasyonlarını uygulamasını sağlar. Çerçeve, daha sonra ne olacağına dair hiçbir bilgi olmadan, birbiri ardına açgözlülükle bir operasyonu yürütmekle sınırlandırıldığında bu imkansızdır.

Giriş olarak tensörleri alan herhangi bir işlevi statik bir grafiğe derleyebilirsiniz. @tf.function bir @tf.function dekoratörü eklemeniz @tf.function , şöyle:

@tf.function
def train_step(x, y):
    with tf.GradientTape() as tape:
        logits = model(x, training=True)
        loss_value = loss_fn(y, logits)
    grads = tape.gradient(loss_value, model.trainable_weights)
    optimizer.apply_gradients(zip(grads, model.trainable_weights))
    train_acc_metric.update_state(y, logits)
    return loss_value

Aynı şeyi değerlendirme adımı için de yapalım:

@tf.function
def test_step(x, y):
    val_logits = model(x, training=False)
    val_acc_metric.update_state(y, val_logits)

Şimdi, bu derlenmiş eğitim adımıyla eğitim döngümüzü yeniden çalıştıralım:

import time

epochs = 2
for epoch in range(epochs):
    print("\nStart of epoch %d" % (epoch,))
    start_time = time.time()

    # Iterate over the batches of the dataset.
    for step, (x_batch_train, y_batch_train) in enumerate(train_dataset):
        loss_value = train_step(x_batch_train, y_batch_train)

        # Log every 200 batches.
        if step % 200 == 0:
            print(
                "Training loss (for one batch) at step %d: %.4f"
                % (step, float(loss_value))
            )
            print("Seen so far: %d samples" % ((step + 1) * batch_size))

    # Display metrics at the end of each epoch.
    train_acc = train_acc_metric.result()
    print("Training acc over epoch: %.4f" % (float(train_acc),))

    # Reset training metrics at the end of each epoch
    train_acc_metric.reset_states()

    # Run a validation loop at the end of each epoch.
    for x_batch_val, y_batch_val in val_dataset:
        test_step(x_batch_val, y_batch_val)

    val_acc = val_acc_metric.result()
    val_acc_metric.reset_states()
    print("Validation acc: %.4f" % (float(val_acc),))
    print("Time taken: %.2fs" % (time.time() - start_time))
Start of epoch 0
Training loss (for one batch) at step 0: 0.4854
Seen so far: 64 samples
Training loss (for one batch) at step 200: 0.5259
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 0.5035
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 0.2240
Seen so far: 38464 samples
Training acc over epoch: 0.8502
Validation acc: 0.8616
Time taken: 1.32s

Start of epoch 1
Training loss (for one batch) at step 0: 0.6278
Seen so far: 64 samples
Training loss (for one batch) at step 200: 0.3667
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 0.3374
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 0.5318
Seen so far: 38464 samples
Training acc over epoch: 0.8709
Validation acc: 0.8720
Time taken: 1.02s

Çok daha hızlı, değil mi?

Model tarafından takip edilen kayıpların düşük seviyede ele alınması

Katmanlar ve modeller, self.add_loss(value) adlandırılan katmanlar tarafından ileri geçiş sırasında oluşan kayıpları yinelemeli olarak izler. Sonuçta ortaya çıkan skaler kayıp değerleri listesi, ileri geçişin sonunda özellik model.losses . model.losses aracılığıyla kullanılabilir.

Bu kayıp bileşenlerini kullanmak istiyorsanız, bunları toplamalı ve eğitim adımınızda ana kaybına eklemelisiniz.

Etkinlik düzenleme kaybı yaratan bu katmanı düşünün:

class ActivityRegularizationLayer(layers.Layer):
    def call(self, inputs):
        self.add_loss(1e-2 * tf.reduce_sum(inputs))
        return inputs

Onu kullanan gerçekten basit bir model oluşturalım:

inputs = keras.Input(shape=(784,), name="digits")
x = layers.Dense(64, activation="relu")(inputs)
# Insert activity regularization as a layer
x = ActivityRegularizationLayer()(x)
x = layers.Dense(64, activation="relu")(x)
outputs = layers.Dense(10, name="predictions")(x)

model = keras.Model(inputs=inputs, outputs=outputs)

İşte eğitim adımımızın şimdi nasıl görünmesi gerektiği:

@tf.function
def train_step(x, y):
    with tf.GradientTape() as tape:
        logits = model(x, training=True)
        loss_value = loss_fn(y, logits)
        # Add any extra losses created during the forward pass.
        loss_value += sum(model.losses)
    grads = tape.gradient(loss_value, model.trainable_weights)
    optimizer.apply_gradients(zip(grads, model.trainable_weights))
    train_acc_metric.update_state(y, logits)
    return loss_value

Özet

Artık yerleşik eğitim döngülerini kullanma ve sıfırdan kendi yazınızı yazma hakkında bilmeniz gereken her şeyi biliyorsunuz.

Sonuç olarak, işte bu kılavuzda öğrendiğiniz her şeyi birbirine bağlayan basit bir uçtan uca örnek: MNIST rakamları üzerinde eğitilmiş bir DCGAN.

Uçtan uca örnek: sıfırdan bir GAN eğitim döngüsü

Generative Adversarial Networks'e (GAN) aşina olabilirsiniz. GAN'lar, görüntülerin eğitim veri kümesinin (görüntülerin "gizli alanı") gizli dağılımını öğrenerek neredeyse gerçek görünen yeni görüntüler oluşturabilir.

Bir GAN iki bölümden oluşur: gizli uzaydaki noktaları görüntü alanındaki noktalara eşleyen bir "oluşturucu" modeli, bir "ayırıcı" modeli, gerçek görüntüler (eğitim veri kümesinden) ve sahte görüntüler arasındaki farkı söyleyebilen bir sınıflandırıcı görüntüler (jeneratör ağının çıktısı).

Bir GAN eğitim döngüsü şuna benzer:

1) Ayrımcıyı eğitin. - Gizli alanda rastgele noktaları örnekleyin. - "Jeneratör" modeli ile noktaları sahte görüntülere dönüştürün. - Bir grup gerçek görüntü alın ve bunları oluşturulan görüntülerle birleştirin. - Oluşturulan ve gerçek görüntüleri sınıflandırmak için "ayırt edici" modeli eğitin.

2) Jeneratörü eğitin. - Gizli alanda rastgele noktaları örnekleyin. - "Jeneratör" ağı üzerinden noktaları sahte görüntülere dönüştürün. - Bir grup gerçek görüntü alın ve bunları oluşturulan görüntülerle birleştirin. - Ayırıcıyı "kandırmak" için "oluşturucu" modeli eğitin ve sahte görüntüleri gerçek olarak sınıflandırın.

GAN'ların nasıl çalıştığına dair çok daha ayrıntılı bir genel bakış için Python ile Derin Öğrenme konusuna bakın.

Bu eğitim döngüsünü uygulayalım. İlk olarak, sahte ve gerçek rakamları sınıflandırmak için ayrıştırıcıyı oluşturun:

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",
)
discriminator.summary()
Model: "discriminator"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 14, 14, 64)        640       
_________________________________________________________________
leaky_re_lu (LeakyReLU)      (None, 14, 14, 64)        0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 7, 7, 128)         73856     
_________________________________________________________________
leaky_re_lu_1 (LeakyReLU)    (None, 7, 7, 128)         0         
_________________________________________________________________
global_max_pooling2d (Global (None, 128)               0         
_________________________________________________________________
dense_4 (Dense)              (None, 1)                 129       
=================================================================
Total params: 74,625
Trainable params: 74,625
Non-trainable params: 0
_________________________________________________________________

O halde, gizli vektörleri şekil çıktılarına (28, 28, 1) (MNIST rakamlarını temsil eden (28, 28, 1) dönüştüren bir jeneratör ağı oluşturalım:

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",
)

İşte kilit nokta: eğitim döngüsü. Gördüğünüz gibi oldukça basit. Eğitim adımı işlevi yalnızca 17 satır alır.

# Instantiate one optimizer for the discriminator and another for the generator.
d_optimizer = keras.optimizers.Adam(learning_rate=0.0003)
g_optimizer = keras.optimizers.Adam(learning_rate=0.0004)

# Instantiate a loss function.
loss_fn = keras.losses.BinaryCrossentropy(from_logits=True)


@tf.function
def train_step(real_images):
    # Sample random points in the latent space
    random_latent_vectors = tf.random.normal(shape=(batch_size, latent_dim))
    # Decode them to fake images
    generated_images = 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((real_images.shape[0], 1))], axis=0
    )
    # Add random noise to the labels - important trick!
    labels += 0.05 * tf.random.uniform(labels.shape)

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

    # Sample random points in the latent space
    random_latent_vectors = tf.random.normal(shape=(batch_size, 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 = discriminator(generator(random_latent_vectors))
        g_loss = loss_fn(misleading_labels, predictions)
    grads = tape.gradient(g_loss, generator.trainable_weights)
    g_optimizer.apply_gradients(zip(grads, generator.trainable_weights))
    return d_loss, g_loss, generated_images

train_step , görüntü yığınları üzerinde art arda train_step çağırarak train_step .

Ayrıştırıcımız ve oluşturucumuz konvnet olduğundan, bu kodu bir GPU'da çalıştırmak isteyeceksiniz.

import os

# 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)

epochs = 1  # In practice you need at least 20 epochs to generate nice digits.
save_dir = "./"

for epoch in range(epochs):
    print("\nStart epoch", epoch)

    for step, real_images in enumerate(dataset):
        # Train the discriminator & generator on one batch of real images.
        d_loss, g_loss, generated_images = train_step(real_images)

        # Logging.
        if step % 200 == 0:
            # Print metrics
            print("discriminator loss at step %d: %.2f" % (step, d_loss))
            print("adversarial loss at step %d: %.2f" % (step, g_loss))

            # Save one generated image
            img = tf.keras.preprocessing.image.array_to_img(
                generated_images[0] * 255.0, scale=False
            )
            img.save(os.path.join(save_dir, "generated_img" + str(step) + ".png"))

        # To limit execution time we stop after 10 steps.
        # Remove the lines below to actually train the model!
        if step > 10:
            break
Start epoch 0
discriminator loss at step 0: 0.68
adversarial loss at step 0: 0.67

Bu kadar! Colab GPU'da yaklaşık 30 saniyelik eğitimden sonra hoş görünümlü sahte MNIST rakamları elde edeceksiniz.