Zobacz na TensorFlow.org | Uruchom w Google Colab | Wyświetl źródło na GitHub | Pobierz notatnik |
Ten samouczek pokazuje, jak zbudować i wytrenować warunkową generatywną sieć adwersarza (cGAN) o nazwie pix2pix, która uczy się mapowania z obrazów wejściowych na obrazy wyjściowe, zgodnie z opisem w Translacja obrazu na obraz za pomocą warunkowych sieci adwersarzy autorstwa Isola i in. (2017). pix2pix nie jest specyficzny dla aplikacji — można go zastosować do szerokiego zakresu zadań, w tym do syntezy zdjęć z map etykiet, generowania kolorowych zdjęć z obrazów czarno-białych, przekształcania zdjęć Google Maps w obrazy lotnicze, a nawet przekształcania szkiców w zdjęcia.
W tym przykładzie Twoja sieć wygeneruje obrazy fasad budynków przy użyciu bazy danych fasad CMP udostępnionej przez Centrum Percepcji Maszyn na Czeskim Uniwersytecie Technicznym w Pradze . Krótko mówiąc, użyjesz wstępnie przetworzonej kopii tego zbioru danych stworzonej przez autorów pix2pix.
W pix2pix cGAN warunkujesz obrazy wejściowe i generujesz odpowiednie obrazy wyjściowe. cGAN zostały po raz pierwszy zaproponowane w Warunkowych Generacyjnych Sieciach Przeciwnych (Mirza i Osindero, 2014)
Architektura Twojej sieci będzie zawierać:
- Generator o architekturze opartej na U-Net .
- Dyskryminator reprezentowany przez splotowy klasyfikator PatchGAN (zaproponowany w artykule pix2pix ).
Zwróć uwagę, że każda epoka może zająć około 15 sekund na pojedynczym GPU V100.
Poniżej znajduje się kilka przykładów danych wyjściowych generowanych przez cGAN pix2pix po treningu dla 200 epok na zestawie danych elewacji (80k kroków).
Importuj TensorFlow i inne biblioteki
import tensorflow as tf
import os
import pathlib
import time
import datetime
from matplotlib import pyplot as plt
from IPython import display
Załaduj zbiór danych
Pobierz dane bazy danych fasad CMP (30 MB). Dodatkowe zbiory danych są dostępne w tym samym formacie tutaj . W Colab możesz wybrać inne zbiory danych z menu rozwijanego. Zauważ, że niektóre inne zestawy danych są znacznie większe ( edges2handbags
to 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')]
Każdy oryginalny obraz ma rozmiar 256 x 512
zawiera dwa obrazy 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>
Musisz oddzielić rzeczywiste obrazy elewacji budynku od obrazów etykiet architektury — wszystkie będą miały rozmiar 256 x 256
.
Zdefiniuj funkcję, która ładuje pliki obrazów i wyświetla dwa tensory obrazu:
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
Wykreśl próbkę obrazu wejściowego (obraz etykiety architektury) i rzeczywistego (zdjęcie elewacji budynku):
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>
Jak opisano w artykule pix2pix , musisz zastosować losowe jittering i mirroring, aby wstępnie przetworzyć zbiór treningowy.
Zdefiniuj kilka funkcji, które:
- Zmień rozmiar każdego obrazu
256 x 256
na większą wysokość i szerokość —286 x 286
. - Przytnij go losowo do
256 x 256
. - Losowo przerzuć obraz w poziomie, tj. od lewej do prawej (losowe odbicie lustrzane).
- Normalizuj obrazy do zakresu
[-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
Możesz sprawdzić niektóre wstępnie przetworzone dane wyjściowe:
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()
Po sprawdzeniu, czy ładowanie i przetwarzanie wstępne działa, zdefiniujmy kilka funkcji pomocniczych, które ładują i wstępnie przetwarzają zbiory uczące i testowe:
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
Zbuduj potok wejściowy za pomocą 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)
Zbuduj generator
Generatorem twojego pix2pix cGAN jest zmodyfikowany U-Net . Sieć U-Net składa się z kodera (downsamplera) i dekodera (upsamplera). (Więcej informacji na ten temat znajdziesz w samouczku dotyczącym segmentacji obrazów oraz na stronie projektu U-Net .)
- Każdy blok w enkoderze to: Konwolucja -> Normalizacja wsadowa -> Leaky ReLU
- Każdy blok w dekoderze to: Konwolucja transponowana -> Normalizacja wsadowa -> Odrzucenie (dotyczy pierwszych 3 bloków) -> ReLU
- Między koderem a dekoderem występują połączenia pomijane (jak w U-Net).
Zdefiniuj downsampler (enkoder):
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)
Zdefiniuj upsampler (dekoder):
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)
Zdefiniuj generator za pomocą downsamplera i upsamplera:
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)
Wizualizuj architekturę modelu generatora:
generator = Generator()
tf.keras.utils.plot_model(generator, show_shapes=True, dpi=64)
Przetestuj generator:
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>
Zdefiniuj stratę generatora
GAN uczą się straty, która dostosowuje się do danych, podczas gdy cGAN uczą się straty strukturalnej, która karze możliwą strukturę, która różni się od wyjścia sieciowego i docelowego obrazu, jak opisano w artykule pix2pix .
- Strata generatora jest sigmoidalną stratą w entropii krzyżowej generowanych obrazów i szeregu jedynków .
- Artykuł pix2pix wspomina również o utracie L1, która jest MAE (średni błąd bezwzględny) między generowanym obrazem a obrazem docelowym.
- Dzięki temu wygenerowany obraz staje się strukturalnie podobny do obrazu docelowego.
- Wzór do obliczenia całkowitej straty generatora to
gan_loss + LAMBDA * l1_loss
, gdzieLAMBDA = 100
. O tej wartości zadecydowali autorzy artykułu.
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
Procedura szkolenia dla generatora wygląda następująco:
Zbuduj dyskryminator
Dyskryminator w pix2pix cGAN jest zawiłym klasyfikatorem PatchGAN — próbuje on sklasyfikować, czy każda łatka obrazu jest prawdziwa, czy nie, jak opisano w artykule pix2pix .
- Każdy blok w dyskryminatorze to: Konwolucja -> Normalizacja wsadowa -> Nieszczelna ReLU.
- Kształt wyniku po ostatniej warstwie to
(batch_size, 30, 30, 1)
. - Każda łatka obrazu
30 x 30
na wyjściu klasyfikuje część70 x 70
obrazu wejściowego. - Dyskryminator otrzymuje 2 wejścia:
- Obraz wejściowy i obraz docelowy, który powinien zaklasyfikować jako rzeczywisty.
- Obraz wejściowy i obraz wygenerowany (wyjście generatora), który powinien zaklasyfikować jako fałszywy.
- Użyj
tf.concat([inp, tar], axis=-1)
, aby połączyć te 2 dane wejściowe.
Zdefiniujmy dyskryminator:
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)
Wizualizuj architekturę modelu dyskryminatora:
discriminator = Discriminator()
tf.keras.utils.plot_model(discriminator, show_shapes=True, dpi=64)
Przetestuj dyskryminator:
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>
Zdefiniuj stratę dyskryminacyjną
- Funkcja
discriminator_loss
przyjmuje 2 dane wejściowe: obrazy rzeczywiste i obrazy wygenerowane . -
real_loss
to sigmoidalna utrata entropii krzyżowej rzeczywistych obrazów i tablicy jedynek (ponieważ są to rzeczywiste obrazy) . -
generated_loss
to sigmoidalna utrata entropii krzyżowej wygenerowanych obrazów i tablicy zer (ponieważ są to fałszywe obrazy) . -
total_loss
to sumareal_loss
igenerated_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
Poniżej przedstawiono procedurę szkolenia dyskryminatora.
Aby dowiedzieć się więcej o architekturze i hiperparametrach, zapoznaj się z artykułem pix2pix .
Zdefiniuj optymalizatory i punkt kontrolny-saver
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)
Generuj obrazy
Napisz funkcję do wykreślania niektórych obrazów podczas treningu.
- Przekaż obrazy z zestawu testowego do generatora.
- Generator następnie przetłumaczy obraz wejściowy na wyjście.
- Ostatnim krokiem jest wykreślenie prognoz i voila !
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()
Przetestuj funkcję:
for example_input, example_target in test_dataset.take(1):
generate_images(generator, example_input, example_target)
Trening
- Dla każdego przykładu wejście generuje dane wyjściowe.
- Dyskryminator odbiera
input_image
i wygenerowany obraz jako pierwszy sygnał wejściowy. Drugie dane wejściowe toinput_image
itarget_image
. - Następnie oblicz generator i stratę dyskryminatora.
- Następnie oblicz gradienty strat w odniesieniu do zmiennych generatora i dyskryminatora (wejścia) i zastosuj je do optymalizatora.
- Na koniec zarejestruj straty w 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)
Rzeczywista pętla treningowa. Ponieważ w tym samouczku można uruchomić więcej niż jeden zestaw danych, a zestawy danych różnią się znacznie rozmiarem, pętla treningowa jest skonfigurowana do pracy w krokach, a nie w epokach.
- Iteruje przez liczbę kroków.
- Co 10 kroków drukuj kropkę (
.
). - Co 1k kroków: wyczyść wyświetlacz i uruchom
generate_images
, aby pokazać postęp. - Co 5k kroków: zapisz punkt kontrolny.
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)
Ta pętla treningowa zapisuje logi, które możesz przeglądać w TensorBoard, aby monitorować postęp treningu.
Jeśli pracujesz na komputerze lokalnym, uruchomisz osobny proces TensorBoard. Podczas pracy w zeszycie uruchom przeglądarkę przed rozpoczęciem szkolenia w zakresie monitorowania za pomocą TensorBoard.
Aby uruchomić przeglądarkę, wklej następujące elementy do komórki kodu:
%load_ext tensorboard
%tensorboard --logdir {log_dir}
Na koniec uruchom pętlę treningową:
fit(train_dataset, test_dataset, steps=40000)
Time taken for 1000 steps: 36.53 sec
Step: 39k ....................................................................................................
Jeśli chcesz udostępnić wyniki TensorBoard publicznie , możesz przesłać logi do TensorBoard.dev , kopiując następujące elementy do komórki kodu.
tensorboard dev upload --logdir {log_dir}
Możesz zobaczyć wyniki poprzedniego uruchomienia tego notatnika na TensorBoard.dev .
TensorBoard.dev to zarządzane środowisko do hostingu, śledzenia i udostępniania eksperymentów ML ze wszystkimi.
Można go również dołączyć w tekście za pomocą <iframe>
:
display.IFrame(
src="https://tensorboard.dev/experiment/lZ0C6FONROaUMfjYkVyJqw",
width="100%",
height="1000px")
Interpretacja logów jest bardziej subtelna podczas trenowania GAN (lub cGAN jak pix2pix) w porównaniu z prostym modelem klasyfikacji lub regresji. Czego szukać:
- Sprawdź, czy ani generator, ani model dyskryminatora nie "wygrał". Jeśli wartość
gen_gan_loss
lubdisc_loss
jest bardzo niska, oznacza to, że ten model dominuje nad innym i nie udaje Ci się trenować modelu połączonego. - Wartość
log(2) = 0.69
jest dobrym punktem odniesienia dla tych strat, ponieważ wskazuje na zakłopotanie 2 – dyskryminator jest średnio równie niepewny co do dwóch opcji. - W przypadku
disc_loss
wartość poniżej0.69
oznacza, że dyskryminator radzi sobie lepiej niż losowo na połączonym zestawie obrazów rzeczywistych i wygenerowanych. - W przypadku
gen_gan_loss
, wartość poniżej0.69
oznacza, że generator radzi sobie lepiej niż losowy w oszukiwaniu dyskryminatora. - Wraz z postępem treningu,
gen_l1_loss
powinna spadać.
Przywróć najnowszy punkt kontrolny i przetestuj sieć
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>
Wygeneruj kilka obrazów za pomocą zestawu testowego
# 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)