DCGAN
이번 년도에는 꼭 딥러닝 관련 지식을 쌓고 싶어 관련 수업들을 듣게 되었는데, 비지도 학습에 대한 프로젝트를 진행하게 되어 공부한 내용들을 조금씩 써보고자 합니다. 완전히 맞지 않는 설명이 있을 수도 있으니 유의하면서 읽어 주세요 :)
이 포스팅은 tensorflow 공식 tutorial 의 게시물을 바탕으로 작성 되었습니다.
(코드 또한 tutorial 에서 확인하실 수 있습니다.)
https://www.tensorflow.org/tutorials/generative/dcgan?authuser=1&hl=ko
DCGAN?
이번에 공부하면서 처음 알게 된 모델입니다. 비지도 학습의 대표적인 모델이라고 할 수 있을 것 같습니다. 생성자(Generator) 와 감별자(Discriminator) 가 동시에 학습을 진행하면서 generator 에서는 loss 를 줄여가는 형식으로 실제 이미지와 유사한 이미지를 만들어가며, 감별자는 학습을 통해 어떤 이미지가 실제 이미지 데이터에서 왔는지, 아니면 generator 에서 만들어진 것인지 점차 잘 구별하게 됩니다. 최종적으로 discriminator 에서 참 거짓을 구별 하지 못할 만큼 generator 에서 실제 이미지와 유사한 이미지를 만들어 내는 것이 이 모델의 목표 입니다.
기존의 GAN 모델에 Convolutional layer, batch normalization layer, LeakyReLU 를 사용하여 모델을 구성 하였다고 합니다.
Code 구현 내용
Model
생성자(G) 모델과 감별자(D) 모델을 만들어야 합니다.
일단 생성자 모델부터 확인 해보면 다음과 같습니다.
def make_generator_model():
model = tf.keras.Sequential()
model.add(layers.Dense(7*7*256, use_bias=False, input_shape=(100,)))
model.add(layers.BatchNormalization())
model.add(layers.LeakyReLU())
model.add(layers.Reshape((7, 7, 256)))
assert model.output_shape == (None, 7, 7, 256) # 주목: 배치사이즈로 None이 주어집니다.
model.add(layers.Conv2DTranspose(128, (5, 5), strides=(1, 1), padding='same', use_bias=False))
assert model.output_shape == (None, 7, 7, 128)
model.add(layers.BatchNormalization())
model.add(layers.LeakyReLU())
model.add(layers.Conv2DTranspose(64, (5, 5), strides=(2, 2), padding='same', use_bias=False))
assert model.output_shape == (None, 14, 14, 64)
model.add(layers.BatchNormalization())
model.add(layers.LeakyReLU())
model.add(layers.Conv2DTranspose(1, (5, 5), strides=(2, 2), padding='same', use_bias=False, activation='tanh'))
assert model.output_shape == (None, 28, 28, 1)
return model
생성자 모델은 다음과 같이 만들 수 있습니다. (100,) 인 1차원 array 가 생성자 모델의 input 이고, 이 모델을 거치면 28 X 28 X 1 (channel last policy) 인 이미지가 생성 되어야 합니다. 28 X 28 X 1 의 데이터가 생성되어야 하는 이유는 이 코드 자체가 MNIST 데이터를 기준으로 하고 있기 때문입니다.
다음으로는 감별자 모델을 확인해보겠습니다.
def make_discriminator_model():
model = tf.keras.Sequential()
model.add(layers.Conv2D(64, (5, 5), strides=(2, 2), padding='same',
input_shape=[28, 28, 1]))
model.add(layers.LeakyReLU())
model.add(layers.Dropout(0.3))
model.add(layers.Conv2D(128, (5, 5), strides=(2, 2), padding='same'))
model.add(layers.LeakyReLU())
model.add(layers.Dropout(0.3))
model.add(layers.Flatten())
model.add(layers.Dense(1))
return model
감별자 모델은 28 X 28 X 1 의 이미지가 input 으로 들어오면 해당 이미지에 대한 classification 을 진행하여 진짜 사용자가 넣은 데이터에서부터의 이미지인지 아니면 생성자가 생성해낸 이미지인지 구별해야 합니다. 그러므로 마지막에 Flatten 을 진행하여 모든 피쳐들을 일차원 배열로 펼치고 Dense layer 을 통해 1가지의 결과를 내는 것입니다.
loss function and optimizer
cross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits=True)
일단 크로스 엔트로피 손실함수를 계산하기 위한 헬퍼 함수를 호출을 해놓고 변수 안에 넣어줘야 합니다.
감별자 손실함수는 감별자가 얼마나 진짜와 가짜(from G) 를 잘 구분하는지 계산하는 역할을 합니다. 진짜라고 예측해야 하는 이미지들 (1이라는 값을 가지고 있어야 함) 과 1로 이루어진 행렬에 대한 계산과 가짜라고 예측해야 하는 이미지들(0 이라는 값을 가져야 함) 과 0으로 이루어진 행렬에 대한 계산을 하고 두 값을 더해주면 감별자 손실함수를 구할 수 있습니다.
def discriminator_loss(real_output, fake_output):
real_loss = cross_entropy(tf.ones_like(real_output), real_output)
fake_loss = cross_entropy(tf.zeros_like(fake_output), fake_output)
total_loss = real_loss + fake_loss
return total_loss
생성자 손실함수는 감별자를 얼마나 잘 속였는지, 즉 생성자가 만들어낸 이미지에 대해 감별자가 1이라는 값을 얼마나 내놓았는지 알아야 하는 역할입니다. 생성자가 만들어낸 값들과 1로 이루어진 행렬에 대한 크로스 엔트로피를 계산하여 반환하면 됩니다.
def generator_loss(fake_output):
return cross_entropy(tf.ones_like(fake_output), fake_output)
생성자와 감별자는 따로따로 훈련되기 때문에 옵티마이저가 달라야 하며, 옵티마이저로는 Adam 을 사용합니다. (이 부분은 설명할 것이 있기 때문에 학습한 내용으로 다시 포스팅을 쓸 예정입니다.)
training
이렇게 모델과 손실함수, 옵티마이저를 정의했으면 훈련 시킬 수 있습니다.
# `tf.function`이 어떻게 사용되는지 주목해 주세요.
# 이 데코레이터는 함수를 "컴파일"합니다.
@tf.function
def train_step(images):
noise = tf.random.normal([BATCH_SIZE, noise_dim])
with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:
generated_images = generator(noise, training=True)
real_output = discriminator(images, training=True)
fake_output = discriminator(generated_images, training=True)
gen_loss = generator_loss(fake_output)
disc_loss = discriminator_loss(real_output, fake_output)
gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables)
gradients_of_discriminator = disc_tape.gradient(disc_loss, discriminator.trainable_variables)
generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables))
discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.trainable_variables))
이 부분이 개인적으로 이해하기 좀 어렵긴 했습니다. 일단 순서대로 설명하자면 다음과 같이 설명할 수 있을 것 같습니다.
일단 noise (generator 의 input) 을 (100,) 인 배열로 배치 사이즈 개수만큼 만들어서 generated_images 를 배치 사이즈의 개수만큼 만들어냅니다. real_output 은 감별자가 진짜 이미지에 대해 감별한 결과이고, fake_output 은 generated_images 에 대해 감별한 결과 입니다. 이 결과들을 통해 아까 정의 했던 손실 함수에 output 을 넣어서 감별자 손실함수 값과 생성자 손실함수 값을 구합니다. 저도 처음 본 tf.GradientTape 라는 훈련 루프를 사용하는데, 여기서 gradient 를 구해주고 각각의 옵티마이저에 gradient 값을 넣어주어 최적화를 시키는 데 사용하는 것 같습니다. 위 함수는 훈련 시 한 루프를 돌 때 실행해야 하는 함수이고, 아래 함수가 훈련하는 함수 입니다.
def train(dataset, epochs):
for epoch in range(epochs):
start = time.time()
for image_batch in dataset:
train_step(image_batch)
# GIF를 위한 이미지를 바로 생성합니다.
display.clear_output(wait=True)
generate_and_save_images(generator,
epoch + 1,
seed)
# 15 에포크가 지날 때마다 모델을 저장합니다.
if (epoch + 1) % 15 == 0:
checkpoint.save(file_prefix = checkpoint_prefix)
# print (' 에포크 {} 에서 걸린 시간은 {} 초 입니다'.format(epoch +1, time.time()-start))
print ('Time for epoch {} is {} sec'.format(epoch + 1, time.time()-start))
# 마지막 에포크가 끝난 후 생성합니다.
display.clear_output(wait=True)
generate_and_save_images(generator,
epochs,
seed)
이 예제에서는 epochs 횟수는 50회로 정했습니다. 여기서 dataset 은 배치 사이즈(이 예제에서는 256 으로 설정되었음) 개수만큼 slice 를 진행하여 셔플을 진행한 상태입니다.
# 데이터 배치를 만들고 섞습니다.
train_dataset = tf.data.Dataset.from_tensor_slices(train_images).shuffle(BUFFER_SIZE).batch(BATCH_SIZE)
그러면 50 회 루프를 도는 동안 256 씩 나누어진 전체 데이터셋에 대한 훈련을 진행할 것입니다.
이 모델을 훈련 시킨 결과는 다음과 같습니다.
진짜 숫자와 비슷한 모양을 생성해낸 걸 보니 신기 하기도 합니다 ㅎㅎ
비지도 학습을 사용하여 Anomaly Detection 을 하기 위해 DCGAN 공부부터 시작했는데요, 쉽지 않은 내용이라 계속 보면서 공부해봐야겠습니다. 여기 나온 개념들이나 이후 공부하는 부분도 포스팅 하도록 하겠습니다 ^^
Pytorch 로 공부하시고 싶으신 분들은 https://pytorch.org/tutorials/beginner/dcgan_faces_tutorial.html?highlight=gan
위 링크에 tutorial 이 있으니 시도 해보시면 좋을 것 같습니다. 저도 한 번 해보려고 합니다!!!
Author And Source
이 문제에 관하여(DCGAN), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@soomin9106/DCGAN저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)