Google I/O הוא עטיפה! התעדכן בהפעלות של TensorFlow. צפה בהפעלות

pix2pix: תרגום תמונה לתמונה עם GAN מותנה

הצג באתר TensorFlow.org הפעל בגוגל קולאב צפה במקור ב-GitHub הורד מחברת

מדריך זה מדגים כיצד לבנות ולאמן רשת יריבות מותנית (cGAN) הנקראת pix2pix שלומדת מיפוי מתמונות קלט לתמונות פלט, כמתואר בתרגום תמונה לתמונה עם רשתות יריבות מותנות מאת Isola et al. (2017). pix2pix אינו ספציפי ליישום - ניתן ליישם אותו על מגוון רחב של משימות, כולל סינתזה של תמונות ממפות תוויות, יצירת תמונות צבעוניות מתמונות בשחור-לבן, הפיכת תמונות מפות גוגל לתמונות אוויר ואפילו הפיכת סקיצות לתמונות.

בדוגמה זו, הרשת שלך תייצר תמונות של חזיתות בניינים באמצעות מאגר חזיתות CMP שסופק על ידי המרכז לתפיסת מכונה באוניברסיטה הטכנית הצ'כית בפראג . כדי לשמור את זה קצר, תשתמש בעותק מעובד מראש של מערך הנתונים הזה שנוצר על ידי מחברי pix2pix.

ב-pix2pix cGAN, אתה מתנה על תמונות קלט ויוצר תמונות פלט מתאימות. cGANs הוצעו לראשונה ב- Conditional Generative Adversarial Nets (Mirza and Osindero, 2014)

הארכיטקטורה של הרשת שלך תכיל:

  • גנרטור עם ארכיטקטורה מבוססת U-Net .
  • מפלה המיוצג על ידי מסווג PatchGAN convolutional (מוצע בעיתון pix2pix ).

שימו לב שכל תקופה יכולה להימשך כ-15 שניות ב-V100 GPU יחיד.

להלן כמה דוגמאות של הפלט שנוצר על ידי pix2pix cGAN לאחר אימון במשך 200 עידנים על מערך הנתונים של החזיתות (80 אלף שלבים).

פלט לדוגמה_1פלט לדוגמה_2

ייבוא ​​TensorFlow וספריות אחרות

import tensorflow as tf

import os
import pathlib
import time
import datetime

from matplotlib import pyplot as plt
from IPython import display

טען את מערך הנתונים

הורד את נתוני מסד הנתונים של CMP Facade (30MB). מערכי נתונים נוספים זמינים באותו פורמט כאן . ב-Colab אתה יכול לבחור מערכי נתונים אחרים מהתפריט הנפתח. שים לב שחלק ממערכי הנתונים האחרים גדולים משמעותית ( edges2handbags הוא 8GB).

dataset_name = "facades"
_URL = f'http://efrosgans.eecs.berkeley.edu/pix2pix/datasets/{dataset_name}.tar.gz'

path_to_zip = tf.keras.utils.get_file(
    fname=f"{dataset_name}.tar.gz",
    origin=_URL,
    extract=True)

path_to_zip  = pathlib.Path(path_to_zip)

PATH = path_to_zip.parent/dataset_name
Downloading data from http://efrosgans.eecs.berkeley.edu/pix2pix/datasets/facades.tar.gz
30171136/30168306 [==============================] - 19s 1us/step
30179328/30168306 [==============================] - 19s 1us/step
list(PATH.parent.iterdir())
[PosixPath('/home/kbuilder/.keras/datasets/facades.tar.gz'),
 PosixPath('/home/kbuilder/.keras/datasets/YellowLabradorLooking_new.jpg'),
 PosixPath('/home/kbuilder/.keras/datasets/facades'),
 PosixPath('/home/kbuilder/.keras/datasets/mnist.npz')]

כל תמונה מקורית היא בגודל 256 x 512 המכילה שתי תמונות 256 x 256 :

sample_image = tf.io.read_file(str(PATH / 'train/1.jpg'))
sample_image = tf.io.decode_jpeg(sample_image)
print(sample_image.shape)
(256, 512, 3)
plt.figure()
plt.imshow(sample_image)
<matplotlib.image.AxesImage at 0x7f35a3653c90>

png

עליך להפריד תמונות חזית של בניין אמיתיות מתמונות תווית האדריכלות - כולן יהיו בגודל 256 x 256 .

הגדר פונקציה שטוענת קבצי תמונה ומוציאה שני טנסורי תמונה:

def load(image_file):
  # Read and decode an image file to a uint8 tensor
  image = tf.io.read_file(image_file)
  image = tf.io.decode_jpeg(image)

  # Split each image tensor into two tensors:
  # - one with a real building facade image
  # - one with an architecture label image 
  w = tf.shape(image)[1]
  w = w // 2
  input_image = image[:, w:, :]
  real_image = image[:, :w, :]

  # Convert both images to float32 tensors
  input_image = tf.cast(input_image, tf.float32)
  real_image = tf.cast(real_image, tf.float32)

  return input_image, real_image

צייר דוגמה של הקלט (תמונת תווית אדריכלות) ותמונות אמיתיות (תמונת חזית הבניין):

inp, re = load(str(PATH / 'train/100.jpg'))
# Casting to int for matplotlib to display the images
plt.figure()
plt.imshow(inp / 255.0)
plt.figure()
plt.imshow(re / 255.0)
<matplotlib.image.AxesImage at 0x7f35981a4910>

png

png

כפי שמתואר בנייר pix2pix , עליך להחיל ריצוד ושיקוף אקראי כדי לעבד מראש את ערכת האימונים.

הגדירו מספר פונקציות ש:

  1. שנה את הגודל של כל תמונה 256 x 256 לגובה ורוחב גדולים יותר - 286 x 286 .
  2. חתוך אותו באופן אקראי ל 256 x 256 .
  3. הפוך את התמונה באופן אקראי, כלומר משמאל לימין (שיקוף אקראי).
  4. נרמל את התמונות לטווח [-1, 1] .
# The facade training set consist of 400 images
BUFFER_SIZE = 400
# The batch size of 1 produced better results for the U-Net in the original pix2pix experiment
BATCH_SIZE = 1
# Each image is 256x256 in size
IMG_WIDTH = 256
IMG_HEIGHT = 256
def resize(input_image, real_image, height, width):
  input_image = tf.image.resize(input_image, [height, width],
                                method=tf.image.ResizeMethod.NEAREST_NEIGHBOR)
  real_image = tf.image.resize(real_image, [height, width],
                               method=tf.image.ResizeMethod.NEAREST_NEIGHBOR)

  return input_image, real_image
def random_crop(input_image, real_image):
  stacked_image = tf.stack([input_image, real_image], axis=0)
  cropped_image = tf.image.random_crop(
      stacked_image, size=[2, IMG_HEIGHT, IMG_WIDTH, 3])

  return cropped_image[0], cropped_image[1]
# Normalizing the images to [-1, 1]
def normalize(input_image, real_image):
  input_image = (input_image / 127.5) - 1
  real_image = (real_image / 127.5) - 1

  return input_image, real_image
@tf.function()
def random_jitter(input_image, real_image):
  # Resizing to 286x286
  input_image, real_image = resize(input_image, real_image, 286, 286)

  # Random cropping back to 256x256
  input_image, real_image = random_crop(input_image, real_image)

  if tf.random.uniform(()) > 0.5:
    # Random mirroring
    input_image = tf.image.flip_left_right(input_image)
    real_image = tf.image.flip_left_right(real_image)

  return input_image, real_image

אתה יכול לבדוק חלק מהפלט המעובד מראש:

plt.figure(figsize=(6, 6))
for i in range(4):
  rj_inp, rj_re = random_jitter(inp, re)
  plt.subplot(2, 2, i + 1)
  plt.imshow(rj_inp / 255.0)
  plt.axis('off')
plt.show()

png

לאחר שבדקנו שהטעינה והעיבוד המקדים עובדים, הבה נגדיר כמה פונקציות מסייעות הטוענות ומעבדות מראש את מערכי ההדרכה והבדיקות:

def load_image_train(image_file):
  input_image, real_image = load(image_file)
  input_image, real_image = random_jitter(input_image, real_image)
  input_image, real_image = normalize(input_image, real_image)

  return input_image, real_image
def load_image_test(image_file):
  input_image, real_image = load(image_file)
  input_image, real_image = resize(input_image, real_image,
                                   IMG_HEIGHT, IMG_WIDTH)
  input_image, real_image = normalize(input_image, real_image)

  return input_image, real_image

בנה צינור קלט עם tf.data

train_dataset = tf.data.Dataset.list_files(str(PATH / 'train/*.jpg'))
train_dataset = train_dataset.map(load_image_train,
                                  num_parallel_calls=tf.data.AUTOTUNE)
train_dataset = train_dataset.shuffle(BUFFER_SIZE)
train_dataset = train_dataset.batch(BATCH_SIZE)
try:
  test_dataset = tf.data.Dataset.list_files(str(PATH / 'test/*.jpg'))
except tf.errors.InvalidArgumentError:
  test_dataset = tf.data.Dataset.list_files(str(PATH / 'val/*.jpg'))
test_dataset = test_dataset.map(load_image_test)
test_dataset = test_dataset.batch(BATCH_SIZE)

בנה את הגנרטור

המחולל של pix2pix cGAN שלך הוא U-Net שונה . רשת U-Net מורכבת מקודד (Downsampler) ומפענח (Upsampler). (תוכלו לברר על כך במדריך פילוח תמונות ובאתר פרויקט U-Net .)

  • כל בלוק במקודד הוא: Convolution -> נורמליזציה של אצווה -> Leaky ReLU
  • כל בלוק במפענח הוא: טרנספוזיציה -> נורמליזציה של אצווה -> נשירה (מוחל על 3 הבלוקים הראשונים) -> ReLU
  • ישנם חיבורי דילוג בין המקודד למפענח (כמו ב-U-Net).

הגדר את ה-downsampler (מקודד):

OUTPUT_CHANNELS = 3
def downsample(filters, size, apply_batchnorm=True):
  initializer = tf.random_normal_initializer(0., 0.02)

  result = tf.keras.Sequential()
  result.add(
      tf.keras.layers.Conv2D(filters, size, strides=2, padding='same',
                             kernel_initializer=initializer, use_bias=False))

  if apply_batchnorm:
    result.add(tf.keras.layers.BatchNormalization())

  result.add(tf.keras.layers.LeakyReLU())

  return result
down_model = downsample(3, 4)
down_result = down_model(tf.expand_dims(inp, 0))
print (down_result.shape)
(1, 128, 128, 3)

הגדר את ה-upsampler (מפענח):

def upsample(filters, size, apply_dropout=False):
  initializer = tf.random_normal_initializer(0., 0.02)

  result = tf.keras.Sequential()
  result.add(
    tf.keras.layers.Conv2DTranspose(filters, size, strides=2,
                                    padding='same',
                                    kernel_initializer=initializer,
                                    use_bias=False))

  result.add(tf.keras.layers.BatchNormalization())

  if apply_dropout:
      result.add(tf.keras.layers.Dropout(0.5))

  result.add(tf.keras.layers.ReLU())

  return result
up_model = upsample(3, 4)
up_result = up_model(down_result)
print (up_result.shape)
(1, 256, 256, 3)

הגדר את המחולל עם ה-downsampler וה-upsampler:

def Generator():
  inputs = tf.keras.layers.Input(shape=[256, 256, 3])

  down_stack = [
    downsample(64, 4, apply_batchnorm=False),  # (batch_size, 128, 128, 64)
    downsample(128, 4),  # (batch_size, 64, 64, 128)
    downsample(256, 4),  # (batch_size, 32, 32, 256)
    downsample(512, 4),  # (batch_size, 16, 16, 512)
    downsample(512, 4),  # (batch_size, 8, 8, 512)
    downsample(512, 4),  # (batch_size, 4, 4, 512)
    downsample(512, 4),  # (batch_size, 2, 2, 512)
    downsample(512, 4),  # (batch_size, 1, 1, 512)
  ]

  up_stack = [
    upsample(512, 4, apply_dropout=True),  # (batch_size, 2, 2, 1024)
    upsample(512, 4, apply_dropout=True),  # (batch_size, 4, 4, 1024)
    upsample(512, 4, apply_dropout=True),  # (batch_size, 8, 8, 1024)
    upsample(512, 4),  # (batch_size, 16, 16, 1024)
    upsample(256, 4),  # (batch_size, 32, 32, 512)
    upsample(128, 4),  # (batch_size, 64, 64, 256)
    upsample(64, 4),  # (batch_size, 128, 128, 128)
  ]

  initializer = tf.random_normal_initializer(0., 0.02)
  last = tf.keras.layers.Conv2DTranspose(OUTPUT_CHANNELS, 4,
                                         strides=2,
                                         padding='same',
                                         kernel_initializer=initializer,
                                         activation='tanh')  # (batch_size, 256, 256, 3)

  x = inputs

  # Downsampling through the model
  skips = []
  for down in down_stack:
    x = down(x)
    skips.append(x)

  skips = reversed(skips[:-1])

  # Upsampling and establishing the skip connections
  for up, skip in zip(up_stack, skips):
    x = up(x)
    x = tf.keras.layers.Concatenate()([x, skip])

  x = last(x)

  return tf.keras.Model(inputs=inputs, outputs=x)

דמיינו את ארכיטקטורת מודל המחולל:

generator = Generator()
tf.keras.utils.plot_model(generator, show_shapes=True, dpi=64)

png

בדוק את הגנרטור:

gen_output = generator(inp[tf.newaxis, ...], training=False)
plt.imshow(gen_output[0, ...])
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
<matplotlib.image.AxesImage at 0x7f35cfd20610>

png

הגדר את אובדן הגנרטור

GANs לומדים הפסד שמסתגל לנתונים, בעוד cGANs לומדים הפסד מובנה שמעניש מבנה אפשרי השונה מפלט הרשת ותמונת היעד, כפי שמתואר בנייר pix2pix .

  • אובדן המחולל הוא אובדן סיגמואידי צולב אנטרופיה של התמונות שנוצרו ושל מערך של אלה .
  • נייר pix2pix מזכיר גם את אובדן L1, שהוא MAE (ממוצע שגיאה מוחלטת) בין התמונה שנוצרה לתמונת היעד.
  • זה מאפשר לתמונה שנוצרה להיות דומה מבחינה מבנית לתמונת היעד.
  • הנוסחה לחישוב הפסד הגנרטור הכולל היא gan_loss + LAMBDA * l1_loss , כאשר LAMBDA = 100 . ערך זה הוחלט על ידי מחברי המאמר.
LAMBDA = 100
loss_object = tf.keras.losses.BinaryCrossentropy(from_logits=True)
def generator_loss(disc_generated_output, gen_output, target):
  gan_loss = loss_object(tf.ones_like(disc_generated_output), disc_generated_output)

  # Mean absolute error
  l1_loss = tf.reduce_mean(tf.abs(target - gen_output))

  total_gen_loss = gan_loss + (LAMBDA * l1_loss)

  return total_gen_loss, gan_loss, l1_loss

הליך ההכשרה של הגנרטור הוא כדלקמן:

עדכון תמונת מחולל

בנה את המפלה

המאבחן ב-pix2pix cGAN הוא מסווג PatchGAN convolution - הוא מנסה לסווג אם כל תיקון תמונה אמיתי או לא אמיתי, כפי שמתואר בעיתון pix2pix .

  • כל בלוק במאפיין הוא: Convolution -> נורמליזציה של אצווה -> Leaky ReLU.
  • צורת הפלט אחרי השכבה האחרונה היא (batch_size, 30, 30, 1) .
  • כל תיקון תמונה 30 x 30 של הפלט מסווג חלק 70 x 70 של תמונת הקלט.
  • המאבחן מקבל 2 כניסות:
    • תמונת הקלט ותמונת היעד, שאותם היא צריכה לסווג כאמיתית.
    • תמונת הקלט והתמונה שנוצרה (הפלט של המחולל), שאותן הוא צריך לסווג כזיוף.
    • השתמש tf.concat([inp, tar], axis=-1) כדי לשרשר את 2 הכניסות הללו יחד.

בואו נגדיר את המפלה:

def Discriminator():
  initializer = tf.random_normal_initializer(0., 0.02)

  inp = tf.keras.layers.Input(shape=[256, 256, 3], name='input_image')
  tar = tf.keras.layers.Input(shape=[256, 256, 3], name='target_image')

  x = tf.keras.layers.concatenate([inp, tar])  # (batch_size, 256, 256, channels*2)

  down1 = downsample(64, 4, False)(x)  # (batch_size, 128, 128, 64)
  down2 = downsample(128, 4)(down1)  # (batch_size, 64, 64, 128)
  down3 = downsample(256, 4)(down2)  # (batch_size, 32, 32, 256)

  zero_pad1 = tf.keras.layers.ZeroPadding2D()(down3)  # (batch_size, 34, 34, 256)
  conv = tf.keras.layers.Conv2D(512, 4, strides=1,
                                kernel_initializer=initializer,
                                use_bias=False)(zero_pad1)  # (batch_size, 31, 31, 512)

  batchnorm1 = tf.keras.layers.BatchNormalization()(conv)

  leaky_relu = tf.keras.layers.LeakyReLU()(batchnorm1)

  zero_pad2 = tf.keras.layers.ZeroPadding2D()(leaky_relu)  # (batch_size, 33, 33, 512)

  last = tf.keras.layers.Conv2D(1, 4, strides=1,
                                kernel_initializer=initializer)(zero_pad2)  # (batch_size, 30, 30, 1)

  return tf.keras.Model(inputs=[inp, tar], outputs=last)

דמיינו את ארכיטקטורת מודל המפלה:

discriminator = Discriminator()
tf.keras.utils.plot_model(discriminator, show_shapes=True, dpi=64)

png

בדוק את המפלה:

disc_out = discriminator([inp[tf.newaxis, ...], gen_output], training=False)
plt.imshow(disc_out[0, ..., -1], vmin=-20, vmax=20, cmap='RdBu_r')
plt.colorbar()
<matplotlib.colorbar.Colorbar at 0x7f35cec82c50>

png

הגדר את אובדן המפלה

  • הפונקציה discriminator_loss לוקחת 2 כניסות: תמונות אמיתיות ותמונות שנוצרו .
  • real_loss הוא אובדן צולב אנטרופיה סיגמואידי של התמונות האמיתיות ומערך של אלה (מכיוון שאלו הן התמונות האמיתיות) .
  • generated_loss הוא אובדן צולב אנטרופיה סיגמואידי של התמונות שנוצרו ומערך של אפסים (מכיוון שאלו הן התמונות המזויפות) .
  • ה- total_loss הוא הסכום של real_loss ו- generated_loss .
def discriminator_loss(disc_real_output, disc_generated_output):
  real_loss = loss_object(tf.ones_like(disc_real_output), disc_real_output)

  generated_loss = loss_object(tf.zeros_like(disc_generated_output), disc_generated_output)

  total_disc_loss = real_loss + generated_loss

  return total_disc_loss

הליך ההכשרה של המפלה מוצג להלן.

כדי ללמוד עוד על הארכיטקטורה ועל הפרמטרים ההיפר אתה יכול לעיין בנייר pix2pix .

מפלה עדכון תמונה

הגדר את מטבי האופטימיזציה ושומר נקודות ביקורת

generator_optimizer = tf.keras.optimizers.Adam(2e-4, beta_1=0.5)
discriminator_optimizer = tf.keras.optimizers.Adam(2e-4, beta_1=0.5)
checkpoint_dir = './training_checkpoints'
checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt")
checkpoint = tf.train.Checkpoint(generator_optimizer=generator_optimizer,
                                 discriminator_optimizer=discriminator_optimizer,
                                 generator=generator,
                                 discriminator=discriminator)

צור תמונות

כתוב פונקציה כדי לשרטט כמה תמונות במהלך האימון.

  • העבירו תמונות ממערך המבחן לגנרטור.
  • לאחר מכן, המחולל יתרגם את תמונת הקלט לפלט.
  • השלב האחרון הוא לתכנן את התחזיות והלא !
def generate_images(model, test_input, tar):
  prediction = model(test_input, training=True)
  plt.figure(figsize=(15, 15))

  display_list = [test_input[0], tar[0], prediction[0]]
  title = ['Input Image', 'Ground Truth', 'Predicted Image']

  for i in range(3):
    plt.subplot(1, 3, i+1)
    plt.title(title[i])
    # Getting the pixel values in the [0, 1] range to plot.
    plt.imshow(display_list[i] * 0.5 + 0.5)
    plt.axis('off')
  plt.show()

בדוק את הפונקציה:

for example_input, example_target in test_dataset.take(1):
  generate_images(generator, example_input, example_target)

png

הַדְרָכָה

  • עבור כל דוגמה קלט יוצר פלט.
  • המאבחן מקבל את ה- input_image ואת התמונה שנוצרה בתור הקלט הראשון. הקלט השני הוא input_image ו- target_image .
  • לאחר מכן, חשב את המחולל ואת אובדן המפלה.
  • לאחר מכן, חשב את דרגות האובדן הן ביחס למחולל והן למשתנים (תשומות) המפלה והחל את אלה על האופטימיזר.
  • לבסוף, רשום את ההפסדים ל-TensorBoard.
log_dir="logs/"

summary_writer = tf.summary.create_file_writer(
  log_dir + "fit/" + datetime.datetime.now().strftime("%Y%m%d-%H%M%S"))
@tf.function
def train_step(input_image, target, step):
  with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:
    gen_output = generator(input_image, training=True)

    disc_real_output = discriminator([input_image, target], training=True)
    disc_generated_output = discriminator([input_image, gen_output], training=True)

    gen_total_loss, gen_gan_loss, gen_l1_loss = generator_loss(disc_generated_output, gen_output, target)
    disc_loss = discriminator_loss(disc_real_output, disc_generated_output)

  generator_gradients = gen_tape.gradient(gen_total_loss,
                                          generator.trainable_variables)
  discriminator_gradients = disc_tape.gradient(disc_loss,
                                               discriminator.trainable_variables)

  generator_optimizer.apply_gradients(zip(generator_gradients,
                                          generator.trainable_variables))
  discriminator_optimizer.apply_gradients(zip(discriminator_gradients,
                                              discriminator.trainable_variables))

  with summary_writer.as_default():
    tf.summary.scalar('gen_total_loss', gen_total_loss, step=step//1000)
    tf.summary.scalar('gen_gan_loss', gen_gan_loss, step=step//1000)
    tf.summary.scalar('gen_l1_loss', gen_l1_loss, step=step//1000)
    tf.summary.scalar('disc_loss', disc_loss, step=step//1000)

לולאת האימון בפועל. מכיוון שמדריך זה יכול להופיע על יותר ממערך נתונים אחד, ומערכי הנתונים משתנים מאוד בגודלם, לולאת האימון מוגדרת לעבוד בשלבים במקום בתקופות.

  • חוזר על מספר השלבים.
  • כל 10 שלבים מדפיסים נקודה ( . ).
  • כל אלף שלבים: נקה את התצוגה והפעל את generate_images כדי להציג את ההתקדמות.
  • כל 5,000 שלבים: שמור מחסום.
def fit(train_ds, test_ds, steps):
  example_input, example_target = next(iter(test_ds.take(1)))
  start = time.time()

  for step, (input_image, target) in train_ds.repeat().take(steps).enumerate():
    if (step) % 1000 == 0:
      display.clear_output(wait=True)

      if step != 0:
        print(f'Time taken for 1000 steps: {time.time()-start:.2f} sec\n')

      start = time.time()

      generate_images(generator, example_input, example_target)
      print(f"Step: {step//1000}k")

    train_step(input_image, target, step)

    # Training step
    if (step+1) % 10 == 0:
      print('.', end='', flush=True)


    # Save (checkpoint) the model every 5k steps
    if (step + 1) % 5000 == 0:
      checkpoint.save(file_prefix=checkpoint_prefix)

לולאת אימון זו שומרת יומנים שתוכל להציג ב-TensorBoard כדי לעקוב אחר התקדמות האימון.

אם אתה עובד על מכונה מקומית, תפעיל תהליך TensorBoard נפרד. כאשר עובדים במחברת, הפעל את הצופה לפני תחילת ההדרכה לניטור עם TensorBoard.

כדי להפעיל את הצופה הדבק את הדברים הבאים לתוך תא קוד:

%load_ext tensorboard
%tensorboard --logdir {log_dir}

לבסוף, הפעל את לולאת האימון:

fit(train_dataset, test_dataset, steps=40000)
Time taken for 1000 steps: 36.53 sec

png

Step: 39k
....................................................................................................

אם ברצונך לשתף את תוצאות TensorBoard בפומבי , תוכל להעלות את היומנים אל TensorBoard.dev על ידי העתקת הדברים הבאים לתא קוד.

tensorboard dev upload --logdir {log_dir}

אתה יכול לראות את התוצאות של הפעלה קודמת של מחברת זו ב- TensorBoard.dev .

TensorBoard.dev היא חוויה מנוהלת לאירוח, מעקב ושיתוף של ניסויי ML עם כולם.

זה יכול גם לכלול בתוך שורה באמצעות <iframe> :

display.IFrame(
    src="https://tensorboard.dev/experiment/lZ0C6FONROaUMfjYkVyJqw",
    width="100%",
    height="1000px")

פירוש היומנים הוא עדין יותר בעת אימון GAN (או cGAN כמו pix2pix) בהשוואה למודל סיווג פשוט או רגרסיה. דברים שצריך לחפש:

  • בדוק שלא הגנרטור ולא דגם המפלה "ניצחו". אם ה- gen_gan_loss או disc_loss נמוכים מאוד, זה אינדיקציה לכך שהמודל הזה שולט באחר, ואתה לא מצליח לאמן את המודל המשולב.
  • הערך log(2) = 0.69 הוא נקודת התייחסות טובה להפסדים אלה, שכן הוא מצביע על תמיהה של 2 - המאבחן, בממוצע, לא בטוח באותה מידה לגבי שתי האפשרויות.
  • עבור disc_loss , ערך מתחת ל 0.69 פירושו שהמאבחן מצליח יותר מאשר אקראי בקבוצה המשולבת של תמונות אמיתיות ומוצרות.
  • עבור gen_gan_loss , ערך מתחת ל 0.69 פירושו שהמחולל מצליח יותר מאשר אקראי בהטעיית המפלה.
  • ככל שהאימונים מתקדמים, ההפסד של gen_l1_loss אמור לרדת.

שחזר את נקודת הבידוק העדכנית ובדוק את הרשת

ls {checkpoint_dir}
checkpoint          ckpt-5.data-00000-of-00001
ckpt-1.data-00000-of-00001  ckpt-5.index
ckpt-1.index            ckpt-6.data-00000-of-00001
ckpt-2.data-00000-of-00001  ckpt-6.index
ckpt-2.index            ckpt-7.data-00000-of-00001
ckpt-3.data-00000-of-00001  ckpt-7.index
ckpt-3.index            ckpt-8.data-00000-of-00001
ckpt-4.data-00000-of-00001  ckpt-8.index
ckpt-4.index
# Restoring the latest checkpoint in checkpoint_dir
checkpoint.restore(tf.train.latest_checkpoint(checkpoint_dir))
<tensorflow.python.training.tracking.util.CheckpointLoadStatus at 0x7f35cfd6b8d0>

צור כמה תמונות באמצעות ערכת הבדיקה

# Run the trained model on a few examples from the test set
for inp, tar in test_dataset.take(5):
  generate_images(generator, inp, tar)

png

png

png

png

png