목표: 판별자가 판별 불가능한 img생성 input: 다변량표준정규분포에서 뽑은 벡터 output: 원본 train data에 있는 img와 동일한 크기의 img 위 설명이 마치 VAE같다면? 실제로 VAE의 Decoder와 동일한 목적을 수행한다. latent space의 벡터를 조작, 기존 domain에서 img의 고수준 특성을 바꾸는 기능을 제공하기 때문.
batch img생성→판별자에 통과→각 img에 대한 점수 get. ∙ G_Loss: BCELoss (0: fake img / 1: real img) ∙ D_Loss: BCELoss (0: fake img / 1: real img) 이때, 한번에 한 신경망 가중치만 update되도록 두 신경망을 번갈아 train해줘야함.
이때, DCGAN훈련과정이 불안정할 수 있다. (∵ 판별자와 생성자가 우위를 차지하려 서로 계속 경쟁하기 때문.) 시간이 충분히 지나면, 판별자가 우세해지는 경향이 있다. 다만, 이시점에는 생성자가 충분히 고품질 Img생성이 가능해서 큰 문제는 되지 않는다.
Label Smoothing
또한, GAN에 random noise를 조금 추가하면 유용한데, train과정의 안정성 개성 및 img선명도가 증가한다. (마치 Denoise Auto Encoder와 같은 느낌.)
GAN 훈련 팁 & Trick
∙ D >> G 인 경우.
판별자가 너무 강하면 Loss신호가 너무 약해진다. 이로 인해 생성자에서 의미있는 향상을 도모하기 어려워진다. 따라서 다음과 같이 판별자를 약화할 방법이 필요하다.
∙ 판별자에 Dropout rate 증가.
∙ 판별자의 LR 감소.
∙ 판별자의 Conv filter 수 감소.
∙ 판별자 훈련 시, Label에 Noise추가. (Label Smoothing)
∙ 판별자 훈련 시, 일부 img의 label을 random으로 뒤집는다.
∙ G >> D 인 경우.
mode collapse: 생성자가 거의 동일한 몇개의 img로 판별자를 "쉽게 속이는 방법" mode: 판별자를 항상 속이는 하나의 sample.
생성자는 이런 mode를 찾으려는 경향이 있고, latent space의 모든 point를 이 img에 mapping가능하다. 또한, 손실함수의 Gradient가 0에 가까운값으로 붕괴(collapse)하기에 이상태에서 벗어나기 어려워진다.
∙ 유용하지 않은 Loss
손실이 작을수록 생성된 img품질이 더 좋을 것이라 생각할 수 있다. 하지만 생성자는 현재 판별자에 의해서만 평가된다. 판별자는 계속 향상되기에 train과정의 다른지점에서 평가된 손실을 비교할 수 없다. 즉, 판별Loss는 감소하고, 생성Loss는 증가한다.→ GAN train과정 모니터링이 어려운 이유.
2. WGAN-GP (Wasserstein GAN with Gradient Penalty)
GAN Loss
GAN의 판별자∙생성자 훈련 시 사용한 BCE Loss를 살펴보자. 판별자 D훈련: real_img에 대한 예측 pi=D(xi)와 target yi=1을 비교. 생성자 G훈련: 생성_img에 대한 예측 pi=D(G(zi))와 target yi=0을 비교.
[GAN D_Loss 최대화 식]: [GAN G_Loss 최소화 식]:
Wesserstein Loss
[GAN Loss와의 차이점]: ∙ 1과 0대신, yi = 1, yi = -1을 사용. ∙ D의 마지막층에서 sigmoid제거. → 예측 pi가 [0,1]범위에 국한되지 않고 [-∞,∞] 범위의 어떤 숫자도 될 수 있게함. 위의 이유들로 WGAN의 판별자는 보통 비평자(Critic)라 부르며, 확률대신 점수"score"를 반환한다.
[Wesserstein Loss함수]:
WGAN의 critic D를 훈련하기위해 real_img에 대한 예측(D(xi))과 타겟(= 1)을 비교. 생성_img에 대한 예측(D(G(zi)))과 타겟(= -1)을 비교. ∴ 손실을 계산
[WGAN Critic D_Loss 최소화]: real과 생성간의 예측차이 최대화. [WGAN G_Loss 최소화]: Critic에서 가능한 높은 점수를 받는 img생성. (= Critic을 속여 real_img라 생각하게 만드는 것.)
1-Lipshitz Continuous function
sigmoid로 [0,1]범위에 국한하지 않고 Critic이 [-∞,∞] 범위의 어떤 숫자도 될 수 있게한다는 점은 Wessertein Loss가 제한없이 아주 큰 값일 수 있다는 것인데, 보통 신경망에서 큰 수는 피해야한다.
그렇기에, "Critic에 추가적인 제약이 필요"하다. 특히, Critic은 1-Lipshitz 연속함수여야 하는데, 이에대해 살펴보자.
Critic은 하나의 img를 하나의 예측으로 변환하는 함수 D이다. 임의의 두 input_img x1, x2에 대해 다음 부등식을 만족하면, 이 함수를 1-Lipshitz라 한다: |x1-x2| : 두 img 픽셀의 평균적인 절댓값 차이 |D(x1) - D(x2)| : Critic 예측간의 절댓값 차이 Lipshitz Continuous Function 기본적으로 기울기의 절댓값이 어디에서나 최대 1이어야한다 = 두 img간 Critic예측변화비율 제한이 필요하다는 의미.
WGAN-GP
WGAN의 Critic의 가중치를 작은 [-0.01, 0.01]범위에 놓이도록 train batch 이후 weight clipping으로 Lipshitz제약을 부과한다.
이때, 학습속도가 크게 감소하기에 Lipshitz제약을 위해 다른 방법을 적용한다: 바로 Wesserstein GAN-Gradient Penalty이다.
[WGAN-GP]: Gradient Norm이 1에서 벗어나면 모델에 불이익을 주는 방식이다. [Gradient Penalty Loss]: input_img에 대한 예측의 Gradient Norm과 1사이 차이를 제곱한 것. 모델은 자연스레 GP항을 최소화하는 가중치를 찾으려하기에 이 모델은 립시츠 제약을 따르게 한다.
Train과정동안 모든곳에서 Gradient계산은 힘들기에 WGAN-GP는 일부지점에서만 Gradient를 계산한다. 이때, real_img와 fake_img쌍 간의 interpolation img를 사용한다.
from torch.autograd import Variable
from torch.autograd import grad as torch_grad
def gradient_penalty(self, real_data, generated_data):
batch_size = real_data.size()[0]
# Calculate interpolation
alpha = torch.rand(batch_size, 1, 1, 1)
alpha = alpha.expand_as(real_data)
interpolated = alpha*real_data.data + (1 - alpha)*generated_data.data
interpolated = Variable(interpolated, requires_grad=True)
# Calculate probability of interpolated examples
prob_interpolated = self.D(interpolated)
# Calculate gradients of probabilities with respect to examples
gradients = torch_grad(outputs=prob_interpolated, inputs=interpolated,
grad_outputs=torch.ones(prob_interpolated.size()).cuda() if self.use_cuda else torch.ones(
prob_interpolated.size()),
create_graph=True, retain_graph=True)[0]
# Gradients have shape (B,C,W,H)
# so flatten to easily take norm per example in batch
gradients = gradients.view(batch_size, -1)
self.losses['gradient_norm'].append(gradients.norm(2, dim=1).mean().data[0])
# Derivatives of the gradient close to 0 can cause problems because of
# the square root, so manually calculate norm and add epsilon
gradients_norm = torch.sqrt(torch.sum(gradients**2, dim=1) + 1e-12)
# Return gradient penalty
return self.gp_weight * ((gradients_norm-1)**2).mean()
[WGAN-GP에서의 Batch Normalization]
BN은 같은 batch안의 img간의 correlation을 만든다. 그렇기에 gradient penalty loss의 효과가 떨어지에 WGAN-GP는 Critic에서 BN을 사용해서는 안된다.
3. CGAN (Conditional GAN)
prev.
앞서 설명한 모델들은 "주어진 trainset에서 사실적인 img를 생성하는 GAN"이었다. 하지만, "생성하려는 img의 유형을 제어할 수 는 없었다." (ex. 생성하려는 img유형: 크거나 작은 벽돌, 흑발/금발 등등)
latent space에서 random한 하나의 point sampling은 가능하다. latent variable을 선택하면 어떤 종류의 img가 생성될 지 쉽게 파악가능하다.
CGAN
[GAN v.s CGAN]: CGAN은 GAN과 달리 "label과 관련된 추가정보를 생성자와 critic에 전달한다는 점"이다. ∙ 생성자: 이 정보를 one-hot encoding vector로 latent space sample에 단순히 추가. ∙ Critic: label 정보를 RGB img의 채널에 추가채널로 추가. → input img가 동일한 크기가 될 때 까지 one-hot encoding vector를 반복. [유일한 구조 변경사항]: label정보를 G,D의 기존 입력에 연결하는 것.