from tensorflow import keras
keras.__version__
'2.4.0'
이 노트북은 케라스 창시자에게 배우는 딥러닝 책의 8장 5절의 코드 예제입니다. 책에는 더 많은 내용과 그림이 있습니다. 이 노트북에는 소스 코드에 관련된 설명만 포함합니다. 이 노트북의 설명은 케라스 버전 2.2.2에 맞추어져 있습니다. 케라스 최신 버전이 릴리스되면 노트북을 다시 테스트하기 때문에 설명과 코드의 결과가 조금 다를 수 있습니다.
[...]
이 절에서는 케라스에서 가장 기본적인 형태의 GAN을 구현하는 방법을 설명하겠습니다. GAN은 수준 높은 기술이기 때문에 기술적인 내용을 깊이 설명하는 것은 이 책의 범위를 벗어납니다. 구체적인 구현은 심층 합성곱 GAN(DCGAN)입니다. 생성자와 판별자가 심층 컨브넷입니다. 특히 생성자에서 이미지 업샘플링을 위해 Conv2DTranspose
층을 사용합니다.
CIFAR10 데이터셋의 이미지로 GAN을 훈련하겠습니다. 이 데이터셋은 32 × 32 크기의 RGB 이미지 50,000개로 이루어져 있고 10개의 클래스를 가집니다(클래스마다 5,000개의 이미지가 있습니다). 문제를 간단하게 만들기 위해 “frog” 클래스의 이미지만 사용하겠습니다.
GAN 구조는 다음과 같습니다:
generator
네트워크는 (latent_dim,)
크기의 벡터를 (32, 32, 3)
크기의 이미지로 매핑합니다.discriminator
네트워크는 (32, 32, 3) 크기의 이미지가 진짜일 확률을 추정하여 이진 값으로 매핑합니다.gan
네트워크를 만듭니다. gan(x) = discriminator(generator(x))
입니다. 이 gan
네트워크는 잠재 공간의 벡터를 판별자의 평가로 매핑합니다. 판별자는 생성자가 잠재 공간의 벡터를 디코딩한 것이 얼마나 현실적인지를 평가합니다.gan
모델의 손실에 대한 생성자 가중치의 그래디언트를 사용합니다. 이 말은 매 단계마다 생성자에 의해 디코딩된 이미지를 판별자가 “진짜"로 분류하도록 만드는 방향으로 생성자의 가중치를 이동한다는 뜻입니다. 다른 말로하면 판별자를 속이도록 생성자를 훈련합니다.GAN을 훈련하고 튜닝하는 과정은 어렵기로 유명합니다. 알아두어야 할 몇 가지 유용한 기법이 있습니다. 딥러닝의 대부분이 그렇듯이 이는 과학보다는 연금술에 가깝습니다. 이런 기법들은 이론에 바탕을 둔 지침이 아니고 경험을 통해 발견된 것입니다. 실제 일어난 현상을 직관적으로 이해하는 수준에서 검증되었습니다. 모든 문제에 반드시 적용해야 것은 아니지만 경험상 잘 작동한다고 알려져 있습니다.
다음은 이 절에서 GAN 생성자와 판별자를 구현하는 데 사용할 몇 가지 기법입니다. 이 목록이 GAN에 관련된 전체 팁이 아닙니다. GAN 논문들에서 더 많은 방법을 볼 수 있습니다.
sigmoid
대신 tanh
함수를 사용합니다. LeakyReLU
층을 사용하세요. ReLU와 비슷하지만 음수의 활성화 값을 조금 허용하기 때문에 희소가 조금 완화됩니다.Conv2DTranpose
나 Conv2D
를 사용할 때 스트라이드 크기로 나누어질 수 있는 커널 크기를 사용합니다.먼저 벡터(훈련하는 동안 잠재 공간에서 무작위로 샘플링됩니다)를 후보 이미지로 변환하는 generator
모델을 만들어 보죠. GAN에서 발생하는 많은 문제 중 하나는 생성자가 노이즈 같은 이미지를 생성하는 데서 멈추는 것입니다. 판별자와 생성자 양쪽에 모두 드롭아웃을 사용하는 것이 해결 방법이 될 수 있습니다.
from tensorflow import keras
from tensorflow.keras import layers
import numpy as np
latent_dim = 32
height = 32
width = 32
channels = 3
generator_input = keras.Input(shape=(latent_dim,))
# 입력을 16 × 16 크기의 128개 채널을 가진 특성 맵으로 변환합니다
x = layers.Dense(128 * 16 * 16)(generator_input)
x = layers.LeakyReLU()(x)
x = layers.Reshape((16, 16, 128))(x)
# 합성곱 층을 추가합니다
x = layers.Conv2D(256, 5, padding='same')(x)
x = layers.LeakyReLU()(x)
# 32 × 32 크기로 업샘플링합니다
x = layers.Conv2DTranspose(256, 4, strides=2, padding='same')(x)
x = layers.LeakyReLU()(x)
# 합성곱 층을 더 추가합니다
x = layers.Conv2D(256, 5, padding='same')(x)
x = layers.LeakyReLU()(x)
x = layers.Conv2D(256, 5, padding='same')(x)
x = layers.LeakyReLU()(x)
# 32 × 32 크기의 1개 채널을 가진 특성 맵을 생성합니다
x = layers.Conv2D(channels, 7, activation='tanh', padding='same')(x)
generator = keras.models.Model(generator_input, x)
generator.summary()
Model: "functional_1" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= input_1 (InputLayer) [(None, 32)] 0 _________________________________________________________________ dense (Dense) (None, 32768) 1081344 _________________________________________________________________ leaky_re_lu (LeakyReLU) (None, 32768) 0 _________________________________________________________________ reshape (Reshape) (None, 16, 16, 128) 0 _________________________________________________________________ conv2d (Conv2D) (None, 16, 16, 256) 819456 _________________________________________________________________ leaky_re_lu_1 (LeakyReLU) (None, 16, 16, 256) 0 _________________________________________________________________ conv2d_transpose (Conv2DTran (None, 32, 32, 256) 1048832 _________________________________________________________________ leaky_re_lu_2 (LeakyReLU) (None, 32, 32, 256) 0 _________________________________________________________________ conv2d_1 (Conv2D) (None, 32, 32, 256) 1638656 _________________________________________________________________ leaky_re_lu_3 (LeakyReLU) (None, 32, 32, 256) 0 _________________________________________________________________ conv2d_2 (Conv2D) (None, 32, 32, 256) 1638656 _________________________________________________________________ leaky_re_lu_4 (LeakyReLU) (None, 32, 32, 256) 0 _________________________________________________________________ conv2d_3 (Conv2D) (None, 32, 32, 3) 37635 ================================================================= Total params: 6,264,579 Trainable params: 6,264,579 Non-trainable params: 0 _________________________________________________________________
다음은 후보 이미지(진짜 혹은 가짜)를 입력으로 받고 두 개의 클래스로 분류하는 discriminator
모델을 만들겠습니다. 이 클래스는 '생성된 이미지' 또는 '훈련 세트에서 온 진짜 이미지'입니다.
discriminator_input = layers.Input(shape=(height, width, channels))
x = layers.Conv2D(128, 3)(discriminator_input)
x = layers.LeakyReLU()(x)
x = layers.Conv2D(128, 4, strides=2)(x)
x = layers.LeakyReLU()(x)
x = layers.Conv2D(128, 4, strides=2)(x)
x = layers.LeakyReLU()(x)
x = layers.Conv2D(128, 4, strides=2)(x)
x = layers.LeakyReLU()(x)
x = layers.Flatten()(x)
# 드롭아웃 층을 넣는 것이 아주 중요합니다!
x = layers.Dropout(0.4)(x)
# 분류 층
x = layers.Dense(1, activation='sigmoid')(x)
discriminator = keras.models.Model(discriminator_input, x)
discriminator.summary()
# 옵티마이저에서 (값을 지정하여) 그래디언트 클리핑을 사용합니다
# 안정된 훈련을 위해서 학습률 감쇠를 사용합니다
discriminator_optimizer = keras.optimizers.RMSprop(lr=0.0008, clipvalue=1.0, decay=1e-8)
discriminator.compile(optimizer=discriminator_optimizer, loss='binary_crossentropy')
Model: "functional_3" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= input_2 (InputLayer) [(None, 32, 32, 3)] 0 _________________________________________________________________ conv2d_4 (Conv2D) (None, 30, 30, 128) 3584 _________________________________________________________________ leaky_re_lu_5 (LeakyReLU) (None, 30, 30, 128) 0 _________________________________________________________________ conv2d_5 (Conv2D) (None, 14, 14, 128) 262272 _________________________________________________________________ leaky_re_lu_6 (LeakyReLU) (None, 14, 14, 128) 0 _________________________________________________________________ conv2d_6 (Conv2D) (None, 6, 6, 128) 262272 _________________________________________________________________ leaky_re_lu_7 (LeakyReLU) (None, 6, 6, 128) 0 _________________________________________________________________ conv2d_7 (Conv2D) (None, 2, 2, 128) 262272 _________________________________________________________________ leaky_re_lu_8 (LeakyReLU) (None, 2, 2, 128) 0 _________________________________________________________________ flatten (Flatten) (None, 512) 0 _________________________________________________________________ dropout (Dropout) (None, 512) 0 _________________________________________________________________ dense_1 (Dense) (None, 1) 513 ================================================================= Total params: 790,913 Trainable params: 790,913 Non-trainable params: 0 _________________________________________________________________
마지막으로 생성자와 판별자를 연결하여 GAN을 설정합니다. 훈련할 때 생성자가 판별자를 속이는 능력이 커지도록 학습합니다. 이 모델은 잠재 공간의 포인트를 “진짜" 또는 “가짜"의 분류 결정으로 변환합니다. 훈련에 사용되는 타깃 레이블은 항상 '진짜 이미지'입니다. gan
을 훈련하는 것은 discriminator
가 가짜 이미지를 보았을 때 진짜라고 예측하도록 만들기 위해 generator
의 가중치를 업데이트하는 것입니다. 훈련하는 동안 판별자를 동결(학습되지 않도록)하는 것이 아주 중요합니다. gan
을 훈련할 때 가중치가 업데이트되지 않습니다. 판별자의 가중치가 훈련하는 동안 업데이트되면 판별자는 항상 “진짜"를 예측하도록 훈련됩니다. 이것이 우리가 원하는 바는 아니죠!
# 판별자의 가중치가 훈련되지 않도록 설정합니다(gan 모델에만 적용됩니다)
discriminator.trainable = False
gan_input = keras.Input(shape=(latent_dim,))
gan_output = discriminator(generator(gan_input))
gan = keras.models.Model(gan_input, gan_output)
gan_optimizer = keras.optimizers.RMSprop(lr=0.0004, clipvalue=1.0, decay=1e-8)
gan.compile(optimizer=gan_optimizer, loss='binary_crossentropy')
이제 훈련을 시작합니다. 훈련 반복의 내용을 요약 정리해 보겠습니다.
매 반복마다 다음을 수행합니다:
1.잠재 공간에서 무작위로 포인트를 뽑습니다(랜덤 노이즈).
2.이 랜덤 노이즈를 사용해 `generator`에서 이미지를 생성합니다.
3.생성된 이미지와 진짜 이미지를 섞습니다.
4.진짜와 가짜가 섞인 이미지와 이에 대응하는 타깃을 사용해 `discriminator`를 훈련합니다. 타깃은 “진짜"(실제 이미지일 경우) 또는 “가짜"(생성된 이미지일 경우)입니다.
5.잠재 공간에서 무작위로 새로운 포인트를 뽑습니다.
6.이 랜덤 벡터를 사용해 `gan`을 훈련합니다. 모든 타깃은 “진짜"로 설정합니다. 판별자가 생성된 이미지를 모두 “진짜 이미지"라고 예측하도록 생성자의 가중치를 업데이트합니다(`gan` 안에서 판별자는 동결되기 때문에 생성자만 업데이트합니다). 결국 생성자는 판별자를 속이도록 훈련합니다.
실제로 만들어 보죠:
import os
from tensorflow.keras.preprocessing import image
# CIFAR10 데이터를 로드합니다
(x_train, y_train), (_, _) = keras.datasets.cifar10.load_data()
# 개구리 이미지를 선택합니다(클래스 6)
x_train = x_train[y_train.flatten() == 6]
# 데이터를 정규화합니다
x_train = x_train.reshape(
(x_train.shape[0],) + (height, width, channels)).astype('float32') / 255.
iterations = 10000
batch_size = 20
save_dir = './datasets/gan_images/'
if not os.path.exists(save_dir):
os.mkdir(save_dir)
# 훈련 반복 시작
start = 0
for step in range(iterations):
# 잠재 공간에서 무작위로 포인트를 샘플링합니다
random_latent_vectors = np.random.normal(size=(batch_size, latent_dim))
# 가짜 이미지를 디코딩합니다
generated_images = generator.predict(random_latent_vectors)
# 진짜 이미지와 연결합니다
stop = start + batch_size
real_images = x_train[start: stop]
combined_images = np.concatenate([generated_images, real_images])
# 진짜와 가짜 이미지를 구분하여 레이블을 합칩니다
labels = np.concatenate([np.ones((batch_size, 1)),
np.zeros((batch_size, 1))])
# 레이블에 랜덤 노이즈를 추가합니다. 아주 중요합니다!
labels += 0.05 * np.random.random(labels.shape)
# discriminator를 훈련합니다
d_loss = discriminator.train_on_batch(combined_images, labels)
# 잠재 공간에서 무작위로 포인트를 샘플링합니다
random_latent_vectors = np.random.normal(size=(batch_size, latent_dim))
# 모두 “진짜 이미지"라고 레이블을 만듭니다
misleading_targets = np.zeros((batch_size, 1))
# generator를 훈련합니다(gan 모델에서 discriminator의 가중치는 동결됩니다)
a_loss = gan.train_on_batch(random_latent_vectors, misleading_targets)
start += batch_size
if start > len(x_train) - batch_size:
start = 0
# 중간 중간 저장하고 그래프를 그립니다
if step % 100 == 0:
# 모델 가중치를 저장합니다
gan.save_weights('gan.h5')
# 측정 지표를 출력합니다
print('스텝 %s에서 판별자 손실: %s' % (step, d_loss))
print('스텝 %s에서 적대적 손실: %s' % (step, a_loss))
# 생성된 이미지 하나를 저장합니다
img = image.array_to_img(generated_images[0] * 255., scale=False)
img.save(os.path.join(save_dir, 'generated_frog' + str(step) + '.png'))
# 비교를 위해 진짜 이미지 하나를 저장합니다
img = image.array_to_img(real_images[0] * 255., scale=False)
img.save(os.path.join(save_dir, 'real_frog' + str(step) + '.png'))
Downloading data from https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz 170500096/170498071 [==============================] - 117s 1us/step 스텝 0에서 판별자 손실: 0.6954938173294067 스텝 0에서 적대적 손실: 0.6839010715484619 스텝 100에서 판별자 손실: 0.5293288230895996 스텝 100에서 적대적 손실: 1.4702720642089844 스텝 200에서 판별자 손실: 0.7253757119178772 스텝 200에서 적대적 손실: 0.764021635055542 스텝 300에서 판별자 손실: 0.6853467226028442 스텝 300에서 적대적 손실: 0.7592910528182983 스텝 400에서 판별자 손실: 0.6928459405899048 스텝 400에서 적대적 손실: 0.7896996736526489 스텝 500에서 판별자 손실: 0.6881178617477417 스텝 500에서 적대적 손실: 0.7745105028152466 스텝 600에서 판별자 손실: 0.7012399435043335 스텝 600에서 적대적 손실: 0.7359456419944763 스텝 700에서 판별자 손실: 0.6747683882713318 스텝 700에서 적대적 손실: 0.713494062423706 스텝 800에서 판별자 손실: 0.6974363327026367 스텝 800에서 적대적 손실: 0.7557658553123474 스텝 900에서 판별자 손실: 0.7019640803337097 스텝 900에서 적대적 손실: 0.7655956745147705 스텝 1000에서 판별자 손실: 0.6988890171051025 스텝 1000에서 적대적 손실: 0.7527780532836914 스텝 1100에서 판별자 손실: 0.6727069020271301 스텝 1100에서 적대적 손실: 0.9365208745002747 스텝 1200에서 판별자 손실: 0.7142273783683777 스텝 1200에서 적대적 손실: 0.7480975389480591 스텝 1300에서 판별자 손실: 0.6958877444267273 스텝 1300에서 적대적 손실: 0.7540103197097778 스텝 1400에서 판별자 손실: 0.694217324256897 스텝 1400에서 적대적 손실: 0.7503114938735962 스텝 1500에서 판별자 손실: 0.6972348093986511 스텝 1500에서 적대적 손실: 0.7861263155937195 스텝 1600에서 판별자 손실: 0.7046893835067749 스텝 1600에서 적대적 손실: 0.8621726036071777 스텝 1700에서 판별자 손실: 0.6933452486991882 스텝 1700에서 적대적 손실: 0.7262558341026306 스텝 1800에서 판별자 손실: 0.681158721446991 스텝 1800에서 적대적 손실: 0.7801971435546875 스텝 1900에서 판별자 손실: 0.6946350336074829 스텝 1900에서 적대적 손실: 0.7540804147720337 스텝 2000에서 판별자 손실: 0.6851427555084229 스텝 2000에서 적대적 손실: 0.7599497437477112 스텝 2100에서 판별자 손실: 0.6946498155593872 스텝 2100에서 적대적 손실: 0.7407844662666321 스텝 2200에서 판별자 손실: 0.6894062161445618 스텝 2200에서 적대적 손실: 0.7773017883300781 스텝 2300에서 판별자 손실: 0.6826938390731812 스텝 2300에서 적대적 손실: 0.7418308258056641 스텝 2400에서 판별자 손실: 0.6929084658622742 스텝 2400에서 적대적 손실: 0.7430875301361084 스텝 2500에서 판별자 손실: 0.6969627141952515 스텝 2500에서 적대적 손실: 0.7861038446426392 스텝 2600에서 판별자 손실: 0.6888699531555176 스텝 2600에서 적대적 손실: 0.8277886509895325 스텝 2700에서 판별자 손실: 0.6935863494873047 스텝 2700에서 적대적 손실: 1.0680965185165405 스텝 2800에서 판별자 손실: 0.6951996684074402 스텝 2800에서 적대적 손실: 0.7351120710372925 스텝 2900에서 판별자 손실: 0.7019563913345337 스텝 2900에서 적대적 손실: 0.7674177885055542 스텝 3000에서 판별자 손실: 1.0697238445281982 스텝 3000에서 적대적 손실: 0.8496044278144836 스텝 3100에서 판별자 손실: 0.6980506181716919 스텝 3100에서 적대적 손실: 0.7367621660232544 스텝 3300에서 판별자 손실: 0.6957570314407349 스텝 3300에서 적대적 손실: 0.7642474174499512 스텝 3400에서 판별자 손실: 0.6948242783546448 스텝 3400에서 적대적 손실: 0.714725136756897 스텝 3500에서 판별자 손실: 0.6987977027893066 스텝 3500에서 적대적 손실: 0.7100650072097778 스텝 3600에서 판별자 손실: 0.7076869010925293 스텝 3600에서 적대적 손실: 0.784033477306366 스텝 3700에서 판별자 손실: 0.704955518245697 스텝 3700에서 적대적 손실: 0.766324520111084 스텝 3800에서 판별자 손실: 0.7246819734573364 스텝 3800에서 적대적 손실: 0.7831839919090271 스텝 3900에서 판별자 손실: 0.6971052289009094 스텝 3900에서 적대적 손실: 0.7835835814476013 스텝 4000에서 판별자 손실: 0.6894428730010986 스텝 4000에서 적대적 손실: 0.7678594589233398 스텝 4100에서 판별자 손실: 0.6992965936660767 스텝 4100에서 적대적 손실: 0.7764323949813843 스텝 4200에서 판별자 손실: 0.7272781133651733 스텝 4200에서 적대적 손실: 0.7847261428833008 스텝 4300에서 판별자 손실: 0.7030823230743408 스텝 4300에서 적대적 손실: 0.7524744272232056 스텝 4400에서 판별자 손실: 0.700865626335144 스텝 4400에서 적대적 손실: 0.8114334940910339 스텝 4500에서 판별자 손실: 0.6944526433944702 스텝 4500에서 적대적 손실: 0.731998085975647 스텝 4600에서 판별자 손실: 0.703859269618988 스텝 4600에서 적대적 손실: 0.7620692253112793 스텝 4700에서 판별자 손실: 0.7200272083282471 스텝 4700에서 적대적 손실: 0.7217906713485718 스텝 4800에서 판별자 손실: 0.6864199638366699 스텝 4800에서 적대적 손실: 0.7747318148612976 스텝 4900에서 판별자 손실: 0.713912844657898 스텝 4900에서 적대적 손실: 0.7701746821403503 스텝 5000에서 판별자 손실: 0.6810040473937988 스텝 5000에서 적대적 손실: 0.7833706140518188 스텝 5100에서 판별자 손실: 0.7078413963317871 스텝 5100에서 적대적 손실: 0.7711440324783325 스텝 5200에서 판별자 손실: 0.6994761228561401 스텝 5200에서 적대적 손실: 0.8838688135147095 스텝 5300에서 판별자 손실: 0.6806604266166687 스텝 5300에서 적대적 손실: 0.7126954793930054 스텝 5400에서 판별자 손실: 0.6827183961868286 스텝 5400에서 적대적 손실: 0.8984538912773132 스텝 5500에서 판별자 손실: 0.7980812788009644 스텝 5500에서 적대적 손실: 1.2263977527618408 스텝 5600에서 판별자 손실: 0.6831499934196472 스텝 5600에서 적대적 손실: 0.8456770777702332 스텝 5700에서 판별자 손실: 0.7043609619140625 스텝 5700에서 적대적 손실: 0.8788458704948425 스텝 5800에서 판별자 손실: 0.6890338659286499 스텝 5800에서 적대적 손실: 0.8350523114204407 스텝 5900에서 판별자 손실: 0.7137543559074402 스텝 5900에서 적대적 손실: 0.7547172904014587 스텝 6000에서 판별자 손실: 0.7036588788032532 스텝 6000에서 적대적 손실: 0.6981458067893982 스텝 6100에서 판별자 손실: 0.6665136218070984 스텝 6100에서 적대적 손실: 0.6760081052780151 스텝 6200에서 판별자 손실: 0.68841153383255 스텝 6200에서 적대적 손실: 0.7986423373222351 스텝 6300에서 판별자 손실: 0.6563520431518555 스텝 6300에서 적대적 손실: 0.8301820755004883 스텝 6400에서 판별자 손실: 0.7142050862312317 스텝 6400에서 적대적 손실: 0.630608320236206 스텝 6500에서 판별자 손실: 0.6511518359184265 스텝 6500에서 적대적 손실: 0.7464785575866699 스텝 6600에서 판별자 손실: 0.6865206360816956 스텝 6600에서 적대적 손실: 0.7669548392295837 스텝 6700에서 판별자 손실: 0.7358461618423462 스텝 6700에서 적대적 손실: 0.842680811882019 스텝 6800에서 판별자 손실: 0.6516304612159729 스텝 6800에서 적대적 손실: 0.7748488783836365 스텝 6900에서 판별자 손실: 0.7846569418907166 스텝 6900에서 적대적 손실: 0.44944435358047485 스텝 7000에서 판별자 손실: 0.656812310218811 스텝 7000에서 적대적 손실: 0.8753320574760437 스텝 7100에서 판별자 손실: 0.6967417597770691 스텝 7100에서 적대적 손실: 0.8476675152778625 스텝 7200에서 판별자 손실: 0.6813312768936157 스텝 7200에서 적대적 손실: 0.985464870929718 스텝 7300에서 판별자 손실: 0.7410323023796082 스텝 7300에서 적대적 손실: 0.9480243921279907 스텝 7400에서 판별자 손실: 0.6957409381866455 스텝 7400에서 적대적 손실: 0.7619909644126892 스텝 7500에서 판별자 손실: 0.6931641697883606 스텝 7500에서 적대적 손실: 0.6645042300224304 스텝 7600에서 판별자 손실: 0.6817981004714966 스텝 7600에서 적대적 손실: 0.6930878162384033 스텝 7700에서 판별자 손실: 0.6938729286193848 스텝 7700에서 적대적 손실: 0.9163234829902649 스텝 7800에서 판별자 손실: 0.7595785856246948 스텝 7800에서 적대적 손실: 0.8172329664230347 스텝 7900에서 판별자 손실: 0.6647912263870239 스텝 7900에서 적대적 손실: 0.8471579551696777 스텝 8000에서 판별자 손실: 0.6667997241020203 스텝 8000에서 적대적 손실: 0.7422307729721069 스텝 8100에서 판별자 손실: 0.6976957321166992 스텝 8100에서 적대적 손실: 0.7267211079597473 스텝 8200에서 판별자 손실: 0.7258110642433167 스텝 8200에서 적대적 손실: 0.8224571347236633 스텝 8300에서 판별자 손실: 0.6501212120056152 스텝 8300에서 적대적 손실: 1.0031243562698364 스텝 8400에서 판별자 손실: 0.6956596970558167 스텝 8400에서 적대적 손실: 0.8700739145278931 스텝 8500에서 판별자 손실: 0.694157063961029 스텝 8500에서 적대적 손실: 0.800123393535614 스텝 8600에서 판별자 손실: 0.6714283227920532 스텝 8600에서 적대적 손실: 0.750914454460144 스텝 8700에서 판별자 손실: 0.7249020338058472 스텝 8700에서 적대적 손실: 0.806562602519989 스텝 8800에서 판별자 손실: 0.6476495862007141 스텝 8800에서 적대적 손실: 0.8264656066894531 스텝 8900에서 판별자 손실: 0.6533183455467224 스텝 8900에서 적대적 손실: 0.7467435598373413 스텝 9000에서 판별자 손실: 0.687132716178894 스텝 9000에서 적대적 손실: 0.9054478406906128 스텝 9100에서 판별자 손실: 0.6684829592704773 스텝 9100에서 적대적 손실: 0.7047704458236694 스텝 9200에서 판별자 손실: 0.6910877227783203 스텝 9200에서 적대적 손실: 0.760394811630249 스텝 9300에서 판별자 손실: 0.6879705190658569 스텝 9300에서 적대적 손실: 0.8744182586669922 스텝 9400에서 판별자 손실: 0.6681801676750183 스텝 9400에서 적대적 손실: 0.7699354887008667 스텝 9500에서 판별자 손실: 0.6621791124343872 스텝 9500에서 적대적 손실: 0.9276946187019348 스텝 9600에서 판별자 손실: 0.6805607676506042 스텝 9600에서 적대적 손실: 0.7946552038192749 스텝 9700에서 판별자 손실: 0.6810634732246399 스텝 9700에서 적대적 손실: 0.8227903246879578 스텝 9800에서 판별자 손실: 0.6792944669723511 스텝 9800에서 적대적 손실: 0.7747868299484253 스텝 9900에서 판별자 손실: 0.6984797716140747 스텝 9900에서 적대적 손실: 0.7750678062438965
가짜 이미지 몇개를 출력해 보죠:
import matplotlib.pyplot as plt
# 잠재 공간에서 랜덤한 포인트를 샘플링합니다
random_latent_vectors = np.random.normal(size=(10, latent_dim))
# 가짜 이미지로 디코딩합니다
generated_images = generator.predict(random_latent_vectors)
for i in range(generated_images.shape[0]):
img = image.array_to_img(generated_images[i] * 255., scale=False)
plt.figure()
plt.imshow(img)
plt.show()
픽셀 경계가 두드러진 개구리처럼 보이는 이미지를 얻었습니다.