loading

GAN variations(DCGAN, CGAN, PGGAN and StyleGAN)에 대하여


Various framework for adversarial learning

Published on December 11, 2022 by JunYoung

AI deep learning generative model adversarial training

14 min READ

DCGAN: Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks

GAN(Generative Adversarial Network)에 대해서는 이미 기존에 다룬 적이 있다. 간단하게 요약하자면 생성자인 generatoe($G$)와 판별자인 discriminator($D$)가 서로 적대적으로 학습하는 구조가 된다고 했는데, 앞서 소개했던 GAN에서의 구조는 MLP였다.
그런 와중에 처음으로 convolutional network를 사용하여 좋은 성능을 낼 수 있는 GAN 구조에 대해 연구를 했던 것이 바로 DCGAN이며 GAN 특성상 네트워크나 최적화 구조에 따라 학습 형태가 급격하게 변하기 때문에 학습이 힘든데, 이러한 부분에서 많은 ablation이 진행된 paper라고 볼 수 있다.

좌측 그림이 살짝 깨져보여서 죄송스럽지만 슬프게도 논문에서는 generator 구조만 그려서 보여주었고 discriminator 구조는 인터넷에 떠도는 그림을 가져왔다. 암튼 구조를 보면 알 수 있지만 각각 upsampling하는 convolutional networks, downsampling하는 networks로 구성되어있는 걸 알 수 있다.
DCGAN 구조에서 가장 주목할 점은 max pooling layer가 전혀 사용되지 않는다는 점이다. 아무래도 high level, low resolution feature를 사용하는 classification이나 detection 등등과는 다르게 실제로 이미지를 생성해야하기 때문에 filter로 작용되는 pooling layer가 정보를 많이 손실시키지 않을까 싶다.
Activation function은 generator의 경우에는 ReLU activation function을 사용하였고, discriminator에서는 마지막 layer에서 확률 범위인 $0 \sim 1$을 맞춰주기 위해 sigmoid function을 사용한 것 이외에는 Leaky ReLU function을 사용한다.

[ \begin{aligned} ReLU(x) =& \begin{cases} x, & \text{if }x \geq 0 \newline 0, & \text{otherwise} \end{cases} \newline LeakyReLU(x) =& \begin{cases} x, & \text{if }x \geq 0 \newline -0.01x, & \text{otherwise} \end{cases} \end{aligned} ] Leaky ReLU는 단순히 음수 부분에 대한 학습을 무시하는게 아니라, 작은 값으로 gradient를 주게 된다. 아마 초반 generator의 분포에 따라 discriminator가 먼저 수렴해버리거나 overfitting이 되면 서로 균형된 학습을 하지 못하게 될 수 있기 때문에 이렇게 구성하지 않았나 싶다. 이건 개인적인 견해.
다시 generator 구조를 자세히 보면 처음에는 100 차원의 latent vector가 input으로 들어가게 되는데, 이를 convolution 연산이 가능하게끔 확장하여 넣어주게 된다.

class Generator(nn.Module):
    def __init__(self):
        super(Generator, self).__init__()

        self.init_size = opt.img_size // 4
        self.l1 = nn.Sequential(nn.Linear(opt.latent_dim, 128 * self.init_size ** 2))

        self.conv_blocks = nn.Sequential(
            nn.BatchNorm2d(128),
            nn.Upsample(scale_factor=2),
            nn.Conv2d(128, 128, 3, stride=1, padding=1),
            nn.BatchNorm2d(128, 0.8),
            nn.LeakyReLU(0.2, inplace=True),
            nn.Upsample(scale_factor=2),
            nn.Conv2d(128, 64, 3, stride=1, padding=1),
            nn.BatchNorm2d(64, 0.8),
            nn.LeakyReLU(0.2, inplace=True),
            nn.Conv2d(64, opt.channels, 3, stride=1, padding=1),
            nn.Tanh(),
        )

    def forward(self, z):
        out = self.l1(z)
        out = out.view(out.shape[0], 128, self.init_size, self.init_size)
        img = self.conv_blocks(out)
        return img

위는 오피셜 Pytorch 코드에서 generator를 잠시 가져온건데, 코드를 보면 latent $z$를 받은 뒤 Linear 연산을 통해 차원을 확장시키고, 이를 view를 통해 resize하는 형태를 가진다. 즉 1차원의 텐서의 차원 수를 확장 시킨 뒤에 이를 적절히 나누어 (Batch)$\times$(Channel)$\times H \times W$ 형태로 만들어주게 된다. 이를 project and reshape이라 한다.
그 다음부터는 fractionally strided convolution을 통해 spatial dimension을 확장한다.

기존의 convolution(우측)과 fractionally strided(좌측)이 다른 점은 input(아래쪽 사각형)에 대해 convolution은 그대로 연산을 진행하지만 fractionally strided는 input 사이사이 padding을 넣어 연산을 진행한다. 즉 일단 쭉 늘려놓은 다음에 convolution을 하는 형태다. Max-unpooling하는 형태나 bilinear interpolation같은 방식보다 해당 메트릭이 더 효과적이었던 것 같다. 암튼 DCGAN에서의 생성자는 이런 구조를 가지고 최종적으로는 generated image의 scale을 맞춰주기 위해 $\tanh$ activation을 사용했다.
그리고 Batch Normalization을 사용했는데, 여기서 중요한 점은 generator는 가장 마지막 convolutional layer에는 normalization을 사용하지 않았고 discriminator에서는 가장 처음 convolutional layer에 normalization을 사용하지 않았다.


Conditional GAN

앞서 DCGAN을 살펴보았다. 해당 paper는 어떤 방식으로 generative adversarial network를 convolution 구조로 풀어냈는지 설명하였고, 그 다음으로 살펴볼 것은 생성자가 ‘원하는 샘플’을 생성할 수 있을까에 대한 논문인 conditional GAN이다.

기존의 GAN 방식을 보면 $z$ space에서 임의의 latent를 샘플링하고, 해당 latent를 generator에 통과시켜 얻은 이미지가 타겟 도메인으로 하는 modality에 실제로 있을법한지 구분하는 형태였다. 그러나 이러한 방식은 $z$ space에서의 latent를 유의미하게 분리할 수 있는 supervision이 없으며, 실제로 생성하고 싶은 이미지가 어떤 class의 이미지인지(예를 들어 MNIST라면, 숫자 $1$을 생성하고 싶은지 아니면 $8$을 생성하고 싶은지 등등) control할 수 없다는 문제가 생긴다.
따라서 cGAN에서는 간단하게 이를 label $y$를 추가 parameter로 generator에 주면서 조건부 확률을 만들어내고자 했다. 이전에 보았던 mini-max optimization GAN 식에서, [ \begin{aligned} &\min_G \max_D V(D,G) \newline V(D,G) =& E_{x \sim p_{data}(x)}(\log D(x \vert y)) + E_{z \sim p_z(z)}(\log (1-D(G(z \vert y)))) \end{aligned}
] 각 term에 label $y$에 대한 조건만 추가해주면 된다. 이렇게 최적화가 되고 나서는 generator에 latent sample $z$과 생성하고픈 class를 같이 parameter로 넣어주면 된다.

class generator(nn.Module):
    # Network Architecture is exactly same as in infoGAN (https://arxiv.org/abs/1606.03657)
    # Architecture : FC1024_BR-FC7x7x128_BR-(64)4dc2s_BR-(1)4dc2s_S
    def __init__(self, input_dim=100, output_dim=1, input_size=32, class_num=10):
        super(generator, self).__init__()
        self.input_dim = input_dim
        self.output_dim = output_dim
        self.input_size = input_size
        self.class_num = class_num

        self.fc = nn.Sequential(
            nn.Linear(self.input_dim + self.class_num, 1024),
            nn.BatchNorm1d(1024),
            nn.ReLU(),
            nn.Linear(1024, 128 * (self.input_size // 4) * (self.input_size // 4)),
            nn.BatchNorm1d(128 * (self.input_size // 4) * (self.input_size // 4)),
            nn.ReLU(),
        )
        self.deconv = nn.Sequential(
            nn.ConvTranspose2d(128, 64, 4, 2, 1),
            nn.BatchNorm2d(64),
            nn.ReLU(),
            nn.ConvTranspose2d(64, self.output_dim, 4, 2, 1),
            nn.Tanh(),
        )
        utils.initialize_weights(self)

    def forward(self, input, label):
        x = torch.cat([input, label], 1)
        x = self.fc(x)
        x = x.view(-1, 128, (self.input_size // 4), (self.input_size // 4))
        x = self.deconv(x)

        return x

Official code는 아닌 것 같은데 해당 링크를 참고하였다. 구조를 보면 concatenate을 통해 latel과 input을 옆으로 이어붙인다(첫번째 차원 기준). 그런 뒤에 나머지 구조는 GAN과 동일하게 진행된다.

비슷한 형태로 deep convolutional GAN 구조에 적용될 수도 있다.


PGGAN: Progressive Growing of GANs for Improved Quality, Stability, and Variation

사실 설명하고 싶었던 논문 중에 WGAN(Wassertein-1 distance GAN)도 있는데, 이건 수학이 너무 많이 필요해서 나중에 post 하나에 담아보기로 했다.
그렇다면 그거 대신에 볼만한 논문 중에서 GAN이 low resolution에만 한정될 수 밖에 없는 문제를 해결해주었던 PG(progressive-growing)-GAN에 대해 리뷰해보도록 하겠다. 사실 왜 WGAN을 같이 다루고 싶었냐면 PGGAN에서 학습 전략으로 사용했던 것이 WGAN-GP(Gradient penalty) loss였는데, 이는 이전까지 설명했던 mini-max optimization과는 조금 다르기 때문이다.
초반 GAN 논문들은 아카이브에서 로드할 때 금방 됐었는데 생성 네트워크가 보다 high resolution sample들을 만들어내면서 논문 용량들이 쉽지가 않다. 그렇다고 저자들을 탓하는건 아니고…

PGGAN은 GAN 학습을 안정적으로 바꾸기 위해 위와 같은 구조의 학습법을 제시했으며, $1024 \times 1024$의 고화질 이미지를 생성하면서도 안정적인 학습을 할 수 있다는 장점이 있다. 말 그대로 PGGAN은 단계별로 학습을 한다.

학습 과정은 위의 그림과 같다. 처음에는 low resolution image에 대한 GAN 구조를 학습시키고, 거기다가 layer를 추가해서 학습시키고 다음 네트워크를 학습할 때 보조적으로 사용하게 된다.

바로 $\alpha$ 값이 resolution transition을 할 때에 residual connection 처럼 구성하기 위한 친구로, 새롭게 추가된 layer의 학습을 안정적으로 만들 수 있다. $alpha$는 낮은 resolution일 때 $0$부터 높은 resolution일 때 $1$까지 linearly 증가시켰다고 한다. 초반에는 이전 레이어가 upsampling된 녀석이 학습에 보조적인 역할을 해야하지만, 궁극적으로 최종으로 얻고자하는 generator는 오직 마지막 레이어의 output만을 고려해야하기 때문이다.
참고로 $2\times$나 $0.5\times$로 된 부분은 각각 scale을 키우는 입장($G$)에서는 Nearest Neighborhood interpolation 방식을, scale을 줄이는 입장($D$)에서는 Average pooling 방식을 사용하였다.

학습에 사용된 구조는 위와 같다.

참고로 해당 논문에서 소개한 데이터셋이 그 유명한 CelebA-HQ이며, StyleGAN에서 소개된 FFHQ-1024와 같이 다양한 형태로 활용되고 있다. 참고르 바로 다음에 소개할 논문이 StyleGAN이다.


StyleGAN: A Style-Based Generator Architecture for Generative Adversarial Networks

StyleGAN은 PGGAN structure를 가지되, 다음과 같은 구조적 변경이 있다. 물론 그 구조적 변경이 해당 논문에서 가장 중요한 부분이고, 어떻게 style에 따른 disentanglement(feature 분리)와 image 생성을 했는지 설명해보도록 하겠다.

  • Nearest Neighborhood interpolation 대신 Bilinear upsampling 사용
  • Mapping network($\mathcal{Z} \rightarrow \mathcal{W}$)와 AdaIN 추가
  • Generator에서의 latent vector를 없앰
  • 각 block에 noise 추가
  • Mixing regularization 추가

기존 구조와 차이점은 StyleGAN에서 만들고자 한 generator 구조는 style image 생성에 특화된다는 점이다. 가장 눈에 띄게 다른 점은 Latent $z \in \mathcal{Z}$ 부분이 synthesis network $g$에서 사라진다는 것인데, 왜냐하면 해당 부분이 style에 대한 정보를 추출해서 이를 Adaptive Instance Normalization하는 구조로 바뀌었기 때문이다. 이런 관점에서 StyleGAN에서의 generator $G$는 이미지를 “생성”한다는 느낌보다는 도화지에 그림을 입혀나가는 느낌이다.
그리고 특이하게도, StyleGAN에서는 $z \sim \mathcal{Z}$를 바로 affine하여 style에 사용하지 않고 8개의 linear layer로 구성된 MLP를 통해 생성된 새로운 space $\mathcal{W}$를 기준으로 한다. 이 내용이 바로 위에서 언급했던 Mapping network의 추가와 관련된 내용인데, 다음 그림을 보도록 하자.

만약 데이터셋에 안경을 쓴 남자만 있고, 안경을 쓰지 않은 남자는 없었다고 가정해보자. 그러면 그림 (a)에서 가로축이 안경의 유무, 세로축이 성별이라고 가정하면 안경을 쓴 여자, 안경을 쓰지 않은 여자 그리고 안경을 쓴 남자는 있는데 안경을 쓰지 않은 남자만 없는 것이다. 해당 부분이 아예 비어있는 것으로 표현된다.
이를 단순히 $z \sim \mathcal{Z}$에 최적화하면 가우시안 distribution에 그대로 mapping되므로, 비어있는 부분이 서로 엉겨붙게 된다(그림 (a)에서 보라색 윗부분이랑 노란색 좌측부분이 서로 이어붙여졌다고 생각하면 됨). 여기서 생기는 문제점은 이럴 경우 style image 생성 시에 특정 style에 대한 latent interpolation을 진행하게 되면 다른 style latent까지 영향을 받는다는 것이다.
예컨데, 안경을 쓰지 않은 남자가 데이터셋에 없었기 때문에, 안경을 쓴 여자의 성별을 안경을 쓴 남자로 바꾸려고 했더니 갑자기 안경도 같이 사라지는 문제가 생기는 것이다.
바로 이런 문제점을 Entanglement라고 부르며, 저자들이 8개의 MLP layer를 사용한 이유가 바로 여기에 있다. 그림 (c)를 보게 되면 데이터셋의 분포를 고려한 space를 만들어, 부족한 feature에 대한 엉겨붙는 현상(entanglement)를 해결(disentanglement)할 수 있는 걸 볼 수 있다.
그리고 “Synthesis network”라는 이름을 통해 실질적으로 추출된 style vector를 constant에 단계적으로 입히는 과정(PGGAN이랑 유사하다)을 명시한다. 즉 어떠한 스타일의 이미지를 만들고 싶어서 latent로부터 만들겠다라는 느낌보다는 low level부터 style을 계속 합성(얼굴 구조, 성벌, 머리 색, 눈 크기, 입 모양 등등)시켜서 원하는 이미지를 만들고자 하는 것이다.
구조를 보면 각 블록에서 style이 affine된 벡터가 AdaIN에 사용된다. Adaptive Instance Normalization이라 부르는 식은 다음과 같다. [ \text{AdaIN}(x_i,~y) = y_{s,~i}\frac{x_i - \mu(x_i)}{\sigma(x_i)}+y_{n,~i}
] $i$번째 feature map을 평균과 분산을 통해 standard gaussian으로 정규화해준 뒤, style vector를 통해 얻은 style scale과 style bias로 다시 normalize한다. 참고로 affine transform은 단순히 style vector $w \sim \mathcal{W}$를 weight $A$에 곱하여 얻은 $y = Aw$와 같다. 이를 통해 추출된 $y$에는 style scale factor $y_s$와 style bias factor $y_b$이다.

합성 네트워크에서 style이 들어가는 부분에 대해 조절할 수 있는 feature에 대해 저자들은 다음과 같이 소개한다. 낮은 resolution에서의 style feature($4 \times 4$ 혹은 $8 \times 8$)는 얼굴 모양, 포즈 그리고 헤어스타일과 같이 큰 맥락을 잡는데 사용하고, 중간 resolution에서의 style feature($16 \times 16$ 혹은 $32 \times 32$)는 얼굴의 이목구비, 보다 자세한 헤어스타일에 대해 조절할 수 있다고 한다. 마찬가지로 높은 resolution($64 \times 64$ 에서 $1024 \times 1024$)까지의 resolution은 미세한 디테일에 영향을 준다. 근데 그림에서 보면 알 수 있듯이 middle, fine detail을 바꾸는 과정에서 배경과의 entanglement는 해결하지 못한게 한계점으로 보여진다.
Style mixing은 단순히 latent $z$ 하나를 $\mathcal{W}$로 매핑한 $w$ 하나만 style synthesis에 사용하는 것이 아니라 중간 부분에서 다른 latent인 $z’$를 사용하여 cross-over되는 부분을 만들겠다는 것이다. 위에서 봤던 그림을 그대로 사용해서 설명하자면, 각각의 이미지가 행과 열로 표현된 table에서 가장 윗 가로줄이 source 1, 가장 좌측 세로줄이 source 2라고 생각해보자.
Source 1은 latent $z_1$로부터 생성된 이미지, source 2는 latent $z_2$로부터 생성된 이미지라면, 두 스타일을 섞을 때 다음과 같이 진행한다.

바로 해당 cross-over 지점을 조절해주는 것이 각 소스에서 fine detail을 가져올지, coarse feature를 가져올지 결정되는 부분이다.
마지막으로 볼 것이 noise를 injection하는 파트이다.

Gaussian noise tensor를 input map과 같은 사이즈로 조절해준 뒤에 이를 feature map에 그대로 더해준다. 이를 사용하는 이유는 noise가 이미지의 세부 디테일을 변화시켜주고, 항상 같은 이미지가 아닌 무작위의 style이 샘플링될 수 있는 방법으로 사용된다.

보통 coarse level에서 사용되는 noise는 보다 큰 형태의 variation으로 나타나고, fine level에서 사용되는 noise는 디테일에 대한 variation으로 나타난다. 예컨데 우측 이미지에서, (a)는 모든 레이어에 대해 노이즈를 추가한 샘플, (b)는 노이즈가 아예 없는 샘플 (c)는 fine layer에만 노이즈를 추가한 샘플, (d)는 coarse layer에만 노이즈를 추가한 샘플이다. 노이즈가 없었던 것과 비교했을 때 coarse layer에 노이즈를 추가한 샘플 (d)는 머리 스타일을 바꾸거나 하는 큰 형태의 stochastic을 볼 수 있고, fine-layer에 노이즈를 추가한 샘플(c)에서는 피부톤, 전체적인 색감 그리고 머리카락 하나하나의 디테일이 보이는 걸 확인할 수 있다.
그리고 또 중요하게 살펴볼 paper detail 중 하나는 CelebA-HQ의 경우에는 학습할 때 WGAN-GP loss를 사용했었지만 이를 FFHQ 데이터셋 학습 시에는 non-saturating loss로 바꿨다는 점이다. Non-saturating loss는 기존 GAN loss에서 생성자의 loss만 바꾼 식이다. [ \begin{aligned} \mathcal{L_D} = \max_D V(D,G) \newline V(D,G) =& E_{x \sim p_{data}(x)}(\log D(x)) + E_{z \sim p_z(z)}(\log (1-D(G(z)))) \newline \newline \mathcal{L_G} = \max_G V’(G) \newline V’(G) =& E_{z \sim p_z(z)}(\log (D(G(z)))) + \frac{\gamma}{2} E_{p_D(x)}(\parallel \nabla D_\psi(x) \parallel^2) \end{aligned}
] Generator는 더이상 negative log likelihood에 대한 minimization이 아닌 discriminator에 의해 실제 이미지 분포로 측정될 가능성을 높이는 방향으로 학습된다. 뒤에 붙어있는 $\gamma$ term은 $R_1$ regularization이고 $\gamma = 10$을 사용했다.
또한 낮은 probability density region에 해당되는 $z$ 혹은 $w$ space로부터의 샘플은 해당 네트워크를 최적화하기 힘들다. 그렇기 때문에 sampling region을 한정적으로 사용하는 것이 샘플링 퀄리티를 높이게 된다. 이는 $z$나 $w$를 truncating하는 것으로 해결했다. [ \begin{aligned} w’ =& \bar{w} + \psi(w-\bar{w}) \newline \bar{w} =& E_{z \sim p(z)}(f(z)) \end{aligned}
]

이를 간단히 설명하자면 평균 face를 구하고, 해당 face로부터 $\psi$ 값을 조정하면서 feasible space를 서칭하고자 하는 것이다.

A n o t h e r p o s t i n c a t e g o r y