머신러닝&딥러닝/논문리뷰

논문 리뷰: DCGAN(Deep Convolutional GAN)

Like_Me 2020. 3. 17. 00:33
"UNSUPERVISED REPRESENTATION LEARNING WITH DEEP CONVOLUTIONAL GENERATIVE ADVERSARIAL NETWORKS"(2015) - Facebook AI Research
https://www.tensorflow.org/tutorials/generative/dcgan

 

  GAN이 발표되고 1년 반 뒤에 Convolution을 깊게 쌓아 더 안정적으로 학습시킬 수 있는 DCGAN이 발표되었다. GAN은 생성자(Generator)와 판별자(Discriminator)가 경쟁을 하며 학습을 시키는 구조로 이전까지와는 다르게 2개의 모델을 학습시키게 된다. DCGAN은 이전의 GAN을 더 발전시킨 구조를 제안한 논문으로 유명하여 읽게 되었다.


 

모델 핵심

 

이 모델에서 제시하는 모델의 핵심은 5가지이다.

 

   1. Pooling layer를 사용하는 대신 Discriminator는 strided convolution을 Generator는 fractional-strided를 사용한다.

   2. BatchNormalization을 모델에 적극 사용한다.

   3. Fully connected layer를 쓰지 않는다.

   4. Generator의 activation function으로 Relu를 사용한다. (단, output layer에서는 Tanh 사용)

   5. Discriminator의 activation function으로 LeakyRelu를 사용한다. (slope of the leak=0.2)

 

- Generator

   첫 번째에서 Generator가 fractional-strided를 사용한다 했는데 이는 Transposed Convolution을 사용하는 것이다. Stride를 이용하여 이미지를 생성해 나가며 최종적으로는 만들고자 하는 이미지의 크기(Channel 도 포함)와 같아지게 한다.

Image shape이 (28,28,1)인 mnist를 학습시킨다고 할 때 실제 구현한 Generator는 아래와 같다. 최종 크기가 (28,28,1)이므로 (7,7,256)에서 시작하여 크기를 늘려간다. 논문에 나온 대로 첫 Transposed Convolution에서는 크기를 늘리지 않고 channel만 변경해준다. 다음으로는 2배씩 늘리며 (28,28,1)의 이미지를 생성해준다. 

class Generator(keras.Model):
    def __init__(self):
        super(Generator, self).__init__()
        self.bn1 = keras.layers.BatchNormalization()
        self.dense1 = keras.layers.Dense(
            7*7*512, use_bias=False, input_shape=(100,))  # mnist는 크기가 28이라 7사용
        self.relu1 = keras.layers.ReLU()

        self.bn2 = keras.layers.BatchNormalization()
        self.deconv1 = keras.layers.Conv2DTranspose(
            filters=256, kernel_size=(5, 5), strides=(1, 1), padding='same', use_bias=False)
        self.relu2 = keras.layers.ReLU()

        self.bn3 = keras.layers.BatchNormalization()
        self.deconv2 = keras.layers.Conv2DTranspose(
            filters=128, kernel_size=(5, 5), strides=(2, 2), padding='same', use_bias=False)
        self.relu3 = keras.layers.ReLU()

        self.deconv3 = keras.layers.Conv2DTranspose(
            filters=1, kernel_size=(5, 5), strides=(2, 2), padding='same', use_bias=False, activation='tanh')

    def call(self, x, training=False):
        x = self.relu1(self.bn1(self.dense1(x), training=training))
        x = tf.reshape(x, (-1, 7, 7, 512))
        x = self.relu2(self.bn2(self.deconv1(
            x), training=training))  # (7,7,256)
        x = self.relu3(self.bn3(self.deconv2(
            x), training=training))  # (14,14,128)
        x = self.deconv3(x)  # (28,28,1)
        return x

 

- Discriminator

   Discriminator는 아래와 같이 구현할 수 있다. Convolution block의 기본 구조는 Conv-BN-Relu를 사용하고 있다. 최종 결과로 이미지가 진짜인지 가짜인지 판별하게 하였다.

class Discriminator(keras.Model):
    def __init__(self):
        super(Discriminator, self).__init__()
        self.conv1 = keras.layers.Conv2D(
            64, (5, 5), strides=2, padding='same', input_shape=(28, 28, 1), use_bias=False)
        self.bn1 = keras.layers.BatchNormalization()
        self.lrelu1 = keras.layers.LeakyReLU(alpha=0.2)

        self.conv2 = keras.layers.Conv2D(
            128, (5, 5), strides=2, padding='same', use_bias=False)
        self.bn2 = keras.layers.BatchNormalization()
        self.lrelu2 = keras.layers.LeakyReLU(alpha=0.2)

        self.flatten = keras.layers.Flatten()
        self.dense = keras.layers.Dense(1, activation='sigmoid')

    def call(self, x, training=False):
        x = self.lrelu1(self.bn1(self.conv1(x), training=training))
        x = self.lrelu2(self.bn2(self.conv2(x), training=training))
        x = self.flatten(x)
        return self.dense(x)

Experimentsal reults

 

   논문을 보며 흥미로웠던 부분은 Generator 모델에 입력값으로 넣는 9개의 Uniform noise distribution Z를 각각 Interpolation하여 10개를 만들어 넣어줬을 때 이미지가 조금씩 변해가는 모습이었다. 임의의 노이즈 분포가 어떻게 이미지를 생성하는지 의아했는데 그 값이 변해감에 따라 이미지가 변해가는 것을 보며 각 차원에 주어진 값들이 의미를 가지고 들어가는 것을 확인했다.

Z의 변화에 따른 이미지 변화

   GAN에서 Discriminator도 학습을 하여 좋은 Convolution filter를 생성하게 되는데 이에 대한 확인과 이를 따로 사용하여 모델 분류에 사용을 하여도 잘 되는 것이 논문에 나와있다. 때로는 CNN을 가지고 학습을 한 것보다 더 좋은 성능을 내는데 이는 Supervised Learning의 라벨링 늪에서 벗어나게 해 줄 Magic-Key처럼 보인다. 

Random filter를 통과시킨 이미지와 Discriminator가 학습한 필터를 통과시킨 사진

  자연어처리에서 단어의 임베딩이 잘 되었을 경우 King - Man + Woman를 계산하면 Queen이 나오게 된다. 그런데 신기하게 생성 모델에서도 Z값을 통한 연산으로 이런 결과가 나온다는 것이 논문에 실려있다. 예를 들면 아래와 같은 결과이다.

이미지 연산결과

   이는 Input 값인 Z vector에 따라 Image가 조금씩 변해가는 위의 결과와 한 맥락일 것이다. Vector에 이미지에 대한 정보가 내포되어 있어 이런 결과가 나오는 것일 것이다. 이런 결과는 임베딩의 경우처럼 학습이 잘 되었다는 증거로도 볼 수 있을 것이다.

 

 

*깃헙에 colab에서 돌린 파일을 올려두었습니다.