
Unconditional Generative Model (조건 없는 생성모델)
- 다양한 노이즈를 계속 입력으로 넣어보고, 특정 이미지가 생성되기를 기다림.
- 원하는 종류의 이미지를 바로 생성해 내지 못함.
- 생성하고자 하는 데이터에 대해 제어하기 힘듦.
- Generator와 Discriminator의 두 신경망이 minimax game을 통해 서로 경쟁하며 발전함.
- z = 임의 노이즈 / D = Discriminator / G = Generator
- D(x)는 1이 되도록, D(G(z))는 0이 되도록 해야함.


Conditional Generative Model (조건 있는 생성모델)
- 우리가 원하는 이미지를 바로바로 생성해 냄.
- 원하는 이미지를 만들기 위한 특정 조건을 줌.
- GAN에 우변의 + 를 기준으로 양쪽 항에 라벨 정보인 y가 추가됨.
- 특정 조건 y가 함께 입력되므로 z를 어떠한 이미지로 만들어야 할지에 대한 방향을 제어할 수 있음.
- 노이즈와 레이블 정보를 함께 입력하며, fully-connected 레이어를 연속적으로 쌓아 만듦.
- L1손실과 GAN손실을 같이 사용하면 더욱 좋은 결과를 얻을 수 있음.


Subclassing
- tensorflow.keras.Model 을 상속받아 클래스를 만듦.
- __init__() 메서드 안에서 레이어 구성을 정의함.
- 구성된 레이어를 call() 메서드에서 사용해 forward propagation을 진행함.
# GAN Generator 구성하기
from tensorflow.keras import layers, Input, Model
class GeneratorGAN(Model):
def __init__(self):
super(GeneratorGAN, self).__init__()
self.dense_1 = layers.Dense(128, activation='relu')
self.dense_2 = layers.Dense(256, activation='relu')
self.dense_3 = layers.Dense(512, activation='relu')
self.dense_4 = layers.Dense(28*28*1, activation='tanh')
self.reshape = layers.Reshape((28, 28, 1))
def call(self, noise):
out = self.dense_1(noise)
out = self.dense_2(out)
out = self.dense_3(out)
out = self.dense_4(out)
return self.reshape(out)
# GAN Discriminator 구성하기
class DiscriminatorGAN(Model):
def __init__(self):
super(DiscriminatorGAN, self).__init__()
self.flatten = layers.Flatten()
self.blocks = []
for f in [512, 256, 128, 1]:
self.blocks.append(
layers.Dense(f, activation=None if f==1 else "relu")
)
def call(self, x):
x = self.flatten(x)
for block in self.blocks:
x = block(x)
return x
---------------------------------------------------------
# cGAN Generator 구성하기
class GeneratorCGAN(Model):
def __init__(self):
super(GeneratorCGAN, self).__init__()
self.dense_z = layers.Dense(256, activation='relu')
self.dense_y = layers.Dense(256, activation='relu')
self.combined_dense = layers.Dense(512, activation='relu')
self.final_dense = layers.Dense(28 * 28 * 1, activation='tanh')
self.reshape = layers.Reshape((28, 28, 1))
def call(self, noise, label):
noise = self.dense_z(noise)
label = self.dense_y(label)
out = self.combined_dense(tf.concat([noise, label], axis=-1))
out = self.final_dense(out)
return self.reshape(out)
# cGAN Discriminator 구성하기
class Maxout(layers.Layer):
def __init__(self, units, pieces):
super(Maxout, self).__init__()
self.dense = layers.Dense(units*pieces, activation="relu")
self.dropout = layers.Dropout(.5)
self.reshape = layers.Reshape((-1, pieces, units))
def call(self, x):
x = self.dense(x)
x = self.dropout(x)
x = self.reshape(x)
return tf.math.reduce_max(x, axis=2)


class DiscriminatorCGAN(Model):
def __init__(self):
super(DiscriminatorCGAN, self).__init__()
self.flatten = layers.Flatten()
self.image_block = Maxout(240, 5)
self.label_block = Maxout(50, 5)
self.combine_block = Maxout(240, 4)
self.dense = layers.Dense(1, activation=None)
def call(self, image, label):
image = self.flatten(image)
image = self.image_block(image)
label = self.label_block(label)
x = layers.Concatenate()([image, label])
x = self.combine_block(x)
return self.dense(x)
--------------------------------------------------
# loss function과 optimizer 정의
from tensorflow.keras import optimizers, losses
bce = losses.BinaryCrossentropy(from_logits=True)
def generator_loss(fake_output):
return bce(tf.ones_like(fake_output), fake_output)
def discriminator_loss(real_output, fake_output):
return bce(tf.ones_like(real_output), real_output) + bce(tf.zeros_like(fake_output), fake_output)
gene_opt = optimizers.Adam(1e-4)
disc_opt = optimizers.Adam(1e-4)
-----------------------------------------------
# GAN으로 MNIST 학습
gan_generator = GeneratorGAN()
gan_discriminator = DiscriminatorGAN()
@tf.function()
def gan_step(real_images):
noise = tf.random.normal([real_images.shape[0], 100])
with tf.GradientTape(persistent=True) as tape:
# Generator를 이용해 가짜 이미지 생성
fake_images = gan_generator(noise)
# Discriminator를 이용해 진짜 및 가짜이미지를 각각 판별
real_out = gan_discriminator(real_images)
fake_out = gan_discriminator(fake_images)
# 각 손실(loss)을 계산
gene_loss = generator_loss(fake_out)
disc_loss = discriminator_loss(real_out, fake_out)
# gradient 계산
gene_grad = tape.gradient(gene_loss, gan_generator.trainable_variables)
disc_grad = tape.gradient(disc_loss, gan_discriminator.trainable_variables)
# 모델 학습
gene_opt.apply_gradients(zip(gene_grad, gan_generator.trainable_variables))
disc_opt.apply_gradients(zip(disc_grad, gan_discriminator.trainable_variables))
return gene_loss, disc_loss
EPOCHS = 10
for epoch in range(1, EPOCHS+1):
for i, images in enumerate(gan_datasets):
gene_loss, disc_loss = gan_step(images)
if (i+1) % 100 == 0:
print(f"[{epoch}/{EPOCHS} EPOCHS, {i+1} ITER] G:{gene_loss}, D:{disc_loss}")
import numpy as np
noise = tf.random.normal([10, 100])
output = gan_generator(noise)
output = np.squeeze(output.numpy())
plt.figure(figsize=(15,6))
for i in range(1, 11):
plt.subplot(2,5,i)
plt.imshow(output[i-1])
import os
weight_path = os.getenv('HOME')+'/aiffel/conditional_generation/gan/GAN_500'
noise = tf.random.normal([10, 100])
gan_generator = GeneratorGAN()
gan_generator.load_weights(weight_path)
output = gan_generator(noise)
output = np.squeeze(output.numpy())
plt.figure(figsize=(15,6))
for i in range(1, 11):
plt.subplot(2,5,i)
plt.imshow(output[i-1])
--------------------------------------------------
# cGAN으로 MNIST 학습하기
cgan_generator = GeneratorCGAN()
cgan_discriminator = DiscriminatorCGAN()
@tf.function()
def cgan_step(real_images, labels):
noise = tf.random.normal([real_images.shape[0], 100])
with tf.GradientTape(persistent=True) as tape:
fake_images = cgan_generator(noise, labels)
real_out = cgan_discriminator(real_images, labels)
fake_out = cgan_discriminator(fake_images, labels)
gene_loss = generator_loss(fake_out)
disc_loss = discriminator_loss(real_out, fake_out)
gene_grad = tape.gradient(gene_loss, cgan_generator.trainable_variables)
disc_grad = tape.gradient(disc_loss, cgan_discriminator.trainable_variables)
gene_opt.apply_gradients(zip(gene_grad, cgan_generator.trainable_variables))
disc_opt.apply_gradients(zip(disc_grad, cgan_discriminator.trainable_variables))
return gene_loss, disc_loss
EPOCHS = 1
for epoch in range(1, EPOCHS+1):
for i, (images, labels) in enumerate(cgan_datasets):
gene_loss, disc_loss = cgan_step(images, labels)
if (i+1) % 100 == 0:
print(f"[{epoch}/{EPOCHS} EPOCHS, {i} ITER] G:{gene_loss}, D:{disc_loss}")
number = 3 # 생성하길 원하는 숫자
weight_path = os.getenv('HOME')+'/aiffel/conditional_generation/cgan/CGAN_500'
noise = tf.random.normal([10, 100])
label = tf.one_hot(number, 10)
label = tf.expand_dims(label, axis=0)
label = tf.repeat(label, 10, axis=0)
generator = GeneratorCGAN()
generator.load_weights(weight_path)
output = generator(noise, label)
output = np.squeeze(output.numpy())
plt.figure(figsize=(15,6))
for i in range(1, 11):
plt.subplot(2,5,i)
plt.imshow(output[i-1])
Pix2Pix
- 이미지를 입력으로 하여 원하는 다른 형태의 이미지로 변환시킬 수 있는 모델
- 원하는 이미지를 얻기 위해 이미지를 조건으로 줌.
- Conditional Adversarial Networks로 Image-to-Image Translation을 수행함.
- 이미지의 픽셀에서 다른 이미지의 픽셀로(pixel to pixel)변환한다는 뜻에서 Pix2Pix라는 이름으로 불림.
- PatchGAN을 활용하며, 크게는 Generator와 Discriminator 두 가지 구성 요소로 이루어짐.
- 이미지를 다루는데 효율적인 convolution 레이어를 활용함.
- Encoder-Decoder구조와 U-Net 구조가 있음.
- 간단한 이미지를 입력할 경우 실제 사진처럼 보이도록 바꿔줌.
- 단순화된 이미지(Input Image)와 실제 이미지(Ground Truth)가 쌍을 이루는 데이터셋으로 학습을 진행함.
- 한 방향으로의 변환만 가능함.



Encoder-Decoder
- Encoder에서 입력 이미지x를 받으면 단계적으로 이미지를 down-sampling하면서 입력 이미지의 중요한 representation을 학습함.
- Decoder에서는 이를 이용해 반대로 다시 이미지를 up-sampling하여 입력 이미지와 동일한 크기의 변환된 이미지y를 생성해냄.
- Encoder의 최종 출력은 그림 중간에 위치한 가장 작은 사각형이며, bottleneck이라고도 불림.
=> 입력 이미지(x)의 가장 중요한 특징만을 담고 있음.

U-Net
- U-Net은 각 레이어마다 Encoder와 Decoder가 연결(skip connection)되어 있음.
- Decoder가 변환된 이미지를 더 잘 생성하도록 Encoder로부터 더 많은 추가 정보를 이용함.
- 단순한 Encoder-Decoder구조를 사용한 결과에 비해 선명한 결과를 얻을 수 있음.

PatchGAN
- 하나의 이미지가 Discriminator의 입력으로 들어오면, convolution 레이어를 거쳐 확률 값을 나타내는 최종 결과를 생성하는데, 그 결과는 여러 개의 값을 가짐.
- 서로 다른 영역에 대해 진짜/가짜를 나타내는 여러 개의 확률 값을 계산할 수 있으며, 이 값을 평균하여 최종 Discriminator의 출력을 생성함.
- 이미지의 일부 영역(patch)을 이용함.
- 이미지에서 거리가 먼 두 픽셀은 서로 연관성이 거의 없으므로, 특정 크기를 가진 일부 영역에 대해 세부적으로 진짜/가짜를 판별하는 것

판별 모델링 (Discriminative Modeling)
- 직접 데이터셋을 만들고, 각 이미지를 알맞은 카테고리로 분류할 수 있도록 학습시키는 모델
- 입력받은 데이터를 어떤 기준에 대해 판별하는 것이 목표인 모델링
- 입력된 데이터셋을 특정 기준에 따라 분류하거나, 특정 값을 맞히는 모델
생성 모델링 (Generative Modeling)
- 학습한 데이터셋과 비슷하면서도 기존에는 없던 새로운 데이터셋을 생성하는 모델
- 데이터와 모델로부터 도출할 수 있는 여러 확률 분포와 베이즈 이론을 이용해 데이터의 실제 분포를 간접적으로 모델링함.
생성 모델을 학습시킬 때는 두 확률 분포의 차이를 나타내는 지표인 쿨백-라이블러 발산이 사용됨.
CycleGAN
- 한 이미지와 다른 이미지를 번갈아가며 변형시킴.
- 데이터가 쌍을 이루지 않더라도, 사진 각각의 데이터셋만 있다면 학습시킬 수 있음.
- 양방향으로의 이미지 변환이 가능함. (실사 이미지를 그림으로 바꾸는 것과 그림을 실사 이미지로 바꾸는 것 모두 가능)

Neural Style Transfer
- 전체 이미지의 구성을 유지하고 싶은 Base Image와 입히고 싶은 스타일이 담긴 Style Image 두 장을 활용해 새로운 이미지를 만들어 냄.
- Base Image에서는 Content(내용)만, Style Image에서는 Style(스타일)만 추출해서 합침.

GAN (Generative Adversarial Network)
- GAN에는 생성자(Generator)와 판별자(Discriminator) 두 가지 네트워크가 있음.
- 생성자: 아무 의미 없는 랜덤 노이즈로부터 신경망 연산을 통해 이미지 형상의 벡터를 생성해냄.
- 판별자: 기존에 있던 진짜 이미지와 생성자가 만들어낸 이미지를 입력받아 각 이미지가 진짜(Real)인지, 가짜 (Fake)인지에 대한 판단 정도를 실숫값으로 출력함.
- 생성자와 판별자 서로 간의 경쟁이 둘 모두를 성장하게 하며, 결국 진짜와 구분될 수 없는 위조를 만들어 냄.
- 손실함수로 교차 엔트로피(Cross Entropy)를 사용함.

DCGAN (Deep Convolutional GAN)
- GAN 중 특히 Convolutional Layer으로 이루어진 딥러닝 모델
- GAN을 더욱 발전시켜서 훨씬 그럴듯한 고화질 이미지 생성해냄.