Aiffel) Exploration - 생성형 AI(Generative Modeling) 정리
내용 출처 : Aiffel
1. 생성 모델링
1) 판별 모델(분류, 회귀 등)과의 차이
- 판별 모델 : 분류, 회귀
- 생성 모델 : 학습 데이터셋에 기반하여 새로운 데이터셋을 생성
- ✨최근 예시 : AWS의 DeepComposer
- GAN(생성적 적대 신경망) = Generator(생성자) vs Discriminator(판별자) 으로 학습진행
- Pix2Pix(그림→사진 변경 AI)
📌 주요 용어 : Ground Truth(실제 이미지)
- ✨ 논문을 통한 이해 : https://arxiv.org/pdf/1611.07004.pdf
- ✨최근 예시 : AWS의 DeepComposer
2) 모델1_CycleGAN
- 한 이미지와 다른 이미지를 번갈아 가며 Cyclic하게 변환
- paired data가 필요X → annotation 비용 X
- 📌
주요 용어 : paired data(쌍으로 이루어진 데이터 셋), annotation(라벨링)
3) 모델2_Neural Style Transfer
-
스타일 변환
- ✨한 개의 Base Image로 5가지 스타일 변환을 적용시킨 모습
- Base Image의 Content + Style Image의 Style을 합
📌 주요 용어 : Content(내용), Style(스타일)
4) 코드 구현을 통한 GAN이해하기
📌 Conv2DTranspose(생성) ↔ Conv2D(판별)
✨ 사전 준비
import os
import glob
import time
import PIL
import imageio
import numpy as np
import tensorflow as tf
from tensorflow.keras import layers
from IPython import display
import matplotlib.pyplot as plt
%matplotlib inline
print("tensorflow", tf.__version__)
fashion_mnist = tf.keras.datasets.fashion_mnist
(train_x, _), (test_x, _) = fashion_mnist.load_data()
train_x = (train_x - 127.5)/127.5
train_x = train_x.reshape(train_x.shape[0], 28, 28, 1).astype('float32')
plt.imshow(train_x[0].reshape(28, 28), cmap='gray')
plt.colorbar()
plt.show()
BUFFER_SIZE = 60000
BATCH_SIZE = 256
train_dataset = tf.data.Dataset.from_tensor_slices(train_x).shuffle(BUFFER_SIZE).batch(BATCH_SIZE)
(1) Generator
- 코드 구현
def make_generator_model(): model = tf.keras.Sequential() # First: Dense layer # units은 7x7x256, 편향은 사용하지 않음, 입력 값의 크기는 100차원 벡터 model.add(tf.keras.layers.Dense(7*7*256, use_bias=False, input_shape=(100,))) model.add(tf.keras.layers.BatchNormalization()) # 배치 정규화 사용 model.add(tf.keras.layers.LeakyReLU()) # LeakyReLU 활성화 함수 사용 # Second: Reshape layer model.add(layers.Reshape((7, 7, 256))) # Third: Conv2DTranspose layer # 커널 크기는 5, stride는 1, 패딩은 사용, 편향은 사용하지 않음 model.add(tf.keras.layers.Conv2DTranspose(128, kernel_size=5, use_bias=False, strides=1, padding='same')) model.add(tf.keras.layers.BatchNormalization()) # 배치 정규화 사용 model.add(tf.keras.layers.LeakyReLU()) # LeakyReLU 활성화 함수 사용 # Fourth: Conv2DTranspose layer # 커널 크기는 5, stride는 2, 패딩은 사용, 편향은 사용하지 않음 model.add(tf.keras.layers.Conv2DTranspose(128, kernel_size=5, use_bias=False, strides=2, padding='same')) model.add(tf.keras.layers.BatchNormalization()) # 배치 정규화 사용 model.add(tf.keras.layers.LeakyReLU()) # LeakyReLU 활성화 함수 사용 # Fifth: Conv2DTranspose layer model.add(tf.keras.layers.Conv2DTranspose(1, kernel_size=(5, 5), strides=(2, 2), padding='same', use_bias=False, activation='tanh')) return model
generator = make_generator_model() generator.summary()
- 확인
noise = tf.random.normal([1, 100]) generated_image = generator(noise, training=False) plt.imshow(generated_image[0, :, :, 0], cmap='gray') plt.colorbar() plt.show()
(2) discriminator
- 정의
def make_discriminator_model(): model = tf.keras.Sequential() # First: Conv2D Layer # 필터의 크기는 5이며 64개 사용, stride는 2, 패딩은 사용, 입력 값의 크기는 준비된 흑백 사진 model.add(tf.keras.layers.Conv2D(64, kernel_size=5, strides=2, padding='same', input_shape=(28, 28, 1))) model.add(tf.keras.layers.LeakyReLU()) # LeakyReLU 활성화 함수 사용 model.add(tf.keras.layers.Dropout(0.3)) # Dropout은 0.3을 사용 # Second: Conv2D Layer # 필터의 크기는 5이며 128개 사용, stride는 2, 패딩은 사용 model.add(tf.keras.layers.Conv2D(128, kernel_size=5, strides=2, padding='same')) model.add(tf.keras.layers.LeakyReLU()) # LeakyReLU 활성화 함수 사용 model.add(tf.keras.layers.Dropout(0.3)) # Dropout은 0.3을 사용 # Third: Flatten Layer model.add(layers.Flatten()) # Fourth: Dense Layer model.add(layers.Dense(1)) return model
discriminator = make_discriminator_model() discriminator.summary()
- 확인
decision = discriminator(generated_image, training=False) decision
(3) Loss function
- Cross Entropy : 두 값이 얼마나 큰 차이가 나는지를 정량적으로 계산
- Real Image 라벨 : 1 / Fake Image 라벨 : 0로 가정 시
- 생성자의 학습 목표 : Fake Image에 대해 판별값이 1에 가까워지는 것
- 판별자의 학습 목표 : Real Image 판별값이 1, Fake Image 판별값이 0에 가까워지는 것
- Real Image 라벨 : 1 / Fake Image 라벨 : 0로 가정 시
- 코드 구현
cross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits=True)
- generator_loss는
fake_output가 1에 가까워지는 것
이 목표def generator_loss(fake_output): return cross_entropy(tf.ones_like(fake_output), fake_output)
- discriminator_loss는
real_output은 1, fake_output은 0에 가까워지는 것
이 목표def discriminator_loss(real_output, fake_output): # 손실함수는 cross entropy를 사용, ones_like를 활용 real_loss = cross_entropy(tf.ones_like(real_output), real_output) # 손실함수는 cross entropy를 사용, zeros_like를 활용 fake_loss = cross_entropy(tf.zeros_like(fake_output), fake_output) total_loss = real_loss + fake_loss # real_loss와 fake_loss를 더한 값 return total_loss
- generator_loss는
(4) Accuracy
- 목표 : 1이 아닌
둘다 0.5에 가까워지는 것
(1이면 판별자가 매우 쉽게 판별해 내고 있다는 뜻) - 코드 구현
def discriminator_accuracy(real_output, fake_output): real_accuracy = tf.reduce_mean(tf.cast(tf.math.greater_equal(real_output, tf.constant([0.5])), tf.float32)) fake_accuracy = tf.reduce_mean(tf.cast(tf.math.less(fake_output, tf.constant([0.5])), tf.float32)) return real_accuracy, fake_accuracy
(5) Optimizer : Adam
- 코드 구현(
learning rate는 0.0001
)generator_optimizer = tf.keras.optimizers.Adam(1e-4) discriminator_optimizer = tf.keras.optimizers.Adam(1e-4)
(6) 학습 설계
- 샘플 생성
noise_dim = 100 num_examples_to_generate = 16 seed = tf.random.normal([num_examples_to_generate, noise_dim]) seed.shape
- 훈련 과정 설계(tf.function 데코 레이터 활용) : 1 step
@tf.function def train_step(images): #(1) 입력데이터 noise = tf.random.normal([BATCH_SIZE, noise_dim]) #(2) 생성자 입력 노이즈 with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape: #(3) tf.GradientTape() 오픈 generated_images = generator(noise, training=True) #(4) generated_images 생성 #(5) discriminator 판별 real_output = discriminator(images, training=True) fake_output = discriminator(generated_images, training=True) #(6) loss 계산 gen_loss = generator_loss(fake_output) disc_loss = discriminator_loss(real_output, fake_output) #(7) accuracy 계산 real_accuracy, fake_accuracy = discriminator_accuracy(real_output, fake_output) #(8) gradient 계산 gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables) gradients_of_discriminator = disc_tape.gradient(disc_loss, discriminator.trainable_variables) #(9) 모델 학습 generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables)) discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.trainable_variables)) return gen_loss, disc_loss, real_accuracy, fake_accuracy #(10) 리턴값
- 학습 현황 확인
- 샘플 생성 후 확인
def generate_and_save_images(model, epoch, it, sample_seeds): predictions = model(sample_seeds, training=False) fig = plt.figure(figsize=(4, 4)) for i in range(predictions.shape[0]): plt.subplot(4, 4, i+1) plt.imshow(predictions[i, :, :, 0], cmap='gray') plt.axis('off') plt.savefig('sample_epoch_{:04d}_iter_{:03d}.png'.format(epoch, it)) plt.show()
- loss와 accuracy 그래프 시각화
from matplotlib.pylab import rcParams rcParams['figure.figsize'] = 15, 6 # matlab 차트의 기본 크기를 15,6으로 지정해 줍니다. def draw_train_history(history, epoch): # summarize history for loss plt.subplot(211) plt.plot(history['gen_loss']) plt.plot(history['disc_loss']) plt.title('model loss') plt.ylabel('loss') plt.xlabel('batch iters') plt.legend(['gen_loss', 'disc_loss'], loc='upper left') # summarize history for accuracy plt.subplot(212) plt.plot(history['fake_accuracy']) plt.plot(history['real_accuracy']) plt.title('discriminator accuracy') plt.ylabel('accuracy') plt.xlabel('batch iters') plt.legend(['fake_accuracy', 'real_accuracy'], loc='upper left') # training_history 디렉토리에 epoch별로 그래프를 이미지 파일로 저장합니다. plt.savefig('train_history_{:04d}.png'.format(epoch)) plt.show()
- 체크포인트 정의
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)
- 샘플 생성 후 확인
(7) 학습
- 학습 구현
def train(dataset, epochs, save_every): start = time.time() history = {'gen_loss':[], 'disc_loss':[], 'real_accuracy':[], 'fake_accuracy':[]} for epoch in range(epochs): epoch_start = time.time() for it, image_batch in enumerate(dataset): gen_loss, disc_loss, real_accuracy, fake_accuracy = train_step(image_batch) history['gen_loss'].append(gen_loss) history['disc_loss'].append(disc_loss) history['real_accuracy'].append(real_accuracy) history['fake_accuracy'].append(fake_accuracy) if it % 50 == 0: display.clear_output(wait=True) generate_and_save_images(generator, epoch+1, it+1, seed) print('Epoch {} | iter {}'.format(epoch+1, it+1)) print('Time for epoch {} : {} sec'.format(epoch+1, int(time.time()-epoch_start))) if (epoch + 1) % save_every == 0: checkpoint.save(file_prefix=checkpoint_prefix) display.clear_output(wait=True) generate_and_save_images(generator, epochs, it, seed) print('Time for training : {} sec'.format(int(time.time()-start))) draw_train_history(history, epoch)
save_every = 5 EPOCHS = 100 # 사용가능한 GPU 디바이스 확인 tf.config.list_physical_devices("GPU")
%%time train(seed, EPOCHS, save_every) # 학습과정의 loss, accuracy 그래프 이미지 파일이 ~/training_history 경로에 생성되고 있으니 # 진행 과정을 수시로 확인해 보시길 권합니다.
(8) 학습과정 시각화
anim_file = 'fashion_mnist_dcgan.gif'
with imageio.get_writer(anim_file, mode='I') as writer:
filenames = glob.glob('generated_samples/sample*.png')
filenames = sorted(filenames)
last = -1
for i, filename in enumerate(filenames):
frame = 2*(i**0.5)
if round(frame) > round(last):
last = frame
else:
continue
image = imageio.imread(filename)
writer.append_data(image)
image = imageio.imread(filename)
writer.append_data(image)
# fashion/fashion_mnist_dcgan.gif
✨ 주요 성능 개선 방식
- 참고링크 : https://github.com/soumith/ganhacks
- Normalize the inputs : normalize the images between -1 and 1 ☑️
- A modified loss function : Flip labels when training generator: real = fake, fake = real ☑️
- Use a spherical Z : Sample from a gaussian distribution ☑️
- BatchNorm : Construct different mini-batches for real and fake ☑️
- Avoid Sparse Gradients: ReLU, MaxPool ☑️
- Use Soft and Noisy Labels : if it is real, then replace the label with a random number between 0.7 and 1.2, and if it is a fake sample, replace it with 0.0 and 0.3
- DCGAN / Hybrid Models ☑️
- Use stability tricks from RL : All stability tricks that work for deep deterministic policy gradients
- Use the ADAM Optimizer ☑️
- Track failures early ☑️
- Dont balance loss via statistics (unless you have a good reason to) ☑️
- If you have labels, use them
- Add noise to inputs, decay over time
notsure
Train discriminator more (sometimes)notsure
Batch Discrimination- Discrete variables in Conditional GANs : Use an Embedding layer, Add as additional channels to images
- Use Dropouts in G in both train and test phase : Provide noise in the form of dropout (50%).
댓글남기기