Generative Models Part 2: ImprovedGAN,InfoGAN,EBGAN

 

Computer Vision and Machine Learning Study Post 2

Generative Models Part 2: ImprovedGAN/InfoGAN/EBGAN

 

Reference는 다음과 같습니다.

? Salimans, Tim, et al. “Improved techniques for training gans.” Advances in Neural Information Processing Systems. 2016.

? Chen, Xi, et al. “Infogan: Interpretable representation learning by information maximizing generative adversarial nets.” Advances in Neural Information Processing Systems. 2016.

? Zhao, Junbo, Michael Mathieu, and Yann LeCun. “Energy-based generative adversarial network.” arXiv preprint arXiv:1609.03126 (2016).

정리에는 다음 자료들이 큰 도움이 되었습니다.

? (Yunjey의 DCGAN Pytorch Tutorial)[https://github.com/yunjey/pytorch-tutorial]

? (Takato Horii Slideshare, 김홍배님 번역)[https://www.slideshare.net/ssuser06e0c5/infogan-interpretable-representation-learning-by-information-maximizing-generative-adversarial-nets-72268213]

? (Github Pytorch Code: buriburisuri)[https://github.com/buriburisuri/ebgan]

? (Github Pytorch Code: Wiseodd)[https://github.com/wiseodd/generative-models]

? (Github Pytorch Code: AaronYALai)[https://github.com/AaronYALai/Generative_Adversarial_Networks_PyTorch]

 

1. Introduction

 

GAN이 수렴하기 힘들고 Training도 힘들다는 것은 많이 알려진 사실이다. GAN 이후 여러 유명한 논문들이 많이 나오게 되었는데, 그 발자취를 공부 겸 계속 따라가 볼 예정이고, 요약 정리 및 구현할 논문의 기준은 우선은 인용 수를 기준으로 어느정도 추려 보았다.

Improved GAN은 Ian Goodfellow가 2저자로 들어가 있는 논문인데, 내용은 그냥 추가로 이것 저것 해보았다 정도이고, 성능도 약간 향상된 정도인 것 같다. 구현에 실패하기도 했고, 논문에 수식적인 전개가 거의 없고 대부분 경험적으로 되어있기 때문에 간단히 서술하고 넘어갈 예정이다. 흥미로웠던 것은 InfoGAN과 EBGAN인데 사실 성능상으로는 InfoGAN이 좀 더 매력적이었다. 하지만 InfoGAN의 이론적 배경은 좀 어렵다. EBGAN은 이론적 배경보다는 새로운 구조를 제시했는데 사실 구현을 잘 못한 것인지 성능은 만족스럽지 못했으나, 향후에 나오는 GAN 모델에서 EBGAN을 베이스로 많이 사용하기에 공부해 보았다.

 

2. Improved GAN

 

2-1. Paper Summary

 

Ian Goodfellow가 2저자로 들어가 있는 본 논문의 Contribution을 간단하게 요약하고자 한다. 사실 논문의 대부분의 방법들이 경험적인 방법들이라 수식이 없다. 그래서 성능도 Dramatic한 변화는 없다.


1. Feature Matching

Discriminator가 단순히 Real과 Fake를 나누는 것이 아니라. Input Data의 통계를 더 유사하게 따라가기를 원하기 때문에, Discriminator의 중간 Activation이 Fake와 Real Input 사이에서 유사하기를 바란다. 따라서 이것을 Generator에 Objective Function으로 부여하는 Idea이다. 수식으로는 다음과 같다.

|Expdataf(x)−Ezpzf(G(z))||Ex∼pdataf(x)−Ez∼pzf(G(z))|

논문에 의하면 시각적 결과는 별 소득이 없었지만. Semi-supervised Learning에서 좋은 성능을 보여줬다고 한다.


2. Minibatch Discrimination

시각적 결과는 이것이 더 좋은데, 짧게 요약하면. Discriminator에서 구별 할 때 여러 Sample을 같이 보자는 것이다. 여기서 Mode Collapse의 개념이 나오는데, Generator는 Discriminator를 속이는 것이 목적이지 Image를 다양하고 잘 만드는 것이 목적이 아니기 때문에 그쪽으로 특화가 되게 되는데 그 이유가 Sample간의 Dependency가 없기 때문이다. 따라서 여러 Sample을 같이 보게 하자는 것이 핵심 Idea가 되며 실제 구현은 Discriminator의 중간 Layer에서 Batch 단위로 Tensor Combination (코드 구현을 못해서 확실히 이해하지는 않았지만, 논문에서는 Tensor에 곱하는 식으로 표현했지만 Reshape가 아닐까 생각해 본다)을 통해서 다음 Layer로 넘겨주자는 것이다. 스칼라 값으로 넘겨줄 때 Sample 간 Distance가 낮을 수록 높은 값을 (Collapse되지 않도록), 높을 수록 낮은 값(기존 Discriminator처럼)을 전달해주게 된다. 즉 Real과 Fake 판단은 각 Sample별로 하지만 거기에 들어가는 값들은 여러 Sample의 Feature가 동시에 효율적으로 하게 된다.

하지만 Minibatch Discrimination은 Semi-supervised Learning에는 별 소득이 없었다고 한다.


3. Historical Averaging과 One-sided Label Smoothing

Historical Averaging은 Training을 여러 번 할 때 기존 Cost들을 Averaging하는 Online-learning 방법이고, One-sided Label Smoothing은 Discriminator Label로서 0과 1대신 좀 더 Smooth한 값을 쓰자는 것인데 사실 웹 어디에도 이것에 대해 자세한 설명이 없고 코드도 없는 걸 보면 사실 별 성능 향상은 없는 듯 싶다.


4. Inception Score

그나마 새로운 개념은 Inception Score인데, Generative Model의 문제 중 하나인 성능 평가에 대해서 하나의 제시 방법이라고 볼 수 있다. 수식은 다음과 같다.

InceptS=exp(ExKL(p(y|x)||p(y))=exp(ExEp(y|x)[log(p(y|x)p(y))]InceptS=exp(ExKL(p(y|x)||p(y))=exp(ExEp(y|x)[log(p(y|x)p(y))]

논문에서는 Human Judgement와의 Correlation이 높았다.. 라고만 주장하고 어떠한 실험도 없이 그냥 넘어가는데, 수식을 그대로 해석하면 “잘” Generated된 Image는 다음과 같을 것이다.

? Inception Model로 Training된 Label Prediction에서 p(y|x)의 Entropy가 낮을 것이다

? Data가 다양하게 Generation되어 p(y)의 Entropy가 높을 것이다

즉, 좋은 Generator일 수록 분자가 높고 분모가 낮을 테니 높은 값이라는 주장이다.


5. Semi-supervised Learning

Semi-supervised는 하나의 분야로서, 결국은 Supervised Learning (ex. Classification)을 위한 것이다. 좋은 GAN으로 생성된 Image는 적은 Label의 영향을 받아서 일종의 Data Augmentation이 되고, 최종적으로 Classification Accuracy가 높다는 것이다. 이것은 새로운 Supervised-Loss를 정의함으로서 구현 가능하다.

일단은 Semi-supervised도 나름대로의 분야이기에 지금은 Generative Model을 좀 더 공부해야 하므로 나중에 기회가 되면 보기로 하고 마무리..

 

3. Info-GAN

 

3-1. Info-GAN Description

 

원 논문의 Chap. 4부터 시작되는 내용이 핵심인데, Original GAN은 z로부터 Generation을 하지만 결국 z가 Data의 Semantic Feature라고 보기는 어렵다는 것이다. 즉 기존에는, DCGAN처럼 z에서 의미를 추출해내기 위해서는 z를 찾기 위해 주먹구구식으로 찾는 방법을 사용했었는데, 그렇게 하지 말고 Latent Vector가 의미를 갖도록 만들자는 것이 Info-GAN의 핵심이다.

즉 핵심 아이디어는 2가지가 되는데.

? 1. Latent Vector z는 Data를 Generation하는 아주 기본적인 요소들 (Meaningless한)을 담당한다.

? 2. Latent Code c는 Data의 의미론적인 Feature로서 역할을 할 수 있게 학습하며, 이 c와 이로부터 Generation된 Data간의 상호 정보량이 최대가 되도록 학습한다.

즉, 딱 보았을 때는 새로운 Generator의 Input c가 추가되는 것에 불과하다. 하지만 이대로 학습하면 결국 다른 점이 없다!

결국 최종 Objective Function은 Original GAN으로 부터 다음과 같이 변하게 되는데,

minGmaxDVI(D,G)=V(D,G)−λI(c;G(z,c))minGmaxDVI(D,G)=V(D,G)−λI(c;G(z,c))

여기서 V(D, G)는 기존 GAN의 Min-Max Objective Function인데. 여기에 I 부분이 추가되는 것이다. 단 I는 Generator의 제약식이므로, 전체 식이 Minimize되어야 좋은 것이므로 I는 최대가 되어야 좋다는 것을 직관적으로 알 수 있다. 그렇다면 남은 것은 I가 무엇이냐 하는 것이다.

I는 앞서 말한 핵심 아이디어의 2. 번에 해당하는 Latent Code c와 이로부터 생성된 Generator Output 사이의 상호 정보량(Mutual Information)이라는 것이다.

우선 복습을 좀 해 보면, 우선 엔트로피는 다음과 같이 정의되며,

H(X)=−p(x)log(p(x))H(X)=−∑p(x)log(p(x))

쉽게 해석하면 불확실성, 불확정성이라고 해석할 수 있다. 그렇다면 다음은?

H(X|Y)=Ey,x[−log(p(x|y))]H(X|Y)=Ey,x[−log(p(x|y))]

Conditional Entropy로서, Y가 주어졌을 경우의 X의 엔트로피, 불확실성, 불확정성이 된다. 수식이 저렇게 정리되는 것은 엔트로피와 조건부 확률의 정의에 의해서이다.

그렇다면 상호 정보량은 수식으로 다음과 같이 나타내는데,

I(X;Y)=H(X)−H(X|Y)I(X;Y)=H(X)−H(X|Y)

이 말을 글자 그대로 해석하면, 상호 정보량이란, X만 알 때의 불확실성이, Y를 알게 됨으로서 사라지는 정도라고 볼 수 있다. 예를 들어 Y와 X가 서로 관계가 없다면. H(X|Y)는 H(X)와 별 차이가 없게 되어 상호 정보량은 감소한다. 반대로 Y와 X가 서로 관계가 높을수록 H(X|Y), 즉 Y를 알 때의 X의 불확실성이 크게 감소하여 상호 정보량이 커지게 된다. 따라서 우리의 Objective Function 속에 들어 있는

I(c;G(z,c))I(c;G(z,c))

을 통해서, Latent Code가 들어갔을 경우 생성되는 Generated Data가 서로 상호 정보량이 많도록, 즉 긴밀한 관계를 유지하도록 전체 GAN이 학습되기를 원하는 것이다.

그렇다면, 우리가 Maximize하고자 하는 이 상호 정보량은 어떻게 계산할까? 수식 전개를 보면,

I(c;G(z,c))=H(c)−H(c|G(z,c))I(c;G(z,c))=H(c)−H(c|G(z,c))=H(c)−(−ExG(z,c)[EcP(c|x)[log(P(c|x))]])=H(c)−(−Ex∼G(z,c)[Ec′∼P(c|x)[log(P(c′|x))]])

여기서 P(c|x)를 알 수 없기에, 이를 근사하는 Q Network를 만들었다고 가정해 보면 수식은 다음과 같이 단순화된다.

H(c)+ExG(z,c)[EcP(c|x)[log(Q(c|x))+DKL(P|Q)]]H(c)+Ex∼G(z,c)[Ec′∼P(c|x)[log(Q(c′|x))+DKL(P|Q)]]

이것은 P = Q(P의 근사) + P와 Q의 차이(KL-Divergence)이기에 가능하다. 다만 KL-Divergence Term은 항상 양수이므로 무시할 수 있게 되어 결국 다음을 최대화하면 된다.

H(c)+ExG(z,c)[EcP(c|x)[log(Q(c|x))]]H(c)+Ex∼G(z,c)[Ec′∼P(c|x)[log(Q(c′|x))]]

문제는 기댓값을 구할 때 Sampling하는 과정에서도 P(c|x)가 존재하기에 한번 더 수학적 Trick을 사용하는데, 논문에 나오는 Lemma 5.1을 이용하는 것이다. (더 공부 필요)

ExG(z,c),cP(c)[log(Q(c|x))]+H(c)Ex∼G(z,c),c∼P(c)[log(Q(c|x))]+H(c)

결국 이 식은 Maximum Likelihood 식이 된다. 어떤 경우에? 확률분포 P(c)로부터 c를 얻어서 Generator를 통과시켜서 나온 x가 있을 경우에 이것이 Q Network를 통과했을 경우 c의 Maximum Likelihood라는 것이다. 이것을 최대화 한다는 것은 결국 다음 문장으로 요약 가능하다.

“Latent Code c의 복원 오차를 최소화 하자”

코드를 짜는 입장에서는 Loss Function을 Minimize하는 것이 더 좋으므로 저 복원 오차를 Loss로 잡아서 Objective Function에 더해 주면 된다.

DLoss=DGANLoss+λCLossDLoss=DGANLoss+λCLossGLoss=GGANLoss+λCLossGLoss=GGANLoss+λCLoss

여기서 중요한 것은 Latent Code인 c를 넣어주는 방식인데, 우리는 Semantic Feature가 Label값과 같은 Class로 구분되기를 원할 수도 있고 (ex. MNIST), 연속적인 값 (얼굴의 회전 정도, 표정 등)을 갖기를 원할수도 있다. 이에 대해 가이드라인이 존재하는데, Cateogrical Code의 경우에는 마치 One-hot Encoding을 하듯이 넣어 주고, (즉, Dimension이 Class의 수가 된다) Cross Entropy-like한 방법으로 복원 오차를 구하면 된다. Continuous Code는 Uniform Distribution이나 Gaussian Distribution으로 넣어 주고 복원된 c를 가지고 몬테-카를로 방법으로 추정한 확률분포 사이의 Continuous Error Measurement를 하면 된다.

요약하여, GAN과 비교하여 전체 System의 구조를 보면 다음과 같게 된다.

그림입니다.
원본 그림의 이름: mem000ae8f8000d.tmp
원본 그림의 크기: 가로 932pixel, 세로 682pixel

어떻게 Latent Code를 이용해서 의미 있는 Output을 쉽게 Generation을 하는 지는 실제 구현 단계인 다음 절에서 살펴보도록 하자.

 

3-2. Info-GAN Implemenation

 

코드는 Pytorch로 구현하였으며, Database는 CelebA, DCGAN 코드를 수정해서 만들었는데, DCGAN Baseline Code는 Yunjey님의 Github Repo.에서 참조하였다.

코드 링크: Pytorch Github Repository

기본적으로 GAN (또는 DCGAN)의 구조를 가지고 있고, Discrete(Categorical) Code와 Continuous Code를 랜덤하게 만들어서 Generator에 Input으로 넣어 줘야 한다. 아래 코드에서 볼 수 있듯이 z에 붙여서 넣으면 된다. 학습이 완료 되면 나중에 원하는 Feature를 가진 Image를 Generation할 때 그 분포와 Categorical Class를 따라가게 된다.

input_to_G = torch.cat((noise, continuous_code_input, discrete_code_input), 1)

fake = G(input_to_G)

 

input_to_D = torch.cat([images, fake])

 

outputs = D(input_to_D)

 

 

위 코드에서 결과로 나오는 outputs에는 기존 Real이냐 Fake냐를 나타내는 1x1 Size의 Label값과 더불어 Code도 나오게 된다. 따라서 Code의 Loss를 측정해서 Discriminator Loss에 더해 주고 학습시키면 된다.

D.zero_grad()

 

continuous_code_output = outputs[batch_size:, 1:1+num_of_continuous_code]

discrete_code_output = outputs[batch_size:, 1+num_of_continuous_code:1+num_of_continuous_code+num_of_class_of_category]

 

Loss_continuous = MSE(continuous_code_output, Distribution_of_continuous_code_generator)

Loss_discrete = CrossEntropy(discrete_code_output, discrete_code_input)

 

Loss_D = BCE(outputs[:,0], real_and_fake_labels) + Lambda1 * Loss_continuous + Lambda2 * Loss_discrete

 

Loss_D.backward(retain_variables=True)

D_optimizer.step()

 

 

이어서 Generator를 학습하는 코드인데, Fake Image만 학습한다는 것 빼고는 Discriminator와 동일하다.

G.zero_grad()

 

Loss_G = BCE(outputs[batch_size:,0], real_labels) + Lambda1 * Loss_continuous + Lambda2 * Loss_discrete

 

Loss_G.backward()

G_optimizer.step()

 

 

학습이 끝나면 우리가 원하는 Feature를 가진 Image를 만드는 방법은 확실하게 보기 위해서 z를 고정하고. Continuous Code의 경우에는 linspace를 이용해서 점차적으로 변화되도록 c를 Generator에 Input으로 넣어 주면 되고. Categorical의 경우에는 One-hot Vector의 형식으로 넣어주면 된다. 아래 코드를 참조하자.

fix_cont_codes = np.zeros((100,2))

fix_cont_codes[0:25,0] = np.linspace(-3,3,25)

fix_cont_codes[50:75,0] = np.linspace(-3,3,25)

fix_cont_codes[25:50,1] = np.linspace(-3,3,25)

fix_cont_codes[75:100,1] = np.linspace(-3,3,25)

fix_cont_codes = Variable(fix_cont_codes)

 

fix_disc_codes = np.zeros((100,2))

fix_disc_codes[0:50,0] = 1

fix_disc_codes[50:100,1] = 1

fix_disc_codes = Variable(fix_disc_codes)

 

fix_noise = Variable(torch.Tensor(np.zeros((100, z_dim))))

 

inputs = torch.cat((fix_noise, fix_cont_codes, fix_disc_codes), 1)

generated_images = G(inputs)

 

torchvision.utils.save_image(Denormalization(generated_image.data), Output File Name, nrow=10)

 

 

 

3-3. Info-GAN Results

경축! 아무것도 안하여 에스천사게임즈가 새로운 모습으로 재오픈 하였습니다.
어린이용이며, 설치가 필요없는 브라우저 게임입니다.
https://s1004games.com

 

CelebA 결과를 보면, 중요한 특징은 돌릴 때마다 c가 캐치해 내는 Feature가 조금씩 바뀐다는 것이다. 하지만 순서만 바뀔 뿐, 대충 사람이 인지하기에도 중요한 Feature들이 학습되는 것을 확인할 수가 있다.

첫 번째 수행결과의 경우에는, 위/아래 Set는 2개의 Categorical Code로 나뉘어진 것인데, 바라보는 방향에 따라 나누어진 것을 볼 수 있다.

각 Category Set에서는 노란색 선을 기준으로 2개의 Continuous Code가 점차적으로 변화했을 경우를 보여주는데, 하나는 대충 피부색인 듯 하고, 하나는 표정 변화를 보여주고 있다.

즉, 바라보는 방향 (Categorical), 피부색과 표정 (Continuous) 모두 중요한 얼굴의 Feature임을 보여주고 있다.

그림입니다.
원본 그림의 이름: mem000ae8f8000e.tmp
원본 그림의 크기: 가로 726pixel, 세로 733pixel

그리고 생각보다 생성 영상의 Quality가 훌륭하다.

다음은 두번째 수행한 경우인데, 이번엔 미묘하지만 표정으로 Category가 나눠지고, 첫 번째 Continuous Code는 주름의 변화 비슷하게, 두 번째 Continuous Code는 머리와 피부색으로 변화되는 것을 볼 수 있다.

그림입니다.
원본 그림의 이름: mem000ae8f8000f.tmp
원본 그림의 크기: 가로 724pixel, 세로 731pixel

 

4. EBGAN

 

4-1. EBGAN Description

 

EBGAN은 LeCun교수 팀에서 낸 논문으로, Energy-based라는 거창한 Title을 달고 있다. 근데 사실 Energy라는게 어려운 것이 아니고 틀리면 높은 에너지, 맞으면 낮은 에너지를 부여하는 전통적인 방법이다. 결국 논문의 Contribution은 Discriminator가 진짜와 가짜를 구분해 내기 보다는 Energy Function으로서 행동하기를 원한다는 것이다. 그렇게 하기 위해서 Discriminator를 Auto-encoder로 만들었고, 나름 결과가 좋았다 라는게 논문의 핵심 내용이다.

논문에서는 다음과 같이 새로운 Loss Function을 제안하는데,

LD(x,z)=D(x)+[mD(G(z))]+LD(x,z)=D(x)+[m−D(G(z))]+LG(z)=D(G(z))LG(z)=D(G(z))

논문에서는 이렇게 Loss Function을 잡으면 System의 Nash Equilibrium이 존재하며, 그 때의 Value Function의 값과 그것을 만족하는 Energy 값이 존재함을 수식적으로 증명한 것 같다. 확실하게 이해했는지는 모르겠지만. 해당 방법으로 Training했을 경우 완벽하게 Nash Equilibrium으로 “안정적으로” 수렴한다고 볼 수는 없는 것 같다.

우선 위 두2개의 Loss Function에 대한 Value Function을 다음과 같이 정의한다.

V(G,D)=x,zLD(x,z)pdata(x)pz(z)dxdzV(G,D)=∫x,zLD(x,z)pdata(x)pz(z)dxdzU(G,D)=zLG(z)pz(z)dzU(G,D)=∫zLG(z)pz(z)dz

결국 Loss의 Minimum을 원하므로, Nash Equilibrium은 다음을 만족한다고 할 수 있다. *이 붙으면 Optimum임을 의미한다.

V(G,D)≤V(G,D)∀DV(G∗,D∗)≤V(G∗,D)∀DU(G,D)≤U(G,D)∀GU(G∗,D∗)≤U(G,D∗)∀G

논문에서는 다음 두 가지를 증명하고 있다

? 1번. D*, G가 Nash Equilibrium이라면, Generator의 Data 확률 분포와 원본 Data 확률분포가 일치하며 (p_g = p_data), 그 때의 V(D, G*) = m 이다.

? 2번. Nash Equilibrium이 존재하며, 그 때 D*(x)=C를 만족하는 상수 C가 존재한다.

우선 1.에 대한 증명을 먼저 살펴 보면, 우선 Discriminator의 Loss Function을 그대로 Value Function에 대입 한 것인데, G를 Optimum으로 가정해서 그런지 수식 전개가 (분배법칙) 깔끔한 느낌은 아니었다. (이해 안되는 부분 1)

V(G,D)=xpdata(x)D(x)dx+zpz(z)[mD(G(z))]+dzV(G∗,D)=∫xpdata(x)D(x)dx+∫zpz(z)[m−D(G∗(z))]+dz

일단 맞다고 가정하고. G가 Optimum이므로 z domain이 합쳐지면 다음 수식으로 정리된다.

V(G,D)=xpdata(x)D(x)+pG(x)[mD(x)]+dx…(6)V(G∗,D)=∫xpdata(x)D(x)+pG∗(x)[m−D(x)]+dx…(6)

적분식 안에 있는 수식을 단순화하면 다음으로 치환할 수 있는데,

φ(x)=ax+b(mx)+wherea=pdata,b=pG,x=D(x)φ(x)=ax+b(m−x)+wherea=pdata,b=pG∗,x=D(x)

저 함수를 1차함수라고 생각하면 누구나 쉽게 대략적인 모습을 그려볼 수 있다.

결론적으로는 다음처럼 되는데,

? a <b 라면, 최소값이 되는 D(x) = m, 그때의 최소값은 am

? a >b 라면, 최소값이 되는 D(x)는 m보다 작고, 그 때의 최소값은 bm (D(x)=0일 경우)

즉,

D(x)≤mD∗(x)≤m

이다. 이 조건도 뒤에 나오므로 기억해 두자.

이 말을 그대로 수식으로 풀면 다음 전개로 넘어갈 수가 있다.

V(G,D)=mx1pdata(x)<pGpdata(x)dx+mx1pdata(x)≥pGpG(x)dxV(G∗,D∗)=m∫x1pdata(x)<pG∗⋅pdata(x)dx+m∫x1pdata(x)≥pG∗⋅pG∗(x)dx

여기서 1은 아래 조건이 만족되면 1, 아니면 0을 의미한다.

간단하게 정리를 하면 최종적으로 다음 수식이 된다.

V(G,D)=m+mx1pdata(x)<pG⋅(pdata(x)−pG(x))dx…(10)V(G∗,D∗)=m+m∫x1pdata(x)<pG∗⋅(pdata(x)−pG∗(x))dx…(10)

여기서 2번째 항을 잘 보면, a <b일때 (p_data <p_G) 만 값을 가지는데, 그러면 (p_data - p_G)가 음수가 된다. 즉 0보다 같거나 작다, 즉 최종적으로

V(G,D)≤mV(G∗,D∗)≤m

이라는 것이 된다.

이어서, (이해가 안되는 부분 2) 다음 수식(위에 있는 Value Function)으로 부터,

U(G,D)≤U(G,D)U(G∗,D∗)≤U(G,D∗)

왜 이걸 대입하는지 이해가 잘 안 되지만, 완벽한 Generator라면 Real Data 분포일때 보다 Generator 부분의 Loss가 작아야 한다는 것이 아닐까? 라고 스스로 해석하고 넘어가 본다.. 즉, 위 식의 우변에 p_data를 대입하는 것인데, 사실 이부분도 결국 깔끔하게 이해가 되지는 않았다.

xpG(x)D(x)dxxpdata(x)D(x)dx…(11)∫xpG∗(x)D∗(x)dx≤∫xpdata(x)D∗(x)dx…(11)

아무튼 이 수식을 기억한 채로, (6)수식을 다시 가져온 뒤 D를 D*를 치환하면,

V(G,D)=xpdata(x)D(x)+pG(x)[mD(x)]+dxV(G∗,D∗)=∫xpdata(x)D∗(x)+pG∗(x)[m−D∗(x)]+dx

여기서 p_data(x)를 p_G*(x)로 바꾸면 식 (11)에 의하여,

xpG(x)D(x)+pG(x)[mD(x)]+dxV(G,D)∫xpG∗(x)D∗(x)+pG∗(x)[m−D∗(x)]+dx≤V(G∗,D∗)

위 식의 좌변은

D(x)≤mD∗(x)≤m

이라면,

xpG(x)⋅mdx∫xpG∗(x)⋅mdx=mxpG(x)dx=m⋅∫xpG∗(x)dx=m=m

이 된다. (확률의 총 합은 1이므로) 즉, 다음과 같이 결론지을 수 있다.

mV(G,D)≤mm≤V(G∗,D∗)≤mV(G,D)=mV(G∗,D∗)=m

여기에서 식 (10)에 의해 이 경우가 되려면 우변의 두번째 항 = 0이어야 하므로,

pdata(x)=pG(x)pdata(x)=pG∗(x)

여야만 하기에, 1번에 대한 증명이 마무리가 된다. (수학적으로 Almost Everywhere인 부분은 편의상 생략..)

하지만 2번 증명은 (증명이 길지도 않은데 Appendix로 왜 넘겼을까..) 뭔가 Tricky하게 넘어가는데 우선은 자세한 증명은 생략하도록 하지만. 글로서 풀어보자면. 일단 Nash Equilibrium 존재하고, 그때의 확률분포도 정해져 있다는 것이 증명되었고 (1번 증명을 포함하여) D*(x) <m 이고.. 결국 추가된 건 다음 하나뿐인데,

D(x)=CD∗(x)=C

이러한 Constant값이 Almost Everywhere에서 반드시 존재한다는 것이다. (이 말이 Discriminator가 Equilibrium에 도달했다는 것을 의미하는 것 같다.)

Appendix에서는 이것이 Constant가 아닐 경우의 모순을 보여주면서 증명이 끝이 난다.. 그래서 결국 내 생각 (혹은 이해 부족)이지만 논문 어디에도 이 System 혹은 Network의 구조가 반드시 수렴한다는 (Equilibrium에 도달한다는) 이야기는 없다. 논문 제목과 내용이 겉도는 느낌이 드는 이유가 여기에 있는 것 같다. 후속 GAN 논문을 계속 읽어보면서 내가 이해를 못 한 건지 뭔지에 대해 좀 더 살펴봐야 할 것 같다.

하지만 Practical하게 사용하는 엔지니어의 입장으로서는.. 그냥 넘어가는게 정신건강에 좋은 것 같은 생각도 들긴 한다. 아무튼 실제 Network은 다음 절을 참고하자.

 

4-2. EBGAN Model with Auto-Encoder and Implementation

 

결국! Energy Function은 Discriminator의 Auto-Encoder의 Reconstruction Error로 쓰겠다는 것이 실제 구현시의 핵심이다. EBGAN 구조는 아래처럼 된다.

그림입니다.
원본 그림의 이름: mem000ae8f80010.tmp
원본 그림의 크기: 가로 963pixel, 세로 225pixel

사실 왜 Auto Encoder를 썼는지에 대한 설명은 Energy Function 이상의 것은 아닌 것 같다. 직관적으로 보기에도 단순히 Real, Fake를 구분하는 것 보다는 나아 보인다. Auto Encoder가 Real Image를 넣었을 경우 제대로 다시 복원해 낼 줄 안다면, Fake Image가 들어갔을 경우에도 그렇게 학습되도록 해서 Generator가 더 실제적인 Image를 만들어 내게 하는 것이다. 좀 더 유연해지는 느낌이다.

그리고 논문에서는 Generator Loss에 Regularizer를 추가한다. 일종의 Minibatch Discrimination과 같은 맥락이라 볼 수 있는데 Batch Set의 Feature Output을 추가하는 것이다. 수식으로는 다음과 같다.

LG(z)=D(G(z))+λfPTLG(z)=D(G(z))+λ⋅fPT

fPT(S)=1N(N−1)Ni=1Nj=1,ji(STiSj|Si|⋅∣∣Sj∣∣)2fPT(S)=1N(N−1)∑i=1N∑j=1,j≠iN(SiT⋅Sj|Si|⋅|Sj|)2

마찬가지로 DCGAN 기반의 코드 구현으로, 링크는 다음과 같다.

코드 링크: Pytorch Github Repository

하나씩 보면, 논문에서 제안한 Loss를 기반으로 Discriminator를 학습하는 코드이다. Loss가 MSE인 것이 Point이다.

fake = G(noise)

fake_recon = D(fake)

real_recon = D(images)

 

fake_loss = torch.mean(torch.sum((fake_recon - fake) ** 2, 1))

real_loss = torch.mean(torch.sum((real_recon - images) ** 2, 1))

 

D_loss = real_loss + F.relu(Margin - fake_loss)

 

D.zero_grad()

D_loss.backward()

D_opt.step()

 

 

다음은 Generator를 학습하는 코드이다.

fake = G(noise)

fake_recon = D(fake)

 

G_loss = torch.mean(torch.sum((fake_recon - fake) ** 2, 1))

 

 

Regularizer를 추가하려면 위 수식을 구현해야 하는데, 위 수식은 다음과 같이 해석된다.

? Sample 단위로 곱한다

? 그것을 각 Sample의 Norm의 곱으로 나눈다

? 제곱해서 다 더한뒤 N(N-1)로 나눈다

이것을 For문 없이 Matrix로 다음 그림처럼 만들 수 있다. (분자 부분)

그림입니다.
원본 그림의 이름: mem000ae8f80011.tmp
원본 그림의 크기: 가로 837pixel, 세로 234pixel

그러면 결국 모든 Cross Sample간의 곱으로 Matrix가 생기는데, 같은 Sample끼리는 제외되어야 하므로 대각 성분을 제거해 준다.

그림입니다.
원본 그림의 이름: mem000ae8f80012.tmp
원본 그림의 크기: 가로 808pixel, 세로 214pixel

분모 부분(Norm)은 각 Sample별로 구한 뒤 분자와 Size를 맞춰주기 위해서 (feature_dimension x batch_size)형태의 Matrix로 만들어 준다.

그림입니다.
원본 그림의 이름: mem000ae8f80013.tmp
원본 그림의 크기: 가로 680pixel, 세로 219pixel

그러면 아래처럼 곱하면 각 Element가 Norm의 Cross곱을 가지게 되며, 이것을 분자에서 나누고 제곱하면 된다.

그림입니다.
원본 그림의 이름: mem000ae8f80014.tmp
원본 그림의 크기: 가로 503pixel, 세로 220pixel그림입니다.
원본 그림의 이름: mem000ae8f80015.tmp
원본 그림의 크기: 가로 360pixel, 세로 197pixel

모든 Element를 더해서 N(N-1)로 나눠준 스칼라 값을 Generator Loss에 더하면 된다.

코드는 아래와 같다.

M = fake.view(-1, batch_size) # Data_Dimension x Batch_Size

 

nom = torch.mm(torch.transpose(M,0,1), M) # Batch_Size x Batch_Size

 

tmp = torch.zeros((data_dimension, batch_size))

tmp_column = torch.sqrt(torch.sum((M)**2), 0))

tmp[0,:] = tmp_column.data

 

denom = torch.mm(torch.transpose(tmp,0,1), tmp)

 

pt = ( nom.data / denom.cuda() ) ** 2

 

# Remove Diagonal Elements

pt -= torch.diag(torch.diag(pt, 0))

pt = torch.sum(pt) / (batch_size * (batch_size - 1))

 

G_loss += pt_lambda * pt

 

 

Training/Test과정은 일반적인 GAN과 동일하다.

 

4-3. EBGAN Results

 

CelebA 결과를 보자. PT는 Regularizer이며, m은 Margin을 의미한다. 사실 결과만 두고 봤을 경우 DCGAN과 크게 뭐가 다른가 싶다. 아직은 EBGAN의 진가를 확인하려면 좀 더 연구와 공부가 필요해 보인다.

그림입니다.
원본 그림의 이름: mem000ae8f80016.tmp
원본 그림의 크기: 가로 500pixel, 세로 608pixel

 

마치며..

2016년정도까지 나왔던, DCGAN 이후의 몇 개의 GAN 논문들을 읽고, 분석하고 구현해 보았다. 이어서 언제가 될 지 모르지만 머지 않은 시간 내에 2016년 후반 - 2017년 GAN Trend까지 Rough하게나마 Follow하는 것을 목표로 하고 있다.

 

[출처]

https://taeoh-kim.github.io/blog/generative-models-part-2-improvedganinfoganebgan/

본 웹사이트는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.
번호 제목 글쓴이 날짜 조회 수
공지 오라클 기본 샘플 데이터베이스 졸리운_곰 2014.01.02 86146
공지 [SQL컨셉] 서적 "SQL컨셉"의 샘플 데이타 베이스 SAMPLE DATABASE of ORACLE 가을의 곰을... 2013.02.10 78646
공지 [G_SQL] Sample Database 가을의 곰을... 2012.05.20 95376
13 [데이터 수집 및 전처리] 주식 전종목 어떻게 불러올까? 거래소 종목 불러오기 file 졸리운_곰 2023.12.09 1701
12 [데이터 수집 및 전처리] [Python/파이썬]네이버증권API 활용 - 회사명, 종목코드 받아오기 file 졸리운_곰 2023.12.08 1509
11 [데이터 수집 및 전처리] 네이버 금융(차트)에서 주가 갈무리(크롤링)하기 file 졸리운_곰 2023.12.08 1495
10 [데이터 수집 및 전처리] 네이버 증권에서 일봉, 주봉 데이터 가져오기 file 졸리운_곰 2023.12.08 1499
9 [데이터 수집 및 전처리] (놀라운) 한글 데이터 짱! AwesomeKorean_Data file 졸리운_곰 2023.03.07 1197
8 [데이터 수집 및 전처리] Crawling, Scraping file 졸리운_곰 2022.05.21 1476
7 [데이터분석][데이터수집 전처리] MS 엑셀(Excel)에서 UTF-8 로 된 csv 파일 가져오기 file 졸리운_곰 2021.09.30 1415
6 카프카 설치 시 가장 중요한 설정 4가지 졸리운_곰 2021.07.13 1927
5 Prometheus Query(PromQL) 기본 이해하기 file 졸리운_곰 2020.12.17 1413
4 [인프라 모니터링 오픈소스] Prometheus 를 알아보자 file 졸리운_곰 2020.12.17 1906
3 Prometheus + Grafana 대시보드 file 졸리운_곰 2020.12.17 2488
2 Grafana란? file 졸리운_곰 2020.12.17 2158
1 Importing wikipedia dump to MySql 졸리운_곰 2020.10.04 2494
대표 김성준 주소 : 경기 용인 분당수지 U타워 등록번호 : 142-07-27414
통신판매업 신고 : 제2012-용인수지-0185호 출판업 신고 : 수지구청 제 123호 개인정보보호최고책임자 : 김성준 sjkim70@stechstar.com
대표전화 : 010-4589-2193 [fax] 02-6280-1294 COPYRIGHT(C) stechstar.com ALL RIGHTS RESERVED