for textmining

generative model 응용 사례

|

이번 글에서는 Generative model, 특히 Generative Adversarial Network(GAN)의 다양한 응용 연구들에 대해 살펴보도록 하겠습니다. 이 글은 전인수 서울대 박사과정이 2017년 12월에 진행한 패스트캠퍼스 강의와 위키피디아 등을 정리했음을 먼저 밝힙니다. PyTorch 코드는 이곳을 참고하였습니다. GAN과 관련해서는 이곳을 참고하시면 좋을 것 같습니다. 그럼 시작하겠습니다.

EnhanceNet

EnhanceNet은 GAN의 손실함수를 적용해 Super Resolution 기법의 성능을 높였습니다. Super Resolution(SR)이란 아래 그림처럼 저해상도의 이미지/영상을 고해상도로 변환하는 작업을 가리킵니다.

EnhanceNet이 SR 문제에 GAN 구조를 적용한 아이디어는 이렇습니다. 생성자 $G$의 인풋으로 노이즈 대신 저해상도의 이미지를 입력합니다. 판별자 $D$는 $G$가 생성한 가짜 고해상도 이미지와 실제 고해상도 이미지를 구분하는 역할을 합니다. $G$는 $D$를 속이도록 고해상도 이미지를 생성하도록 학습됩니다. 이처럼 EnhanceNet은 어떤 task이든 GAN 손실함수를 사용하여 원하는 결과를 얻어낼 수 있다는 점에서 눈길을 끕니다.

GAN 손실함수의 효과를 직관적으로 나타낸 그림은 아래와 같습니다. 원본 고해상도 이미지 $I_{HR}$에서 인위적으로 해상도를 낮춘 이미지 $I_{LR}$이 있다고 칩시다. $I_{LR}$을 인풋, $I_{HR}$을 정답으로 놓고 학습시킬 때 평균제곱오차(Mean Squared Error)를 손실함수로 많이들 씁니다. MSE는 아래 그림처럼 입력값과 정답을 평균(average)하려는 성향이 강하다(MSE와 관련해서는 이곳 참고)는 점이 단점입니다. 그런데 GAN 손실함수(adversarial loss)를 썼더니 $I_{HR}$을 제법 잘 근사하는 걸 확인했다고 합니다.

SimGAN

SimGAN의 목적은 현실감 있는 인공데이터를 만들어내는 데 있습니다. 이 때 GAN을 씁니다. 아이디어는 이렇습니다. 기존 GAN의 생성자 역할을 하는 Refiner $R$은 인공데이터를 받아서 실제 데이터로 변환하는 역할을 합니다. 판별자 $D$는 $R$이 생성한 가짜(refined) 데이터와 실제(real) 데이터를 구분하는 역할을 합니다. $R$이 $D$를 속이려고 하는 과정에서 $R$이 학습됩니다. 이 과정을 도식적으로 나타내면 다음 그림과 같습니다.

SimGAN에서는 다른 연구에 참고가 될 만한 몇 가지 기술들이 포함돼 있습니다. 우선 Self-Regularization입니다. $R$을 학습시킬 때 기존 GAN 손실함수만 사용할 경우, $R$이 그저 $D$를 속이는 데만 집중하게 되면서 $R$이 생성한 데이터가 실제 데이터와 완전히 동떨어지게 될 염려가 크기 때문입니다. $R$의 입력값인 인공데이터도 조악하나마 실제 데이터의 특징을 담고 있으므로 $R$의 손실함수에 다음과 같은 regularization 항을 추가했습니다.

[{ L }{ reg }={ \left| \psi \left( R\left( x \right) \right) -\psi \left( x \right) \right| }{ 1 }]

위와 같은 항을 손실함수에 추가하게 되면 $R$에 입력되는 인공데이터 $x$와 $R$이 산출한 데이터 $R(x)$ 사이의 오차가 줄어들게 됩니다. 결과적으로 $R$이 생성하는 데이터가 입력데이터에서 너무 벗어나지 않게 되는 셈이죠. 단 여기에서 $ψ$는 입력값의 feature를 뽑는 함수인데요. 논문에서는 identity mapping을 사용했다고 합니다.

또 한가지 기법은 Local patch-Discriminator입니다. 기존 GAN에서 $D$는 입력데이터 전체를 보고 real/fake 여부를 판별합니다. 이 때문에 $G$는 대개 $D$를 속이기 위해 데이터의 일부 특징을 과장하려는 경향이 있습니다. 이 문제를 완화하기 위해 다음 그림과 같이 $D$가 데이터의 일부 영역만 보도록 하고, 전체적인 판단은 이들 패치의 평균이라든지 가중합이라든지 하는 방식으로 취하도록 했습니다. 그 결과 $D$의 능력을 어느 정도 제한하면서 $R$의 성능을 높일 수 있었다고 합니다.

마지막으로 언급할 부분은 History Buffer입니다. 기존 GAN에선 $D$를 학습시키는 과정에서 최근 학습샘플에만 적합하는 문제가 있었습니다. 이를 continual learning issue 내지 catastrophic forgetting problem이라고도 합니다. 이 문제를 해결하기 위해 도입된 것이 바로 History Buffer입니다. buffer에 예전 학습 데이터를 모아뒀다가 최신 데이터와 함께 학습시키자는 아이디어입니다. 결과적으로 $D$로 하여금 $R$이 예전에 생성한 데이터를 지속적으로 기억할 수 있게 도와줍니다. SimGAN에서는 아래와 같이 적용됐습니다.

  • 학습 도중 $R$이 생성한 데이터가 가운데 랜덤하게 반을 선택하여 buffer에 넣는다.
  • 각 step에서 buffer에서 랜덤하게 선택한 반과 $R$이 현재 생성한 데이터 반을 $D$에 전달한다.

Pix2Pix

Pix2Pix는 다음과 같이 image to image translation을 하는 데 GAN을 접목한 연구입니다.

아키텍처는 다음과 같습니다. 우선 $G$는 소스 도메인의 데이터를 입력 받아 타겟 도메인의 데이터를 생성합니다. $G$는 소스 도메인의 데이터와 생성된 타겟 도메인의 데이터를 쌍으로 묶어 $D$에 전달합니다. $D$는 $G$가 생성한 가짜 데이터 pair와 실제 데이터 pair를 구분합니다. $G$는 $D$를 속이도록 하는 과정에서 데이터를 더 잘 생성하게 됩니다.

$G$는 다음과 같이 오토인코더(autoencoder)에 Unet을 결합한 아키텍처를 썼다고 합니다. encoder, decoder 사이에 발생할 수 있는 정보손실을 skip-connection을 이용해 완화한 겁니다. Pix2Pix 역시 SimGAN의 Local Patch Disciriminator를 사용했다고 합니다.

CycleGAN

Pix2Pix의 단점은 학습데이터가 항상 pair로 존재해야 한다는 겁니다. CycleGAN은 이러한 문제를 해결하기 위해 제안됐습니다. 이를 도식적으로 나타낸 그림은 다음과 같습니다.

CycleGAN의 기본 프레임워크는 다음과 같습니다. 두 개의 생성자 $G$, $F$와 두 개의 판별자 $D_X$, $D_Y$를 씁니다. $G$는 $X$ 도메인의 데이터를 $Y$ 도메인으로 변환하는 역할을 합니다. $F$는 $Y$ 도메인의 데이터를 $X$ 도메인으로 변환합니다. $D_X$는 $F$가 생성한 가짜 데이터와 $X$ 도메인의 실제 데이터를 구분합니다. $D_Y$는 $G$가 생성한 가짜 데이터와 $Y$ 도메인의 실제 데이터를 구분합니다. $G$와 $F$는 반대 도메인의 구분자를 속이도록 적대적으로 학습됩니다. 이렇게 학습이 진행되면서 굳이 데이터가 pair 형태로 존재하지 않아도 됩니다.

CycleGAN에는 기존 GAN loss 이외에 cycle-consitency loss라는 것이 추가됐습니다. 아래 그림처럼 도메인을 변경했다가 다시 돌아왔을 때 모습이 원래 입력값과 비슷한 형태가 되도록 regularization을 걸어주는 것입니다. 이렇게 되면 도메인을 넘나들 때 더욱 현실감 있는 데이터가 생성될 것입니다.

CycleGAN을 PyTorch로 구현한 코드를 살펴보겠습니다. 우선 두 개의 $G$와 두 개의 $D$를 정의합니다. (일반적인 generator, discriminator 사용)

# Generator arguments : input_dim, num_filter, output_dim, num_resnet
G_A = Generator(3, params.ngf, 3, params.num_resnet) 
G_B = Generator(3, params.ngf, 3, params.num_resnet)
# Discriminator arguments : input_dim, num_filter, output_dim
D_A = Discriminator(3, params.ndf, 1) 
D_B = Discriminator(3, params.ndf, 1) 

$G$의 loss를 구하는 코드는 다음과 같습니다.

# A -> B
fake_B = G_A(real_A)
D_B_fake_decision = D_B(fake_B)
G_A_loss = MSE_loss(D_B_fake_decision, Variable(torch.ones(D_B_fake_decision.size())))

# forward cycle loss
recon_A = G_B(fake_B)
cycle_A_loss = L1_loss(recon_A, real_A) * params.lambdaA

# B -> A
fake_A = G_B(real_B)
D_A_fake_decision = D_A(fake_A)
G_B_loss = MSE_loss(D_A_fake_decision, Variable(torch.ones(D_A_fake_decision.size())))

# backward cycle loss
recon_B = G_A(fake_A)
cycle_B_loss = L1_loss(recon_B, real_B) * params.lambdaB

# Back propagation
G_loss = G_A_loss + G_B_loss + cycle_A_loss + cycle_B_loss

$D$의 loss를 구하는 코드는 다음과 같습니다.

# Train discriminator D_A
D_A_real_decision = D_A(real_A)
D_A_real_loss = MSE_loss(D_A_real_decision, Variable(torch.ones(D_A_real_decision.size())))
fake_A = fake_A_pool.query(fake_A)
D_A_fake_decision = D_A(fake_A)
D_A_fake_loss = MSE_loss(D_A_fake_decision, Variable(torch.zeros(D_A_fake_decision.size())))
D_A_loss = (D_A_real_loss + D_A_fake_loss) * 0.5

# Train discriminator D_B
D_B_real_decision = D_B(real_B)
D_B_real_loss = MSE_loss(D_B_real_decision, Variable(torch.ones(D_B_real_decision.size())))
fake_B = fake_B_pool.query(fake_B)
D_B_fake_decision = D_B(fake_B)
D_B_fake_loss = MSE_loss(D_B_fake_decision, Variable(torch.zeros(D_B_fake_decision.size())))
D_B_loss = (D_B_real_loss + D_B_fake_loss) * 0.5

StarGAN

StarGAN은 CycleGAN의 단점을 보완한 연구입니다. CycleGAN은 도메인 수가 늘어나게 되면 필요한 $G$의 수가 기하급수적으로 증가하고, $D$는 선형적으로 증가합니다. 아래 그림의 (a)의 경우 도메인 수가 4개가 되자, $G$는 12개, $D$는 4개가 필요한 것을 확인할 수 있습니다. 반면 StarGAN은 (b)와 같이 생겼습니다. $D$와 $G$는 각각 하나만 있으면 됩니다.

StarGAN의 학습 방식을 저자가 든 예시 기준으로 설명해 보겠습니다. CelebA 데이터셋과 RaFD 데이터 셋이 있고 각각의 레이블은 아래 그림과 같이 주황색, 녹색 박스라고 칩시다. Mask Vector는 모델이 현재 다루는 데이터가 CelebA에 속하는지, RaFD에 속하는지 나타내는 one-hot-vector입니다.

우선 (b)의 $G$에 입력되는 데이터를 보겠습니다. 이 데이터는 검은색 머리의 젊은 여성인데요, 타겟 도메인을 검은색 머리의 젊은 남자로 설정하였습니다. $G$의 입력데이터는 소스 도메인의 원래 이미지, 레이블 벡터, Mask Vector 셋을 합친 형태입니다. 어쨌든 $G$는 이 데이터를 검은색 머리의 젊은 남자로 변환해야 합니다. 이 데이터를 (d)의 $D$에 보내서 $D$가 실제 데이터로 구분하도록, 그리고 검은색 머리의 젊은 남자($[1,0,0,1,1]$)로 분류하도록 속여야 하니까요. $G$는 $D$를 잘 속일 수 있도록 학습됩니다.

(b)의 $G$가 생성한 데이터는 다시 $G$에 보내집니다. 이번에 $G$에 입력되는 데이터는 방금 전 $G$가 생성한 타겟 도메인의 이미지, 원 데이터의 레이블 벡터(검은색 머리의 젊은 여성, $[0,0,1,0,1]$), Mask Vector 셋을 합친 형태입니다. $G$는 이렇게 만든 데이터가 원래 이미지와 유사하도록 학습됩니다. 이는 CycleGAN의 cycle-consitency loss를 그대로 차용했습니다. 결과적으로 $G$는 하나의 이미지 데이터로 $D$를 속이는 과정에서, cycle-consitency loss를 줄이는 과정에서 두 번 학습되는 셈이죠. StarGAN의 목적함수는 다음과 같습니다.

Anormaly detection with GAN

GAN을 이상치 탐지, 즉 Novelty Detection에 활용한 연구도 있습니다. GAN은 $G$가 노이즈 $z$를 받아 현실감 있는 데이터를 생성하도록 학습되는데요. $G$가 생성한 데이터를 역추적해 latent space를 분석하면 이 latent space 내에서 특정 영역이 이상치에 해당할 것이라는 전제가 깔려 있습니다. 이를 도식적으로 나타낸 그림은 다음과 같습니다.

TripleGAN

TripleGAN은 GAN으로 새롭게 생성한 데이터를 활용해 성능이 좋은 분류기(classifier)를 만들어내는 게 목표입니다. 아키텍처와 목적함수를 도식화한 그림은 다음과 같습니다.

차근차근 살펴보겠습니다. 우선 생성자 $G$는 노이즈 $z$를 받아서 인공데이터 $x$를 산출하고 $y$는 실제 데이터로부터 샘플링합니다. $G$가 만든 데이터는 판별자 $D$와 분류기 $C$로 보내집니다. $D$는 이 데이터가 진짜인지 가짜인지 구분하며, $C$는 $G$가 만든 $x$를 입력으로 하고 역시 $G$가 만든 $y$를 출력으로 하는 classification task를 수행합니다.

레이블이 있는 실제 데이터($x_l, y_l$)는 분류기 $C$를 학습하는 데 사용됩니다(supervised learning). 이 데이터는 $D$에도 들어가 $D$가 진짜인지 가짜인지 구분하도록 학습됩니다. 마지막으로 레이블이 없는 실제 데이터($x_c$)가 분류기 $C$에 입력되면 $C$는 이 데이터의 레이블을 예측합니다. $C$가 잘 학습되어 있다면 $x_c$ 역시 레이블이 있는 데이터처럼 역할을 하게 되겠지요. 어쨌든 이렇게 레이블이 추가된 $x_c$ 역시 $D$에 입력돼 진짜/가짜 여부를 판별합니다.

TripleGAN에서는 $G$가 $D$를 속이는 과정에서 생성한 데이터가 $C$ 학습에 활용돼 $C$의 성능을 높일 수 있습니다. 뿐만 아니라 $C$는 레이블이 없는 데이터 $x_c$의 레이블을 예측할 때 $D$가 가짜라고 구분하지 않도록 그럴듯한 레이블을 달도록 학습이 진행됩니다. 결과적으로 TripleGAN의 학습이 잘 되면 기존 데이터만 가지고 학습한 것보다 성능이 좋은 분류기 $C$가 도출되리라고 기대할 수 있습니다.

Comment  Read more

Normalizing Flow

|

이번 글에서는 Normalizaing Flow(NF) 개념에 대해 살펴보도록 하겠습니다. 이 글은 전인수 서울대 박사과정이 2017년 12월에 진행한 패스트캠퍼스 강의와 위키피디아 등을 정리했음을 먼저 밝힙니다.

목적

변분추론(Variational Inference)의 목적은 계산이 어려운 사후확률 분포 $p(z$|$x)$를 계산이 쉬운 $q(z$|$x)$로 근사하는 것입니다. 우리는 evidence인 $p(x)$를 최대화하는 모델, 즉 데이터 $x$의 분포를 잘 설명하는 확률모형을 학습시키고자 합니다. 몇 가지 수식 정리 과정을 거치면 Evidence Lower Bound(ELBO)를 다음과 같이 도출할 수 있습니다. (수식 유도 과정에 대해서는 이곳 참고)

[ELBO={ E }_{ z\sim q\left( z x\right) }\left[ \log { p(x z) } \right] -{ D }_{ KL }\left( q\left( z x\right)   p\left( z x\right) \right)]

Variational AutoEncoder(VAE)에서는 근사 대상 확률함수 $q$를 다음과 같이 정의합니다.

[q\left( z x \right) =N\left( { \mu }{q}\left( x \right) ,\Sigma{q} \left( x \right) \right)]

VAE에서 사후확률분포 $q$를 위와 같이 정한 이유는 샘플링과 계산의 용이성 때문입니다. 그런데 이보다 더 복잡한 형태의 사후확률분포를 만들어낼 수는 없을까요? 예컨대 $q$에서 뽑은 잠재변수를 $z_0$라 둡시다. 그런데 VAE는 $q$를 단순한 가우시안 분포로 가정하기 하기 때문에 아래와 같이 $z_0$에 특정한 형태의 함수 $f_k$들을 반복 적용하여 모델이 더욱 복잡한 형태의 잠재변수를 표현하게끔 만들어주자는 것입니다.

[{ z }{ K }={ f }{ K }\circ …\circ { f }{ 2 }\circ { f }{ 1 }\left( { z }_{ 0 } \right)]

이것이 바로 NF가 노리는 바입니다.

Change of Variables

$z’=f(z)$일 때 역함수가 존재하는 $f$와 임의의 확률분포 $q(z)$에 대해 다음 공식이 성립한다고 합니다.

[q\left( z’ \right) =q\left( z \right) \left det\frac { \partial { f }^{ -1 } }{ \partial z’ } \right =q\left( z \right) { \left det\frac { \partial f }{ \partial z } \right }^{ -1 }]

따라서 $f$를 $K$번 적용한 $z_K$의 로그확률 $q_K(z_K)$값을 다음과 같이 표현할 수 있다고 합니다.

[\log { { q }{ K }\left( { z }{ K } \right) } =\log { { q }{ 0 }\left( { z }{ 0 } \right) } -\sum _{ k=1 }^{ K }{ \log { det\left \frac { \partial { f }{ k } }{ \partial { z }{ k } } \right } }]

Normalizing Flow

그런데 이때 특정 함수 $f$를 사용하면 위 식의 행렬식(determinant) 부분을 쉽게 계산할 수 있다고 합니다. 그 종류는 다음과 같습니다.

$f$를 planar로 정했다고 칩시다. 이 경우 행렬식 부분은 다음과 같습니다.

[det\left \frac { \partial { f }{ k } }{ \partial { z }{ k } } \right =\left 1+{ u }^{ T }\psi \left( { z }_{ k } \right) \right \,where\quad \psi \left( { z }_{ k } \right) =h’\left( { w }^{ T }z+b \right) w]

$K$를 키울 수록 잠재변수를 더 복잡하게 모델링할 수 있습니다. 예컨대 다음과 같습니다.

Comment  Read more

Advanced VAEs

|

이번 글에서는 Variational AutoEncoder(VAE)의 발전된 모델들에 대해 살펴보도록 하겠습니다. 이 글은 전인수 서울대 박사과정이 2017년 12월에 진행한 패스트캠퍼스 강의와 위키피디아 등을 정리했음을 먼저 밝힙니다. PyTorch 코드는 이곳을 참고하였습니다. VAE의 기본적 내용에 대해서는 이곳을 참고하시면 좋을 것 같습니다. 그럼 시작하겠습니다.

Conditional VAE

Conditional VAE(CVAE)란 다음 그림과 같이 기존 VAE 구조를 지도학습(supervised learning)이 가능하도록 바꾼 것입니다. encoderdecoder에 정답 레이블 $y$가 추가된 형태입니다.

encoder에서 $z$를 만들 때 $y$ 정보가 추가됩니다. PyTorch 코드는 다음과 같습니다.

def Q(X, c):
    inputs = torch.cat([X, c], 1)# (X,y)
    z = encoder(inputs)
    z_mu = z[:,:Z_dim]
    z_var = z[:,Z_dim:]
    return z_mu, z_var

decoder에서 $x$를 복원할 때 $y$ 정보가 필요합니다. PyTorch 코드는 다음과 같습니다.

def P(z, c):
    inputs = torch.cat([z, c], 1) # (Z,y)
    x = decoder(inputs)
    return x

CVAE의 손실함수는 $y$의 추가로 수식은 달라지지만, 코드상으로는 기존 VAE와 동일합니다. 마지막 아웃풋에 VAE처럼 $x$만 있기 때문입니다. 다음과 같습니다.

def sample_z(mu, log_var):
    eps = Variable(torch.randn(mb_size, Z_dim))
    return mu + torch.exp(log_var / 2) * eps.cuda()

# Forward
z_mu, z_var = Q(X, c)
z = sample_z(z_mu, z_var)
X_sample = P(z, c)

# Loss
recon_loss = nn.functional.binary_cross_entropy(X_sample, X, size_average=False) / mb_size
kl_loss = torch.mean(0.5 * torch.sum(torch.exp(z_var) + z_mu**2 - 1. - z_var, 1))
loss = recon_loss + kl_loss

학습된 CVAE에 아래 그림과 같이 실제 손으로 3이라고 쓴 그림과 함께 label 정보를 바꿔가며 입력하게 되면 다음과 같이 출력된다고 합니다. 다시 말해 CVAE 모델이 데이터 분포를 학습할 때 범주 정보까지 함께 고려하게 된다는 의미입니다.

Adversarial Autoencoder

Adversarial Autoencoder(AAE)란 VAE에 GAN를 덧입힌 구조입니다. 다음 그림과 같습니다. GAN과 관련 자세한 내용은 이곳을 참고하시면 좋을 것 같습니다.

AAE에서는 기존 VAE 구조가 기존 GAN에서의 생성자(generator) 역할을 합니다. 생성자의 encoder는 데이터 $x$를 받아서 잠재변수 $z$를 샘플링하고, 생성자의 decoder는 이로부터 다시 $x$를 복원합니다. AAE가 기존 VAE와 다른 점은 기존 GAN의 구분자(discriminator) 역할을 하는 네트워크가 추가되었다는 점입니다. 이 구분자는 생성자의 encoder가 샘플링한 가짜 $z$와 $p(z)$로부터 직접 샘플링한 진짜 $z$를 구분하는 역할을 합니다.

이렇게 복잡한 네트워크를 만든 이유는 VAE 특유의 단점 때문입니다. VAE는 사전확률 분포 $p(z)$를 표준정규분포로 가정하고, $q(z$|$x)$를 이와 비슷하게 맞추는 과정에서 학습이 이루어집니다. VAE 아키텍처가 이처럼 구성되어 있는 이유는 표준정규분포와 같이 간단한(?) 확률함수여야 샘플링에 용이하고, KLD 계산을 쉽게 할 수 있기 때문입니다. (자세한 내용은 이곳 참고) 그런데 실제 데이터 분포가 정규분포를 따르지 않거나 이보다 복잡할 경우 VAE 성능에 문제가 발생할 수 있습니다.

그런데 GAN의 경우 모델에 특정 확률분포를 전제할 필요가 없습니다. GAN은 데이터가 어떤 분포를 따르든, 데이터의 실제 분포와 생성자(모델)가 만들어내는 분포 사이의 차이를 줄이도록 학습되기 때문입니다. (자세한 내용은 이곳 참고) VAE의 regularization term을 GAN Loss로 대체할 경우 사전확률과 사후확률 분포를 정규분포 이외에 다른 분포를 쓸 수 있게 돼 모델 선택의 폭이 넓어지는 효과를 누릴 수 있습니다. 어쨌든 개별 데이터 샘플 $x_i$와 사전확률분포 $p(z)$에서 뽑은 $z_i$에 대해 AAE의 학습과정은 다음과 같습니다.

AAE의 PyTorch 코드는 다음과 같습니다. 우선 생성자(encoder, decoder)와 구분자를 정의합니다.

# Encoder
Q = torch.nn.Sequential(
    torch.nn.Linear(X_dim, h_dim),
    torch.nn.ReLU(),
    torch.nn.Linear(h_dim, z_dim))

# Decoder
P = torch.nn.Sequential(
    torch.nn.Linear(z_dim, h_dim),
    torch.nn.ReLU(),
    torch.nn.Linear(h_dim, X_dim),
    torch.nn.Sigmoid())

# Discriminator
D = torch.nn.Sequential(
    torch.nn.Linear(z_dim, h_dim),
    torch.nn.ReLU(),
    torch.nn.Linear(h_dim, 1),
    torch.nn.Sigmoid())

Step1의 reconstruction error를 계산하는 과정은 다음과 같습니다.

""" Reconstruction phase """
z_sample = Q(X)
X_sample = P(z_sample)
recon_loss = nn.binary_cross_entropy(X_sample, X)

Step2의 구분자 학습을 위한 손실을 구하는 과정은 다음과 같습니다. 생성자의 encoder가 샘플링하는 $z$는 가짜, $p(z)$로부터 직접 뽑는 $z$는 진짜라고 레이블을 부여합니다.

# Discriminator
z_real = Variable(torch.randn(mb_size, z_dim))
z_fake = Q(X)
D_real = D(z_real)
D_fake = D(z_fake)
D_loss = -torch.mean(torch.log(D_real) + torch.log(1 - D_fake))

Step3의 생성자 학습을 위한 손실을 구하는 과정은 다음과 같습니다.

# Generator
z_fake = Q(X)
D_fake = D(z_fake)
G_loss = -torch.mean(torch.log(D_fake))

GAN vs VAE

GAN과 VAE의 차이점을 도식적으로 나타낸 표는 다음과 같습니다

Model Optimization Converge Image Quality Generalization 비고
VAE Stochastic Gradient Descent Local Minimum 부드럽고 흐리다 오버피팅 경향이 상대적으로 큼 -
GAN Alternating Stochastic Gradient Descent Saddel points 선명하나 아티팩트가 많다 새로운 영상을 잘 생성해냄 Mode collapsing 문제 발생, 수렴이 어렵다

GAN과 VAE의 장점을 모두 취해 만든 연구로는 Energy-based GAN(EBGAN), Stack GAN 등이 있습니다.

Sketch RNN

Sketch RNN은 VAE에 RNN 구조를 덧입힌 아키텍처입니다. encoderdecoder에 RNN를 썼습니다. 다음 그림과 같습니다.

Comment  Read more

Variational AutoEncoder

|

이번 글에서는 Variational AutoEncoder(VAE)에 대해 살펴보도록 하겠습니다. 이 글은 전인수 서울대 박사과정이 2017년 12월에 진행한 패스트캠퍼스 강의와 위키피디아, 그리고 이곳 등을 정리했음을 먼저 밝힙니다. PyTorch 코드는 이곳을 참고하였습니다. 그럼 시작하겠습니다.

concept

VAE는 데이터가 생성되는 과정, 즉 데이터의 확률분포를 학습하기 위한 두 개의 뉴럴네트워크로 구성되어 있습니다. VAE는 잠재변수(latent variable) $z$를 가정하고 있는데요. 우선 encoder라 불리는 뉴럴네트워크는 관측된 데이터 $x$를 받아서 잠재변수 $z$를 만들어 냅니다. decoder라 불리는 뉴럴네트워크는 encoder가 만든 $z$를 활용해 $x$를 복원해내는 역할을 합니다. VAE 아키텍처는 다음 그림과 같습니다.

그렇다면 여기에서 잠재변수 $z$는 어떤 의미인 걸까요? 고양이 그림 예시를 들어 생각해보겠습니다. 수많은 고양이 사진이 있다고 칩시다. 사람은 고양이 사진들이 저마다 다르게 생겼다 하더라도 이들 사진이 고양이임을 단박에 알아낼 수 있습니다. 사람들은 고양이 사진을 픽셀 단위로 자세하게 보고 고양이라고 판단하는게 아니라, 털 색깔, 눈 모양, 이빨 개수 등 추상화된 특징을 보고 고양이라는 결론을 냅니다.

이를 잠재변수 $z$와 VAE 아키텍처 관점에서 이해해 보자면, encoder는 입력 데이터를 추상화하여 잠재적인 특징을 추출하는 역할, decoder는 이러한 잠재적인 특징을 바탕으로 원 데이터로 복원하는 역할을 한다고 해석해볼 수 있겠습니다. 실제로 잘 학습된 VAE는 임의의 $z$값을 decoder에 넣으면 다양한 데이터를 생성할 수 있다고 합니다.

latent vector 만들기

VAE의 decoder 파트는 다음과 같이 정규분포를 전제로 하고 있습니다. 다시 말해 encoder가 만들어낸 $z$의 평균과 분산을 모수로 하는 정규분포입니다.

[p\left( { x } { z } \right) =N\left( { x } { { f }{ \mu }\left( z \right) },{ { f }{ \sigma }\left( z \right) }^{ 2 }\times I \right)]

최대우도추정(MLE) 방식으로 VAE 모델의 파라메터를 추정하려면 다음과 같이 정의된 marginal log-likelihood $\log{p(x)}$를 최대화하면 됩니다. 아래 식을 최대화하면 모델이 데이터를 그럴싸하게 설명할 수 있게 됩니다.

[\log { p\left( x \right) } =\log { \sum _{ z }^{ }{ p\left( { x } { { { f }{ \mu }\left( z \right) },{ { f }{ \sigma }\left( z \right) }^{ 2 }\times I } \right) } p\left( z \right) }]

위 식은 최적화하기 어렵습니다. $z$는 무수히 많은 경우가 존재할 수 있는데 가능한 모든 $z$에 대해서 고려해야 하기 때문입니다. 이럴 때 써먹는 것이 변분추론(Variational Inference)입니다. 변분추론은 계산이 어려운 확률분포를, 다루기 쉬운 분포 $q(z)$로 근사하는 방법입니다. 한편 $p(x)$는 베이즈 정리에서 evidence라고 이름이 붙여진 항인데요. 몇 가지 수식 유도 과정을 거치면 evidence의 하한(ELBO)을 다음과 같이 구할 수 있습니다.

[\log { p\left( x \right) } \ge { E }_{ z\sim q\left( z \right) }\left[ \log { p(x z) } \right] -{ D }_{ KL }\left( q\left( z \right)   p\left( z \right) \right)]

계산이 쉬운 위 부등식 우변, 즉 ELBO를 최대화하면 $\log{p(x)}$를 최대화할 수 있을 것입니다. 일반적인 변분추론에서는 $q(z)$를 정규분포로 정합니다. 예컨대 다음과 같습니다.

[q\left( z \right) =N\left( { \mu }{ q },{ \sigma }{ q }^{2} \right)]

그런데 데이터 $x$가 고차원일 때는 $q$를 위와 같이 정하게 되면 학습이 대단히 어렵다고 합니다. 그도 그럴 것이 모든 데이터에 대해 동일한 평균과 분산, 즉 단 하나의 정규분포를 가정하게 되는 셈이니, 데이터가 복잡한 데 비해 모델이 너무 단순하기 때문인 것 같습니다. VAE에서는 이 문제를 해결하기 위해 $q$의 파라메터를 $x$에 대한 함수로 둡니다. 다음과 같습니다.

[q\left( z x \right) =N\left( { \mu }{q}\left( x \right) ,\Sigma{q} \left( x \right) \right)]

$q$를 위와 같이 설정하고 ELBO를 최대화하는 방향으로 $q$를 잘 학습하면, $x$가 달라질 때마다 $q$의 분포도 계속 달라지게 됩니다. $x$에 따라 $q$의 모수(평균, 분산)가 바뀌게 되니까요. VAE의 encoder에는, $x$를 받아서 $z$의 평균과 분산을 만들어내는 뉴럴네트워크 두 개($f_μ$, $f_σ$)가 포함되어 있습니다. 이 덕분에 복잡한 데이터에 대해서도 모델이 적절하게 대응할 수 있게 됩니다.

어쨌든 노이즈를 zero-mean Gaussian에서 하나 뽑아 $f_μ$, $f_σ$가 산출한 평균과 분산을 더하고 곱해줘서 sampled latent vector $z$를 만듭니다. 수식은 다음과 같으며, 이같은 과정을 reparameterization trick이라고 부릅니다. 다시 말해 $z$를 직접 샘플링하는게 아니고 노이즈를 샘플링하는 방식입니다. 이렇게 되면 역전파를 통해 encoder가 산출하면 평균과 분산을 업데이트할 수 있게 됩니다.

[z={ \mu }{ (x) }+{ \sigma }{ (x) } \times\epsilon ,\quad \epsilon \sim N\left( 0,1 \right)]

VAE는 코드로 보는 것이 훨씬 잘 이해가 되는데요. 지금까지 설명한 내용이 아래 코드에 함축돼 있습니다(pytorch).

def __init__(self):
    self.fc1_1 = nn.Linear(784, hidden_size)
    self.fc1_2 = nn.Linear(784, hidden_size)
    self.relu = nn.ReLU()
                        
def encode(self,x):
    x = x.view(batch_size,-1)
    mu = self.relu(self.fc1_1(x))
    log_var = self.relu(self.fc1_2(x))
    return mu,log_var
    
def reparametrize(self, mu, logvar):
    std = logvar.mul(0.5).exp_()
    eps = torch.FloatTensor(std.size()).normal_()
    return eps.mul(std).add_(mu)

VAE는 latent vector $z$를 위와 같이 만들기 때문에 데이터 $x$가 동일하다 하더라도 $z$는 얼마든지 달라질 수 있고, decoder의 최종 결과물 역시 변종이 발생할 가능성이 있습니다. $z$ 생성 과정에 zero-mean Gaussian으로 뽑은 노이즈가 개입되기 때문입니다. 데이터 $x$를 넣어 다시 $x$가 출력되는 구조의 autoencoder이지만, 맨 앞에 variational이 붙은 이유가 바로 여기에 있는 것 같습니다.

VAE의 목적함수

VAE의 decoder는 데이터의 사후확률 $p(z$|$x)$를 학습합니다. 하지만 사후확률은 계산이 어렵기 때문에 다루기 쉬운 분포 $q(z)$로 근사하는 변분추론 기법을 적용하게 됩니다. 변분추론은 $p(z$|$x)$와 $q(z)$ 사이의 KL Divergence를 계산하고, KLD가 줄어드는 쪽으로 $q(z)$를 조금씩 업데이트해서 $q(z)$를 얻어냅니다. KLD 식을 조금 변형하면 다음과 같은 식을 유도할 수 있습니다. (자세한 내용은 이곳을 참고하시면 좋을 것 같습니다)

[{ D }_{ KL }\left( q\left( z \right)   p\left( z x \right) \right) ={ D }_{ KL }\left( q\left( z \right)   p\left( z \right) \right) +\log { p\left( x \right) } -{ E }_{ z\sim q\left( z \right) }\left[ \log { p(x z) } \right]]

그런데 우리는 전 챕터에서 $q$를 정규분포로 두고, $q$의 평균과 분산을 $x$에 대한 함수로 정의한 바 있습니다. 이를 반영하고, evidence인 $\log{p(x)}$ 중심으로 식을 다시 쓰면 아래와 같습니다. 이 식을 $A$라고 두겠습니다.

[\begin{align} \log { p\left( x \right) } =&{ E }_{ z\sim q\left( z|x \right) }\left[ \log { p(x|z) } \right] -{ D }_{ KL }\left( q\left( z|x \right) ||p\left( z \right) \right) +{ D }_{ KL }\left( q\left( z|x \right) ||p\left( z|x \right) \right)\=&ELBO+{ D }_{ KL }\left( q\left( z|x \right) ||p\left( z|x \right) \right) \end{align}]

동일한 확률변수에 대한 KLD 값(위 식 우변 세번째 항)은 항상 양수이므로 아래와 같은 부등식이 항상 성립합니다. 아래 식을 $B$라고 두겠습니다.

[\log { p\left( x \right) } \ge { E }_{ z\sim q\left( z x \right) }\left[ \log { p(x z) } \right] -{ D }_{ KL }\left( q\left( z x \right)   p\left( z \right) \right)=ELBO]

따라서 위 부등식 우변, 즉 ELBO를 최대화하면 우리의 목적인 marginal log-likelihood $\log{p(x)}$를 최대화할 수 있게 됩니다. 아울러 $A$와 $B$를 비교하면서 보면 ELBO를 최대화한다는 것은 $q(z$|$x)$와 $p(z$|$x)$ 사이의 KLD를 최소화하는 의미가 됩니다.

딥러닝 모델은 보통 손실함수를 목적함수로 쓰는 경향이 있으므로 위 부등식의 우변에 음수를 곱한 식이 loss function이 되고, 이 함수를 최소화하는 게 학습 목표가 됩니다. 손실함수는 다음과 같습니다.

[L=-{ E }_{ z\sim q\left( z x \right) }\left[ \log { p(x z) } \right] +{ D }_{ KL }\left( q\left( z x \right)   p\left( z \right) \right)]

위 식 우변 첫번째 항은 reconstruction loss에 해당합니다. encoder가 데이터 $x$를 받아서 $q$로부터 $z$를 뽑습니다. decoderencoder가 만든 $z$를 받아서 원 데이터 $x$를 복원합니다. 위 식 우변 첫번째 항은 이 둘 사이의 크로스 엔트로피를 가리킵니다.

위 식 우변 두번째 항은 KL Divergence Regularizer에 해당합니다. VAE는 $z$가 zero-mean Gaussian이라고 가정합니다. 정규분포끼리의 KLD는 분석적인 방식으로 도출 가능합니다. 계산이 쉽다는 말이지요. $q(z$|$x)$를 각 변수가 독립인 다변량 정규분포로 볼 경우 위 식 우변 두번째 항을 다음과 같이 다시 쓸 수 있습니다.

[\begin{align} { D }_{ KL }\left( q\left( z|x \right) ||p\left( z \right) \right) =&{ D }_{ KL }\left[ N\left( { \left( { \mu }_{ 1 },…,{ \mu }_{ k } \right) }^{ T }, \textrm{diag}\left( { \sigma }_{ 1 }^{ 2 },…,{ \sigma }_{ k }^{ 2 } \right) \right) ||N\left( 0,1 \right) \right] \ =&\frac { 1 }{ 2 } \sum _{ i=1 }^{ }{ \left( { \sigma }_{ i }^{ 2 }+{ \mu }_{ i }^{ 2 }-\ln { \left( { \sigma }_{ i }^{ 2 } \right) } -1 \right) } \end{align}]

위 식 우변 두번째 항을 최소화한다는 말은 $q$를 zero-mean Gaussian에 가깝게 만든다는 의미입니다. 지금까지 말씀드린 내용을 종합해 VAE의 손실함수를 도식적으로 나타내면 다음과 같습니다.

KLD를 아래처럼 분해해서 다음과 같이 해석하는 것도 가능합니다.

VAE의 목적함수를 PyTorch 코드로 구현한 결과는 다음과 같습니다.

# the Binary Cross Entropy between the target and the output
reconstruction_function = nn.BCELoss(size_average=False)

def loss_function(recon_x, x, mu, logvar):
    BCE = reconstruction_function(recon_x, x)
    KLD_element = mu.pow(2).add_(logvar.exp()).mul_(-1).add_(1).add_(logvar)
    KLD = torch.sum(KLD_element).mul_(-0.5)
    return BCE + KLD

VAE의 장단점

VAE는 GAN에 비해 학습이 안정적인 편이라고 합니다. 손실함수에서 확인할 수 있듯 reconstruction error과 같이 평가 기준이 명확하기 때문입니다. 아울러 데이터뿐 아니라 데이터에 내재한 잠재변수 $z$도 함께 학습할 수 있다는 장점이 있습니다(feature learning). 하지만 출력이 선명하지 않고 평균값 형태로 표시되는 문제, reparameterization trick이 모든 경우에 적용되지 않는 문제 등이 단점으로 꼽힌다고 합니다.

Comment  Read more

KKT 조건

|

이번 글에서는 KKT 조건을 살펴보도록 하겠습니다. 이 글은 미국 카네기멜런대학 강의를 기본으로 하되 영문 위키피디아 또한 참고하였습니다. 그럼 시작하겠습니다.

concept

Karush-Kuhn-Tucker 조건은 primal, dual solution과의 관계에서 도출된 조건인데요. 최적화 이론과 실제 구현에서 핵심적인 역할을 합니다. duality와 관련해서는 이곳을 참고하시면 좋을 것 같습니다. 어쨌든 KKT 조건의 구체적인 내용은 다음과 같습니다.

Necessity

다음과 같은 명제가 성립합니다. primal, dual, duality gap, lagrange dual function 등 개념과 관련해서는 이곳을 참고하시면 좋을 것 같습니다.

zero duality라면 $f^*=g$입니다. 그런데 $x$-star는 primal problem의 목적함수 $f$의 solution입니다. 또한 $u$-star, $v$-star는 dual problem의 목적함수 $g$의 solution입니다. 따라서 다음 식처럼 쓸 수 있습니다.

[f\left( { x }^{ * } \right) =g\left( { u }^{ * },{ v }^{ * } \right)]

위 식 우변을 라그랑지안 듀얼 함수 $g$의 정의대로 쓰면 다음 식과 같습니다.

[g\left( { u }^{ * },{ v }^{ * } \right) =\min _{ x }{ L\left( x,{ u }^{ * },{ v }^{ * } \right) }]

$L$은 우리가 구하려는 미지수 $x$로 편미분한 식을 0으로 만드는 지점에서 최소값을 지닙니다. 그런데 전제조건에서 이미 언급했듯 primal problem의 해는 $x$-star이기 때문에 $L$을 $x$로 편미분한 결과를 0으로 만드는 지점은 바로 $x$-star가 될 것입니다. 이는 KKT 조건의 stationary condition을 만족한다는 이야기입니다. 이를 식으로 쓰면 다음과 같습니다.

\(\min _{ x }{ L\left( x,{ u }^{ * },{ v }^{ * } \right) } =L\left( { x }^{ * },{ u }^{ * },{ v }^{ * } \right)\) primal problem과 라그랑지 함수 $L$, 라그랑지안 듀얼 함수 $g$ 사이에는 다음 부등식이 성립합니다. (왜 아래 식이 도출되는지는 이곳 참고)

[f\left( { x }^{ * } \right) \ge \min _{ x\in C }{ L\left( x,u,v \right) } \ge \min _{ x }{ L\left( x,u,v \right) } =g\left( u,v \right)]

이를 우리가 들여다보던 식에 맞춰서 다시 적으면 다음 식을 유도할 수 있습니다.

[\begin{align} &L\left( { x }^{ * },{ u }^{ * },{ v }^{ * } \right) \ &\le f\left( { x }^{ * } \right) +\sum _{ i=1 }^{ m }{ { u }_{ i }^{ * }{ h }_{ i }\left( { x }^{ * } \right) } +\sum _{ j=1 }^{ r }{ { v }_{ j }^{ * }l_{ j }\left( { x }^{ * } \right) } \ &\le f\left( { x }^{ * } \right) \end{align}]

결과적으로 처음 항, 즉 $f(x^*)$가 마지막에 다시 유도된 걸 확인할 수 있습니다. 다시 말해 우리가 논의하고 있는 명제의 전제조건을 만족할 경우 위 모든 부등식이 사실상 등식과 같다는 이야기입니다. 이 경우 다음 식이 모두 0이 되어야 등식을 만족하게 됩니다. 그렇다면 모든 식에 등호가 붙으려면 다음 두 개 항이 모두 0이 되어야 함을 확인할 수 있습니다.

[\begin{align} &A:\sum _{ i=1 }^{ m }{ { u }_{ i }^{ * }{ h }_{ i }\left( { x }^{ * } \right) } =0\ &B:\sum _{ j=1 }^{ r }{ { v }_{ j }^{ * }l_{ j }\left( { x }^{ * } \right) } =0 \end{align}]

그런데 primal problem를 다시 살펴보면 $l_j$는 모두 0이기 때문에 $B$에 관련된 항들은 본래 모두 0입니다. 문제는 $A$인데요. $A$의 경우에도 모두 0이 되어야 등식을 만족합니다. 다시 말해 KKT 조건의 complementary slackness를 만족한다는 이야기입니다.

따라서 결과적으로 위 명제의 전제조건이 만족된다면 KKT 조건 또한 만족하게 됩니다.

Sufficiency

다음과 같은 명제가 성립합니다.

$g$를 정의대로 적으면 아래 식의 첫 줄이 됩니다. 아울러 $x$-star, $u$-star, $v$-star는 KKT 조건을 만족한다고 했으므로 KKT 조건의 stationarity condition에 의해 $L(x,u^,v^)$는 $x$-star에서 최소값을 가집니다. 따라서 다음이 성립합니다.

[\begin{align} g\left( { u }^{ * },{ v }^{ * } \right) =&\min _{ x }{ L\left( x,{ u }^{ * },{ v }^{ * } \right) } \ =&f\left( { x }^{ * } \right) +\sum _{ i=1 }^{ m }{ { u }_{ i }^{ * }{ h }_{ i }\left( { x }^{ * } \right) } +\sum _{ j=1 }^{ r }{ { v }_{ j }^{ * }l_{ j }\left( { x }^{ * } \right) } \end{align}]

KKT 조건의 complementary slackness에 따라 아래 식 좌변의 두번째 항이 모두 0입니다. primal problem을 다시 살펴보면 $l_j$ 역시 모두 0입니다. 따라서 아래 식이 성립합니다. 바꿔 말해 $x$-star는 primal problem의 해, $u$-star, $v$-star는 dual problem의 해라는 이야기입니다.

[f\left( { x }^{ * } \right) +\sum { i=1 }^{ m }{ { u }{ i }^{ * }{ h }{ i }\left( { x }^{ * } \right) } +\sum _{ j=1 }^{ r }{ { v }{ j }^{ * }l_{ j }\left( { x }^{ * } \right) } =f\left( { x }^{ * } \right)]

정리

지금까지 말씀드린 내용을 정리하면 아래 그림과 같습니다.

strong duality를 만족하는 경우 위 두 명제가 동치 관계를 갖습니다. (strong duality, slater’s condition 등은 이곳 참고)

SVM에 적용

마진(margin) 내 관측치를 허용하는 C-SVM을 기준으로 설명해 보겠습니다. C-SVM과 관련해서는 이곳을 참고하시면 좋을 것 같습니다. 어쨌든 C-SVM의 primal problem은 다음과 같습니다. 아래 제약식을 만족하면서 목적함수를 최소화하는 $w, b, ξ$을 찾아야 합니다.

[\begin{align} &\min _{ w,b,{ \xi } }&&{ \frac { 1 }{ 2 } { \left| w \right| }_{ 2 }^{ 2 }+C\sum _{ i=1 }^{ n }{ { \xi }_{ i } } } \ &subject\quad to\quad &&{ \xi }_{ i }\ge 0,\quad i=1,…,n\ &&&{ y }_{ i }({ w }^{ T }{ x }_{ i }+b)\ge 1-{ \xi }_{ i },\quad i=1,…,n \end{align}]

primal problem을 바탕으로 라그랑지안 함수 $L$을 만듭니다. 제약식에 라그랑지안 승수를 곱해 목적식에 합치면 됩니다. 여기에서 라그랑지안 승수 $α, μ$를 dual variable이라고 합니다. 단 여기에서 $α, μ$는 0 이상의 부등식에 적용되는 라그랑지안 승수이므로 0 이상의 제약을 갖습니다.

\(L(w,b,{ { \xi }_{ i },\alpha }_{ i },{ \mu }_{ i })=\frac { 1 }{ 2 } { \left\| w \right\| }_{ 2 }^{ 2 }+C\sum _{ i=1 }^{ n }{ { \xi }_{ i } } -\sum _{ i=1 }^{ n }{ { \alpha }_{ i }({ y }_{ i }({ w }^{ T }{ x }_{ i }+b)-1+{ \xi }_{ i }) } -\sum _{ i=1 }^{ n }{ { { \mu }_{ i }\xi }_{ i } }\) 여기에서 우리가 사용할 조건은 KKT의 충분조건입니다. $w, b, ξ, α, μ$가 KKT 조건을 만족한다면, $w, b, ξ$는 primal problem의 최적해, $α, μ$는 dual problem의 최적해가 된다는 이야기입니다. 다시 말해 약간 풀기 어려운 primal problem을 풀기 쉬운 dual problem으로 바꿔 풀어도 최적해를 구한다는 점에선 같은 의미라는 뜻이 되는거죠.

KKT 조건 가운데 stationarity 조건은, 최적화하려는 미지수로 편미분한 식이 0이 된다는 조건입니다. 아래 세 개 식을 만족하게끔 $α, μ$를 구하면 stationarity 조건이 클리어됩니다.

[\begin{align} \frac { \partial L }{ \partial w } =0\quad &\rightarrow \quad w=\sum _{ i=1 }^{ n }{ { \alpha }_{ i }{ y }_{ i }{ x }_{ i } } \ \frac { \partial L }{ \partial b } =0\quad &\rightarrow \quad \sum _{ i=1 }^{ n }{ { \alpha }_{ i }{ y }_{ i } } =0\\frac { \partial L }{ \partial { \xi }_{ i } } =0\quad &\rightarrow \quad C-{ \alpha }_{ i }-{ \mu }_{ i }=0 \end{align}]

KKT 조건 가운데 complementary slackness 조건을 클리어하려면 아래 식을 만족해야 합니다.

[\begin{align} { { \mu }_{ i }\xi }_{ i }=0,\quad i=1,…n\ { \alpha }_{ i }({ y }_{ i }({ w }^{ T }{ x }_{ i }+b)-1+{ \xi }_{ i })=0,\quad i=1,…n \end{align}]

dual problem의 목적식은 다음과 같습니다. 즉 stationarity 조건으로 도출된 식을 라그랑지안 함수 $L$에 넣어 정리해준 결과입니다. dual problem의 목적식과 제약식 도출과 관련한 자세한 내용은 이곳을 참고하면 좋을 것 같습니다.

[\begin{align} g({\alpha}_{i},{\mu}_{i})=&{ \min_{w,b,\xi} { L(w,b,{ { \xi }_{ i },\alpha }_{ i },{ \mu }_{ i }) } } \end{align}]

$w, b, ξ, α, μ$가 KKT 조건을 만족하도록 정했기 때문에, dual problem의 최적해인 $α, μ$를 구하는 것만으로도 primal problem을 푸는 것과 같은 효과를 낼 수 있습니다.

Comment  Read more