<Reference>
https://www.youtube.com/watch?v=odpjk7_tGY0
Generative Model의 Goal
- pdata(x)에 근사하는 pmodel(x)를 찾기
- pdata(x) : 실제 학습 데이터의 분포
- pmodel(x) : 모델이 생성한 데이터의 분포
- 두 분포의 차이를 최소화하기
Brief Introduction - GAN(Generative Adversarial Networks)
- D(Discriminator Model)
- G(Generator Model)
최종 목표는 G를 학습하는 것, 이를 위해 D를 먼저 학습시킬 필요가 있음
STEP 1) D 학습시키기
- 진짜 이미지는 1, 가짜 이미지는 0 라벨로 분류하는 것이 학습 목적
- Input : 고정 이미지 벡터
- Output : Binary, 1dim, sigmoid(0.5)
STEP 2) G 학습시키기
- 랜덤한 코드(latent code z)를 받아 이미지를 생성
- 생성한 이미지로 D를 속이는 것이 목표 → D의 output이 1이 되도록
- 학습할 수록 진짜같은 가짜이미지를 생성하게됨
Objective(Loss) Function of GAN
GminDmaxV(D,G)=Ex∼pdata(x)[logD(x)]+Ez∼pz(z)[log(1−D(G(z)))]
A. Discriminator 관점
- Left Term : Ex∼pdata(x)[logD(x)]
- x∼pdata(x) : 확률 밀도 함수, 실제 데이터에서 샘플링,
- logD(x) 최대화 : 실제 데이터에서 받은 데이터를 입력으로 받으면, D는 1에 가까운 값을 출력해야함, D(x)는 0~1사이 값을 출력
- Right Term : Ez∼pz(z)[log(1−D(G(z)))]
- z∼pz(z) : z는 Generator로 들어가는 입력, 표준 정규 분포/uniform 분포에서 랜덤하게 추출된 100차원의 벡터
- G(z) : Random 하게 생성한 벡터를 입력으로 받아 Generate한 이미지, 출력은 가짜 이미지
- D(G(z)) : 이를 다시 Discriminator에 넣어 Fake, Real Binary classification
- log(1−D(G(z))) : D(G(z))값이 0일때 최대 = z로 부터 생성된 가짜 이미지를 가짜로 분류하였을때 최대값을 가짐 = 학습 목표
B. Generator 관점
- Left Term : Ex∼pdata(x)[logD(x)]
- 실제이미지를 discriminate하는 것과 Generator는 독립
- Right Term : Ez∼pz(z)[log(1−D(G(z)))]
- 가짜이미지를 입력으로 받았을 때 Discriminator가 진짜 이미지로 분류하도록 하는 것이 목적
- D(G(z)) 값이 1일때 최소 = z로 부터 생성된 가짜 이미지를 진짜로 분류하였을때 최소값을 가짐 = 학습 목표
Pytorch Implementation
DCGAN Tutorial - PyTorch Tutorials 1.13.1+cu117 documentation
import torch
import torch.nn. as nn
D = nn.Sequential(
nn.Linear(784 ,128),
nn.ReLU(),
nn.Linear(128, 1),
nn.Sigmoid())
G = nn.Sequential(
nn.Linear(100, 128),
nn.ReLU(),
nn.Linear(128, 784),
nn.Tanh())
criterion = nn.BCELoss()
d_optimizer = torch.optim.Adam(D.parameters(), lr=0.01)
g_optimizer = torch.optim.Adam(G.parameters(), lr=0.01)
while True:
loss = criterion(D(x), 1) + criterion(D(G(z)), 0)
loss.backward()
d_optimizer.step()
loss = criterion(D(G(z)), 1)
loss.backward()
g_optimizer.step()
Binary Cross Entropy Loss (h(x),y)
−ylogh(x)−(1−y)log(1−h(x))
criterion = nn.BCELoss()
Loss function
GminDmaxV(D,G)=Ex∼pdata(x)[logD(x)]+Ez∼pz(z)[log(1−D(G(z)))]
loss = criterion(D(x),1) + criterion(D(G(x)),0)
criterion(D(x),1)
: −logD(x)
criterion(D(G(x)),0)
: −log(1−D(G(x)))
**Note : Gradient Descent로 학습되기 때문에 기존 loss function에 -를 붙여준 형태
**Train G에서 주의할 점
- Generator를 학습할 때 Discriminator는 고정이어야함.
- optimizer를 D,G 파라미터에 대해 각각 설정해두고, genrator학습 시
g_optimizer.step()
만 수행
Non-Saturating GAN Loss
G의 objective Function
GminV(G)=Ez∼pz(z)[log(1−D(G(z)))]
- log(1−x) 그래프
G는 학습 초반에 매우 평편없는 이미지를 생성하게 되고, D는 이를 가짜 이미지라고 확신하게됨 → D가 0에 매우 가까운 값을 출력
⚠️ 이때의 gradient가 상대적으로 작다
💡 log(1−x)를 최소화 하는 대신 log(x)를 최대화 하자
→ 상대적으로 큰 graident
⇒ 초반에 Generator가 매우 안좋은 상황을 최대한 빠르게 벗어날 수 있게됨
Implementation
loss = criterion(D(G(z)), 1)
Why does GANs work?
GAN의 loss function을 최대화 하는 것이 실제 데이터와 가짜 데이터의 분포 차이를 줄이는 것이 맞는가? → O
⟺GminDmaxV(D,G)G,DminJSD(pdata∣∣pg)
어떤식으로 GAN이 학습되는지 돌아보고 다시 아래에서 증명을 이어서 해보자!
- 파란색 점선 : D, discriminative distribution (판별 모델의 분포)
- 검정색 점선 : px, 데이터에서 생성된 분포 (원본 데이터의 분포)
- 초록색 실선 : pg(G) , generative distribution (생성 모델의 분포)
- z 실선 : uniformly sampling된 z의 domain
- z → x 화살표 : x=G(z) 매핑, non-uniform 분포 pg로 변환되는 과정
- x 실선 : 매핑/변환된 x
GAN은 Discriminative distribution과 동시에 실제 데이터에서 샘플링하여 생성된 분포 px와 Generator를 통해 생성된 분포에서 샘플링한 pg(G)를 구분하도록 학습
G contracts in regions of high density and expands in regions of low density of pg.
(a) x=G(z) 매핑을 통해 만들어진 가짜 분포 pg
(b) D∗(x)=pdata(x)+pg(x)pdata(x)을 통해 판별 모델 확률 분포 D 업데이트
(c) pg가 pdata에 가깝도록 업데이트
(d) 학습을 계속 반복하여 pg=pdata 가 되면 두 분포를 구분할 수 없어져 D(x)=21로 수렴
-
KL divergence
Note. BCE
BCE=x∈0,1∑(−P(x)log(Q(x)))
- P(x) = 희망하는 타겟에 대한 결괏값
- Q(x) = 모델에서 출력한 출력값
- Q라는 모델의 결과에 대해 P라는 이상적인 값을 기대했을 때 그와 실제 결과의 차이에 대한 감각
KL divergence
확률분포 P를 모델링한다고 할때, 이산 확률 분포 P와 Q가 동일한 샘플 공간 x에서 정의된다고 하면 KL divergence는 다음과 같다.
DKL(P∥Q)=x∈χ∑P(x)logb(Q(x)P(x))=−x∈χ∑P(x)logb(P(x)Q(x))=−x∈χ∑P(x)logbQ(x)+x∈χ∑P(x)logbP(x)
이를 기댓값(=∑x×prob(x))으로 치환하면
⇒−EP[logbQ(x)]+EP[logbP(x)]
*여기서 EP는 P(x)라는 확률 분포에 대한 기댓값 연산임을 의미
이를 전개하면
⇒HP(Q)−H(P)
*여기서 HP(Q)는 P를 기준으로 봤을 때 Q에대한 cross entropy, H(P)는 P에 대한 정보 엔트로피
HP(Q) : 어떠한 확률분포 P가 있을 때, 샘플링 과정에서 확률분포 Q를 P 대신 사용할 경우 엔트로피
HP(Q)−H(P) : 위에서 H(P)를 빼주게 되면, 기존에서의 엔트로피의 변화를 의미하게됨
- 항상 0이상
- aysymmetric : distance개념이 아니다.
-
JSD(Jensen-Shannon Divergence)
KL divergence를 distance metric으로 쓸 수 있는 방법은 없을까
M을 확률 분포 P와 Q의 평균이라고 할 때
JSD(p∣∣q)=21KL(p∣∣M)+21KL(q∣∣M)where,M=21(p+q)
Algorithm
Variations of GAN
1. DCGAN(Deep Convolutional GAN)
- Discriminator
- Generator
- deep convolutional NN
- deconvolution, transpose convolution → upsampling
- No pooling layer
- stride size>2 의 convolution,deconvolution
- BN
- Adam optimizer
- Momentum = 0.5, 0.999
- 64x64이미지를 사용할때 실험적으로 위 숫자들을 사용할 때 성능이 좋은 것을 확인
- Generator의 입력인 Latent vector z간의 산술적 연산이 가능! (선형적 관계)
- ex. man with glasses - man without glassed + woman without glasses ⇒ woman with glasses
2. LSGAN(Least Squares GAN)
- 기존의 GAN Loss → D를 속이기만 하면 됨
- 파란색 선 = D의 decision boundary → 낮으면 진짜, 높으면 가짜
- 빨간색 점들 → 진짜 이미지
- 파란색 점들 → 가짜 이미지
- ⇒ 빨간점에 가까이 있는 파란 점들은 잘만든 가짜 이미지
- 핑크색 점들 → discriminator를 완벽히 속인 가짜 이미지 (Decision boundary완전 안쪽에 있어서)
💡 그렇다고 핑크색 점들이 잘 만들어진 이미지인가? ⇒ NO
🤷 why? ⇒ 실제 이미지에 가깝게 만들어진게 잘 만들어진 이미지, discriminator를 완벽히 속였어도, 실제와 비슷하다는 보장을 할 수가 없다.
⇒ LSGAN에서는 핑크색 점들을 decision boundary근처로 끌어 올린다.
Vanilla GAN → LSGAN
- D의 마지막 레이어 sigmoid 제거
- G는 동일
- Cross entropy loss ⇒ Least squeares loss
- LSGAN - loss of D
- (D(x)-1)**2 → 진짜 이미지 D(x)는 1에 가깝게
- (D(G(z))**2 → 가짜 이미지 D(G(z))는 0에 가깝게
- LSGAN - loss of G
- (D(G(z))-1)**2 → 가짜 이미지 D(G(z))는 1에 가깝게
- cross entropy loss와의 차이 :
*Note. 코드로 비교해보자~!
- Vanilla GAN
import torch
import torch.nn. as nn
D = nn.Sequential(
nn.Linear(784 ,128),
nn.ReLU(),
nn.Linear(128, 1),
nn.Sigmoid())
G = nn.Sequential(
nn.Linear(100, 128),
nn.ReLU(),
nn.Linear(128, 784),
nn.Tanh())
D_loss = -torch.mean(torch.log(D(x))) - torch.mean(torch.log(1-D(G(z))))))
G_loss = -torch.mean(torch.log(D(G(z))))
- LSGAN
import torch
import torch.nn. as nn
D = nn.Sequential(
nn.Linear(784 ,128),
nn.ReLU(),
nn.Linear(128, 1))
G = nn.Sequential(
nn.Linear(100, 128),
nn.ReLU(),
nn.Linear(128, 784),
nn.Tanh())
D_loss = -torch.mean((D(x)-1)**2) - torch.mean(D(G(z))**2))
G_loss = -torch.mean((D(G(z))-1)**2)
3. SGAN(Semi-Supervised GAN)
- MNIST data
- D가 진짜/가짜를 구분하는 것이 아닌 class를 구분(0~9) + Fake class를 추가해 11개의 class ⇒ softmax ⇒ one-hot vector
- G는 one-hot vector + latent vector z를 input으로 받아 fake image생성
- D는 라벨이 있어야 하는 supervised learning, G는 generator가 만든 이미지로 분류하는 unsupervised learning ⇒ Semi-Supervised GAN
4. ACGAN(Auxiliary Classifier GAN)
- D → Multi-task learning
- 진짜이미지 vs 가짜이미지 (0 or 1) → sigmoid
- 이미지의 진위 여부와 관계 없이 0~9중 어떤 숫자에 해당하는지 → softmax
- G
- input = one-hot vector + latent vector z
- 여기서 생성한 가짜 이미지로 D는 다음 두가지 task 시행
- 진짜이미지 vs 가짜이미지 (0 or 1)
- 이미지의 진위 여부와 관계 없이 0~9중 어떤 숫자에 해당하는지
- Data augmentation의 효과 (Noise가 포함된 이미지)
⇒ Loss의 경우 두가지 task의 loss합한 것을 사용
Extensions of GAN
1. CycleGAN : Unpaired Image-to-Image Translation
- 이미지의 style, domain을 바꾸는 task
💡 Pair image가 없는 unsupervised 상태에서도 이러한 task의 학습이 가능하지 않을까?
⇒ How does it work?
ex. 얼룩말 이미지를 말로 변환하기
- D
- G
- latent code z대신 Real image입력을 받게됨
- 차원을 줄였다가 다시 복구하는 encoder decoder 구조
- 얼룩말 이미지를 받아 D를 속이기 위해 말 모양으로 변환
**Note. 얼룩말 이미지를 말로 변환하되, 이미지의 형태는 유지해야함!
- GBA로 다시 원래 이미지로 복원하려면 모양이 최대한 적게 바뀌어야함 → reconstrunction error를 줄이는 방향
Implementation
https://github.com/yunjey/mnist-svhn-transfer
2. StackGAN : Text to Photo-realistic Image Synthesis
⚠️ 128x128, 256x256 고해상도 이미지를 z벡터에서 바로 생성하기 어렵다는 문제
💡 64x64 저해상도 이미지를 먼저 생성한 후 이를 기반으로 또다른 Generator로 upsampling하기