نوشتن حلقه آموزش از ابتدا

مشاهده در TensorFlow.org در Google Colab اجرا کنید مشاهده منبع در GitHub دانلود دفترچه یادداشت

برپایی

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

معرفی

Keras آموزش به طور پیش فرض و حلقه ارزیابی، فراهم می کند fit() و evaluate() . استفاده از آنها را در راهنمای تحت پوشش آموزش و ارزیابی با روش ساخته شده است در .

اگر شما می خواهید برای سفارشی کردن الگوریتم یادگیری از مدل خود را در حالی که هنوز اعمال نفوذ راحتی از fit() (به عنوان مثال، برای آموزش یک GAN با استفاده از fit() )، شما می توانید زیر مجموعه Model کلاس و اجرای خود را train_step() روش، که بارها و بارها در طول نام fit() . این است که در راهنمای تحت پوشش سفارشی آنچه در اتفاق می افتد fit() .

حال ، اگر می خواهید کنترل بسیار پایینی بر آموزش و ارزیابی داشته باشید ، باید حلقه های آموزشی و ارزیابی خود را از ابتدا بنویسید. این همان چیزی است که این راهنما در مورد آن صحبت می کند.

با استفاده از GradientTape : یک مثال اول پایان به پایان

فراخوانی یک مدل در داخل یک GradientTape دامنه شما را قادر به بازیابی شیب وزن تربیت شدنی از لایه با توجه به ارزش از دست دادن. با استفاده از نمونه بهینه ساز، شما می توانید این شیب برای به روز رسانی این متغیرها (که شما می توانید با استفاده از بازیابی استفاده model.trainable_weights ).

بیایید یک مدل ساده MNIST را در نظر بگیریم:

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)

بیایید آن را با استفاده از گرادیان مینی دسته ای با یک حلقه آموزشی سفارشی آموزش دهیم.

ابتدا ، ما به یک بهینه ساز ، یک تابع ضرر و یک مجموعه داده نیاز داریم:

# 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)
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11493376/11490434 [==============================] - 1s 0us/step
11501568/11490434 [==============================] - 1s 0us/step

اینم حلقه آموزشی ما:

  • ما باز کردن یک for حلقه می کند که بیش از دوره
  • برای هر دوره، ما باز کردن یک for حلقه می کند که بیش از مجموعه داده، در دسته
  • برای هر دسته ای، ما باز کردن یک GradientTape() دامنه
  • در داخل این محدوده ، ما مدل را (forward pass) می نامیم و ضرر را محاسبه می کنیم
  • خارج از محدوده ، گرادیان های وزن مدل را با توجه به از دست دادن بازیابی می کنیم
  • در نهایت ، ما از بهینه ساز برای به روز رسانی وزن مدل بر اساس شیب استفاده می کنیم
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: 68.7478
Seen so far: 64 samples
Training loss (for one batch) at step 200: 1.9448
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 1.1859
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 0.6914
Seen so far: 38464 samples

Start of epoch 1
Training loss (for one batch) at step 0: 0.9113
Seen so far: 64 samples
Training loss (for one batch) at step 200: 0.9550
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 0.5139
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 0.7227
Seen so far: 38464 samples

مدیریت سطح پایین معیارها

اجازه دهید نظارت متریک را به این حلقه اساسی اضافه کنیم.

می توانید به راحتی از معیارهای داخلی (یا معیارهایی که نوشتید) در چنین حلقه های آموزشی که از ابتدا نوشته شده اند استفاده کنید. در اینجا جریان است:

  • متریک را در ابتدای حلقه مثال بزنید
  • پاسخ metric.update_state() بعد از هر دسته ای
  • پاسخ metric.result() هنگامی که شما نیاز به نمایش ارزش فعلی از متریک
  • پاسخ metric.reset_states() هنگامی که شما نیاز به پاک کردن دولت از متریک (معمولا در پایان یک عصر)

اجازه دهید با استفاده از این دانش برای محاسبه 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()

در اینجا حلقه آموزش و ارزیابی ما آمده است:

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: 88.9958
Seen so far: 64 samples
Training loss (for one batch) at step 200: 2.2214
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 1.3083
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 0.8282
Seen so far: 38464 samples
Training acc over epoch: 0.7406
Validation acc: 0.8201
Time taken: 6.31s

Start of epoch 1
Training loss (for one batch) at step 0: 0.3276
Seen so far: 64 samples
Training loss (for one batch) at step 200: 0.4819
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 0.5971
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 0.5862
Seen so far: 38464 samples
Training acc over epoch: 0.8474
Validation acc: 0.8676
Time taken: 5.98s

سرعت بالا به گام آموزش خود را با tf.function

زمان اجرا به طور پیش فرض در TensorFlow 2 است اعدام مشتاق . به این ترتیب ، حلقه آموزشی ما در بالا با اشتیاق اجرا می شود.

این برای اشکال زدایی عالی است ، اما تدوین نمودار مزیت عملکرد مشخصی دارد. توصیف محاسبه خود به عنوان نمودار ایستا ، چارچوب را قادر می سازد تا بهینه سازی عملکرد جهانی را اعمال کند. این امر زمانی غیرممکن است که چارچوب به گونه ای حرص زده باشد که عملیات را یکی پس از دیگری انجام دهد ، بدون آنکه از آنچه در ادامه می آید آگاهی داشته باشد.

شما می توانید هر تابعی را که تانسورها را به عنوان ورودی می گیرد به صورت نمودار کامپایل کنید. فقط یک اضافه @tf.function دکوراتور بر روی آن، مثل این:

@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

بیایید همین کار را با مرحله ارزیابی انجام دهیم:

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

اکنون ، بیایید حلقه آموزشی خود را با این مرحله آموزش کامپایل شده مجدداً اجرا کنیم:

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.7921
Seen so far: 64 samples
Training loss (for one batch) at step 200: 0.7755
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 0.1564
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 0.3181
Seen so far: 38464 samples
Training acc over epoch: 0.8788
Validation acc: 0.8866
Time taken: 1.59s

Start of epoch 1
Training loss (for one batch) at step 0: 0.5222
Seen so far: 64 samples
Training loss (for one batch) at step 200: 0.4574
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 0.4035
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 0.7561
Seen so far: 38464 samples
Training acc over epoch: 0.8959
Validation acc: 0.9028
Time taken: 1.27s

خیلی سریعتر ، اینطور نیست؟

مدیریت سطح پایین تلفات پیگیری شده توسط مدل

لایه ها و مدل های به صورت بازگشتی پیگیری هر گونه ضرر و زیان ایجاد شده در طی پاس رو به جلو توسط لایه هایی که پاسخ self.add_loss(value) . لیست به دست آمده از ارزش از دست دادن اسکالر در دسترس از طریق مالکیت model.losses در پایان از پاس رو به جلو.

اگر می خواهید از این اجزای ضرر استفاده کنید ، باید آنها را جمع بندی کرده و در مرحله تمرین خود به ضرر اصلی اضافه کنید.

این لایه را در نظر بگیرید که باعث از دست دادن منظم فعالیت می شود:

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

بیایید یک مدل واقعا ساده بسازیم که از آن استفاده می کند:

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)

در اینجا مرحله آموزشی ما باید به نظر برسد:

@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

خلاصه

اکنون شما همه چیز را در مورد استفاده از حلقه های آموزشی داخلی و نوشتن خود از ابتدا می دانید.

برای خاتمه ، در اینجا یک مثال ساده از پایان به پایان است که همه آنچه را که در این راهنما آموخته اید به هم پیوند می دهد: DCGAN که بر روی ارقام MNIST آموزش دیده است.

مثال سر به سر: یک حلقه آموزشی GAN از ابتدا

ممکن است با شبکه های مخالف تولیدی (GAN) آشنا باشید. GAN ها می توانند با یادگیری توزیع پنهان مجموعه داده آموزشی ("فضای پنهان" تصاویر) تصاویر جدیدی را که تقریباً واقعی به نظر می رسند ، تولید کنند.

GAN از دو بخش تشکیل شده است: یک مدل "ژنراتور" که نقاط موجود در فضای نهفته را به نقاطی در فضای تصویر ترسیم می کند ، یک مدل "تشخیص دهنده" ، یک طبقه بندی کننده که می تواند تفاوت بین تصاویر واقعی (از مجموعه داده آموزشی) و جعلی را تشخیص دهد. تصاویر (خروجی شبکه ژنراتور).

یک حلقه آموزشی GAN به این شکل است:

1) متمایز کننده را آموزش دهید. - مجموعه ای از نقاط تصادفی در فضای پنهان. - نقاط را به تصاویر جعلی از طریق مدل "ژنراتور" تبدیل کنید. - دسته ای از تصاویر واقعی را دریافت کرده و آنها را با تصاویر تولید شده ترکیب کنید. - مدل "تمایز" را برای طبقه بندی تصاویر ایجاد شده در مقابل تصاویر واقعی آموزش دهید.

2) ژنراتور را آموزش دهید. - نمونه برداری از نقاط تصادفی در فضای نهفته. - نقاط را از طریق شبکه "ژنراتور" به تصاویر جعلی تبدیل کنید. - دسته ای از تصاویر واقعی را دریافت کرده و آنها را با تصاویر تولید شده ترکیب کنید. - مدل "ژنراتور" را آموزش دهید تا متمایز را "فریب" دهد و تصاویر جعلی را به عنوان واقعی طبقه بندی کند.

برای یک نمای کلی با جزئیات بیشتری از گانز چگونه کار می کند، و یادگیری عمیق با پایتون .

بیایید این حلقه آموزشی را پیاده سازی کنیم. ابتدا ، برای تشخیص اعداد تقلبی در مقابل واقعی ، تبعیض ایجاد کنید:

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
_________________________________________________________________

سپس اجازه دهید ایجاد یک شبکه ژنراتور، که تبدیل بردار نهفته به خروجی شکل (28, 28, 1) (به نمایندگی از رقم MNIST):

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

در اینجا نکته اصلی وجود دارد: حلقه آموزش. همانطور که می بینید کاملاً ساده است. عملکرد مرحله آموزش فقط 17 خط را می گیرد.

# 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

اجازه دهید آموزش GAN ما، بارها و بارها خواستار train_step در دسته ای از تصاویر.

از آنجا که تشخیص دهنده و مولد ما متصل هستند ، شما می خواهید این کد را روی GPU اجرا کنید.

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.69
adversarial loss at step 0: 0.69

خودشه! شما فقط پس از آموزش 30 پوندی روی GPU Colab ، ارقام MNIST جعلی با ظاهر زیبا دریافت خواهید کرد.