Generative Adversarial Nets(GAN) 논문 번역 및 요약
초록
우리는 적대적인 생성 모델을 추정하는 새로운 프레임워크를 제안한다. 여기서 두 모델을 동시에 학습시키는데, 하나는 데이터의 분포를 따라잡는 생성 모델 G 그리고 어떤 샘플이 G가 아니라 학습 데이터에 속할 확률을 계산하는 구별 모델 D이다. G는 D가 잘못 구별하는 확률을 최대화하도록 학습된다. 이 프레임워크는 두 플레이어가 참여하는 최대최소(minimax) 게임과 상응한다. G와 D가 임의적으로 기능하는 공간에서 유일한 해(unique solution)가 존재한다. 이때 G가 학습 데이터의 분포를 찾아내며 D의 확률은 어디서든 50%가 된다. G와 D가 다중레이어 퍼셉트론으로 정의될 때, 모든 시스템이 역전파로 학습될 수 있다. 샘플을 훈련시키거나 생성하는 동안에 마르코프 연쇄나 unrolled approximate inference networks 가 필요하지 않다. 실험에서 생성된 샘플의 질적, 양적 추정을 통하여 프레임워크의 잠재력을 증명하였다.
- 최대최소 게임(minimax game)
- 가짜와 진짜를 구별할 수 없게 만들기(너의 정확도를 최소화) vs. 가짜와 진짜를 구별하기(나의 정확도를 최대화)
- 상대방의 행동을 예측하고(상대방이 자신의 이익을 최대화 하는 결정을 내릴 것이라고 예측) 그 결과를 바탕으로 나의 이익을 최대화 하는 결정을 찾아야 함
- 마르코프 연쇄(Markov chain)
- 미래 상태의 조건부 확률 분포가 과거 상태와는 독립적으로 현재 상태에 의해서만 결정
1. Introduction
- 딥러닝 분야에서 눈에 띄게 성과를 보이는 ‘분류 모델’ - 역전파 및 드롭아웃 알고리즘 기반
- 딥러닝 ‘생성 모델’은 임팩트가 덜했는데, 최대우도추정(maximum likelihood estimation)에서 발생하는 복잡한 확률 계산의 어려움과 조각형 선형함수 유닛(Piecewise Linear Unit) 활용의 어려움 때문
- 구별 모델은 샘플이 모델의 분포에서 오는지, 데이터 분포에서 오는지 판단하도록 학습
- 경찰과 가짜 화폐의 비유
- maximum likelihood estimation(MLE)
- 데이터가 특정 분포에 속할 likelihood 최대화하는 파라미터
- piecewise Linear Unit(PLU)
- 조각별로 정의된 함수
2. Related work
- log-likelihood를 최대화 시키는 방법으로 훈련하는 확률 분포 함수 → 가장 성공적이었던 볼츠만 머신(Boltzmann machine) 볼츠만 머신: 생성모형의 원리 : 다루기 힘든 우도 함수, 우도 경사에 대한 복잡한 계산
- 우도를 나타내지 않으면서 원하는 분포의 샘플을 생성할 수 있는 생성 모델
- Generative stochastic networks(GSN)에 쓰인 마르코브 연쇄를 생략하는 아이디어
- 일반 확률적 역전파 규칙: 유한 분산의 가우시안 분포를 통해 역전파. 평균과 마찬가지로 공분산 파라미터로 역전파. → 생성기의 조건부 분산을 학습할 수 있게 됨
- variational autoencoders (VAEs) vs. GANs : 전자는 히든 유닛을 통해 미분하기 때문에 이산 잠재 변수(discrete latent variables)를 가질 수 없고, 후자는 가시 유닛을 통해 미분하기 때문에 이산 데이터를 모델링할 수 없음
- discriminative criterion을 사용해 생성기를 학습시키는 접근: 변분근사법(variational approximations - lower bound [알고리즘] Lower bound, Upper bound ) 으로 추정할 수 없는 확률들을 포함. Noise-contrastive estimation(NCE)는 고정된 노이즈 분포에서 데이터를 잘 분류하도록 weight를 학습하는 방식으로 생성 모델을 훈련
- NCE의 주요 한계: 노이즈 분포 및 모델 분포의 확률 밀도 비율에 한정되기 때문에 두 밀도를 통해 역전파하고 계산하는 능력이 요구됨
- 1) 신경망 간의 경쟁이 유일한 학습 기준이 됨: Predictability minimization만이 신경망의 히든 유닛이 독립적이도록 정규화해줌
- 2) 경쟁의 성질 자체가 다름: Predictability minimization에서는 한 신경망이 출력을 비슷하게 만들고 다른 신경망이 출력을 다르게 만들며 이 두 값이 비교됨. 반면 GAN에서 한 신경망은 다른 신경망의 인풋으로 사용할 고차원 벡터를 생성하고, 다른 신경망이 이해하지 못하도록 입력을 고름.
- 3) 학습 단계의 사양이 다름: GAN은 최적화 문제보다는 미니맥스 게임에 기반하고 한쪽은 최대화를 추구하고 다른 한쪽은 최소화를 추구하는 가치 함수를 가짐. 안장점(saddle point)에서 게임 종료.
- “adversarial examples” 개념과 헷갈리지 말 것
3. Adversarial nets
적대 모델 프레임워크는 모델들이 모두 다중레이어 퍼셉트론일 때 가장 적용하기 쉽다. 데이터 x에 대한 생성기의 분포 Pg를 학습하기 위해, 입력 노이즈 변수에 대한 사전확률(prior) Pz(z)를 정의한다. 그리고 데이터 공간에 대한 매핑을 G(z;θg)로 표현하는데, 여기서 G는 파라미터 θg를 가진 다중레이어 퍼셉트론으로 표현되는 미분가능 함수이다. 두번째 다중레이어 퍼셉트론 D(x;θd)을 정의, 출력값은 단일 스칼라. D(x)는 x가 Pg보다는 데이터에서 왔을 확률을 나타낸다. 학습 데이터와 G의 샘플 모두 올바르게 라벨링하는 확률을 최대화하도록 D를 학습시킨다. 동시에 log(1-D(G(z)))를 최소화하도록 G를 학습시킨다. 즉, D와 G는 다음 V(G, D) 가치 함수로 나타나는 미니맥스 게임을 플레이하게 된다.
다음 섹션에서 우리는 적대 네트워크를 이론적으로 분석하여, G와 D에 충분한 여유가 주어진다면(in the non-parametric limit) 학습 기준을 통해 데이터 생성? 발생? 분포를 찾을 수 있음을 보여준다. 반복적이고 수적인 접근을 통해 게임을 적용해야 한다. 훈련의 inner loop 을 통해 D를 완벽하게 최적화하는 것은 계산적으로 어렵고, 한정된 데이터셋에서 오버피팅으로 이어질 것이다. 대신에 우리는 D를 최적화하는 k스텝과 G를 최적화하는 원스텝 사이에서 오간다. 이 결과로 G가 충분히 천천히 변화하는 한, D는 최적의 해에 가깝게 유지된다. (알고리즘 1)
- ∇(나블라=그라디언트)
실제로 식(1)은 G가 잘 학습하도록 충분한 그라디언트를 제공하지 못할 수 있다. G의 성능이 좋지 않은 학습 초반에는 D가 높은 자신감으로 샘플을 거부할 수 있는데, 샘플들이 훈련데이터와 확연하게 다르기 때문이다. 이 경우 log(1 − D(G(z)))는 포화된다. log(1 − D(G(z)))가 최소화되도록 G를 학습시키기보다는 log D(G(z))를 최대화하도록 G를 학습시킬 수 있다. 이 목적 함수는 G와 D의 역학관계에서 동일한 고정점으로 이어지지만 학습 초반에 훨씬 더 강력한 그라디언트를 제공한다.
4. Theoretical Results
- 생성기 G는 내재적으로 확률분포 Pg를 정의하므로, z와 Pz가 유사할 때 샘플들의 분포 G(z)가 얻어진다(?)
- 알고리즘 1 → Pdata 추정값으로 수렴 (무한개의 파라미터 셋팅)
- Pg = Pdata 를 위한 전역 최적해를 가질 수 있음
-
- (a) 적대쌍이 수렴에 근접한 경우로 Pg가 Pdata와 근사하고 D는 부분적으로 정확한 분류기다.
- (b) inner loop 알고리즘에서 D는 데이터에서 샘플들을 구별해내며
D*(x) =
로 수렴한다.
- (c) G에 업데이트가 이루어지면 D의 그라디언트를 통해 데이터로 더 잘 분류되는 지역으로 G(z)가 이동하도록 한다.
- (d) 몇 번의 학습 스텝을 마치고 G와 D에 충분한 capacity가 주어진다면 Pg = Pdata이기 때문에 둘다 더이상 향상되지 않는 지점에 이른다. 분류기는 두 분포를 구분하지 못한다. 즉 D(x) = 1/2
이해하기
1. 생성 모델과 구별 모델
생성 모델(generative model)은 훈련 데이터와 유사한 데이터를 생성합니다. 따라서, 생성 모델의 핵심은 “훈련 데이터의 분포를 학습하는 것”입니다. 반면에, 클래스를 분류하는 데 사용하는 구별 모델(discriminative model)의 핵심은 “훈련 데이터의 경계를 학습하는 것”입니다.
이를 수학적으로 표현하면 다음과 같습니다. 입력 X와 라벨 Y의 분포가 주어졌을 때, 구별 모델은 조건부 분포(conditional distribution) P(Y|X), 즉 X가 주어졌을 때 Y일 확률분포를 계산합니다. 생성 모델은 결합 확률 분포 P(X, Y), 즉 X와 Y가 동시에 발생하는 확률을 계산합니다. 만약 라벨이 없는 경우 P(X)만을 계산합니다.
*결합확률 = 동시에 발생하는 확률 = 교집합의 확률
#### P(Y|X)를 직접 구하는지, 아니면 다른 방식으로 유도해 구하는지의 차이라고 볼 수 있다. 베이즈 정리에 따라 P(Y|X) = P(X|Y)P(Y)/P(X) 인데, 이때 확률이 최대가 되는 경우를 보려고 하기 때문에 P(X)를 제외한다. 즉, P(Y|X) = P(X|Y) * P(Y) 가 된다. P(X|Y) * P(Y)는 결국 결합 확률 P(X, Y)와 같다. ####
+ 동물원 비유
2. Generative Adversarial Networks(GAN)
2.1. 위조지폐범과 경찰의 minimax game
2014년 발표된 GAN은 비지도 학습 방식을 사용합니다. 논문에서 설명한 위조지폐의 비유로 GAN의 원리를 직관적으로 이해할 수 있습니다.
위조지폐범은 가짜 돈을 찍어냅니다. 경찰은 위조 지폐와 실제 지폐를 구분해야 합니다. 위조지폐범은 경찰이 구별할 수 없을 정도로 점점 더 정교한 가짜 돈을 만들어냅니다. 결국에 위조 지폐는 경찰이 구분할 수 없을 정도로 실제 지폐와 아주 비슷해집니다. 논문은 이 같은 위조지폐범과 경찰의 적대적인 관계(adversrial)를 차용해서 모델을 학습시킵니다. 여기서 위조지폐범은 생성기(generator)이고 경찰은 식별기(discriminative)입니다.
우리는 적대적인 생성 모델을 추정하는 새로운 프레임워크를 제안한다. 여기서 두 모델을 동시에 학습시키는데, 하나는 데이터의 분포를 따라잡는 생성 모델 G 그리고 어떤 샘플이 G가 아니라 학습 데이터에 속할 확률을 계산하는 구별 모델 D이다.
*이 둘의 관계를 적대적이라고 표현하긴 했지만, 식별기는 생성기가 훈련 데이터와 더욱 비슷한 출력물을 내놓도록 가이드 하는 역할을 한다고 볼 수 있습니다.
이 관계는 minimax game 으로 설명할 수 있습니다. minimax game에서 참가자는 상대방의 행동을 예측하고 그 결과를 바탕으로 나의 이익을 최대화하는 결정을 찾아야 합니다. 참가자는 상대방도 자신의 이익을 최대화하는 결정을 내릴 것이라고 예측합니다. 마찬가지로 참가자인 생성기는 자신의 결과물이 훈련 데이터에 가깝도록 정확도를 최대화해야 하는데, 이를 위해서는 훈련 데이터와 생성 데이터를 구분하지 못하도록 상대방(식별기)의 정확도를 최소화하는 방법을 찾아야 합니다.
2.2. GAN의 생성기, 식별기 구조
GAN은 생성기(이하 G)와 식별기(이하 D)의 적대적인 관계를 통해 훈련 데이터와 매우 유사한 데이터를 생성합니다. 먼저 D는 진짜 샘플과 생성된 샘플을 구분하며, 가짜 샘플로 판단할 경우 0, 진짜 샘플로 판단할 경우 1을 출력하는 이진 분류기입니다. 그리고 이 D는 분류의 실수(loss)를 최소화하는 것이 목적이기 때문에 다른 분류 모델과 같이 역전파를 통해 훈련됩니다.
한편 G는 임의로 고정된 분포로부터 노이즈 데이터(z)를 받아와 훈련 데이터와 유사한 가짜 샘플을 생성합니다. G는 D의 실수(loss)를 최대화하는 것이 목적이기 때문에 역전파를 통해 업데이트 됩니다.
2.3. 수식
이러한 G와 D의 적대적인 방식은 위와 같은 가치 함수 V(D, G)로 표현됩니다. D(x)는 D가 실제 데이터라고 분류한 확률이고, D(G(z))는 G가 노이즈 z로부터 생성한 샘플을 분류한 결과입니다. 값은 0과 1 사이로 출력됩니다. 이때, 이 가치함수를 각 신경망의 입장에서 생각해볼 수 있습니다. D의 입장에서는 D(x)를 최대화하여 1에 가깝게 만들어야 하며, D(G(z))는 최소화하여 0에 가깝게 해야 합니다. 반면에 G의 입장에서는 D(G(z))를 최대화하여 1에 가깝게 만드는 것이 목적입니다.
*가치함수: 특정 상태에 대한 '가치'를 계산해주어 에이전트가 행동을 선택할 수 있도록 도와줌
(yi → D(x), P(yi) → D(G(z))로 생각해보기)
논문에서는 가치 함수의 global optimum을 구합니다. D 입장에서 최대, G 입장에서 최소가 되는 해입니다. 먼저 G가 z로부터 생성하는 데이터는 x와 거의 같아지기 때문에 G(z)를 x로 바꿔볼 수 있습니다. 이때 G는 더이상 업데이트되지 않고 고정된 상태입니다. 위 식을 더 단순화해서 다음과 같이 표현할 수 있습니다.
a * log(y) + b * log(1-y)
이 식을 y에 대해 편미분하면 [0, 1]에서 y는 a/a+b 일 때 최대입니다. 즉 Pdata(x) / Pdata(x) + Pg(x) 가 가장 optimal한 D(x)입니다. optimal D를 원래 식에 대입합니다.
D(x)가 최대화되는 것을 위에서 고려하였으므로, 이제는 G 입장에서 C(G)가 최소화되는 경우를 고려해야 합니다. 젠슨-섀넌 발산(Jensen-Shannon Divergence)을 사용하여 구하면, global minimum은 Pdata(x) = Pg(x) 일 때 달성되며 C(G) 값은 -log 4가 됩니다.
보충: 확률 분포
코드 (vanila GAN)
tensorflow 예제를 참고합니다. Google Colaboratory
(train_images, train_labels), (test_images, test_labels) = tf.keras.datasets.mnist.load_data()
sample = train_images[0].reshape(28, 28)
plt.imshow(sample, cmap='gray')
print(train_labels[0])
MINST 손글씨 이미지 데이터를 불러오고, 첫번째 데이터를 확인합니다. 라벨이 5인 이미지입니다.
train_images = train_images.reshape(train_images.shape[0], 28, 28, 1).astype('float32')
train_images = (train_images - 127.5) / 127.5 # [-1, 1]로 정규화
test_images = test_images.reshape(test_images.shape[0], 28, 28, 1).astype('float32')
test_images = (test_images - 127.5) / 127.5
훈련 데이터의 개수는 60,000이며, 크기는 28*28입니다.
BUFFER_SIZE = 60000
BATCH_SIZE = 128
train_dataset = tf.data.Dataset.from_tensor_slices(train_images).shuffle(BUFFER_SIZE).batch(BATCH_SIZE)
버퍼사이즈 60000, 배치사이즈 128
# Generator
# Input : 100차원의 noise
# Output : Mnist 이미지 크기인 28*28
inputs = keras.Input(shape=(100,))
x = inputs
x = layers.Dense(256)(x)
x = layers.LeakyReLU()(x)
x = layers.Dense(28*28, activation = 'tanh')(x)
outputs = layers.Reshape((28,28))(x)
G = keras.Model(inputs, outputs)
생성기는 100차원의 노이즈를 input으로 받고 28*28 사이즈의 가짜 샘플 output을 출력합니다.
# Discriminator 모델 작성
# Input : Mnist 이미지 (28*28)
# Output : 실제 데이터일 확률 (0~1 사이의 값)
inputs = keras.Input(shape=(28,28))
x = layers.Flatten()(inputs)
x = layers.Dense(256)(x)
x = layers.LeakyReLU()(x)
x = layers.Dropout(0.3)(x)
outputs = layers.Dense(1)(x)
D = keras.Model(inputs, outputs)
식별기는 28*28 사이즈의 샘플을 input으로 받고 훈련 데이터에서 온 진짜 샘플일 확률 (0~1)을 output으로 출력합니다.
test_noise = tf.random.normal([1, 100])
fake_image_test = G(test_noise, training=False)
plt.imshow(fake_image_test[0], cmap='gray')
생성기가 노이즈로 랜덤 이미지를 생성합니다.
decision = D(fake_image_test, training=False)
print(decision)
tf.Tensor([[0.19702005]], shape=(1, 1), dtype=float32)
랜덤 이미지를 식별기에 입력하면 해당 이미지가 진짜 샘플일 확률을 출력합니다.
EPOCHS = 50
noise_dim = 100
seed = tf.random.normal([BATCH_SIZE, noise_dim])
G_optimizer = tf.keras.optimizers.Adam(1e-4)
D_optimizer = tf.keras.optimizers.Adam(1e-4)
각 모델을 위한 optimizer를 정의합니다.
cross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits=True)
def D_loss(real_output, fake_output):
real_loss = cross_entropy(tf.ones_like(real_output), real_output)
fake_loss = cross_entropy(tf.zeros_like(fake_output), fake_output)
total_loss = real_loss + fake_loss
return total_loss
def G_loss(fake_output):
return cross_entropy(tf.ones_like(fake_output), fake_output)
ones_like: 모든 원소의 값이 1이고 주어진 텐서와 같은 shape을 가진 텐서를 반환
- 식별기의 손실(D_loss) = real_loss + fake_loss
- real_loss: 실제 이미지를 실제라고 분류하지 못했을 경우 발생하는 loss, 진짜 샘플이기 때문에 라벨이 모두 1인 array와 비교해서 loss 를 구할 수 있습니다. 즉, 실제 이미지를 넣었을 때 model G의 output(0과 1 사이의 값)이 1에 가까워지도록 손실을 계산합니다.
- fake_loss: 가짜 이미지를 실제라고 분류했을 경우 발생하는 loss, 가짜 샘플이기 때문에 라벨이 모두 0인 array와 비교해서 loss 를 구할 수 있습니다. 즉, 가짜 이미지를 넣었을 때 model G의 output(0과 1 사이의 값)이 0에 가까워지도록 손실을 계산합니다.
- 생성기의 손실 (G_loss)
- fake_loss: 가짜 이미지에 대한 식별기의 출력(fake_output)이 1에 가까워지도록 라벨이 모두 1인 array와 비교해서 loss를 구합니다.
@tf.function
def train_step(real_images):
noises = tf.random.normal([BATCH_SIZE, noise_dim])
with tf.GradientTape() as gen_tape, tf.GradientTape() as dsc_tape:
fake_images = G(noises, training=True)
real_output = D(real_images, training=True)
fake_output = D(fake_images, training=True)
gen_loss = G_loss(fake_output)
dsc_loss = D_loss(real_output, fake_output)
gen_gradients = gen_tape.gradient(gen_loss, G.trainable_variables)
dsc_gradients = dsc_tape.gradient(dsc_loss, D.trainable_variables)
G_optimizer.apply_gradients(zip(gen_gradients, G.trainable_variables))
D_optimizer.apply_gradients(zip(dsc_gradients, D.trainable_variables))
@tf.function: 함수를 호출가능한 tensorflow graph로 컴파일하는 데코레이터
def test_step(real_images):
noises = tf.random.normal([BATCH_SIZE, noise_dim])
fake_images = G(noises, training=False)
real_output = D(real_images, training=False)
fake_output = D(fake_images, training=False)
gen_loss = G_loss(fake_output)
dsc_loss = D_loss(real_output, fake_output)
print("Generator loss:", gen_loss.numpy(), "Discriminator loss:", dsc_loss.numpy())
검증 loss 확인
# 학습 함수
def train(dataset, epochs):
for epoch in range(epochs):
start = time.time()
for i, image_batch in enumerate(dataset):
train_step(image_batch)
if i == 0:
test_step(image_batch)
print ('Time for epoch {} is {} sec'.format(epoch + 1, time.time()-start))
훈련 한 스텝당 배치사이즈만큼 훈련 데이터를 가져와 학습시키고 한번씩 검증합니다.
%%time
train(train_dataset, EPOCHS)
import matplotlib.pyplot as plt
noises = tf.random.normal([50, 100]) # 50장
generated_image = G(noises, training=False)
plt.imshow(generated_image[0], cmap='gray')
노이즈로부터 생성된 첫번째 가짜 샘플 확인
<참조>
01 Generative Adversarial Networks
[알기쉬운 AI - 31] GAN (Generative Adversarial Networks)
생성모델(Generative model)이란 무엇일까?
Background: What is a Generative Model? | Generative Adversarial Networks | Google Developers
01 Generative Adversarial Networks
Generative Adversarial Nets; GAN (NIPS'14) 논문리뷰
The math behind GANs (Generative Adversarial Networks)
The Mathematics behind the Generative Adversarial Networks (GANs)
Generative Adversarial Nets; GAN (NIPS'14) 논문리뷰
What is the difference between a generative and a discriminative algorithm?
모델 capacity 코딩새내기 일상일지 • github blog
'인공지능 > computer vision' 카테고리의 다른 글
styleGAN 이해하기 (0) | 2022.05.31 |
---|---|
PGGAN의 공식 코드 살펴보기 (0) | 2022.05.31 |
PGGAN 이해하기 (0) | 2022.05.31 |
DCGAN 이해하기 (0) | 2022.05.16 |
distinctive image features from scale-invariant keypoints (0) | 2022.05.11 |