해당 논문의 paper review : cumulu-s.tistory.com/24

 

2. Auto-Encoding Variational Bayes (VAE) - paper review

두 번째로 다뤄볼 논문은 VAE라는 이름으로 잘 알려진 Auto-Encoding Variational Bayes라는 논문입니다. 너무 유명한 논문이라 다양한 자료들이 많이 있지만, 가급적이면 다른 분들이 가공해서 만든 자료

cumulu-s.tistory.com

 

이번 글에서는 VAE를 실제로 구현한 코드와, 각 코드가 논문에서 어떤 것과 대응되는지를 자세히 알아보겠습니다.

 

 

paper review에서는 수많은 수식들이 나왔지만, 막상 구현에서는 그렇게까지 복잡하지 않습니다.

 

 

그럼 시작해보겠습니다.

 

import torch
import torchvision
import torchvision.transforms as transforms
import torch.nn as nn
import torch.optim as optim
from tqdm.auto import tqdm
from torch.nn import functional as F
import numpy as np
import matplotlib.pyplot as plt
from torch.utils.tensorboard import SummaryWriter
import datetime
import os

먼저, 필요한 패키지들을 불러옵니다.

 

이번에는 pytorch에서 지원하는 tensorboard라는 기능을 사용해서 loss function을 plotting 해보고, 생성되는 이미지들을 살펴보려고 합니다.

 

USE_CUDA = torch.cuda.is_available()
DEVICE = torch.device("cuda" if USE_CUDA else "cpu")
print("사용하는 Device : ", DEVICE)

current_time = datetime.datetime.now() + datetime.timedelta(hours= 9)
current_time = current_time.strftime('%Y-%m-%d-%H:%M')

saved_loc = os.path.join('/content/drive/MyDrive/VAE_Result', current_time)
os.mkdir(saved_loc)

print("저장 위치: ", saved_loc)

writer = SummaryWriter(saved_loc)
EPOCHS = 50
BATCH_SIZE = 200

저번 GAN 코드에서와 달리, 저장 위치를 지정해주는 코드를 추가했습니다.

 

tensorboard를 사용하면, 코드가 돌아가면서 loss나 이미지가 찍히는데 이를 돌릴때마다 결과를 나눠서 가지고 있어야 하기 때문에 폴더를 별도로 만드는 방법을 사용했습니다.

 

datetime.datetime.now()를 치면 현재 시간이 나오는데, Google Colab의 경우는 한국 시간보다 9시간 느리기 때문에 datetime.timedelta(hours = 9)를 통해서 9시간을 더해 실제 한국 시간과 시차를 맞춰줍니다.

 

그리고 .strftime을 사용하면 원하는 형태로 출력이 되도록 만들 수 있습니다.

 

/content/drive/MyDrive/VAE_Result <= 이거는 제가 사용하는 경로이므로, 원하시는 경로로 바꿔서 지정해주시면 됩니다.

 

os.mkdir는 원하는 경로에 폴더를 생성하는 코드입니다. 이렇게 해야 돌릴 때마다 폴더가 새로 만들어지고, 만들어진 폴더에 tensorboard를 만드는데 필요한 데이터들이 저장되게 됩니다.

 

writer = SummaryWriter 를 이용해서 tensorboard를 써주는 객체를 만들어줍니다.

 

# Transformer code
transformer = transforms.Compose([
            transforms.ToTensor()
])


# Loading trainset, testset and trainloader, testloader
trainset = torchvision.datasets.MNIST(root = '/content/drive/MyDrive/MNIST', train = True,
                                        download = True, transform = transformer)

trainloader = torch.utils.data.DataLoader(trainset, batch_size = BATCH_SIZE, shuffle = True, num_workers = 2)


testset = torchvision.datasets.MNIST(root = '/content/drive/MyDrive/MNIST', train = False,
                                        download = True, transform = transformer)

testloader = torch.utils.data.DataLoader(testset, batch_size = BATCH_SIZE, shuffle = True, num_workers = 2)

transformer에는 numpy array를 pytorch tensor로 만들어주는 역할을 하는 transforms.ToTensor()만 투입해줍니다.

 

 

# sample check
sample, label = next(iter(trainloader))

# show grid image
def imshow_grid(img):
    img = torchvision.utils.make_grid(img)
    print(type(img))
    print(img.shape)
    plt.imshow(img.permute(1, 2, 0))
    ax = plt.gca()
    ax.axes.xaxis.set_visible(False)
    ax.axes.yaxis.set_visible(False)
    plt.show()


imshow_grid(sample[0:8])

 

해당 코드는 샘플로 8개의 이미지만 grid 형식으로 만들어서 plotting 하는 코드입니다.

 

 

# VAE model
class VAE(nn.Module):
    def __init__(self, image_size, hidden_size_1, hidden_size_2, latent_size):
        super(VAE, self).__init__()

        self.fc1 = nn.Linear(image_size, hidden_size_1)
        self.fc2 = nn.Linear(hidden_size_1, hidden_size_2)
        self.fc31 = nn.Linear(hidden_size_2, latent_size)
        self.fc32 = nn.Linear(hidden_size_2, latent_size)

        self.fc4 = nn.Linear(latent_size, hidden_size_2)
        self.fc5 = nn.Linear(hidden_size_2, hidden_size_1)
        self.fc6 = nn.Linear(hidden_size_1, image_size)

    def encode(self, x):
        h1 = F.relu(self.fc1(x))
        h2 = F.relu(self.fc2(h1))
        return self.fc31(h2), self.fc32(h2)

    def reparameterize(self, mu, logvar):
        std = torch.exp(0.5 * logvar)
        eps = torch.randn_like(std)
        return mu + std * eps

    def decode(self, z):
        h3 = F.relu(self.fc4(z))
        h4 = F.relu(self.fc5(h3))
        return torch.sigmoid(self.fc6(h4))

    def forward(self, x):
        mu, logvar = self.encode(x.view(-1, 784))
        z = self.reparameterize(mu, logvar)
        return self.decode(z), mu, logvar

 

다음으로는, 가장 핵심이 되는 VAE model에 해당하는 class에 대해서 알아보겠습니다.

 

먼저 논문에서 $p_\phi(z|x)$인 Encoder(혹은 Recognition model)부터 보겠습니다.

 

Encoder는 이미지를 받아서, $\mu$와 $\sigma$를 출력해야 합니다.

 

따라서 이 작업이 self.fc1과 self.fc2, self.fc31 / self.fc32를 통해서 이루어집니다.

 

self.fc1는 이미지를 받아서 hidden space_1만큼으로 출력해내고, self.fc2는 hidden space_1만큼의 데이터를 받아서 hidden space_2만큼의 데이터를 뽑아냅니다. self.fc31와 self.fc32는 hidden space_2만큼의 데이터를 받아서 latent space 차원만큼의 $\mu$와 $\sigma$를 출력해냅니다.

 

이 작업은 encode라는 이름의 함수로 만들어 해당 작업이 이루어지게 코드가 짜져있습니다.

 

다음으로는, $\mu$와 $\sigma$가 주어졌을 때, latent variable $z$를 만들어야 합니다.

 

이 작업은 reparameterization이라는 이름으로 논문에서 소개되었고, 코드상에서는 reparameterize라는 함수로 구현되었습니다.

 

근데 잘 보면, 이 함수는 mu와 logvar를 받는다는 것을 확인할 수 있습니다.

 

갑자기 logvar는 무엇을 의미하는 것일까요?

 

이는 표준편차 값이 음수가 되지 않도록 만들기 위해서, encoder에서 나온 self.fc32의 결괏값을 $log\sigma^2$로 생각합니다. 

 

그래서 변수 이름이 logvar(log 분산)인 것이죠.

 

정리해보면, 이렇게 정리할 수 있습니다.

 

$\sigma = e^{log\sigma} = e^{2log\sigma/2} = e^{log\sigma^2/2} = e^{logvar/2}$가 됩니다.

 

이렇게 만들면, $\sigma$의 값이 $e^{logvar/2}$로 구현되기 때문에, 무조건 양수 값이 되게 됩니다. 

 

다시 reparameterize 함수로 돌아가면, 입력으로 받은 mu와 logvar를 가지고,

 

std는 torch.exp(0.5 * logvar)로 만들어주고, eps는 torch.randn_like(std)로 만들어줍니다.

 

torch.randn_like는 입력과 동일한 모양으로 평균이 0이고 분산이 1인 정규분포에서 숫자를 뽑아 tensor를 만들어줍니다.

 

이를 논문에서 나온 것처럼, mu + std * eps로 구현합니다.

 

논문에서 나온 z = mu + std * eps

이것의 결과를 z라는 이름으로 latent vector로 만들어주고, 이것을 decoding 한 결과와 mu, logvar를 출력으로 내보냅니다.

 

self.decode(z)를 해서 나오는 결과는, torch.sigmoid(self.fc6(h4))이므로 이미지의 사이즈와 동일한 차원을 가지면서 sigmoid를 통과해 0부터 1 사이의 값입니다. 

 

이는 우리가 input으로 투입시킨 이미지를 VAE를 통해서 복원한 이미지라고 볼 수 있습니다.

 

VAE_model = VAE(28*28, 512, 256, 2).to(DEVICE)
optimizer = optim.Adam(VAE_model.parameters(), lr = 1e-3)

다음은 VAE 모델을 VAE_model이라는 이름의 객체로 만들어주고, Adam optimizer를 만들어줍니다.

 

hidden space_1의 수는 512로 잡았고, hidden space_2의 수는 256, latent space의 차원은 2차원으로 지정하였습니다.

 

 

def loss_function(recon_x, x, mu, logvar):
    BCE = F.binary_cross_entropy(recon_x, x.view(-1, 784), reduction = 'sum')
    KLD = -0.5 * torch.sum(1 + logvar - mu.pow(2) - logvar.exp())
    return BCE, KLD

다음 코드는 loss function을 계산하는 코드입니다.

 

먼저 논문에서 나온 loss 값(variational lower bound)을 살펴봅시다.

 

 

첫 번째 term은 KL-Divergence라는 얘기를 했었고, 두 번째 term은 reconstruction error입니다.

 

먼저, 논문의 Appendix B를 통해서 어떻게 이것이 도출되었는지 보겠습니다.

 

prior가 $p_\theta(z) = N(0, I)$를 만족시키고, posterior approximation이 $q_\phi(z|x^{(i)})$가 Gaussian이면 이렇게 풀 수 있다고 합니다.

 

맨 마지막을 보시면, - KL term을 한 결과가 바로 1/2로 시작하는 식임을 확인할 수 있습니다.

 

KL Divergence는 항상 양수이므로, - KL term은 항상 음수가 될 것입니다.

 

loss를 계산하고자 하는 것이므로, 실제 코드에서는 여기에 - 를 곱해주는 식으로 계산한 것으로 보입니다.

 

따라서 코드에서는 - 0.5 * torch.sum(1 + logvar - mu.pow(2) -logvar.exp())로 계산됩니다.

 

 

 

 

두 번째 reconstruction error항은 어떻게 구성되는지 Appendix C를 보면 알 수 있습니다.

 

우리 모델의 경우, decoder를 통해서 나오는 값이 sigmoid activation function을 통과해서 나오기 때문에 Bernoulli distribution을 따른다고 생각할 수 있습니다.

 

논문의 Appendix C에서는, $p_\theta(x|z)$가 multivariate Bernoulli 분포일 때, 다음과 같이 계산하라고 나와있습니다.

 

따라서, 이를 F.binary_cross_entropy()를 이용해서 계산하였습니다.

 

원래 코드에서는 BCE와 KLD를 더하도록 되어 있는데, 저는 BCE와 KLD의 학습 / 테스트 그래프를 그리기 위해서 두 값이 합쳐져서 나오는 게 아니라 별도로 output으로 나오도록 코드를 짰습니다.

 

 

def train(epoch, model, train_loader, optimizer):
    model.train()
    train_loss = 0
    for batch_idx, (data, _) in enumerate(train_loader):
        data = data.to(DEVICE)
        optimizer.zero_grad()

        recon_batch, mu, logvar = model(data)

        BCE, KLD = loss_function(recon_batch, data, mu, logvar)

        loss = BCE + KLD

        writer.add_scalar("Train/Reconstruction Error", BCE.item(), batch_idx + epoch * (len(train_loader.dataset)/BATCH_SIZE) )
        writer.add_scalar("Train/KL-Divergence", KLD.item(), batch_idx + epoch * (len(train_loader.dataset)/BATCH_SIZE) )
        writer.add_scalar("Train/Total Loss" , loss.item(), batch_idx + epoch * (len(train_loader.dataset)/BATCH_SIZE) )

        loss.backward()

        train_loss += loss.item()

        optimizer.step()

        if batch_idx % 100 == 0:
            print('Train Epoch: {} [{}/{} ({:.0f}%)]\t Loss: {:.6f}'.format(
                epoch, batch_idx * len(data), len(train_loader.dataset),
                100. * batch_idx / len(train_loader),
                loss.item() / len(data)))
            
    print("======> Epoch: {} Average loss: {:.4f}".format(
        epoch, train_loss / len(train_loader.dataset)
    ))        

다음으로는 학습 코드입니다.

 

VAE에서는 label이 필요 없으므로, train_loader에서 나오는 label을 _로 표기해서 별도로 저장하지 않습니다.

 

data를 model에 넣으면, 아까 위에서 봤듯이 reconstruction 된 이미지와 mu, logvar가 나오게 됩니다.

 

그리고 이 값들을 이용해서 loss_function에 투입하여 loss 값을 계산합니다.

 

최종적인 loss 값은 BCE + KLD가 될 것이며, 이를 backpropagation 시켜줍니다.

 

중간에 writer.add_scalar라는 함수가 보이는데, 이는 tensorboard에 값을 저장하기 위해 추가된 코드입니다.

 

첫 번째인 "Train/Reconstruction Error"는 어떤 이름을 가지는 곳에 저장할 것인지를 정하는 것이고

 

두 번째인 BCE.item()은 tensorboard에 저장하려고 하는 값 그 자체를 나타냅니다.

 

그리고 세 번째 인자는 그래프를 그려줄 때 x축에 해당하는 값을 나타냅니다. 

 

batch_idx가 배치의 index이므로, 여기에 epoch이 지날 때마다 추가적으로 더 더해줘서

 

실제로 몇 번째의 batch인지를 x축의 값으로 넣도록 하였습니다.

 

MNIST의 경우, train data가 6만 장이므로, batch size = 200 기준으로는 1 epoch 당 300번이 그래프에 찍히게 됩니다.

 

해당 코드에서는 학습을 50 epochs 만큼 진행하므로, train 데이터에서는 x축의 값이 0부터 15000까지 찍힐 것입니다.

 

 

def test(epoch, model, test_loader):
    model.eval()
    test_loss = 0
    with torch.no_grad():
        for batch_idx, (data, _) in enumerate(test_loader):
            data = data.to(DEVICE)
            
            recon_batch, mu, logvar = model(data)
            BCE, KLD = loss_function(recon_batch, data, mu, logvar)

            loss = BCE + KLD

            writer.add_scalar("Test/Reconstruction Error", BCE.item(), batch_idx + epoch * (len(test_loader.dataset)/BATCH_SIZE) )
            writer.add_scalar("Test/KL-Divergence", KLD.item(), batch_idx + epoch * (len(test_loader.dataset)/BATCH_SIZE) )
            writer.add_scalar("Test/Total Loss" , loss.item(), batch_idx + epoch * (len(test_loader.dataset)/BATCH_SIZE) )
            test_loss += loss.item()

            if batch_idx == 0:
                n = min(data.size(0), 8)
                comparison = torch.cat([data[:n], recon_batch.view(BATCH_SIZE, 1, 28, 28)[:n]]) # (16, 1, 28, 28)
                grid = torchvision.utils.make_grid(comparison.cpu()) # (3, 62, 242)
                writer.add_image("Test image - Above: Real data, below: reconstruction data", grid, epoch)

다음으로는 test 코드입니다.

 

train과 다른 지점은 if batch_idx == 0:에 해당하는 부분일 것 같습니다.

 

torch.cat을 통해서 test_loader에서 나오는 데이터 8개와, 이를 VAE 모델을 통해서 뽑아낸 reconstruction 결과 8개를 합쳐줍니다. 이는 (16, 1, 28, 28)로 나오게 됩니다.

 

그리고 이를 표현하기 위해서 make_grid 함수를 이용해서 grid 이미지를 만들어주고, 이것을 writer.add_image를 통해 tensorboard에 그려줍니다.

 

def latent_to_image(epoch, model):
    with torch.no_grad():
        sample = torch.randn(64, 2).to(DEVICE)
        recon_image = model.decode(sample).cpu()
        grid = torchvision.utils.make_grid(recon_image.view(64, 1, 28, 28))
        writer.add_image("Latent To Image", grid, epoch)

 

latent_to_image는 latent variable을 이미지로 만들어주는 함수입니다.

 

여기에서는 gradient를 계산하지 않으므로 torch.no_grad()를 먼저 선언해줍니다.

 

torch.randn(64, 2)으로 64개의 2차원짜리 랜덤 값들을 만들어냅니다.

 

그리고 이 값을 model.decode를 통해서 reconstruction 이미지를 만들어줍니다.

 

reconstruction 된 이미지를 grid 이미지로 만들어주고, 이를 또다시 writer.add_image를 사용해서 tensorboard에 그려줍니다.

 

해당 함수를 통해서 평균 0, 분산 1인 Gaussian distribution을 가지는 데이터가 주어졌을 때, 어떻게 이미지로 복원하는지를 확인할 수 있습니다.

 

논문에서 Decoder라고 부르던 $p_\theta(x|z)$ 부분이 잘 기능하는지를 확인해보는 것이라고 보시면 되겠습니다.

 

for epoch in tqdm(range(0, EPOCHS)):
    train(epoch, VAE_model, trainloader, optimizer)
    test(epoch, VAE_model, testloader)
    print("\n")
    latent_to_image(epoch, VAE_model)

writer.close()

얼마나 남았는지 확인해주기 위해서 tqdm을 달아주고, range(0, EPOCHS)를 지정해서 총 50 epochs 동안 학습이 진행되도록 해줍니다.

 

train (VAE 모델 학습) => test (VAE 모델이 잘 복원하는지를 확인) => latent_to_image (잠재 변수가 주어졌을 때 image generation을 잘하는지 확인)을 해주고, epoch을 다 돌면 writer.close()를 통해 tensorboard에 써주는 writer를 닫아줍니다.

 

 

%load_ext tensorboard

%tensorboard --logdir='/content/drive/MyDrive/VAE_Result/2021-03-15-15:39'

해당 코드는 Google colab에서 사용할 때 적용하는 코드입니다.

 

그리고 해당 경로는 제가 돌린 결과를 저장한 폴더의 위치라고 보시면 되겠습니다.

 

다음으로는 해당 코드를 돌렸을 때 얻게 되는 결과들에 대해서 살펴보겠습니다.

 

먼저 Train입니다.

 

Total Loss = KL-Divergence + Reconstruction Error로 계산되며, Reconstruction Error는 감소하고 있고 KL-Divergence는 계속해서 증가하는 모습을 보이고 있습니다.

 

Total Loss 관점에서는 계속해서 떨어지고 있네요.

 

Reconstruction Error가 감소하는 것을 보면, 기존 이미지를 잘 구현해나가고 있구나 라고 생각해볼 수 있습니다.

 

하지만, KL-Divergence가 계속해서 증가하는 모습을 보이고 있는데, 이는 $q_\phi(z|x^{(i)})$가 $p_\theta(z)$와 가까워지지 못하고 있다는 사실을 알 수 있습니다. 

 

논문에서 KL-Divergence term이 결국 $\phi$를 regularization 하는 역할이라고 했었는데, Encoder의 parameter들이 적절하게 학습되지 못하고 있는 것 같습니다.

 

 

Test에서도 크게 다르지 않은 모습을 보이고 있습니다.

 

여전히 KL-Divergence는 증가하고 있고, Reconstruction Error는 감소하여 Total Loss는 감소하고 있는 모습을 보입니다.

 

 

다음으로는 이미지들을 살펴보겠습니다.

 

 

위에 3가지 사진은, Test image가 주어졌을 때 실제 Test image 8개와(위쪽) 이를 model의 input으로 투입했을 때 얻게 되는 reconstruction image 8개를 보여줍니다.

 

 

첫 번째는 0 epoch일 때, 두 번째는 23 epoch일 때, 세 번째는 49 epoch일 때입니다. (사진에서 보이는 오렌지색 동그라미를 움직이면 해당하는 epoch의 이미지를 볼 수 있습니다. interactive 한 화면이라서 처음 중간 끝 정도의 3가지만 보여드립니다.) 

 

 

생각보다는 엄청 깔끔하게 생성하는 것 같지는 않습니다.

 

 

다음으로는 latent variable이 제공되었을 때, 어떻게 이미지를 generation 하는지를 살펴보겠습니다.

 

 

맨 처음 단계에서는 알 수 없는 이미지들이 대부분이지만, 갈수록 원래 데이터에서 보일법한 이미지들이 뚜렷하게 보입니다.

 

엄청 깔끔하진 않지만, 대부분 조금 blur하나 그래도 이게 어떤 숫자인지 인지할 수 있을 정도의 퀄리티는 나오고 있네요.

 

 

위에서 실행했던 코드들을 보고 싶으시다면 아래 주소의 제 깃헙으로 와주시면 됩니다.

 

 

github.com/PeterKim1/paper_code_review

 

 

 

여기까지 Auto-Encoding Variational Bayes(VAE)의 code review를 마치겠습니다.

 

 

감사합니다.

 

 

 

 

 

 

 

 

 

 

 

두 번째로 다뤄볼 논문은 VAE라는 이름으로 잘 알려진 Auto-Encoding Variational Bayes라는 논문입니다.

 

 

너무 유명한 논문이라 다양한 자료들이 많이 있지만, 가급적이면 다른 분들이 가공해서 만든 자료들의 열람은 피하고 논문에 있는 내용만을 토대로 최대한 이해해보는 쪽으로 시도하였습니다.

 

 

시작해보겠습니다.

 

 

Abstract

 

 

큰 데이터셋과 계산이 불가능한 posterior 분포를 가지는 연속형 잠재 변수를 가지고 있을 때, 어떻게 directed probabilistic model을 효율적으로 학습하고 추론할 수 있을까?

 

우리는 큰 데이터셋에도 확장할 수 있고 가벼운 미분가능성 조건이 있다면 계산이 불가능한 경우에도 작동하는 stochastic variational inference and learning 알고리즘을 제안한다.

 

우리의 기여는 두 가지이다.

 

첫 번째, variational lower bound의 reparameterization이 표준적인 stochastic gradient 방법론들을 사용하여 직접적으로 최적화될 수 있는 lower bound estimator를 만들어낸다는 것을 보였다.

 

두 번째, 각 datapoint가 연속형 잠재 변수를 가지는 i.i.d. 데이터셋에 대해서, 제안된 lower bound estimator를 사용해 approximate inference model(또는 recognition model이라고 불림)을 계산이 불가능한 posterior에 fitting 시킴으로써 posterior inference가 특히 효율적으로 만들어질 수 있다는 점을 보인다. 실험 결과에 이론적 이점이 반영되었다.

 

 

 

1. Introduction

 

 

연속적인 잠재 변수 및/또는 매개 변수가 계산하기 어려운 사후 분포를 갖는 directed probabilistic model을 가지고 어떻게 하면 효율적인 approximate inference와 learning을 수행할 수 있을까?

 

Variational Bayesian (VB) 접근법은 계산 불가능한 posterior로의 근사의 최적화를 포함한다.

 

불행하게도, 일반적인 mean-field 접근법은 근사적인 posterior에 대한 기댓값의 분석적 해를 요구하며, 일반적인 경우에 이 또한 계산 불가능하다.

 

우리는 variational lower bound의 reparameterization이 어떻게 lower bound의 미분 가능한 unbiased estimator를 만들어내는지를 보인다.

 

이 SGVB (Stochastic Gradient Variational Bayes) estimator는 연속형 잠재 변수나 파라미터를 가지는 어떤 모델에서도 효율적인 approximate posterior inference를 위해 사용될 수 있으며, 표준 gradient ascent 기법을 사용해서 직접적으로 최적화한다. (approximate posterior inference란, posterior를 직접적으로 계산할 수 없기 때문에 근사적인 방법으로 구하는 것을 의미합니다. 딥러닝에서의 inference와는 차이가 있습니다.)

 

각 datapoint가 연속형 잠재 변수를 가지는 i.i.d. 데이터셋의 경우에, 우리는 Auto-Encoding VB (AEVB) 알고리즘을 제안한다.

 

AEVB 알고리즘에서, 우리는 단순한 ancestral sampling을 사용하여 매우 효율적인 approximate posterior inference를 수행하게 해주는 recognition model을 최적화하기 위해 SGVB estimator를 사용함으로써 추론과 학습을 특히 효율적으로 만들 수 있으며, 이는 각 datapoint에 MCMC와 같은 expensive 한 반복적 추론 방법 없이도 모델의 파라미터들을 효율적으로 학습할 수 있도록 해준다. (여기서 expensive 하다는 의미는 연산 관점에서 연산량이 많다는 의미입니다.)

 

학습된 approximate posterior inference model은 recognition, denoising, representation, visualization와 같은 목적을 위해 사용될 수 있다.

 

Recognition model에 neural network가 사용되었을 때, 우리는 variational auto-encoder에 도달한다.

 

 

 

2. Method

 

 

 

이번 section에서의 전략은 연속형 잠재 변수를 가지고 있는 다양한 directed graphical model을 위한 lower bound estimator (a stochastic objective function)을 도출하는 데 사용될 수 있다.

 

우리는 각 datapoint가 잠재 변수를 가지는 i.i.d 데이터셋이 존재하는 일반적인 경우로만 제한할 것이며, 파라미터들에 대해서는 maximum likelihood (ML)이나 maximum posteriori (MAP) 추론을 진행하고 잠재 변수에 대해서는 variational inference를 수행한다.

 

예를 들어서 이 시나리오를 우리가 variational inference를 global parameters에 수행하는 경우로 확장하는 것은 간단하며, 이는 appendix에 들어있고 이 케이스에 해당하는 실험은 future work로 남긴다.

 

우리의 방법은 streaming data와 같은 online- non-stationary setting에도 적용할 수 있으나, 단순함을 위해 고정된 데이터셋을 가정한다.

 

Figure 1

 

Figure 1: 우리가 고려하는 directed graphical model의 유형을 나타낸다. 실선은 generative model $p_\theta(z)p_\theta(x|z)$이며, 점선은 계산 불가능한 posterior $p_\theta(z|x)$로의 variational approximation $q_\phi(z|x)$를 나타낸다. variational parameters $\phi$는 generative model parameters $\theta$와 함께 학습된다.

 

 

2.1 Problem scenario

 

 

 

연속형 변수 혹은 이산형 변수 $x$의 $N$개의 i.i.d. sample로 구성된 데이터셋 $X = \{x^{(i)}\}^N_{i=1}$을 고려하자.

 

관측되지 않은 연속형 랜덤 변수 $z$를 포함하는 어떤 random process에 의해서 데이터가 생성되었다고 가정하자.

 

Process는 2개의 step으로 구성된다.

 

(1) $z^{(i)}$는 어떤 사전 분포 $p_{\theta^*}(z)$로부터 생성되었다.

 

(2) $x^{(i)}$는 어떤 조건부 분포 $p_{\theta^*}(x|z)$로부터 생성되었다.

 

우리는 prior $p_{\theta^*}(z)$와 likelihood $p_{\theta^*}(x|z)$가 parametric families of distribution $p_\theta(z)$와 $p_\theta(x|z)$로부터 왔다고 가정하며, 그들의 PDF는 $\theta$와 $z$에 대해서 거의 모든 곳에서 미분 가능하다고 가정한다.

 

불행하게도, 우리의 관점에서 이 과정의 많은 것들이 숨겨져 있다: 즉, true parameters $\theta^*$와 잠재 변수들의 값 $z^{(i)}$은 우리에게 알려져 있지 않다.

 

매우 중요하게, 우리는 주변 확률이나 사후 확률에 대해서 일반적인 단순화한 가정을 만들지 않는다.

 

대조적으로, 우리는 이러한 경우에서도 효율적으로 작동하는 일반적인 알고리즘에 관심이 있다.

 

1. Intractability: marginal likelihood의 적분 $p_\theta(x) = \int p_\theta(z)p_\theta(x|z)dz$이 계산 불가능한 경우(따라서 우리는 marginal likelihood를 평가하거나 미분할 수 없다)를 말하며, true posterior density $p_\theta(z|x) = p_\theta(x|z)p_\theta(z)/p_\theta(x)$도 계산 불가능하며(따라서 EM algorithm을 사용할 수 없음), 어떤 합리적인 mean-field VB algorithm을 위해 요구되는 적분도 계산 불가능한 경우를 말한다.

 

이러한 계산 불가능성은 꽤 흔하고 nonlinear hidden layer를 포함하는 신경망과 같은 적당히 복잡한 likelihood function $p_\theta(x|z)$의 경우에서 나타난다. (즉, posterior를 적분과 같은 계산을 통해서 구할 수 없는 경우에도 알고리즘이 작동해야 한다는 말입니다. marginal likelihood의 적분식을 보면, $z$에 대해서 적분을 시행하게 되는데 우리는 $z$에 대해서 알지 못하기 때문에 이를 실제 적분을 시행할 수 없습니다.)

 

2. A large dataset: 우리는 너무 많은 데이터를 가지고 있어서 batch optimization은 너무 연산량이 많다. 우리는 small minibatch나 심지어 하나의 datapoints만 사용해서 parameter update를 하고 싶다.

 

Monte Carlo EM과 같은 Sampling 기반의 솔루션은 일반적으로 매우 느린데, 이는 datapoint마다 전형적으로 연산량이 많은 샘플링 loop를 포함하기 때문이다.

 

 

우리는 위의 시나리오에서 3개의 관련된 문제들에 관심이 있으며, 이에 대한 해결책을 제안한다.

 

 

1. Parameters $\theta$에 대한 Efficient approximate ML or MAP 추정. 예를 들어서, natural process를 분석하는 경우와 같이 매개 변수 자체에 관심이 있을 수 있다. 그들은 또한 우리가 숨겨진 random process를 모방하도록 해주며 real data를 닮은 인공 데이터를 생성하게 해 준다.

 

2. Parameter $\theta$의 선택을 위한 관측된 값 $x$가 주어졌을 때 잠재 변수 $z$에 대한 효율적인 approximate posterior inference. 이는 coding이나 data representation task에 유용하다.

 

3. 변수 $x$의 효율적인 approximate marginal inference. 이는 우리가 $x$에 대한 prior가 요구되는 모든 종류의 inference task를 수행할 수 있도록 한다. computer vision에서의 일반적인 응용 사례는 image denoising, inpainting, super-resolution을 포함한다.

 

 

위의 문제들을 해결하기 위해, recognition model $q_\phi(z|x)$를 도입한다. 이는 계산 불가능한 true posterior $p_\theta(z|x)$에 대한 추정이다. 

 

Mean-field variational inference에서의 approximate posterior와는 대조적으로, 이는 반드시 factorial일 필요는 없으며 parameters $\phi$는 closed-form expectation으로부터 계산되지 않음을 유의한다.

 

그 대신에, 우리는 생성 모델 파라미터 $\theta$와 recognition model 파라미터 $\phi$를 동시에 학습하는 방법을 도입한다.

 

 

Coding theory 관점에서, 관측되지 않은 변수 $z$는 latent representation혹은 code라는 해석을 가지고 있다.

 

이번 논문에서, 우리는 그러므로 recognition model $q_\phi(z|x)$를 확률적 encoder로 지칭하며, 이는 datapoint $x$가 주어졌을 때 이 모델이 datapoint $x$가 생성될 수 있는 지점인 code $z$의 가능한 값들에 대한 분포(예를 들어, Gaussian 분포)를 만들어 내기 때문이다.

 

유사한 방식으로, 우리는 $p_\theta(x|z)$를 확률적 decoder로 지칭할 것이며, code $z$가 주어졌을 때 이는 가능한 $x$의 값들에 대한 분포를 만들어낸다.

 

 

 

 

 

2.2 The Variational Bound

 

 

 

Marginal likelihood는 각각의 datapoint들의 marginal likelihood의 합으로 구성된다.

 

즉, $logp_\theta(x^{(1)}, ..., x^{(N)})$ = $\sum^N_{i=1}logp_\theta(x^{(i)})$이며, 이는 다음과 같이 다시 쓰일 수 있다.

 

식 (1)

 

우변의 첫 번째 항은 true posterior와의 approximate 간의 KL divergence이다. (KL divergence란, GAN paper에서도 봤지만 두 분포의 차이를 계산하는 방법입니다. 위의 식에서 true posterior가 $p_\theta(z|x^{(i)})$를 의미하고, approximate가 $q_\phi(z|x^{(i)})$를 의미해요. approximate는 위에서 Recognition model, 혹은 확률적 Encoder라는 이름으로도 설명되었습니다. )

 

KL-divergence가 비음(non-negative) 이므로, 우변의 두 번째 항 $L(\theta, \phi; x^{(i)})$는 datapoint $i$의 대한 marginal likelihood에 대한 (variational) lower bound이다. 이는 다음과 같이 쓰일 수 있다.

 

식 (2)

 

이는 다음과 같이 쓰일 수 있다.

 

식 (3)

우리는 variational parameters $\phi$와 generative parameters $\theta$ 둘 다에 대해서 lower bound $L(\theta, \phi;x^{(i)})$를 미분하고 최적화하고 싶다. 

 

하지만, lower bound의 $\phi$에 대한 gradient는 다소 문제가 있다.

 

이런 종류의 문제에 대한 일반적인 Monte Carlo gradient estimator는

이며, $z^{(l)} \sim q_\phi(z|x^{(i)})$이다. 

 

이러한 gradient estimator는 매우 높은 variance를 보이고, 우리의 목적에는 실용적이지 않다.

 

 

 

2.3 The SGVB estimator and AEVB algorithm

 

 

 

이번 section에서 우리는 the lower bound와 파라미터에 대한 lower bound의 미분 값에 대한 실용적 estimator를 소개한다.

 

approximate posterior를 $q_\phi(z|x)$의 형태로 가정하지만, 이 기술은 $q_\phi(z)$의 경우에도 적용될 수 있다.

 

즉, $x$에 대한 조건부가 아닐 때도 가능하다는 의미이다.

 

주어진 parameters에 대한 posterior를 추론하는 fully variational Bayesian method는 appendix에 쓰여있다.

 

 

Section 2.4에서 서술된 어떤 가벼운 조건 하에서 주어진 approximate posterior $q_\phi(z|x)$에 대해 우리는 random variable $\tilde{z} \sim q_\phi(z|x)$를 (보조) noise variable $\epsilon$의 미분 가능한 transformation $g_\phi(\epsilon, x)$를 사용하여 reparameterize 할 수 있다.

 

식 (4)

 

적절한 distribution $p(\epsilon)$과 function $g_\phi(\epsilon, x)$를 선택하는 것에 대한 일반적인 전략은 section 2.4에서 확인할 수 있다.

 

우리는 이제 $q_\phi(z|x)$에 대한 어떠한 함수 $f(z)$의 기댓값에 대한 Monte Carlo estimate를 구성할 수 있다. (기댓값을 직접 구하기 어렵기 때문에, Monte Carlo 기법을 활용해서 추정하는 것이라고 보면 될 것 같습니다.)

 

식 (5)

 

우리는 이 기술을 variational lower bound (식 (2))에 적용하여 이를 통해 generic Stochastic Gradient Variational Bayes (SGVB) estimator $\tilde{L^A}(\theta, \phi; x^{(i)}) \simeq L(\theta, \phi; x^{(i)})$를 만들 수 있다.

(논문에 쓰여있는 그대로, Monte Carlo estimate를 식 (2)에 적용하면 식 (6)을 도출할 수 있습니다.)

 

식 (6)

 

종종, 식 (3)의 KL-divergence $D_{KL}(q_\phi(z|x^{(i)}) \parallel p_\theta(z))$는 분석적으로 통합될 수 있으며(appendix B를 참조), expected reconstruction error $E_{q_\phi(z|x^{(i)})}[logp_\theta(x^{(i)} | z)]$는 샘플링에 의한 추정이 요구된다.

 

KL-divergence는 $\phi$를 규제하는 것으로 해석될 수 있으며, approximate posterior가 prior $p_\theta(z)$에 가까워지도록 유도한다.

 

이는 두 번째 버전의 SGVB estimator $\tilde{L^B}(\theta, \phi; x^{(i)}) \simeq L(\theta, \phi; x^{(i)})$를 만들어내며, 이는 식 (3)에 대응되고, generic estimator(식 (6) 번)에 비해서 일반적으로 더 낮은 variance를 가진다.

 

식 (7)

 

$N$ data point를 가지는 dataset $X$로부터 여러 개의 datapoints가 주어질 때, 미니 배치를 기반으로 전체 데이터셋의 marginal likelihood lower bound의 estimator를 만들 수 있다.

 

식 (8)

 

Minibatch $X^M = \left\{x^{(i)}\right\}^M_{i=1}$은 $N$ data point를 가지는 전체 데이터셋 $X$로부터 $M$개의 datapoints가 랜덤 하게 뽑힌 것이다.

 

우리 실험에서, datapoint 당 샘플의 수인 $L$은 1로 지정할 수 있으며, 그동안 minibatch size $M$은 충분히 커야 한다. 예를 들어, $M$ = 100 정도는 되어야 한다.

 

미분 값 $\triangledown_{\theta, \phi}\tilde{L}(\theta; X^M)$는 얻어질 수 있고, 결과로 나오는 gradient는 SGD나 Adagrad와 같은 stochastic optimization emthod와 결합되어 사용될 수 있다.

 

Stochastic gradient를 계산하기 위한 기초적인 접근법이 algorithm 1에 나와있다.

 

Algorithm 1

 

Auto-encoders와의 연결은 식 (7)에 나타난 objective function을 봤을 때 명확해진다. 

 

prior로부터 approximate posterior와의 KL divergence인 첫 번째 항은 regularizer의 역할을 하게 되며, 두 번째 항은 expected negative reconstruction error이다.

 

함수 $g_\phi(.)$은 datapoint $x^{(i)}$와 random noise vector $\epsilon^{(l)}$를 해당 datapoint에 대한 approximate posterior로부터 나온 샘플로 맵핑을 한다.

 

즉, $z^{(i, l)} = g_\phi(\epsilon^{(l)}, x^{(i)})$이고 $z^{(i, l)} \sim q_\phi(z|x^{(i)})$이다. 

 

그 결과로, sample $z^{(i, l)}$는 함수 $logp_\theta(x^{(i)} | z^{(i, l)})$의 input이 되며, 이는 $z^{(i, l)}$가 주어졌을 때, generative model 하에서 datapoint $x^{(i, l)}$의 확률 밀도(혹은 질량)와 같다. 이 항은 auto-encoder에서 negative reconstruction error이다.

 

 

 

2.4 The reparameterization trick

 

 

 

우리의 문제를 해결하기 위해, $q_\phi(z|x)$로부터 샘플을 생성해내기 위한 대안적인 방법을 적용했다.

 

본질적인 parameterization trick은 꽤 단순하다.

 

$z$가 연속적인 랜덤 변수라고 하고, $z \sim q_\phi(z|x)$는 어떤 조건부 분포라고 하자.

 

그러고 나서 랜덤 변수 $z$를 deterministic 변수 $z = g_\phi(\epsilon, x)$로 표현하는 것이 가능하며, $\epsilon$은 독립적인 주변 분포 $p(\epsilon)$에서 나온 보조 변수이며, $g_\phi(.)$은 $\phi$에 의해서 parameterized 된 vector-valued 함수이다.

 

이러한 reparameterization은 $q_\phi(z|x)$에 대한 기댓값을 재작성하는 데 사용될 수 있기 때문에 우리의 경우에 유용하며, 이러한 기댓값의 Monte Carlo estimate는 $\phi$와 관련하여 미분 가능하게 될 수 있다.

 

증명은 다음과 같다.

 

Deterministic mapping $z = g_\phi(\epsilon, x)$가 주어졌을 때, $q_\phi(z|x)\prod_idz_i$ $=$ $p(\epsilon)\prod_{i} d\epsilon_i$ 라는 사실을 알고 있다.

 

그러므로, $\int q_\phi(z|x)f(z)dz = \int p(\epsilon)f(z)d\epsilon = \int p(\epsilon)f(g_\phi(x, \epsilon^{(i)}))d\epsilon$ 이다. 

 

section 2.3에서 우리는 이 trick을 variational lower bound의 미분 가능한 estimator를 얻기 위해 적용했다.

 

예를 들어서, univariate Gaussian case를 생각해보면, $z \sim p(z|x) = N(\mu, \sigma^2)$이다.

 

이 경우에서는, 유효한 reparameterization은 $z = \mu + \sigma\epsilon$이며, $\epsilon$은 $\epsilon \sim N(0, 1)$을 만족하는 보조 노이즈 변수이다.

 

그러므로

를 만족한다.

 

 

어떤 $q_\phi(z|x)$에 대해서, 우리는 미분 가능한 transformation $g_\phi(.)$과 보조 변수 $\epsilon \sim p(\epsilon)$을 선택할 수 있을까? 3가지 기초적인 접근법은 다음과 같다.

 

1. 계산 가능한 inverse CDF. 이 경우에는, $\epsilon \sim u(0, I)$라 하고, $g_\phi(\epsilon, x)$를 $q_\phi(z|x)$의 inverse CDF라고 하자. 예시: Exponential, Cauchy, Logistic, Rayleigh, Pareto, Weibull, Reciprocal, Gompertz, Gumbel and Erlang 분포가 될 수 있다.

 

2. Gaussian 예시와 유사하게, 어떠한 "location-salce" 계통의 분포를 보조 변수 $\epsilon$의  standard distribution(location =0, scale = 1)으로 선택할 수 있으며, $g(.)$ = location + scale $\cdot \epsilon$로 놓는다. 예시: Laplace, Elliptical, Student's t, Logistic, Uniform, Triangular and Gaussian distribution.

 

3. 랜덤 변수를 보조 변수들의 다른 transformation으로 표현할 수 있다. 예시: Log-Normal(normal하게 분포된 변수의 exponentiation), Gamma(exponentially 하게 분포된 변수들에 대한 합), Dirichlet(Gamma variates의 가중 합), Beta, Chi-Squared, and F 분포.

 

모든 세 접근법이 실패했을 때, inverse CDF에 대한 좋은 근사가 존재하면 PDF에 비교될 정도의 시간 복잡도가 요구된다.

 

 

 

3. Example: Variational Auto-Encoder

 

 

 

이번 section에서 확률적 encoder $q_\phi(z|x)$(generative model $p_\theta(x, z)$의 posterior에 대한 근사)로 신경망을 사용했을 때의 예시를 보여주며, parameter $\phi$와 $\theta$가 AEVB algorithm을 가지고 동시에 최적화되는 예시를 보여준다.

 

잠재 변수에 대한 prior를 centered isotropic multivariate Gaussian $p_\theta(z) = N(z; 0, I)$라고 하자.  이런 경우에 prior는 parameter가 없다는 것을 알아두자. 

 

우리는 $p_\theta(x|z)$를 실수 데이터의 경우에 multivariate Gaussian으로, binary data의 경우 Bernoulli로 놓으며 이 분포의 파라미터들은 MLP(하나의 hidden layer를 가지는 fully-connected 신경망)를 가지고 $z$로부터 계산될 수 있다.

 

True posterior $p_\theta(z|x)$는 이 경우에 계산이 불가능하다는 것을 알아두자.

 

반면에, $q_\phi(z|x)$의 형태에는 많은 자유가 있으며, 우리는 true (but intractable) posterior가 approximately diagonal covariance를 가지는 approximate Gaussian을 맡는다고 가정한다. 

 

이 경우에, 우리는 variational approximate posterior를 diagonal covariance structure를 가지는 multivariate Guassian으로 정할 수 있다.

 

식 (9)

 

Approximate posterior의 평균과 표준편차인 $\mu^{(i)}$와 $\sigma^{(i)}$는 datapoint $x^{(i)}$와 variational parameter $\phi$을 가지는 비선형 함수인 encoding MLP의 output이다. (appendix C 참조)

 

Section 2.4에서 설명했던 대로, 우리는 $\epsilon^{(l)} \sim N(0, I)$일 때 $z^{(i, l)} = g_\phi(x^{(i)}, \sigma^{(i)}) = \mu^{(i)} + \sigma^{(i)} \circledcirc \epsilon^{(l)}$을 사용하여 $z^{(i, l)} \sim q_\phi(z|x^{(i)})$로부터 샘플링을 진행한다.

 

$\circledcirc$는 element-wise product를 나타낸다.

 

이 모델에서 $p_\theta(z)$ (the prior)와 $q_\phi(z|x)$는 둘 다 Gaussian이다; 이 경우에, 우리는 식 (7)의 estimator를 사용할 수 있으며 추정 없이 KL divergence가 계산되고 미분될 수 있다.

 

모델에 대한 그 결과로의 estimator와 datapoint $x^{(i)}$는 다음과 같다.

 

식 (10)

appendix C와 위에서 설명한 대로, decoding term $logp_\theta(x^{(i)} | z^{(i, l)})$는 Bernoulli나 Gaussian MLP이며, 우리가 모델링하려는 데이터의 유형에 의존한다.

 

 

4. Related work와 5. Experiment는 생략하였습니다.

 

 

 

6. Conclusion

 

 

우리는 연속형 잠재 변수를 효율적으로 approximate inference 하기 위한 새로운 variational lower bound의 estimator인 Stochastic Gradient VB (SGVB)를 소개했다. 제안된 estimator는 단도직입적으로 미분될 수 있고 standard stochastic gradient method를 사용해 최적화될 수 있다. 

 

각 datapoint가 연속형 잠재 변수를 가지는 i.i.d dataset의 경우에, 우리는 효율적인 추론과 학습이 가능한 효율적인 알고리즘인 Auto-Encoding VB (AEVB)를 소개했으며, SGVB estimator를 사용하여 approximate inference model을 학습한다.

 

 

7. Future work

 

 

SGVB estimator와 AEVB algorithm이 연속형 잠재 변수를 가지는 어떤 추론 문제나 어떤 학습 문제에도 적용될 수 있기 때문에 많은 미래 연구 방향이 존재한다.

 

(i) AEVB와 공동으로 학습한 인코더와 디코더에 사용되는 neural network (예: convolutional networks)을 가진 계층적 생성 architecture를 학습하는 것

 

(ii) dynamic Bayesian networks와 같은 시계열 모델

 

(iii) SGVB의 global parameters로의 응용

 

(iv) 잠재 변수를 이용한 supervised models, 이는 복잡한 노이즈 분포를 학습하는데 유용하다.

 

 

 

수식들도 너무 많고, 이해하기가 쉽지 않은 논문이라서 정리하는데 꽤 오랜 시간이 걸렸습니다.

 

 

기존에는 논문을 좀 세세하게 보는 것을 목표로 하려고 했는데, VAE를 읽다 보니 이게 진짜 효율적인가 하는 의문이 드네요 ㅠㅠ

 

 

다음 글에서는 VAE가 코드로 구현되었을 때 어떻게 구현되는지에 대해서 다루도록 하겠습니다.

 

 

논문이 이렇게 수식적으로 어려운데 비해서, 사실 코드로 구현하는 것 자체는 간단하다고 생각합니다.

 

 

대부분의 수식들이 전개하거나 정리하는 과정에서 도출되는 것들이라.....

 

 

 

 

Generative Adversarial Nets(GAN) 논문 리뷰: cumulu-s.tistory.com/22

 

1. Generative Adversarial Nets(GAN) - paper review

안녕하세요. 새롭게 (paper + code) review라는 항목을 만들었고, 여기에 하나하나 글을 채워나갈 예정입니다. 블로그 글에는 논문의 내용을 조금 더 디테일하게 정리하면서 나름대로 저의 생각을 얹

cumulu-s.tistory.com

 

이전 글에서는, Generative Adversarial Nets(GAN) 논문에 대해서 상세하게 정리해보았습니다.

 

 

이번 글에서는, 직접 코드를 하나하나 짜 보면서 논문에 나온 내용들이 어떻게 구현될 수 있는지를 확인해보겠습니다.

 

 

저는 Deep learning library 중 pytorch를 메인으로 사용하고 있어서, pytorch로 작성하였음을 알립니다.

 

 

그럼 시작해보겠습니다.

 

 

# Import packages
import torch
import torchvision
import torchvision.transforms as transforms
import torch.nn as nn
import torch.optim as optim
from tqdm.auto import tqdm
import numpy as np
import matplotlib.pyplot as plt

먼저 필요한 패키지들을 불러옵니다. torch와 torchvision, numpy 등을 불러옵니다.

 

 

tqdm의 경우, for문이 얼만큼 돌았는지 나타내 주는 패키지로 여러 번의 반복문을 돌아야 하는 코드에서 유용하게 사용되는 패키지입니다.

 

 

USE_CUDA = torch.cuda.is_available()
DEVICE = torch.device("cuda" if USE_CUDA else "cpu")
print("사용하는 Device : ", DEVICE)

EPOCHS = 400
BATCH_SIZE = 200

torch.cuda.is_avilable()은 cuda를 사용할 수 있는지를 True / False로 알려줍니다. 즉 GPU를 사용할 수 있는지 확인하는 코드입니다.

 

DEVICE는 gpu가 가능하면 cuda로, 아니면 cpu로 뜨게 만들어줍니다.

 

그리고 학습을 진행할 에폭과 batch size를 지정해줍니다. 

 

batch size의 경우 Dataloader에서 나오는 데이터의 수를 결정하므로 여기서 먼저 지정해줍니다.

 

# code for MNIST dataset loading error at Google Colab.
from six.moves import urllib    
opener = urllib.request.build_opener()
opener.addheaders = [('User-agent', 'Mozilla/5.0')]
urllib.request.install_opener(opener)

해당 코드는, 혹시나 Google Colab에서 해당 코드를 돌리시는 분들을 위해 작성된 코드입니다.

 

Google Colab을 사용하지 않으신다면 넘어가셔도 좋습니다.

 

현재 torchvision에서 MNIST dataset을 불러올 때, 데이터셋 링크로부터 불러오는 코드가 거절당하는 경우가 있어 이를 세팅해주는 코드입니다.

 

# Transformer code
transformer = transforms.Compose([
            transforms.ToTensor(),
            transforms.Normalize((0.5,), (0.5,))
])


# Loading trainset, testset and trainloader, testloader
trainset = torchvision.datasets.MNIST(root = '/content/drive/MyDrive/MNIST', train = True,
                                        download = True, transform = transformer)

trainloader = torch.utils.data.DataLoader(trainset, batch_size = BATCH_SIZE, shuffle = True, num_workers = 2)


testset = torchvision.datasets.MNIST(root = '/content/drive/MyDrive/MNIST', train = False,
                                        download = True, transform = transformer)

testloader = torch.utils.data.DataLoader(testset, batch_size = BATCH_SIZE, shuffle = True, num_workers = 2)

transformer는 데이터셋을 받아서 변형해주는 도구입니다.

 

classification과 같은 task를 할 때는 여기에 Augmentation을 넣어주기도 합니다. 

 

transforms.ToTensor()는 numpy array를 torch tensor로 바꿔주는 역할을 합니다. 

 

보통 데이터셋들은 numpy array로 되어 있고, 이를 pytorch에서 사용하려면 torch tensor의 형태로 변환해줘야 합니다.

 

transforms.Normalize는 정규화를 해주는 코드입니다.

 

정규화를 진행하지 않으면 MNIST 데이터셋이 0부터 1의 값을 가지도록 조정이 되어있습니다.

 

따라서, 이에 평균 0.5, 표준편차 0.5로 정규화를 진행해줍니다.

 

정규화는 $\frac{x-\mu}{\sigma}$의 형태로 진행됩니다.

 

따라서 0의 값은 -1이 되고, 1의 값은 1이 됩니다. 이 과정을 통해 MNIST 데이터셋이 -1부터 1의 값을 가지도록 바꿔줍니다.

 

torchvision.datasets.MNIST를 통해서 MNIST 데이터셋을 받을 수 있고, root는 데이터셋을 받을 경로입니다. 

 

해당 경로는 제가 사용하고 있는 경로이므로, 직접 사용하실 때는 이 부분을 원하시는 경로로 변경해주셔야 합니다.

 

다음으로는 torch.utils.data.DataLoader를 정의해줍니다. DataLoader는 데이터 전체를 보관하고 있는 일종의 container 같은 것으로, 이를 통해 데이터셋 전체를 메모리에 올리지 않아도 되게 만들어줍니다.

 

즉, batch size만큼만 DataLoader에서 데이터를 내보내주는 방식을 사용하는 것이죠.

 

# sample check
sample, label = next(iter(trainloader))

# show grid image
def imshow_grid(img):
    img = torchvision.utils.make_grid(img.cpu().detach())
    img = (img + 1) / 2
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    ax = plt.gca()
    ax.axes.xaxis.set_visible(False)
    ax.axes.yaxis.set_visible(False)
    plt.show()


imshow_grid(sample[0:8])

 

해당 코드는, DataLoader로부터 데이터가 잘 나오고 있는지를 확인하는 코드입니다.

 

iter()를 통해서 trainloader를 iterator로 만들어주고, next()를 이용해 한 번 데이터를 뽑도록 만들어줍니다.

 

현재 mnist trainloader는 이미지 데이터와 label을 동시에 뱉어내기 때문에, sample , label의 형태로 두 개의 변수를 지정해줘야 합니다.

 

imshow_grid 함수는 이미지를 받아서 이를 grid 형태로 보여주는 함수입니다.

 

torchvision.utils.make_grid()를 이용하면 grid 형태의 이미지를 만들 수 있습니다.

 

여기서 (img + 1) / 2를 적용해주는 이유는, 우리가 0 ~ 1로 되어 있는 데이터를 -1 ~ 1로 만들어줬기 때문입니다.

 

따라서 여기에 다시 1을 더해주고, 2로 나눠주어 원래의 0 ~ 1 형태로 원상복구 해주기 위함입니다.

 

그리고 현재는 데이터셋이 torch tensor로 되어 있으므로, 이를 numpy()를 이용하여 다시 numpy array로 만들어줘야 합니다.

 

그리고 np.transpose를 사용해주는 이유는, mnist 데이터셋은 (channel, width, height) 형태의 shape를 가지고 있기 때문입니다.

 

따라서, 실제로 이를 plt.imshow()를 통해서 시각화하려면 차원의 순서를 바꿔줘야 합니다. 그래서 사용하는 것이고요.

 

이 코드를 통해 아래에 보이는 그림은 8개의 샘플만 뽑아서 시각화를 진행한 결과입니다.

 

# Discriminator class
class Dis_model(nn.Module):
    def __init__(self, image_size, hidden_space):
        super(Dis_model, self).__init__()
        self.features = nn.Sequential(
            nn.Linear(image_size, hidden_space),
            nn.ReLU(),
            nn.Linear(hidden_space, hidden_space),
            nn.ReLU(),
            nn.Linear(hidden_space, 1),
            nn.Sigmoid())
    
    def forward(self, input_x):
        x = self.features(input_x)
        return x

다음으로는 Discriminator class를 만들어주었습니다.

 

보통은 모델을 class형태로 만들어주기 때문에, 비록 GAN 모델 구현은 그리 복잡하진 않아서 이렇게 하지 않고 더 간단하게도 짤 수 있겠지만 그냥 습관적으로 class로 만들어보았습니다.

 

image size를 지정해주면, 이를 입력으로 받아서 hidden space 만큼의 fully connected layer를 만들어주고, 다시 hidden space - hidden space 만큼의 fully connected layer를 만들어준 다음, 마지막으로는 output이 1로 나오도록 해줍니다.

 

이때, activation function은 Sigmoid function을 사용하여 마지막 layer의 결괏값이 0부터 1 사이로 나오게 해 줍니다.

 

이를 통해서, Discriminator model은 image_size라고 하는 크기의 데이터를 받아서 해당 데이터가 만들어진 데이터인지(위조지폐인지) 혹은 실제 데이터셋에 존재하는 데이터인지(실제 지폐인지)를 판단할 수 있게 됩니다.

 

# Generator class
class Gen_model(nn.Module):
    def __init__(self, latent_space, hidden_space, image_size):
        super(Gen_model, self).__init__()
        self.features = nn.Sequential(
            nn.Linear(latent_space, hidden_space),
            nn.ReLU(),
            nn.Linear(hidden_space, hidden_space),
            nn.ReLU(),
            nn.Linear(hidden_space, image_size),
            nn.Tanh())
        
    def forward(self, input_x):
        x = self.features(input_x)
        return x

이번에는 Generator model입니다.

 

Generator model은 latent_space라는 크기의 latent variable을 입력으로 받아서 hidden_space 크기로 output을 내고, 이를 다시 hidden_space 크기로 output을 내며, 마지막으로는 image_size 만큼의 결괏값을 내보내게 됩니다.

 

이를 통해서, Generator model은 latent_space라고 하는 크기의 noise input을 받아서 image_size라고 하는 크기의 데이터를 output으로 내게 됩니다.

 

im_size = 784
hidden_size = 256
latent_size = 100

Dis_net = Dis_model(image_size = im_size, hidden_space = hidden_size).to(DEVICE)
Gen_net = Gen_model(image_size = im_size, hidden_space = hidden_size, latent_space = latent_size).to(DEVICE)

d_optimizer = optim.Adam(Dis_net.parameters(), lr = 0.0002)
g_optimizer = optim.Adam(Gen_net.parameters(), lr = 0.0002)

이미지의 사이즈는 28 * 28 이므로 784로 지정하였고, hidden layer의 차원은 256, Generator에 들어가는 latent variable의 차원은 100차원으로 지정해주었습니다.

 

이미지의 사이즈는 데이터셋에 맞춰서 설정해주시면 되고 hidden layer의 차원이나 latent variable의 차원은 유동적으로 변경하셔도 됩니다.

 

하지만 이게 너무 작거나 하게 되면 Generator나 Discriminator가 충분한 capacity를 가질 수 없을 수도 있으니 과도하게 작게 하는 것은 여러모로 안 좋게 되지 않을까 하는 생각이 듭니다.

 

Discriminator 모델은 Dis_net이라는 이름으로 설정해주고, Generator 모델은 Gen_net이라는 이름으로 설정해주었습니다.

 

만약에 GPU를 사용하시는 분이라면, 반드시 .to(DEVICE)를 지정해주셔서 이를 GPU에서 연산하도록 설정해주셔야 합니다.

 

GPU로 하는 것과 CPU로 하는 것은 딥러닝에서의 연산 시간 차이가 심합니다. 

 

그리고 Discriminator 모델과 Generator 모델을 최적화해줄 optimization algorithm으로는 Adam을 선택했습니다.

 

모델이 두 개이므로, 두 개를 따로 만들어주셔야 합니다. 

 

학습률(learning rate)도 유동적으로 선택하시면 되는데, 저는 그냥 0.0002 정도로 잡았습니다.

 

# Start training
def train(generator, discriminator, train_loader, optimizer_d, optimizer_g):

    # Train version
    generator.train()
    discriminator.train()

    for data, target in train_loader:

        data, target = data.to(DEVICE), target.to(DEVICE)

        # ==========================================#
        # ==========Optimize discriminator==========#
        # ==========================================#

        # initialize discriminator optimizer
        optimizer_d.zero_grad()

        # Make noise samples for discriminator update
        noise_samples_d = torch.randn(BATCH_SIZE, latent_size).to(DEVICE)

        # real loss
        discri_value = discriminator(data.view(-1, 28*28))
        loss_real = -1 * torch.log(discri_value) # gradient ascent

        # fake loss
        gene_value = discriminator(generator(noise_samples_d))
        loss_fake = -1 * torch.log(1.0 - gene_value) # gradient ascent

        # Final loss
        loss_d = (loss_real + loss_fake).mean()

        loss_d.backward()
        optimizer_d.step()


        # ========================================= #
        # ==========Optimize generator============= #
        # ========================================= #

        # initialize generator optimizer
        optimizer_g.zero_grad()

        # Make noise samples for generator update
        noise_samples_g = torch.randn(BATCH_SIZE, latent_size).to(DEVICE)

        # calculate loss
        fake_value = discriminator(generator(noise_samples_g))
        loss_generator = -1 * torch.log(fake_value).mean() # provide much stronger gradients early in learning.
        
        loss_generator.backward()
        optimizer_g.step()

해당 함수는 모델의 학습을 수행합니다.

 

먼저, 모델을 학습하려면 반드시 generator.train()와 같이 이 모델은 학습을 할 것이다 라는 시그널을 보내줘야 합니다.

 

반대로 학습을 하지 않고 추론만 진행하려고 한다면, .eval()를 적용해주시면 됩니다.

 

학습은 train_loader에서 데이터를 받아서 진행하므로, data와 target을 train_loader에서 전달받습니다.

 

이때, data와 target도 모델과 동일하게 .to(DEVICE)를 통해서 GPU 연산을 적용하도록 만들어줘야 합니다.

 

 

혹시나 궁금하시다면, 해당 부분에 .to(DEVICE)를 빼시면 오류가 날 겁니다. (예전에 이런 적이 있었는데, 오류명까지는 기억이 안 나고 오류 내용에 cuda 설정 어쩌고저쩌고 하면서 설명이 잘 나옵니다.)

 

먼저 optimizer_d.zero_grad()로 gradient를 초기화해주고

 

torch.randn을 이용해서 정해진 사이즈의 noise variable을 만들어줍니다. 우리는 정규분포 기준으로 만들어줍니다.

 

 

우리가 BATCH_SIZE 기준으로 데이터를 받고 연산을 진행하기 때문에, noise variable도 크기는 BATCH_SIZE에 맞춰줍니다.

 

다음으로는, 논문에 이 파트를 구현해주는 코드입니다.

 

먼저, 위 식의 첫 번째 항을 만들어줍니다.

 

discri_value는 데이터를 먼저 (200, 784) 형태로 만들어줍니다. 이는 .view()를 이용하시면 됩니다.

 

 

(200, 784)로 만들어준 실제 MNIST 데이터를 discriminator의 input으로 넣어줍니다. 

 

이렇게 하면 discriminator가 해당 데이터를 기반으로 이게 위조지폐인지 실제 지폐인지에 대한 판단을 하겠죠?

 

그 값이 바로 discri_value입니다. 

 

여기에 log 값을 취해준 게 loss_real이 되고, 실제 논문에서는 gradient ascending을 하라고 했기 때문에 여기에 -1을 곱해줘서 구현을 진행했습니다.

 

다음으로는, 두 번째 항을 만들어줍니다.

 

gene_value는 아까 만들어낸 noise sample을 generator에 넣어서 새롭게 데이터를 만들어냅니다. 

 

이를 discriminator에 넣어서 해당 데이터가 위조지폐인지 실제 지폐인지 판단한 결과 값이 gene_value입니다.

 

여기에 log 값을 취해주고, gradient ascending을 위해 -1을 곱해서 이를 loss_fake라고 지정하였습니다.

 

마지막으로, loss_real과 loss_fake를 합해주고, 이를 평균 내는 것이 논문의 내용이였으므로 .mean()을 해줍니다.

 

 

loss_d.backward()는 backpropagation을 진행하는 코드이고, optimizer_d.step()은 optimizer를 한 단계 진행해줍니다.

 

다음으로는 Generator 학습을 진행해줘야 합니다.

 

논문상으로는 해당 파트입니다.

 

 

원래는 위의 식을 사용해야 하나, 논문에서는 초기의 학습을 위해서 다음과 같이 변경하도록 하고 있습니다.

 

 

즉 $log(1-D(G(z^{(i)})))$를 최소화하는 것이 아니라, $logD(G(z))$를 최대화하는 쪽으로 학습하라는 것입니다.

 

 

동일하게, generator의 optimizer gradient를 0으로 갱신해주고

 

다시 한번 noise sample을 만들어줍니다. 이 부분은 위 식에서 $z^{(i)}$로 표현됩니다.

 

 

이를 이용해서 generator의 input으로 투입하고, 이를 다시 discriminator에 넣어서 해당 데이터가 위조지폐인지 실제 지폐인지 discriminator가 판단합니다.

 

그리고 이 값에 log을 취한 뒤에 평균을 취해주고, 최대화를 해야 하므로 여기에 -1을 곱해서 진행합니다.

 

 

마지막으로 loss_generator를 backpropagation 해주고, optimizer_g도 한 단계 진행하도록 해줍니다.

 

 

 

다음으로는 실제 학습을 진행해주는 코드를 다뤄보겠습니다.

 

for epoch in tqdm(range(EPOCHS)):
    train(Gen_net, Dis_net, trainloader, d_optimizer, g_optimizer)

    if (epoch+1)%20 == 0:
        print('epoch %i / 400' % (epoch+1))
        
        noise_sam = torch.randn(16, latent_size).to(DEVICE)
        imshow_grid(Gen_net(noise_sam).view(-1, 1, 28, 28))
        print("\n")

 

위에서 언급한 train이라는 코드가 바로 학습을 진행하는 코드이고, 여기에 Gen_net과 Dis_net, trainloader, d_optimizer, g_optimizer를 인수로 넣어서 학습을 진행해줍니다. 

 

그리고 학습이 잘 되는지를 확인해보기 위해서, 20 에폭마다 16개의 noise variable을 만들어 이를 Generator network에 통과시켜서 나오게 되는 데이터를 시각화해줍니다.

 

epoch 20 ~ 60
에폭 80 ~ 120
에폭 140 ~ 180
에폭 200 ~ 240
에폭 260 ~ 300
에폭 320 ~ 360
에폭 380 ~ 400

 

400 에폭을 학습해감에 따라서, 만들어내는 숫자들이 깔끔해지는 것을 보실 수 있습니다.

 

특히, 가장 초기 20 에폭쯤에서는 완전히 random noise 느낌의 이미지를 만들어내지만 점점 갈수록 모양을 잡아나가는 것을 볼 수 있습니다.

 

물론 아직도 원본 데이터만큼 엄청나게 좋은 데이터를 만든다고 보기는 어렵지만, 굉장히 간단한 모델과 간단한 코드를 가지고도 어느 정도의 생성 성능을 보여줄 수 있음을 확인할 수 있었습니다.

 

해당 모델은 2014년 모델로, 그 이후에 다양한 생성 모델 방법론들이 나오면서 지금은 매우 고화질의 이미지들도 만들어내긴 하지만 이렇게 단순한 코드로도 어느정도 생성을 할 수 있다는 것이 놀랍다고 생각했습니다.

 

마지막으로, 학습이 완료된 모델을 가지고 test를 진행하는 코드를 다뤄보겠습니다.

 

# For test after training
vis_loader = torch.utils.data.DataLoader(testset, 16, True)
img_vis, label_vis = next(iter(vis_loader))
imshow_grid(img_vis)

아까 만들어준 testset을 가지고 DataLoader를 만들어주고, 16개만 뽑아내 줍니다. 

 

이를 통해 맨 처음에 정의한 imshow_grid 함수를 통해 16개만 샘플로 그리드 그림을 그려줍니다.

 

# Make samples by using trained generator model.
sample_noise = torch.randn(16, latent_size).to(DEVICE)
imshow_grid(Gen_net(sample_noise).view(-1, 1, 28, 28))

 

 

16개의 sample noise를 만들어주고, 이를 400 에폭 학습이 끝난 Generator network에 투입시켜서 이미지를 생성하고 이를 (-1, 1, 28, 28) 형태로 데이터를 다시 만들어 이를 plotting 해줍니다.

 

 

물론 그림의 성능이 그렇게 썩 좋지는 않지만 그래도 나름대로 완전한 랜덤 값에서 이미지를 만들어내고 있음을 확인할 수 있었습니다.

 

 

 

여기까지 GAN 코드를 직접 짜고 돌려보면서, 정말로 이미지 생성이 잘 되는지 확인해보았습니다.

 

위에서 설명한 코드는 제 Github 주소에 있으니, 직접 돌려보고 싶은 분들은 이를 확인해주시면 되겠습니다.

 

github.com/PeterKim1/paper_code_review

 

다음에는 또 다른 논문으로 찾아오겠습니다.

 

 

Reference

 

intelligence.korea.ac.kr/members/wschoi/seminar/tutorial/mnist/pytorch/gan/GAN-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC/

 

안녕하세요.

 

 

새롭게 (paper + code) review라는 항목을 만들었고, 여기에 하나하나 글을 채워나갈 예정입니다.

 

 

블로그 글에는 논문의 내용을 조금 더 디테일하게 정리하면서 나름대로 저의 생각을 얹어서 정리할 예정이고

 

 

그와 동시에 code의 세부적인 사항들도 함께 review하면서 논문 정리 글 + 해당 논문 코드 review로 해서

 

 

논문 내용과 코드를 하나의 set로 작성하는 식의 포멧을 이용해 논문을 정리해볼 예정입니다.

 

 

그래서 게시판의 이름을 (paper + code) review 라고 짓게 되었습니다.

 

 

물론 최신 논문들을 다루는 것도 좋지만, 개인적인 생각으로는 최근 모델은 대부분 과거의 모델을 기반으로 이를 더 좋게 향상한 것이므로

 

 

오히려 뿌리, 근본에 해당하는 모델들에 대해서 디테일하게 정리를 하는 것이 중요하지 않나 라는 생각을 했습니다.

 

 

물론 너무나도 유명한 모델들은 저 또한 이 모델이 어떤 컨셉이고 어떻게 쓰이는지 등의 개략적인 내용은 알고 있지만

 

 

단순히 모델만 아는 것 외에 논문을 자세히 들여다보고 코드를 세세히 보는 것을 통해 얻을 수 있는 

 

 

또 다른 insight가 존재한다고 생각합니다. 그래서 개인적으로는 논문을 조금 디테일하게 다루는 걸 좋아하는 편이고요.

 

 

이번 글에서는 너무나도 잘 알려진 모델인 Generative Adversarial Nets(GAN)을 정리해봅니다.

 

 

아무래도 2014년에 나온 논문이고 하다 보니, 그 이전에 사용되던 모델들이 나오고 해서 모든 것을 100% 소화하기가

 

 

사실은 조금 어려운 논문이라는 생각이 들었습니다. 하지만 조금이라도 더 완벽하게 이해하려고 노력을 해봤습니다.

 

 

논문 주소: arxiv.org/abs/1406.2661

 

 

Abstract

 

 

이번 논문에서는 데이터 분포를 포착하는(capture) 생성 모델(generative model)인 $G$와 샘플이 $G$에서 나왔다기보다는 학습 데이터로부터 나왔을 가능성을 추정하는 식별 모델(discriminative model) $D$의 두 모델을 동시에 학습하는 adversarial process를 통해 생성 모델을 추정하는 새로운 framework를 제안한다.

 

$G$에 대한 학습 절차는 $D$의 확률이 실수를 만들어낼 확률을 최대화하는 것을 통해 이루어진다.

 

이러한 framework는 minimax two-player game에 대응된다. 임의의 함수 $G$와 $D$의 공간에서 유일한 해는 존재하며 이때 $G$는 학습 데이터 분포를 회복하고(recover) $D$는 모든 곳에서 $1/2$이다.

 

$G$와 $D$가 multilayer perceptron으로 정의될 때, 전체 시스템은 backpropagation을 가지고 학습될 수 있다.

 

학습할 때나 샘플을 만드는 동안에는 Markov chains이나 unrolled approximate inference network를 필요로 하지 않는다.

 

실험에서는 생성된 샘플에 대한 정량적, 정성적 평가를 통해 framework의 잠재력을 검증하였다. 

 

 

 

1. Introduction

 

 

딥러닝의 가능성은 자연스러운 이미지, speech를 포함하고 있는 오디오 파동, 자연어 말뭉치에서의 symbol과 같은 인공지능 어플리케이션에서 마주할 수 있는 다양한 데이터들에 대해서 확률 분포를 나타낼 수 있는 풍부하고 계층적인 모델을 발견하는 것이다.

 

지금까지, 딥러닝에서의 가장 현저한 성공은 discriminative model과 관련되어 있어 왔으며, 보통 고차원의, 풍부한 센서 input을 class label로 mapping 하는 모델들이다. (흔히 얘기하는 분류 모델을 생각하시면 편합니다.)

 

이러한 현저한 성공은 주로 backpropagation과 dropout 알고리즘에 기반해왔으며, 특히 잘 작동하는(well-behaved) gradient를 가지는 piecewise linear units을 사용했다. (piecewise linear unit은 구간이 나눠져 있는 선형 함수, 대표적으로 RELU와 같은 함수를 의미합니다.)

 

Deep generative models은 적은 영향력을 가지고 있었는데, 이는 최대 우도 추정(maximum likelihood estimation)과 이와 관련된 전략에서 발생하는 다루기 곤란한 확률적 computation을 근사하는 것이 어려웠기 때문이며 piecewise linear units의 이점을 generative context에서 활용하기가 어려웠기 때문이다.

 

우리는 이러한 어려움들을 회피하는 새로운 생성 모델 추정 절차를 제안한다.

 

 

제안된 adversarial nets framework에서, generative model은 적(adversary)에 대해서 pitting 된다.

 

discriminative model은 샘플이 모델 분포에서 왔는지, 혹은 데이터 분포에서 왔는지를 결정하도록 학습한다.

 

generative model은 위조지폐를 만드려고 노력하고 이를 들키지 않고 사용하는 위조범과 비슷하다고 생각되어질 수 있으며

 

discriminative model은 위조 지폐를 발견하려고 노력하는 경찰과 비슷하다.

 

이 게임에서의 경쟁은 위조범과 경찰 모두 가짜가 진짜 지폐와 구별될 수 없을 때까지 각자의 방법을 향상한다.

 

 

이 framework는 많은 종류의 모델과 최적화 알고리즘을 위한 구체적인 학습 알고리즘을 만들어낼 수 있다.

 

이번 논문에서, 우리는 generative model에 random noise를 통과시켰을 때 multilayer perceptron을 통해 샘플들을 생성해내는 특별한 케이스에 대해서 탐구하며, discriminative model 또한 multilayer perceptron이다.

 

우리는 이러한 특별 케이스를 adversarial nets라고 부른다.

 

이런 경우에, 우리는 양쪽 모델을 매우 성공적인 backpropagation과 dropout algorithm만을 사용하여 학습할 수 있으며, generative model로부터 forward propagation만 사용하여 샘플을 만들 수 있다. (forward propagation이란 한국어로는 순전파라고 부르는데, input에 어떤 것을 투입하여 layer들을 거쳐 output을 뽑아내는 것을 의미한다고 이해하시면 됩니다.)

 

approximate inference나 Markov chain이 필요하지 않다. 

 

 

 

2. Related work

 

 

잠재 변수를 이용한 directed graphical model에 대한 대안은 restricted Boltzmann machines(RBMs)나 deep Boltzmann machines (DBMs) 및 수많은 변형들과 같은 잠재 변수를 이용한 undirected graphical model이다. 

 

이러한 모델들 내에서의 상호작용은 unnormalized potential function의 곱으로 제시되며, 랜덤 변수들의 가능한 상태에 대한 전역 합이나 전역 적분에 의해서 정규화된다. (확실하진 않지만, Bayes' theorem에서 분모가 normalize 하는 역할을 해주는 것과 유사한 의미인 것 같습니다...)

 

이 양(partition function)과 이에 대한 gradient는 가장 trivial instance 외에는 모두 intractable이며, 그들은 Markov chain Monte Carlo (MCMC) method에 의해서 추정될 수 있다. Mixing은 MCMC에 의존하는 학습 알고리즘에 중요한 문제를 제기한다. 

 

 

Deep belief networks (DBNs)는 하나의 undirected layer와 여러 개의 directed layers를 포함하고 있는 하이브리드 모델이다. Fast approximate layer-wise 학습 기준이 존재하긴 하지만, DBN는 undirected model과 directed model 모두와 연관된 연산적 어려움을 초래한다. 

 

Score matching이나 noise-contrastive estimation (NCE)와 같이 log-likelihood의 한계치를 정하지 않거나 근사하지 않는 대안적 기준 또한 제안되어 왔다. 두 가지 모두 정규화 상수까지 학습된 확률 밀도를 분석적으로 지정해야 한다. 

 

DBNs나 DBMs처럼 여러 층의 잠재 변수를 가지고 있는 다양한 흥미로운 generative models에서, 계산 가능한 unnormalized 확률 밀도를 끌어내는 것은 불가능하다. 

 

Denoising auto-encoders나 contractive autoencoder와 같은 어떤 모델들은 RBMs에 적용된 score matching과 매우 유사한 학습 규칙을 가지고 있다.

 

NCE에서는, 이번 연구처럼 discriminative 학습 기준은 generative model을 fitting 하기 위해서 사용된다.

 

하지만, 별도의 discriminative model을 fitting 시키기보다는, generative model 자체가 고정된 노이즈 분포로부터 나온 샘플을 구별하는 데 사용된다.

 

NCE는 고정된 노이즈 분포를 사용하기 때문에, 모델이 관측 변수의 작은 부분 집합에 대해 근사적으로 올바른 분포를 학습한 후에는 학습 속도가 크게 느려진다.

 

 

마지막으로, 몇몇 기법들은 확률 분포를 명시적으로 정의하는 것을 포함하는 게 아니라 오히려 희망하는 분포로부터 샘플을 뽑기 위해 generative machine을 학습시킨다. 

 

이 접근법은 이러한 machine들이 backpropagation에 의해서 학습되도록 설계될 수 있다는 이점을 가지고 있다.

 

이러한 분야에서의 저명한 최근 연구는 generative stochastic network (GSN) framework를 포함하며, 이는 generalized denoising auto-encoder를 확장하는 것이다.

 

둘 다 parameterized Markov chain을 정의하는 것으로 볼 수 있으며, 이는 즉 generative Markov chain의 한 단계를 수행하는 machine의 파라미터들을 학습한다.

 

GSNs와 비교했을 때, adversarial nets framework는 샘플링을 위해 Markov chain을 필요로 하지 않는다.

 

Adversarial nets가 생성하는 동안에 feedback loop를 필요로 하지 않기 때문에, 이들은 더더욱 piecewise linear units을 더 잘 활용할 수 있으며 이는 backpropagation의 성능을 향상하지만 feedback loop 안에서 사용되었을 때는 unbounded activation의 문제점을 가지게 된다.

 

Back-propagation을 이용해서 generative machine을 학습시키는 더 최근의 예시들은 auto-encoding variational Bayes와 stochastic backpropagation의 최근 연구들을 포함한다.

 

(개인적인 생각으로는, 워낙 예전 모델들에 대한 설명이라서 모든 걸 다 이해하기는 어려운 것 같습니다. )

 

 

 

3. Adversarial nets

 

 

Adversarial modeling framework는 모델들이 둘 다 multilayer perceptron이면 적용하기에 더욱 직관적이다.

 

Generator의 분포 $p_g$를 데이터 $x$에 대해서 학습하기 위해서, input noise variables $p_z(z)$에 prior를 정의하고 data space로의 mapping을 $G(z; \theta_g)$로 표현하며 $G$는 parameter $\theta_g$를 가지는 multilayer perceptron으로 표현되는 미분 가능한 함수이다.

 

그다음으로 single scalar를 결과로 내는 두 번째 multilayer perceptron $D(x; \theta_d)$를 정의한다.

 

$D(x)$는 $x$가 $p_g$보다 data로부터 나왔을 가능성을 나타낸다.

 

우리는 $D$를 training examples와 $G$로부터 나온 샘플 모두에게 올바른 label을 할당할 확률을 최대화하기 위해 훈련한다.

 

동시에 $G$가 $log(1-D(G(z)))$를 최소화하도록 학습한다.

 

다시 말해서, $D$와 $G$는 다음의 two-player minimax game을 가치 함수 $V(G, D)$를 가지고 진행하게 된다.

 

Equation 1. GAN의 가치함수 V(D, G)

 

다음 논문의 내용을 전개하기 전에, 먼저 GAN의 가치 함수 $V(D, G)$에 대해서 정리를 하고 넘어가겠습니다.

 

결국 이 가치 함수가 $D$와 $G$ 관점에서 지향하는 방향성이 무엇인지를 파악하는 것이 해당 논문의 가장 핵심이라고 생각합니다.

 

먼저 $G$ 관점에서 살펴보겠습니다.

 

$G$ 관점에서는 해당 가치 함수를 minimize 하는 것이 목표이며, Equation 1에서 존재하는 두 항 중에서 실제로 $G$와 관련이 있는 항은 두 번째 항이므로, 두 번째 항을 통해서 $G$가 지향하는 목표가 무엇인지 확인해보겠습니다.

 

Equation 1의 두 번째 항

 

먼저, $E$의 아래 첨자로 들어가는 $z$는 noise입니다. $E$를 표시했으므로, noise 분포인 $p_z(z)$에서 나온 noise $z$에 대해서 해당 값의 기댓값을 계산하겠다는 의미입니다.

 

다음으로, log 안쪽의 식을 보겠습니다.

 

$log(1-D(G(z)))$라고 적혀 있는데, $D(G(z))$를 $x$로 치환한다고 하면 결국 이 식은 $log(1-x)$가 되는 것이죠?

 

즉 $log(1-x)$를 최소화하는 것이 목표라는 것입니다. $log(1-x)$라고 했을 때 바로 그래프가 떠오르시는 분도 계시겠지만, 기억이 안 날 수 있으니 그래프를 들고 와 보겠습니다.

 

y= log(1-x) 그래프

 

해당 그래프에서 함숫값이 가장 작아지려면 어떻게 되어야 할까요?

 

$x$가 1에 가까워지면 함숫값이 매우 작아지게 됩니다. 즉, $D(G(z))$가 1이 되어야 합니다.

 

$D(x)$는 앞에서도 언급했지만, $D(x)$는 $x$가 $p_g$보다 data로부터 나왔을 가능성을 나타냅니다.

 

즉, $D(G(z))$는 noise $z$를 입력으로 받아서 $G$를 통해 나오게 된 결과를 입력으로 받았을 때, 이것이 $p_g$보다 data로부터 나왔을 가능성을 나타냅니다.

 

$D(G(z))$가 1이 된다는 것은, $D$가 해당 데이터를 data로부터 나왔다고 판단할 수 있을 정도로 $G$가 정말 감쪽같은 결과를 만드는 것을 의미합니다. 

 

맨 처음의 예시에서 $G$는 위조지폐범이였고, $D$는 위조지폐를 발견하려고 노력하는 경찰이라고 비유했었는데 $D(G(z))$가 1이 된다는 것은 경찰이 완전히 속을 정도로 위조 지폐범이 완벽한 위조 지폐를 만들었음을 의미합니다.

 

 

다음으로는 $D$ 관점에서 보겠습니다.

 

$D$는 가치 함수 $V(D, G)$의 두 항에 모두 포함되어 있으므로, 두 항을 모두 살펴보겠습니다.

 

$D$는 가치 함수 $V(D, G)$를 최대화하는 것을 목표로 하고 있으며, 두 항의 합이 최대가 되려면 결국 각 항들이 최댓값을 가져야 합니다.

 

방금 봤었던 두 번째 항을 먼저 살펴보겠습니다.

 

Equation 1의 두 번째 항

 

$log(1-D(G(z)))$에서 $D(G(z))$를 $x$로 치환한다면, 아까와 동일하게 결국 $log(1-x)$를 다루는 문제가 됩니다.

 

따라서, $D$ 입장에서는 해당 식을 최대로 하고 싶어 합니다.

 

그렇다면 어떻게 해야 $log(1-x)$가 최댓값을 갖게 될까요? 당연히 $x$의 값을 최대한으로 낮춰주면 됩니다. (위의 그래프 참고)

 

하지만, $x$는 $D(G(z))$로, 어떤 input값이 어떤 분포로부터 나왔는지를 판단한 값으로, 해당 값은 0부터 1 사이의 값을 갖게 됩니다. 따라서, $log(1-x)$의 그래프에서 정의역이 [0, 1]인 상황을 생각해야 된다는 것이죠.

 

이렇게 된다면, $x$가 0이 되었을 때, $log(1-x)$가 최댓값을 갖게 되며 이는 $D(G(z))$가 0이 되는 것을 의미합니다.

 

$D(G(z))$가 0이 된다는 것은, noise $z$를 입력으로 받아서 $G$가 만들어낸 결과를 $D$ 입장에서 판단했을 때 이것이 $p_g$에서 나왔다고 정확하게 판단함을 의미합니다.

 

앞의 비유를 가지고 생각해보자면, 위조지폐범이 만들어낸 위조지폐를 보고 경찰이 이것은 100%로 위조지폐다!라고 완벽하게 판단하는 경우를 의미합니다. 

 

다음으로는, 가치 함수의 첫 번째 항을 살펴보겠습니다.

 

Equation 1의 첫 번째 항

 

$E$의 아래 첨자로 들어간 $x$는 우리가 가지고 있는 실제 데이터를 의미합니다.

 

다음으로, 최대로 만들고 싶은 식이 바로 $logD(x)$인데요, 이번에는 $logx$ 그래프를 보겠습니다.

 

y = logx 그래프

 

아까 얘기했지만, $D(x)$ 값은 [0, 1]에서 값을 갖게 됩니다. 따라서, $logx$ 그래프에서 정의역이 [0, 1]인 경우를 의미하는 것이죠.

 

이는 $x$가 1일 때 최댓값을 가지게 되며, 이는 $D(x)$가 1이 됨을 의미합니다.

 

앞의 비유를 이용해서 설명해보자면, 실제 지폐를 보고 경찰이 정확하게 이 지폐가 실제 지폐임을 알아맞히는 것을 의미하는 것이죠.

 

 

지금까지 설명한 내용을 정리하자면, 가치 함수를 통해서 알 수 있는 사실은

 

$G$의 목표는 위조지폐를 감별하는 경찰이 못 맞출 정도로 매우 정교한 위조 지폐를 만들어내는 것을 목표로 한다.

 

즉, $D(G(z))$가 1이 되도록 학습하는 것이 $G$의 목표가 된다.

 

$D$의 목표는 위조지폐는 위조지폐라고 감별하고, 실제 지폐는 실제 지폐라고 정확히 분류하는 것을 목표로 한다.

 

즉, $D(x)$는 1이 되도록, $D(G(z))$는 0이 되도록 학습하는 것이 $D$의 목표가 된다.

 

 

 

다음 section에서는 adversarial nets에 대한 이론적인 분석을 제시하며, 기본적으로 non-parametric limit에서 $G$와 $D$가 충분한 capacity를 제공받기 때문에 학습 기준이 adversarial nets가 data generating distribution을 복구하도록 허용함을 보여준다.

 

해당 접근법에 대한 덜 공식적이지만 더욱 교육학적인 설명인 Figure 1을 참고하자.

 

실제로, 우리는 이 게임을 반복적이고 수학적인 접근법을 사용하여 실행해야 한다. 

 

$D$를 학습 내부 loop에서 완성에 가까이 최적화하는 것이 computationally prohibitive 하며, 유한한 데이터셋에서는 과적합을 야기하게 된다.

 

그 대신에, 우리는 $D$를 최적화하는 $k$단계와 $G$를 최적화하는 1단계를 번갈아 진행한다.

 

이는 $D$가 이것의 최적해 근처에 있도록 유지되는 결과를 만들며, 그동안 $G$는 충분히 느리게 변화한다.

 

이러한 전략은 학습의 내부 loop의 일부분으로써 Markov chain에서 burning 하는 것을 회피하기 위해 SML/PCD 학습이 one learning step에서부터 다음 learning step까지 Markov chain으로부터의 샘플을 유지하는 방법과 유사하다. (아무리 봐도 여기서 burning의 의미를 알 수가 없네요...)

 

해당 절차는 Algorithm 1에 나와있다.

 

 

실제로, Equation 1은 아마 $G$가 잘 학습하기 위해 충분한 gradient를 제공하지 못한다.

 

학습 초기에, $G$가 잘 못할 때, 생성된 샘플들이 training data와 분명하게 다르기 때문에 $D$는 높은 confidence로 샘플들을 거절할 수 있다.

 

이러한 경우에, $log(1-D(G(z)))$는 saturate 된다. 

 

$log(1-D(G(z)))$를 최소화하기 위해 $G$를 학습하는 것 대신에, $logD(G(z))$를 최대화하도록 $G$를 학습할 수 있다. 

 

이러한 목적 함수는 $G$와 $D$의 dynamics의 동일한 고정된 지점의 결과를 낳지만, 학습 초기에 더욱 강력한 gradient를 제공한다.

 

 

 

위 단락에서 설명한 내용은 다음과 같은 그림으로 이해해볼 수 있습니다.

 

y=logx vs y=log(1-x)

 

학습 초기에는, $G$가 누가 봐도 위조지폐처럼 생긴 샘플들을 만들기 때문에 $D(G(z))$가 매우 작은 값이 나오게 됩니다.

 

따라서, 기존 가치 함수처럼 $log(1-D(G(z)))$를 최소화하도록 한다고 가정하면 오른쪽에 있는 그림처럼 Gradient의 값이 매우 작은 값을 갖게 됩니다. (접선의 기울기가 gradient라고 생각해주시면 이 값이 매우 작음을 알 수 있습니다. 해당 그래프에서의 $x$는 $D(G(z))$이며, 이 값이 0.1라고 생각하고 접선을 오른쪽과 왼쪽 그래프에 만들었습니다.)

 

하지만 이를 $log(D(G(z)))$를 최대화하는 것으로 만들게 되면, 왼쪽 그림에서 보이듯이 $D(G(z))$가 작은 값을 가질 때도 Gradient의 값이 매우 큰 값을 가지는 것을 알 수 있습니다.

 

이러한 방법을 통해 $G$가 학습 초기 단계에서 좋지 못한 샘플을 만들 때도 원활하게 학습이 이루어질 수 있도록 할 수 있습니다.

 

 

Figure 1

 

위의 Figure 1은 Generative adversarial nets가 학습되는 과정을 나타내고 있습니다.

 

Generative adversarial nets는 데이터 생성 분포(검은색 점선) $p_x$에서 나온 샘플을 generative distribution $p_g(G)$ (녹색 실선)에서 나온 샘플로부터 구별하기 위해 discriminative distribution($D$, 파란색 선)을 동시에 업데이트시키면서 학습하게 된다.

 

아래쪽 수평선은 $z$가 샘플링된 domain이며, 이 경우에서는 uniform이다.

 

위쪽 수평선은 $x$ domain의 일부이다. 

 

위쪽을 향한 화살표들은 어떻게 mapping $x = G(z)$가 non-uniform distribution $p_g$를 변형된 샘플에 도입하는지를 나타낸다.

 

$G$는 $p_g$의 높은 밀도의 영역에서 수축하고 $p_g$의 낮은 밀도의 영역에서 확장된다.

 

(a) adversarial pair가 수렴에 가까워졌다고 생각하자. 이때, $p_g$는 $p_{data}$과 유사하며 $D$는 부분적으로 정확한 분류기이다. ($G$가 아직 실제 데이터 분포와 많이 다르고, discriminative distribution도 아직은 많이 부정확한 상태)

 

(b) algorithm의 inner loop에서 $D$는 데이터로부터의 샘플을 구별하도록 학습되며, $D^*(x) = \frac{p_{data}(x)}{p_{data}(x)+p_g(x)}$에 수렴한다.  ($D$는 정확하게 분류할 수 있는 최적의 분류기가 된 상태)

 

(c) $G$가 업데이트된 후, $D$의 gradient는 $G(z)$가 데이터로 분류될 가능성이 더욱 높은 지역으로 흘러가도록 안내한다. ($G$가 점점 실제 데이터 분포에 가까워지고 있는 상태)

 

(d) 여러 단계의 학습 후에, $G$와 $D$가 충분한 용량을 가진다면, 이들은 더욱 향상될 수 없는 지점에 도달하게 되는데 이는 $p_g = p_{data}$이기 때문이다. Discriminator는 두 분포 사이를 구분할 수 없게 되며 즉, $D(x) = \frac {1}{2}$가 된다. ($G$가 실제 지폐와 거의 똑같은 위조지폐를 만들게 되어 경찰이 위조지폐와 실제 지폐를 구분할 수 없는 상태가 되어버림.)

 

 

 

4. Theoretical Results

 

 

 

Generator $G$는 $z$가 $p_z$에서 얻어졌을 때 샘플 $G(z)$의 분포로써 확률 분포 $p_g$를 암시적으로 정의한다.

 

그러므로, 충분한 capacity와 학습 시간이 주어졌을 때 Algorithm 1이 $p_{data}$의 좋은 estimator로 수렴하기를 원한다.

 

이번 section의 결과는 non-parametric setting으로 이루어졌으며, 이는 확률 밀도 함수의 공간에서 수렴을 학습하는 과정을 통해 무한한 capacity를 가지는 모델을 표현한다는 의미이다.

 

section 4.1에서는 해당 minimax game이 global optimum을 $p_g = p_{data}$에서 가짐을 보일 것이다.

 

다음으로 section 4.2에서는 Algorithm 1이 Equation 1을 최적화하여 원하는 결과를 얻음을 보일 것이다.

 

Algorithm 1

 

Algorithm 1은 adversarial nets의 minibatch stochastic gradient descent training을 나타내고 있다. Discriminator에 적용되는 step의 수인 $k$는 hyperparameter이다. 실제 실험에서는 실험에서 가장 저렴한 option인 $k$ = 1로 설정하였다. (연산량 관점에서 가장 부담이 없다는 의미라고 보시면 됩니다.)

 

해당 알고리즘은 크게 두 부분으로 나눠져 있는데, 가볍게 짚고 넘어가 보겠습니다.

 

Algorithm 1 - step 1

 

첫 번째 단계는 noise prior $p_g(z)$에서 $m$개의 샘플을, 데이터 생성 분포 $p_{data}(x)$에서 $m$개의 샘플을 추출하여 이전에 논의해본 가치 함수를 discriminator 관점에서 최대화하는 방향으로 업데이트합니다.

 

일반적으로 딥러닝 library들은 Gradient Descent를 지원하기 때문에, 실제 코드에서 구현할 때는 해당 가치 함수에 음수를 취하여 Gradient Descent를 적용하면 결론적으로는 Gradient Ascent를 구현할 수 있을 것입니다.

 

앞에서 언급했지만, discriminator의 목표는 실제 지폐를 실제 지폐로 정확히 분류하고, 위조지폐를 위조지폐로 정확히 분류하는 것을 목표로 합니다.

 

여기서는 $k$ step 동안 진행하도록 for문으로 구성되어 있는데, 실제 실험에서는 $k = 1$로 진행했다고 합니다.

 

Algorithm 1 - step 2

 

두 번째 단계는 noise prior $p_g(z)$에서 $m$개의 noise sample을 샘플링해서 이를 통해 generator를 업데이트하는 과정입니다.

 

Generator는 해당 식을 최소화 하는 것을 목표로 하기 때문에, gradient descent를 그대로 진행해주면 됩니다.

 

앞에서 언급했지만, Generator는 경찰이 구별하기 어려울 정도로 정교한 위조지폐를 만드는 것을 목표로 합니다.

 

 

 

4.1 Global Optimality of $p_g = p_{data}$

 

 

 

주어진 generator $G$에 대해서 최적의 discriminator $D$를 먼저 고려해보자.

 

Proposition 1. $G$가 고정되어 있을 때, 최적의 discriminator $D$는 다음과 같다.

 

 

Proof. 어떤 generator $G$가 주어졌을 때 Discriminator $D$에 대한 학습 기준은 $V(G, D)$를 최대화하는 것이다.

 

 

(앞에서 언급되었던 $V(G, D)$와 모양이 살짝 다른데, 이는 Expectation 형태로 되어 있던 식을 integral 형태로 변환한 것입니다. 따라서, 식의 모양만 달라진 것이라고 보시면 되겠습니다.)

 

(첫 번째 줄의 두 번째 항이 두 번째 줄로 넘어가는 이유는, 어떤 generator $G$가 주어졌다고 가정하기 때문에 $z$를 $x$로 변환했다고 생각하시면 되겠습니다.)

 

(0, 0)이 아닌 $(a, b) \in R^2$에 대해서, 함수 $y -> alog(y) + blog(1-y)$는 [0, 1]에서 $\frac{a}{a+b}$에서 최댓값을 갖게 된다. Discriminator는 $Supp(p_{data}) \cup Supp(p_g)$의 외부에서 정의될 필요가 없다.

 

(여기서 Supp은 한국어로 지지 집합이라는 개념인데, 함수값이 0이 되지 않는 점들의 집합정도로 이해하면 되는 것 같습니다. (a, b)가 (0, 0)이 아니라고 했기 때문에, 지지집합 외부에서 따로 정의할 필요가 없다고 말하는 것 같습니다.

함수 $y -> alog(y) + blog(1-y)$가 갑자기 나온 이유는, 위의 (3) 식에서 $p_{data}(x)log(D(x)) + p_g(x)log(1-D(x))$를 이와 같은 형태로 만들 수 있기 때문입니다. 즉, $a = p_{data}(x), y = D(x), b = p_g(x)$로 치환하면 형태가 딱 맞습니다.

$alog(y) + blog(1-y)$를 $y$에 대해서 미분해보시면, $\frac{a}{a+b}$에서 최댓값을 가짐을 확인할 수 있습니다.)

 

 

$D$에 대한 학습 목적 함수는 $Y$가 나타내는 것이 $x$가 $p_{data}$로부터 왔는지($y=1$) 아니면 $p_g$에서 왔는지($y=0$)일 때, 조건부 확률 $P(Y = y | x)$을 추정하는          log-likelihood를 최대화하는 것으로 해석될 수 있다.

 

Equation 1에서의 minimax game은 다음과 같이 다시 쓰일 수 있다.

 

 

Theorem 1. 가상의 학습 기준 $C(G)$의 전역 최솟값은 $p_g = p_{data}$일 때 얻어질 수 있다. 해당 지점에서, $C(G)$는 $-log4$의 값을 가진다.

 

Proof. $p_g = p_{data}$일 때, $D^*_G(x) = \frac{1}{2}$이다. (Equation 2.를 고려해보자.) 이러한 이유로, $D^*_G(x) = \frac{1}{2}$에서 Eq.4를 확인해봤을 때, $C(G) = log(1/2) + log(1/2) = -log4$임을 알 수 있다. 이 값이 $C(G)$의 가능한 가장 좋은 값임을 확인하기 위해서, $p_g = p_{data}$에 도달했을 때 다음을 확인할 수 있다.

 

 

그리고 $C(G) = V(D^*_G, G)$에서 해당 식을 뺐을 때, 다음을 얻게 된다.

 

 

 

중간 과정이 다 빠진 상태로 바로 (5) 식이 나오게 되는데, 이 과정을 조금 더 상세하게 적어드리겠습니다.

 

(5) 식 상세 과정

 

처음 줄은 (4)의 가장 마지막 줄의 식부터 시작합니다. $E$ 형식으로 적힌 식을 먼저 integral 형태로 변환해줍니다.

 

다음으로는, 식에 log4를 빼주고, 원래 식과 동일해야 하므로 다시 log4을 더해줍니다.

 

$log4$ = $2log2$ 이므로 이와 같이 표시해줬으며, 그다음 줄에서는 $log2$를 각 integral 식에 분배해줘서 integral 식에 log 2가 하나씩 들어갔습니다. log에서의 합은 안에서의 곱이 되므로, 2 * $p_{data}(x)$와 2 * $p_g(x)$가 된 것이라고 보시면 되겠습니다.

 

그다음으로는 KL이라는 식이 나오게 되는데, 이는 Kullback-Leibler divergence라는 것으로 두 분포의 차이를 나타낼 때 사용됩니다. $KL(p \parallel q)$ = $\int_x plog \frac{p}{q} dx$가 성립합니다. 따라서 (5) 식 상세 과정의 마지막 두 번째 줄에서 KL로 표시가 된 것이라고 보시면 됩니다. KL divergence까지 다루면 글이 너무 길어질 것 같아, KL divergence에 대한 상세한 내용은 생략하겠습니다.

 

이 식을 다시 한번 정리해서 Jensen-Shannon divergence로 바꿀 수 있습니다.

 

 

JSD는 Jensen-Shannon divergence인데, 이는 다음을 만족합니다.

 

 

따라서, $2 * JSD(p_{data} \parallel p_g)$로 표현될 수 있는 것입니다.

 

 

두 분포 사이의 JSD는 항상 non-negative이며 두 분포가 동일할 때만 0이므로, $C^* = -log4$가 $C(G)$의 전역 최솟값임을 알 수 있다. 이때 유일해는 $p_g = p_{data}$이다. 즉, 생성 모델이 데이터 생성 과정을 완벽하게 복제하는 상황이다.

 

 

 

4.2 Convergence of Algorithm 1

 

 

 

Proposition 2. $G$와 $D$가 충분한 capacity를 가진다면, 그리고 Algorithm 1의 각 단계에서, discriminator는 $G$가 주어졌을 때 discriminator의 최적 값에 도달하도록 허용되며, 해당 기준을 향상하기 위해 $p_g$는 업데이트된다.

 

 

그러고 나서 $p_g$는 $p_{data}$에 수렴한다.

 

 

Proof. $V(G, D) = U(p_g, D)$가 위의 기준에서 이루어지는 $p_g$의 함수라고 하자. $U(p_g, D)$는 $p_g$에서 convex이다. Convex function의 상한에 대한 하위 미분은 최댓값이 얻어지는 지점에서의 함수의 미분 값을 포함한다.

 

다르게 얘기해서, 만약 $f(x) = sup_{a \in A} f_\alpha(x)$이고 $f_\alpha (x)$가 모든 $\alpha$에 대해서 $x$에서 convex라면, $\beta = argsup_{a \in A} f_\alpha (x)$일 때 $\partial f_\beta(x) \in \partial f$이다. 

 

이는 최적의 $D$에서 이와 대응되는 $G$가 주어졌을 때 $p_g$에 대해 gradient descent update를 계산하는 것과 동일하다.

 

$sup_D U(p_g, D)$는 Theorem 1에서 증명된 것처럼 유일한 전역 최적 값을 가질 때 $p_g$에서 convex이며, 그러므로 $p_g$의 충분히 작은 업데이트만을 가지고도 $p_g$는 $p_x$에 수렴할 수 있다.

 

(위의 내용이 워낙 수학적인 내용이 많아, 100% 이해를 할 수는 없었지만 대략 우리가 생각하는 가치 함수인 $V(G, D)$가 $p_g$의 함수이며 convex 함수이기 때문에 gradient descent update를 계산하는 것과 동일한 과정을 통해서 결국 몇 번의 업데이트를 통해 $p_g$가 최적 값인 $p_x$에 수렴할 수 있다는 그런 내용으로 보입니다.)

 

실제로, adversarial nets은 함수 $G(z;\theta_g)$를 통해서 $p_g$ 분포의 제한된 부분만 표현할 수 있으며, 따라서 $p_g$ 자체를 최적화하기보다는 $\theta_g$를 최적화한다.

 

$G$를 정의하기 위해서 multilayer perceptron을 사용하는 것은 parameter 공간에서 여러 개의 중요한 문제들을 만든다.

 

하지만, 실전에서 multilayer perceptron가 훌륭한 성능을 가지고 있기 때문에 그들의 이론적 보증이 부족함에도 불구하고 이를 사용하는 것이 합리적이다.

 

 

 

5. Experiments

 

 

 

MNIST, Toronto Face Database (TFD), CIFAR-10을 포함하는 여러 개의 데이터셋에 adversarial nets을 학습시켰다.

 

Generator nets는 RELU와 sigmoid activation을 사용하였으며, discriminator net은 maxout activation을 사용하였다.

 

Dropout은 discriminator net을 학습할 때 적용되었다.

 

비록 우리의 이론적인 framework가 dropout의 사용과 generator의 중간 layer에서의 다른 노이즈를 허용하지만, generator network의 맨 아래 layer의 input으로만 노이즈를 사용했다.

 

 

Gaussian Parzen window를 $G$에 의해 생성된 샘플에 fitting 하고, 이 분포 하에서 log-likelihood를 보고하는 것을 통해 $p_g$하에서의 test set data의 확률을 추정하였다. 

 

Gaussian의 $\sigma$ parameter는 validation set에 cross validation을 통해 얻었다.

 

이 절차는 Breuleux et al.에 의해 도입되었으며, 정확한 가능도를 계산할 수 없는 여러 가지 생성 모델에 대해서 사용되었다.

 

결과는 Table 1에 나타나 있다.

 

가능도를 추정하는 이 방식은 어쨌든 높은 분산을 가지고 있으며, 고차원 공간에서 잘 작동하지 않으나 현재 우리 지식에서는 최선의 방법이다.

 

샘플링은 할 수 있지만 가능도를 직접적으로 추정할 수 없는 생성 모델에서의 진보는 어떻게 이러한 모델들을 평가할 수 있는지에 대한 추후 연구에 동기부여를 제공한다.

 

(아마 이 시기에는 생성 모델들이 많지 않아서 어떤 기준으로 평가해야 되는지 몰라 이러한 방식으로 평가한 것 같습니다.)

 

 

Figure 2와 3에서는 학습이 끝난 후의 generator net에서 나온 샘플을 보여준다. 

 

우리가 기존 방법들에 의해서 만들어진 샘플보다 여기 나온 샘플들이 더 좋다고 단언할 수는 없지만, 여기 나온 샘플들이 다른 연구에서 나타나는 더 좋은 생성 모델과 적어도 경쟁할 수 있으며 adversarial framework의 잠재력을 강조할 수 있을 것이라고 생각한다.

 

Figure 2

 

Figure 3

 

 

6. Advantages and disadvantages 

 

 

 

우리가 제안하는 새로운 framework는 기존의 modeling framework와 비교했을 때 유리한 점과 불리한 점이 존재한다.

 

불리한 점들은 주로 $p_g(x)$에 대한 명시적인 representation이 없다는 것이며, 학습하는 동안에 $D$가 반드시 $G$와 동시에 움직여야 한다는 점이다. (특히, $G$는 $D$가 업데이트되는 것 없이 너무 많이 학습되어서는 안 되며, 이는 $G$가 너무 많은 $z$값들을 같은 값의 $x$로 붕괴(collapse)되는 "Helvetica scenario"를 피하고 모델이 충분한 다양성을 가지도록 하기 위함이다.)  => 일반적으로 'Mode collapse'라고 하는 현상을 이렇게 부르는 것 같습니다. 즉 Generator가 다양한 모습을 만들어내지 못하는 상황을 의미하는 것 같습니다.

 

이는 Boltzmann machine의 negative chain이 학습 단계 동안에 반드시 최신으로 유지되어야 하는 것과 같다.

 

유리한 점은 Markov chains이 결코 필요하지 않다는 점이며, gradient를 얻기 위해 오직 backpropagation만 사용되고 학습 동안에 inference가 필요하지 않으며 매우 다양한 함수들이 모델에 포함될 수 있다.

 

Table 2는 다른 생성 모델 접근법들과 generative adversarial nets와의 비교를 요약한다.

 

Table 2

 

앞에서 서술한 유리한 점은 주로 연산적인 부분이다. Adversarial model들은 또한 데이터 예시를 통해 직접적으로 generator network를 업데이트해주지 않아도 된다는 점에서 어떠한 통계적 이점을 얻을 수 있으며, 또한 gradient가 discriminator를 통해서 흐른다는 이점이 있다.

 

이는 input의 요소들이 직접 generator의 parameter들로 복사되지 않는다는 것을 의미한다.

 

Adversarial networks의 또 다른 이점은 이들이 매우 날카롭고 심지어 퇴보적인 분포를 나타낼 수 있지만, Markov chains를 기반으로 하는 방법들은 체인이 모드 간에 혼합이 될 수 있도록 분포가 다소 blurry 해야 함을 필요로 한다.

 

 

 

7. Conclusions and future work

 

 

해당 framework는 많은 단순한 확장이 가능하다.

 

 

1. 조건부 생성 모델(conditional generative model) $p(x | c)$를 $G$와 $D$에 $c$를 input으로 추가함으로써 얻을 수 있다.

 

2. $x$가 주어졌을 때 $z$를 예측하는 보조적인 네트워크를 학습하는 것을 통해 Learned approximate inference가 수행될 수 있다.

 

이는 wake-sleep algorithm으로 학습된 inference net과 유사하지만, generator net가 학습을 끝낸 후 고정된 generator net에 대해서 inference net을 학습시킬 수 있다는 이점이 있다.

 

3. 파라미터들을 공유하는 조건부 모델들의 family를 학습시키는 것을 통해 $S$가 $x$의 인덱스의 부분집합일 때 해당 모델은 모든 조건부 확률

을 근사적으로 모델링할 수 있다. (아무리 찾아봐도 저 표기를 직접 타이핑할 수 없어서 그림으로 대체합니다.)

 

본질적으로, deterministic MP-DBM의 stochastic exntension을 실행하기 위해서 adversarial nets을 사용할 수 있다.

 

4. Semi-supervised learning: discriminator나 inference net의 features는 제한된 labeled data만 이용 가능할 때 classifier의 성능을 향상할 수 있다.

 

5. Efficiency improvements: $G$와 $D$를 조정하기 위한 더 나은 방법을 분할하거나, 훈련 중에 샘플 $z$에 대한 더 나은 분포를 결정한다면 학습이 크게 가속화될 수 있다. (Generator와 Discriminator를 학습하는 방법을 개발하거나, 혹은 $z$에 대한 prior 분포를 다르게 결정하면 학습에 도움이 될 수 있다는 말인 것 같습니다.)

 

이번 논문은 adversarial modeling framework의 실행 가능성을 검증하였으며, 이러한 연구 방향이 유용하다는 것을 검증할 수 있다고 주장한다.

 

 

 

드디어 논문의 내용을 모두 다뤄보았습니다.

 

일부 내용들은 심도 있게 다루지 않았음에도 생각보다 작성하는데 들어가는 시간이 꽤 오래 걸리네요.

 

다음 글에서는 해당 논문을 코드로 어떻게 구현할 수 있는지를 살펴보겠습니다.

 

 

앞에서 다룬 다항식 곡선 피팅 예시에서 가장 좋은 일반화 능력을 가지는 최적의 다항식 차수가 있다는 것을 확인할 수 있었습니다.

 

 

다항식의 차수에 따라서 모델의 자유 매개변수의 수가 결정되며, 이에 의해서 모델의 복잡도가 결정되게 됩니다.

 

 

또한, 정규화된 최소 제곱법의 경우에는 정규화 계수 $\lambda$도 모델의 실제적인 복잡도에 영향을 미쳤습니다.

 

 

혼합 분포나 신경망 등의 더 복잡한 모델의 경우 복잡도를 통제하는 매개변수가 더 많을 수도 있습니다.

 

 

실제 응용 사례에서는 이러한 매개변수들의 값을 결정해야하며, 이때의 목표는 새로운 데이터에 대한 예측 성능을 최적화하는 것입니다. 

 

주어진 한 모델의 매개변수의 값을 결정하는 것뿐만이 아니라 다양한 여러 모델들을 고려하여 해당 응용 사례에 가장 적합한 모델을 선택해야 할 경우도 있습니다.

 

 

최대 가능도 접근법에서 이미 확인한 것과 같이, 훈련 집합에서의 좋은 성능이 반드시 좋은 예측 성능을 보장해 주지는 못합니다. 이는 과적합 문제 때문입니다. 이를 해결할 한 가지 방법은 데이터가 충분할 경우 일부의 데이터만 사용하여 다양한 모델과 모델의 매개변수들을 훈련시키고 독립적인 데이터 집합인 검증 집합(Validation set)에서 이 모델들과 매개변수들을 비교 / 선택하는 것입니다.

 

 

만약 한정된 크기의 데이터 집합을 바탕으로 반복적으로 모델 디자인을 시행한다면, 검증 집합에 대해서도 과적합 문제가 발생할 수 있습니다. 이런 상황을 방지하기 위해 시험 집합(Test set)을 따로 분리해 두고 이 집합을 통해서 선택된 모델의 최종 성능을 판단하는 것이 좋을 수도 있습니다.

 

 

하지만 대부분의 실제 경우에는 데이터의 공급이 제한적이므로, 시험 집합을 별도로 분리해서 사용하는 것이 부담스러울 수 있습니다.

 

그리고 검증 집합의 크기가 작을 경우는 예측 성능에 대한 추정값이 정확하지 않을 수도 있습니다.

 

이러한 딜레마를 해결할 수 있는 한 가지 방법은 바로 교차 검증법(Cross validation)입니다.

 

그림 1.18

그림 1.18은 교차 검증법을 설명하는 그림입니다. 

 

교차 검증법은 전체 데이터(S) 중 데이터의 (S-1) / S비율만큼 훈련에 사용하고, 모든 데이터를 다 활용하여 성능을 추정할 수 있습니다.

 

특히 데이터가 부족할 경우에는 S = N의 교차 검증법을 고려할 수도 있습니다. 여기서 N은 전체 데이터 포인트의 숫자입니다.

 

따라서, S = N 교차 검증법은 데이터 포인트 하나만 남겨두고(leave-one-out) 모델을 훈련시키는 테크닉입니다.

 

그림에서 빨간색 블록으로 표시되어 있는 것이 남겨 두는 집합이 되며, 이를 이용해서 검증을 진행합니다.

 

그리고 최종 성능 점수를 도출할 때는 S번의 실행에서의 성능 점수를 평균 내어서 도출하게 됩니다.

 

 

 

교차 검증법의 주요 단점 중 하나는 S의 수가 늘어남에 따라서 모델 훈련의 시행 횟수가 함께 늘어난다는 점입니다. 이는 훈련 자체가 계산적으로 복잡할 경우에 문제가 될 수 있습니다. 

 

분리된 데이터를 활용하여 성능을 측정하는 교차 검증법의 또 다른 문제점은, 한 가지 모델에 여러 가지 복잡도 매개변수가 있을 경우(예를 들면 여러 종류의 정규화 매개변수)에 발생합니다. 여러 매개변수들의 조합들을 확인해 보기 위해서는 최악의 경우 매개변수 숫자에 대해 기하급수적인 수의 훈련 실행이 필요할 수 있습니다.

 

따라서 이를 통해, 이보다 더 나은 방식이 필요하다는 것을 알 수 있습니다. 이상적인 방식에서는 훈련 집합만을 활용하여 여러 종류의 hyperparameter와 각 모델 종류에 대한 비교를 한 번의 훈련 과정동안 시행할 수 있어야 합니다.

 

이를 위해서는 오직 훈련 집합만을 활용하는 성능 척도가 필요합니다. 또한, 이 척도는 과적합으로 인한 편향으로부터 자유로워야 합니다.

 

역사적으로 다양한 '정보 기준(information criteria)'들이 최대 가능도 방법의 편향 문제에 대한 대안으로 제시되어 왔으며, 이는 더 복잡한 모델에서 과적합이 일어나지 않도록 하는 페널티항을 추가하는 방식이였습니다.

 

예를 들어, 아카이케 정보량 기준(akaike information criterion, AIC)는 다음의 식 1.73의 값이 가장 큰 모델을 선택하는 방식입니다.

 

식 1.73

여기서 $p(D|\bf{w_{ML}}$$)$은 가장 잘 피팅된 로그 가능도이며, $M$은 모델의 수정 가능한 매개변수의 수 입니다.

 

베이지안 정보 기준(Bayeseian information criterion, BIC)은 AIC의 약간 변형된 버전인데 이에 대해서는 4.4.1절에서 논의할 예정입니다. 

 

이러한 기준들은 모델 매개변수들의 불확실성을 고려하지 않으며, 또한 실제 적용에서 간단한 모델을 선택하는 경향이 있습니다.

 

 

 

 

 

 

해당 글에서는 Chapter 1. (2)인 확률론에서 1.2.4인 가우시안 분포부터를 다루고 있습니다.

 

 

 

1.2.4 가우시안 분포

 

 

이번 장에서는 정규 분포(normal distribution)라고도 불리는 가우시안 분포(Gaussian distribution)에 대해 살펴봅니다.

 

 

단일 실수 변수 $x$에 대해서 가우시안 분포는 다음과 같이 정의됩니다.

 

 

식 1.46

 

식 1.46은 두 개의 매개변수 $\mu$와 $\sigma^2$에 의해 통제됩니다. $\mu$는 평균(mean), $\sigma^2$는 분산(variance)입니다.

 

 

분산의 제곱근 값인 $\sigma$는 표준 편차(standard deviation)이라고 불립니다. 또한, 분산의 역수에 해당하는 $\beta$ = $1/\sigma^2$는 정밀도(precision)이라고도 합니다. 매개변수들이 왜 이런 이름을 가지는지에 대해서 곧 살펴봅니다. 가우시안 분포를 그리면 그림 1.13처럼 나타납니다.

 

그림 1.13

 

종 모양의 그림이 되고, 중심이 $\mu$이며 폭이 $2\sigma$인 그래프가 됩니다.

 

 

식 1.46으로부터, 가우시안 분포가 다음의 성질을 만족함을 확인할 수 있습니다.

 

식 1.47

  

또한, 다음의 성질도 만족합니다.

 

식 1.48

따라서, 식 1.46은 확률 밀도의 두 가지 조건을 만족시킵니다.

 

 

가우시안 분포를 따르는 임의의 $x$에 대한 함수의 기댓값을 구할 수 있습니다. 특히 $x$의 평균값은 다음과 같습니다.

 

식 1.49

이를 통해 평균값 매개변수 $\mu$가 $x$의 기댓값과 동일함을 확인할 수 있습니다. 이와 비슷하게 $x$에 대한 이차 모멘트는 다음과 같습니다.

 

식 1.50

 

식 1.49와 식 1.50으로부터 $x$의 분산을 다음과 같이 계산할 수 있습니다.

 

식 1.51

$\sigma^2$가 분산임을 확인할 수 있습니다. 분포의 최댓값을 최빈값(mode)이라 하는데, 가우시안 분포의 경우에는 최빈값과 평균값이 동일합니다.

 

 

관측된 데이터 $\bf{x}$ = ($x_1$, ... $x_N$)$^T$를 살펴봅니다. 이는 관측된 $N$개의 스칼라 변수 $x$를 지칭합니다. 평균값 $\mu$와 분산 $\sigma^2$를 가지는 가우시안 분포에서 관측값들을 독립적으로 추출한다고 가정합니다.

데이터 집합으로부터 이 매개변수들을 결정하는 것이 우리의 현재 목표입니다.

 

 

같은 분포에서 독립적으로 추출된 데이터 포인트들을 독립적이고 동일하게 분포(independent and identically distributed, i.i.d)되었다고 한다. 앞에서 두 독립 사건의 결합 확률은 각 사건의 주변 확률의 곱이라고 하였으므로, i.i.d인 우리의 데이터 집합 $\bf{x}$은 $\mu$와 $\sigma^2$가 주어졌을 때의 조건부 확률을 다음과 같이 적을 수 있습니다.

 

식 1.53

$\mu$와 $\sigma^2$의 함수로 보면 이 식은 가우시안 분포의 가능도 함수에 해당합니다.  이전에 우리가 가능도 함수를 $p(D|\bf{w}$$)$로 표현했던 것을 생각해보면, 여기서 $\bf{w}$는 매개변수들의 집합이었으며 가우시안 분포에서는 이것이 바로 $\mu$와 $\sigma^2$가 됩니다. 

 

 

식 1.53를 그림으로 표현한 것이 바로 그림 1.14 입니다.

 

그림 1.14

 

그림 1.14에서 빨간색 곡선이 바로 가능도 함수입니다. 여기서 $x$축에 있는 검은색 포인트는 ${x_n}$을 값으로 가지는 데이터 집합을 지칭하며, 식 1.53으로 주어진 가능도 함수는 파란색 포인트의 값들의 곱에 해당합니다. 평균값과 분산을 조정하여 해당 곱을 최대화함으로써 가능도를 최대화할 수 있습니다.

 

 

관측된 데이터 집합을 바탕으로 확률 분포의 매개변수를 결정하는 표준적인 방법 중 하나는 가능도 함수를 최대화하는 매개변수를 찾는 것입니다. 가능도 함수를 그냥 사용하지 않고 여기에 로그를 취해서 사용하게 되는데, 로그 함수는 변수에 대해 단조 증가하는 함수이므로 로그를 취한 후 최댓값을 찾는 것이 결국 원래 함수의 최댓값을 찾는 것과 동일하기 때문입니다. 로그를 취함으로써 추후 수학적 분석이 간단해지고, 컴퓨터 계산의 수치적인 측면에서도 도움이 된다고 합니다.

식 1.46과 식 1.53에 따라 로그 가능도 함수를 다음과 같이 적을 수 있습니다.

 

식 1.54

이에 대해서 $\mu$로 미분했을 때 0이 나오는 최댓값을 찾게 되면 다음의 최대 가능도 해 $\mu_{ML}$를 찾을 수 있습니다.

 

식 1.55

이는 관찰된 값 {$x_n$}들의 평균인 표본 평균(sample mean)입니다. 이와 비슷한 방식으로 식 1.54의 최댓값을 $\sigma^2$에 대해서 찾으면 분산에 대한 최대 가능도 해를 다음과 같이 찾을 수 있습니다.

 

식 1.56

이는 표본 평균에 대해 계산된 표본 분산(sample variance)입니다. 가우시안 분포의 경우는 $\mu$에 대한 해가 $\sigma^2$에 대한 해와 연관되어 있지 않습니다. 따라서 식 1.55를 먼저 계산하고 이 결과를 사용해서 식 1.56를 계산할 수 있습니다.

 

 

이 장의 뒷부분과 책의 나머지 부분에서 최대 가능도 방법의 한계점에 관해 더 자세히 이야기할 것이지만, 여기서는 우리가 현재 다루고 있는 단변량 가우시안 분포를 기준으로 최대 가능도 방법을 통해 계산한 매개변수값이 어떤 문제를 가지고 있는지 살펴보겠습니다.

 

 

최대 가능도 방법은 구조적으로 분포의 분산을 과소평가하게 되는데, 이는 편향(bias)이라고 불리는 현상의 예시로써 다항식 곡선 피팅에서 살펴본 과적합 문제와 연관되어 있습니다. 

 

 

통계에서 편향이란, 추정량과 모수의 차이를 의미합니다. 예를 들어서, 실제로 평균이 1이고 분산이 0인 가우시안 분포에서 샘플을 뽑는다고 가정합니다. 이때, 샘플을 여러 번 뽑는 과정을 통해서 평균이 1이고 분산이 1이라고 추정했습니다. 이렇게 된다면, 분산에 편향이 발생한 것이다 라고 생각하시면 됩니다.

 

 

최대 가능도 해인 $\mu_{ML}$과 $\sigma^2_{ML}$는 데이터 집합인 $x_1, ...x_N$의 함수입니다. $\mu$와 $\sigma^2$를 모수로 하는 가우시안 분포에서 추출된 각 데이터 집합의 값에 대해 이들의 기댓값을 고려해봅시다. 

 

식 1.57, 식 1.58

책에는 너무 당연하다는 식으로 써있지만, 갑자기 왜 이들의 기댓값을 고려해보는지에 대해서 생각해보았습니다.

 

 

우리가 표본추출을 통해서 모집단의 평균을 구할 때, 표본평균의 평균을 구하게 됩니다.

 

 

또, 표본추출을 통해서 모집단의 분산을 구할 때, 표본분산의 평균을 구하게 됩니다.

 

 

따라서, 이와 같은 원리로 식 1.57은 표본평균($\mu_{ML}$)의 평균을 구해서 모집단의 평균을 추정한 것이고

 

 

식 1.58은 표본분산($\sigma^2_{ML}$)의 평균을 구해서 모집단의 분산을 추정한 것입니다.

 

 

따라서 평균적으로 최대 가능도 추정을 이용하면 평균은 올바르게 구할 수 있지만, 분산은 ($N$ - 1)/$N$만큼 과소평가하게 됩니다. 이 결과에 대한 직관적인 설명은 그림 1.15에서 확인할 수 있습니다.

 

그림 1.15

 

그림 1.15를 보면, 최대 가능도 방법을 이용해서 가우시안 분포의 분산을 구하고자 할 때 어떻게 편향이 생기는지를 확인할 수 있습니다. 녹색 곡선은 데이터가 만들어진 실제 가우시안 분포를 나타내며, 세 개의 빨간색 곡선은 세 개의 데이터 집합에 대해 식 1.55와 식 1.56의 최대 가능도 방법을 이용해서 피팅한 가우시안 분포를 나타냅니다.

 

각각의 데이터 집합은 두 개의 데이터 포인트를 포함하고 있으며, 파란색 원으로 표시되어 있습니다. 세 개의 데이터 집합에 대해 평균을 내면 평균값은 올바르게 계산되지만, 분산 값은 실제 평균값이 아닌 표본 평균값을 기준으로 분산을 계산하기 때문에 구조적으로 과소평가될 수밖에 없습니다.

 

즉, 분산 값을 계산할 때 실제 평균값(해당 그림에서 가장 중앙에 뾰족 튀어나온 지점)을 기준으로 해서 분산을 계산하는 것이 아닌, 표본 평균값(해당 빨간색 그래프의 평균값)을 기준으로 분산을 계산하므로 실제 분산 값에 비해서 더 적은 값으로 계산된다는 것입니다.

 

직접 손으로 계산해보지 않더라도, 중앙에 뾰족 튀어나온 지점을 기준으로 분산을 계산하는 것과, 해당 빨간색 그래프의 평균값을 기준으로 분산을 계산하는 것에는 차이가 있다는 것이 느껴지실 것입니다.

 

 

식 1.58로부터 다음 식 1.59에서 보이는 분산 추정치는 비편향임을 알 수 있습니다.

 

식 1.59

데이터 포인트의 개수인 $N$이 커질수록 최대 가능도 해에서의 편향치는 점점 줄어들게 됩니다. 만약 $N$이 무한대로 갈 경우에는 최대 가능도 해의 분산과 데이터가 추출된 원 분포의 분산이 같아짐을 확인할 수 있습니다. 실제 적용 사례에서는 $N$이 아주 작은 경우가 아니면 이 편향은 그렇게까지 큰 문제는 되지 않습니다. 

 

 

하지만 이 책 전반에 걸쳐 많은 매개변수를 포함한 복잡한 모델에 대해 살펴볼 것인데, 이 경우 최대 가능도 방법과 연관된 편향 문제는 더욱 심각해집니다. 최대 가능도 방법의 편향 문제는 우리가 앞에서 살펴본 다항식 곡선 피팅에서의 과적합 문제의 근본적인 원인에 해당합니다.

 

 

 

1.2.5 곡선 피팅

 

 

앞에서는 다항식 곡선 피팅 문제를 오차 최소화의 측면에서 살펴보았습니다. 여기서는 같은 곡선 피팅 문제를 확률적 측면에서 살펴봄으로써 오차 함수와 정규화에 대한 통찰을 얻어봅니다. 또한, 완전한 베이지안 해결법을 도출하는 데 도움이 될 것입니다.

 

 

곡선 피팅 문제의 목표는 $N$개의 입력값 $\bf{x}$ = ($x_1, ... x_N$)$^T$과 해당 표적 값 $\bf{t}$ = ($t_1, ... t_N$)$^T$가 주어진 상황에서 새로운 입력 변수 $x$가 주어졌을 때 그에 대한 타깃 변수 $t$를 예측해 내는 것입니다.

 

 

확률 분포를 이용해서 타깃 변수의 값에 대한 불확실성을 표현할 수 있습니다. 이를 위해서 주어진 $x$값에 대한 $t$값이 $y(x, \bf{w}$$)$를 평균으로 가지는 가우시안 분포를 가진다고 가정합니다. 여기서 $y(x, \bf{w}$$)$는 앞의 식 1.1에서 주어졌던 다항식 곡선입니다. 이를 바탕으로 다음의 조건부 분포를 적을 수 있습니다. 

 

식 1.60

여기서 사용한 $\beta$는 정밀도 매개변수로 분포의 분산의 역수에 해당합니다. 이 식을 도식화해 놓은 것이 바로 그림 1.16입니다.

 

그림 1.16

해당 그림에서 파란색 점과 빨간색 점은 설명을 위해 제가 그린 점입니다.

 

일단 빨간색 곡선으로 그려진 것은 우리가 데이터를 통해서 학습시킨 다항식 곡선이라고 보시면 됩니다. 그림에서는 $y(x, \bf{w}$$)$라고 표현되어 있습니다.

 

그리고 그림에서 $y(x_0, w)$라고 표시된 것은, $x$의 값이 $x_0$일 때, 다항식 곡선을 이용해서 예측된 prediction 값이라고 보시면 되겠습니다.

 

우리는 현재 주어진 $x$ 값에 대한 $t$값이 이 예측된 값을 평균으로 가지는 가우시안 분포를 가진다고 가정하기 때문에, 그림이 현재 형광 녹색으로 표시된 지점을 기준으로 파란색으로 그려진 가우시안 분포가 그려진 것입니다.

 

그렇다면, 해당 그림에서 특정 지점(데이터 포인트)에 대해서 조건부 확률 값을 어떻게 구할까요?

 

예를 들어서, 데이터가 $x$ 값이 $x_0$일 때, 파란색 점이 타깃 변수(정답 값) $t$의 위치라고 하겠습니다. 

 

그렇다면 해당 타깃 변수의 값에 대한 조건부 확률 값은, 파란색 가우시안 곡선으로 그려진 $p(t|x_0, w, \beta)$로 계산되는 값이 될 것이며, 그림 상에서 빨간색 점이 됩니다.  

 

가우시안 분포의 특성상, 타깃 변수의 값이 가우시안 분포의 평균과 동일할 때, 조건부 확률 값이 가장 커질 것이며 타깃 변수의 값과 가우시안 분포의 평균이 멀어질수록 조건부 확률 값이 작아질 것입니다. 

 

 

 

이제 훈련 집합 {$\bf{x}, \bf{t}$}를 바탕으로 최대 가능도 방법을 이용해 알려지지 않은 매개변수 $\bf{w}$와 $\beta$를 구해보도록 합시다. 만약 데이터가 식 1.60의 분포에서 독립적으로 추출되었다고 가정하면, 가능도 함수는 다음과 같이 주어집니다.

 

식 1.61

앞에서와 마찬가지로 가능도 함수에 로그를 취해 그 최댓값을 구하는 것이 편리합니다. 식 1.46의 가우시안 분포를 대입해 넣으면 다음과 같은 형태의 로그 가능도 함수를 얻게 됩니다.

 

식 1.62

첫 번째로 다항식 계수 $\bf{w_{ML}}$의 최대 가능도 해를 구해봅니다.

 

 

$\bf{w}$에 대해 식 1.62를 최대로 만드는 값을 구하면 됩니다. 이 과정에서 식 1.62의 오른쪽 변의 마지막 두 항은 관련이 없으므로 제외합니다. 또한, 로그 가능도에 양의 상수를 곱해도 상관없으므로, 맨 앞의 계수를 1/2로 바꿔줍니다. 마지막으로 로그 가능도를 최대화하는 대신에 로그 가능도의 음의 값을 취한 후, 이를 최소화할 수 있습니다. 이를 식으로 표현하자면 $min \frac{1}{2} \sum_{n=1}^N\left\{y(x_n, w)-t_n\right\}^2$이 됩니다.

 

 

결과적으로 $\bf{w}$를 구하는 경우에 가능도 함수를 최대화하는 것은 식 1.2에서 나왔던 제곱합 오차 함수를 최소화하는 것과 같다는 것을 알 수 있습니다. 노이즈가 가우시안 분포를 가진다는 가정하에 가능도 함수를 최대화하는 시도의 결과로 제곱합 오차 함수를 유도할 수 있는 것입니다.

 

 

마찬가지로 가우시안 조건부 분포의 정밀도 매개변수 $\beta$를 결정하는 데도 최대 가능도 방법을 사용할 수 있으며, 식 1.62를 $\beta$에 대해 최대화하면 다음의 식이 도출됩니다.

 

식 1.63

단순 가우시안 분포의 경우와 마찬가지로 평균값에 해당하는 매개변수 벡터 $\bf{w_{ML}}$을 먼저 구한 후에 이를 사용하여 정밀도 $\beta_{ML}$를 구할 수 있습니다.

 

 

매개변수 $\bf{w}$와 $\beta$를 구했으니 이제 이를 바탕으로 새로운 변수 $x$에 대해 예측값을 구할 수 있습니다. 이제 우리는 확률 모델을 사용하고 있으므로 예측값은 전과 같은 하나의 점 추정값이 아닌 $t$에 대한 예측 분포(predictive distribution)으로 표현될 것입니다. 최대 가능도 매개변수들을 식 1.60에 대입하면 다음을 얻을 수 있습니다.

식 1.64

이를 설명해보자면 다음과 같이 설명할 수 있을 것 같습니다.

 

 

input value $x$에 대해서, 피팅된 곡선(곡선의 가중치 $\bf{w_{ML}}$)를 이용하면 예측값을 구할 수 있습니다.

 

 

이 예측값을 평균으로, $\beta^{-1}_{ML}$를 분산으로 하는 가우시안 분포를 t가 따른다는 것입니다. 그래서 예측 분포라는 용어를 사용하여 식 1.64를 설명한 것이라고 이해를 해볼 수 있을 것 같습니다.

 

 

베이지안 방식을 향해 한 걸음 더 나아가 보겠습니다. 이를 위해 다항 계수 $\bf{w}$에 대한 사전 분포를 도입할 것입니다. 문제의 단순화를 위해 다음 형태를 지닌 가우시안 분포를 사용해봅니다.

 

식 1.65

 여기서 $\alpha$는 분포의 정밀도이며, $M + 1$은 $M$차수 다항식 벡터 $\bf{w}$의 원소의 개수입니다. ($M$차수 이면 마지막 상수항 $w_0$가 존재하므로 $M +1$개가 되는 것으로 생각이 됩니다.) $\alpha$와 같이 모델 매개변수의 분포를 제어하는 변수들을 초매개변수(hyperparameter)라 합니다. 베이지안 정리에 따라서 $\bf{w}$의 사후 분포는 사전 분포와 가능도 함수의 곱에 비례합니다.

 

식 1.66

이제 주어진 데이터에 대해 가장 가능성이 높은 $\bf{w}$를 찾는 방식으로 $\bf{w}$를 결정할 수 있습니다. 바꿔 말하면 사후 분포를 최대화하는 방식으로 $\bf{w}$를 결정할 수 있다는 것입니다. 이 테크닉을 최대 사후 분포(maximum posterior, MAP)라고 합니다. 식 1.66에 대해 음의 로그를 취한 식 1.62, 식 1.65와 결합하면 사후 확률의 최댓값을 찾는 것이 다음 식 값의 최솟값을 찾는 것과 동일함을 알 수 있습니다.

 

식 1.67

 

식 1.66에 음의 로그를 취하면

 

 

식 1.62와 식 1.65의 합으로 구성됩니다. 여기서 우리는 적절한 $\bf{w}$를 구하는 것이 목표이므로, $\frac{N}{2}ln\beta$ 등의 상수항들을 제외해줍니다.

 

 

그러고 나서 -를 곱해줘서 최소화 문제로 바꿔주면, 식 1.67을 최소화하는 문제로 바뀌게 됩니다.

 

 

이처럼 사후 분포를 최대화하는 것이 정규화 매개변수가 $\lambda = \alpha/\beta$로 주어진 식 1.4의 정규화된 제곱합 오차 함수를 최소화하는 것과 동일함을 확인할 수 있습니다.

 

 

1.2.6 베이지안 곡선 피팅

 

 

비록 사전 분포 $p(\bf{w}$|$\alpha$)를 포함시키긴 했지만, 여전히 $\bf{w}$에 대해서 점 추정을 하고 있기 때문에 아직은 완벽한 베이지안 방법론을 구사한다고 말할 수 없습니다. 완전한 베이지안적 접근을 위해서는 확률의 합의 법칙과 곱의 법칙을 일관적으로 적용해야 합니다. 이를 위해서는 모든 $\bf{w}$ 값에 대해서 적분을 시행해야 합니다. 이러한 주변화(marginalization)가 패턴 인식에서의 베이지안 방법론의 핵심입니다. 

 

 

곡선 피팅 문제의 목표는 훈련 집합 데이터 $\bf{x}$와 $\bf{t}$가 주어진 상황에서 새로운 변수 $x$에 대한 타깃 값 $t$를 예측하는 것입니다. 이 목표를 위해서 예측 분포 $p(t|x, \bf{x}, \bf{t}$$)$를 구해봅시다. 여기서는 매개변수 $\alpha$와 $\beta$는 고정되어 있으며, 미리 알려졌다고 가정합니다.

 

 

단순히 말하자면 베이지안 방법은 단지 확률의 합과 곱의 법칙을 계속적으로 적용하는 것입니다. 이를 통해 예측 분포를 다음과 같은 형태로 적을 수 있습니다.

 

식 1.68

여기서 $p(t|x, \bf{w}$$)$는 식 1.60에서 주어진 것입니다. 간략한 표기를 위해 $\alpha$와 $\beta$에 대한 종속성은 생략했습니다.

 

$p(w|\bf{x}, \bf{t}$$)$는 매개변수들에 대한 사후 분포이며, 식 1.66의 오른쪽 변을 정규화함으로써 구할 수 있습니다.

 

3.3절에서는 곡선 피팅 예시와 같은 문제의 경우 사후 분포가 가우시안이며, 해석적으로 계산할 수 있다는 것에 대해서 살펴봅니다. 이와 비슷하게 식 1.68의 적분을 시행하면 예측 분포가 다음의 식 1.69와 같이 가우시안 분포로 주어진다는 것을 알 수 있습니다.

 

식 1.69

여기서 평균과 분산은 다음과 같습니다. 

 

식 1.70, 식 1.71

행렬 $\bf{S}$는 다음처럼 주어집니다.

 

식 1.72

 

$\bf{I}$는 단위 행렬이며, $\phi(x)$는 각각의 원소가 $i = 0, ..., M$에 대해 $\phi_i(x) = x^i$인 벡터입니다. 

 

 

아마 이거 읽는 분들도 이게 왜 나왔을까... 궁금하시겠지만 따로 뭐 도출 과정이나 이런 게 책에 없어서 왜 이렇게 계산되었는지는 알 수가 없었네요 ㅠㅠ

 

 

식 1.69를 살펴보면, 예측 분포의 평균과 분산이 $x$에 종속되어 있음을 알 수 있습니다. 타깃 변수의 노이즈로 인한 예측값 $t$의 불확실성이 식 1.71의 첫 번째 항에 표현되어 있습니다. 이 불확실성은 식 1.64의 최대 가능도 예측 분포에서 $\beta^-1_{ML}$로 이미 표현되었습니다. 하지만 식 1.71의 두 번째 항은 $\bf{w}$의 불확실성으로부터 기인한 것이며, 베이지안 접근법을 통해 구해진 것입니다. 

 

그림 1.17

그림 1.17은 베이지안적 방법을 통해 구한 $M= 9$ 다항식 곡선 피팅 문제의 예측 분포를 나타내는 그림입니다.

 

 

$\alpha = 5 \times 10^{-3}$, $\beta = 11.1$을 사용하였습니다. 빨간색 선은 예측 분포의 평균값을, 그리고 빨간색 영역은 평균값으로부터 $\pm1$ 표준 편찻값을 가지는 부분을 표현한 것입니다. 녹색선은 해당 데이터를 만들 때 사용되었던 $sin(2\pi x)$를 나타낸 그림입니다. 파란색 동그라미인 데이터 포인트는 녹색선을 기준으로 약간의 노이즈를 포함시켜서 만들어낸 데이터라고 보시면 되겠습니다.

 

 

최대한 이해해보려고 노력하면서 읽긴 하지만, 역시나 쉽진 않은 책이라는 게 느껴집니다.

 

특히 조금 간단한 예시를 들었으면 좋았을 텐데... 너무 자명하다는 식으로 쓴 내용들이 많아 이해가 쉽진 않군요 ㅠㅠ

 

다음 장 내용도 이어서 보도록 하겠습니다.

 

 

 

 

 

패턴 인식 분야에서 주요한 콘셉트 중 하나는 바로 불확실성입니다. 불확실성은 측정할 때의 노이즈를 통해서도 발생하고, 데이터 집합 수가 제한되어 있다는 한계점 때문에도 발생합니다. 확률론은 불확실성을 계량화하고 조작하기 위한 이론적인 토대를 마련해 주며, 패턴 인식 분야의 중요한 기반이기도 합니다. 1.5절에서 논의할 의사 결정 이론과 이번 절의 확률론을 함께 활용하면, 주어진 정보가 불확실하거나 완전하지 않은 제약 조건하에서도 최적의 예측을 시행할 수 있게 됩니다.

 

 

 

하나의 예시를 들어보겠습니다. 

 

 

그림 1.9

 

한 개의 빨간색 상자와 한 개의 파란색 상자가 있고, 빨간색 상자에는 두 개의 사과와 여섯 개의 오렌지, 파란색 상자에는 한 개의 오렌지와 세 개의 사과가 있습니다. 랜덤 하게 상자 하나를 골라 임의로 과일 하나를 꺼내고, 어떤 과일인지 확인한 후 꺼냈던 상자에다 도로 집어넣는 상황을 생각해봅니다. 빨간색 상자를 고를 확률은 40%, 파란색 상자를 고를 확률은 60%라고 합니다. 상자 안에서 각각의 과일을 고를 확률은 동일하다고 가정합니다.

 

 

이 예시에서 상자는 확률 변수이며, 확률 변수 $B$라고 지칭합니다. 확률 변수 B는 $r$(빨간색 상자)와 $b$(파란색 상자) 두 개의 값을 가질 수 있습니다.

 

 

과일 또한 확률 변수이며, 여기서는 $F$로 지칭합니다. 확률 변수 F는 $a$(사과) 또는 $o$(오렌지)를 값으로 가질 수 있습니다.

 

 

어떤 사건의 '확률'을 무한 번 시도한다고 가정했을 때 어떤 특정 사건이 일어나는 횟수를 전체 시도의 횟수로 나눈 것으로 정의해본다면, $p(B=r)$ = 4/10이고, $p(B=b)$ = 6/10 입니다.

 

 

이러한 확률 말고, 조금 더 복잡한 확률에 대해서 알아보기 위해 먼저 확률의 두 가지 기본 법칙인 합의 법칙(sum rule)곱의 법칙(product rule)에 대해서 먼저 살펴봅니다.

 

 

확률의 법칙을 설명하기 위해, 그림 1.10의 예시를 고려해보겠습니다.

 

 

그림 1.10

 

해당 예시에서는 $X$와 $Y$라는 두 가지 확률 변수를 생각합니다. $X$는 $x_i(i = 1, ..,M)$ 중 아무 값이나 취할 수 있고, $Y$는 $y_j(j = 1, ... L)$ 중 아무 값이나 취할 수 있다고 가정합니다. 여기서 $M$ = 5이고, $L$ = 3입니다.

 

 

또한, $X$와 $Y$ 각각에서 표본을 추출하는 시도를 $N$번 한다고 합니다. 그리고 $X = x_i, Y = y_j$인 시도의 개수를 $n_{ij}$로 표현합니다. 그리고 $Y$의 값과는 상관없이 $X = x_i$인 시도의 숫자를 $c_i$로, $X$의 값과는 상관없이 $Y = y_j$인 시도의 숫자를 $r_j$로 표현할 것입니다.

 

 

$X$가 $x_i$, $Y$가 $y_j$일 확률을 $p(X = x_i, Y = y_j)$로 적고, 이를 $X = x_i, Y = y_j$일 결합 확률(joint probability)이라고 칭합니다. 이는 $i, j$ 칸에 있는 포인트의 숫자를 전체 포인트들의 숫자로 나눠서 구할 수 있는데요, 따라서 다음 식 1.5와 같이 표현할 수 있습니다.

 

 

식 1.5

 

여기서는 $lim N -> \infty$를 가정합니다. 비슷하게 $Y$ 값과 무관하게 $X$가 $x_i$값을 가질 확률을 $p(X = x_i)$로 적을 수 있으며, 이는 $i$열에 있는 포인트들의 숫자를 전체 포인트들의 숫자로 나눔으로써 구할 수 있습니다. 이를 식으로 표현하면 식 1.6로 표현할 수 있습니다.

 

식 1.6

 

 

그림 1.10에서 $i$열에 있는 사례의 숫자는 해당 열의 각 칸에 있는 사례의 숫자 합입니다. 이는 $c_i = \sum_j n_{ij}$로 표현 가능합니다. 따라서, 식 1.5와 식 1.6을 바탕으로 식 1.7을 도출해 낼 수 있습니다.

 

식 1.7

 

이것이 바로 확률의 합의 법칙(sum rule)입니다. 때때로 $p(X= x_i)$는 주변 확률(marginal probability)이라고 불립니다. 

 

 

합의 법칙을 말로 좀 풀어서 생각해보자면, 어떤 주변 확률을 구하려면 결합 확률을 이용해서 구할 수 있는데 결합 확률에 포함된 다른 확률 변수들의 모든 경우를 다 더했을 때 구할 수 있다 정도로 이해해볼 수 있겠습니다.

 

 

훨씬 단순한 예시를 하나 들자면... 상의 2가지 하의 3가지가 있으면 우리가 입을 수 있는 모든 옷의 경우의 수는 6가지가 될 것인데요. 여기서 1번 상의를 입게 될 확률은 결국 1번 상의 + 1번 하의 / 1번 상의 + 2번 하의 / 1번 상의 + 3번 하의를 입을 확률을 더해야 된다는 것이죠. 즉, 내가 어떤 확률 변수에 대한 주변 확률을 구하려면, 결합 확률에 포함된 다른 확률 변수들의 모든 케이스를 다 더했을 때 구할 수 있다는 것입니다.

 

 

 

$X = x_i$인 사례들만 고려해 봅시다. 그들 중에서 $Y = y_j$인 사례들의 비율을 생각해 볼 수 있고, 이를 확률 $p(Y = y_j | X = x_i)$로 적을 수 있습니다. 이를 조건부 확률(conditional probability)이라고 부릅니다. 이 경우엔 $X = x_i$가 주어졌을 경우 $Y = y_j$일 조건부 확률을 의미합니다. 이는 $i$행에 있는 전체 포인트 수와 $i, j$칸에 있는 포인트 수의 비율을 통해서 계산할 수 있습니다.

 

수식 1.8

 

일반 확률과 조건부 확률의 차이라고 한다면, 일반 확률은 시행 횟수 N으로 나누지만 조건부 확률은 시행 횟수 모두를 고려하는 것이 아닌, 조건에 해당하는 경우(위 수식 1.8에서는 $X = x_i$인 경우)만 고려하기 때문에 확률을 구할 때 분모의 값이 달라지게 됩니다. 이 부분이 가장 핵심이라고 생각합니다.

 

 

식 1.5, 식 1.6, 식 1.8에서 다음의 관계를 도출해 낼 수 있습니다.

 

 

식 1.9

이것이 바로 확률의 곱의 법칙(product rule)입니다.

 

 

이를 말로 풀어서 설명하자면, A와 B의 결합 확률은 A를 선택할 확률과 A를 선택했다고 생각했을 때 B를 선택할 확률의 곱이 된다는 것입니다.

 

 

제가 앞에 들었던 상의 2가지, 하의 3가지의 간단한 예시로 생각해보겠습니다. 1번 상의와 1번 하의를 입는 확률을 생각해본다면, 확률은 1/6이 됩니다. 전체 경우의 수 6가지 중 1가지가 되니까요.

 

 

$p(Y = y_j | X = x_i)$를 생각해보겠습니다. $X$를 상의, $Y$를 하의라고 생각하면 이는 1번 상의를 입는다는 가정 하에 1번 하의를 입을 확률입니다. 1번 상의를 입는다고 가정하면, 하의는 1번 2번 3번 총 3가지밖에 없으므로 이는 1/3이 됩니다. 

 

$p(X = x_i)$를 생각해보겠습니다. 상의는 2가지가 있으므로, 1번 상의를 입을 확률은 1/2가 됩니다.

 

 

따라서 식 1.9에서 정의한 것처럼, 1/6 = 1/3 * 1/2 가 성립함을 확인할 수 있습니다.

 

 

지금까지 얘기한 확률의 두 법칙을 조금 더 간단한 표현법을 사용해 표현하면 다음과 같습니다.

 

 

식 1.10, 식 1.11

 

곱의 법칙과 대칭성 $p(X, Y) = p(Y, X)$로부터 조건부 확률 간의 관계인 다음 식을 도출해낼 수 있습니다.

 

 

식 1.12

 

식 1.12는 머신 러닝과 패턴 인식 전반에 걸쳐서 아주 중요한 역할을 차지하고 있는 베이즈 정리(Bayes' theorem)입니다. 위에서 언급한 합의 법칙을 사용하면 베이지안 정리의 분모를 분자에 있는 항들로 표현할 수 있습니다.

 

 

식 1.13

 

베이지안 정리의 분모는 정규화 상수로 볼 수 있습니다. 즉, 식 1.12의 왼쪽 항을 모든 $Y$값에 대하여 합했을 때 1이 되도록 하는 역할인 것이죠.

 

 

이제 원래 논의했던 과일 상자 예시로 돌아가 봅시다. 빨간색 상자를 선택하거나 파란색 상자를 선택하는 확률은 다음과 같습니다.

 

수식 1.14, 수식 1.15

 

위의 식 1.14와 식 1.15의 합이 1을 만족시킨다는 것을 확인할 수 있습니다.

 

 

상자가 주어졌을 때 사과 또는 오렌지를 선택할 확률 네 가지를 다음과 같이 적을 수 있습니다.

 

 

수식 1.16 ~ 수식 1.19

 

마찬가지로, 이 확률들은 정규화되어 있기 때문에 다음 식 1.20과 식 1.21을 만족시킵니다.

 

 

식 1.20, 식 1.21

 

이제 확률의 합의 법칙과 곱의 법칙을 적용하여 사과를 고를 전체 확률을 계산할 수 있습니다.

 

 

수식 1.22

여기에 다시 합의 법칙을 적용하면 $p(F = 0)$ = 1 - 11/20 = 9/20입니다.

 

 

어떤 한 종류의 과일을 선택했는데 그것이 오렌지고, 이 오렌지가 어떤 상자에서 나왔는지를 알고 싶다고 가정해봅니다. 이는 베이지안 정리를 적용해서 구할 수 있습니다.

 

수식 1.23

 

합의 법칙에 따라 $p(B = b | F = o)$ = 1 - 2/3 = 1/3이 됩니다.

 

 

베이지안 정리를 다음과 같이 해석할 수 있습니다.

 

 

만약 어떤 과일이 선택되었는지를 알기 전에 어떤 박스를 선택했냐고 묻는다면 그 확률은 $p(B)$일 것입니다. 이를 사전 확률(prior probability)이라고 부릅니다. 왜냐하면 어떤 과일이 선택되었는지 관찰하기 '전'의 확률이기 때문이죠. 선택된 과일이 오렌지라는 것을 알게 된다면 베이지안 정리를 활용하여 $p(B|F)$를 구할 수 있습니다. 이는 사후 확률(posterior probability)이라고 부를 수 있는데, 이는 사건 $F$를 관측한 '후'의 확률이기 때문입니다.

 

 

이 예시에서 빨간색 상자를 고를 사전 확률은 4/10이므로, 파란색 상자를 고를 확률이 더 높습니다. 하지만, 선택된 과일이 오렌지라는 것을 확인하고 난 후엔 빨간색 상자를 고를 사후 확률이 2/3입니다. 따라서 이제는 우리가 고른 상자가 빨간색이었을 확률이 더 높게 됩니다. 이는 빨간색 상자 안의 오렌지의 비율이 파란색 상자 안의 오렌지의 비율보다 높기 때문이죠. 오렌지를 골랐다는 증거가 충분히 강력하기 때문에 사전 지식을 뒤엎고 빨간색 상자를 골랐을 확률을 파란색 상자를 골랐을 확률보다 더 높게 만들어주는 것입니다.

 

 

$p(X, Y) = p(X)p(Y)$인 경우를 고려해 봅시다. 이처럼 각각의 주변 확률을 곱한 것이 결합 확률과 같을 경우 두 확률 변수를 독립적(independent)이라고 합니다. 곱의 법칙에 따라 $p(Y|X) = p(Y)$임을 알 수 있고, 이는 $X$가 주어졌을 때 $Y$의 조건부 확률은 실제로 $X$의 값과 독립적임을 확인할 수 있습니다. 

 

 

 

1.2.1 확률 밀도

 

 

지금까지는 이산적인 사건들을 바탕으로 확률을 알아보았는데, 이번에는 연속적인 변수에서의 확률에 대해 알아봅니다.

 

 

만약 실수 변수 $x$가 ($x$, $x+\delta x$) 구간 안의 값을 가지고 그 변수의 확률이 $p(x) \delta x$($\delta x$ -> 0 일 경우)로 주어진다면, $p(x)$를 $x$의 확률 밀도(probability density)라고 부릅니다. 이는 그림 1.12로 표현될 수 있습니다.

 

그림 1.12 p(x) => 확률 밀도, P(x) => 누적 분포 함수

 

 

이때 $x$가 (a, b) 구간 사이의 값을 가질 확률은 다음과 같이 주어집니다.

 

식 1.24

 

단순하게, 확률 밀도 함수를 x = a부터 x = b까지 적분을 해주면 확률을 구할 수 있습니다.

 

 

확률은 양의 값을 가지고 $x$의 값은 실수축상에 존재해야 하므로, 다음 두 조건을 만족시켜야 합니다.

 

 

식 1.25, 식 1.26

 

확률 분포 함수는 야코비안 인자로 인해 비선형 변수 변환 시에 일반적인 단순 함수와는 다른 방식으로 변화하게 됩니다. 예를 들어, $x = g(y)$의 변수 변환을 고려해 봅시다. 그러면 함수 $f(x)$는 $\tilde{f}(y)$ = $f(g(y))$가 됩니다. $x$의 확률 밀도 함수 $p_x(x)$와 새로운 변수 $y$의 확률 밀도 함수 $p_y(y)$를 살펴보면 둘이 다른 확률 밀도를 가진다는 것이 자명합니다. ($x$, $x + \delta x$) 범위에 속하는 관찰 값은 범위 ($y$, $y + \delta y$)로 변환될 것입니다. 이때 $p_x(x) \delta x $ $\simeq$ $p_y(y) \delta y $입니다. 따라서 다음과 같습니다.

 

식 1.27

 

이로부터, 확률 밀도의 최댓값은 어떤 변수를 선택하는지에 따라 달라짐을 알 수 있습니다.

 

 

책에는 식 1.27가 어떤 식으로 도출되는지에 대한 내용이 전혀 없어서, 왜 갑자기 저런 식이 나오는지 알 수 없었습니다. 이에 저는 관련된 내용을 찾아서 나름대로 정리를 해 보았는데요.

 

 

식 1.27가 나오는 과정이 궁금하시다면, 아래에 있는 더보기 부분을 누르셔서 확인하실 수 있습니다.

 

더보기

식 1.27을 도출하는 과정

 

저도 다른 분이 쓰신 내용을 토대로 정리한 내용이라, 내용이 틀릴 수 있습니다.

 

$x$ = $g(y)$의 변수 변환을 고려한다고 가정합니다.

 

확률변수 $X$의 확률 밀도 함수는 $f_X(x)$이고, $g^-1(x)$가 $f_X(x)$의 정의역에서 증가하거나 감소하면, $y = g^-1(x)$의 확률밀도함수는 다음과 같습니다.

 

$f_Y(y)$ = $f_X(g(y)) \left| \frac{dx}{dy} \right|$ ($x = g(y)$)

 

i) $g^-1(x)$가 단조 증가함수이면,

$F_Y(y) = P(Y \leq y) = P(g^-1(X) \leq y) = P(X \leq g(y)) = F_X(g(y))$ 이므로 

 

Y의 확률 밀도 함수는 $f_Y(y) = \frac{d}{dy}F_X(g(y)) = \frac{d}{dx}F_X(x)\frac{dx}{dy} = f_X(g(y))\frac{dx}{dy}$

 

ii) $g^-1(x)$가 단조 감소 함수이면,

$F_Y(y) = P(Y \geq y) = P(g^-1(X) \geq y) = P(X \geq g(y)) = 1 - P(x \leq g(y)) = 1 - F_X(g(y))$ 이므로

 

Y의 확률 밀도 함수는 $f_Y(y) = \frac{d}{dy}F_Y(y) = \frac{d}{dy}[1 - F_X(g(y))] = \frac{d}{dy}[1 - F_X(x)]$

 

$= \frac{d}{dx}[1-F_X(x)]\frac{dx}{dy} = -f_X(x)\frac{dx}{dy} = -f_X(g(y))\frac{dx}{dy} = f_X(g(y))(-\frac{dx}{dy})$

 

따라서, Y의 확률 밀도 함수는 $f_Y(y) = f_X(g(y)) \left| \frac{dx}{dy} \right|$ 입니다.

 

이 확률 밀도 함수를 이용하면, 식 1.27처럼 $p_y(y) = p_x(x) \left| \frac{dx}{dy} \right| = p_x(g(y))|g'(y)|$가 됩니다.

 

 

$x$가 ($-\infty, z$) 범위에 속할 확률은 누적 분포 함수(cumulative distribution function)로 표현될 수 있습니다.

 

식 1.28

누적 분포 함수는 곧 $-\infty$부터 $z$까지 확률밀도함수를 적분한 것을 의미합니다.

 

또, 그림 1.12에서 보인 것처럼 $P'(x) = p(x)$ 입니다.

 

 

만약 여러 개의 연속적인 변수 $x_1, ... x_D$가 주어지고 이 변수들이 벡터 $\bf{x}$로 표현될 경우에 결합 확률 밀도 $p(\bf{x}$$)$ = $p(x_1, .. x_D)$를 정의할 수 있습니다. 이 확률 밀도에서 $\bf{x}$가 포인트 $\bf{x}$를 포함한 극솟값 $\delta \bf{x}$에 포함될 확률은 $p(\bf{x}$$)$$\delta \bf{x}$로 주어집니다. 이 다변량 확률 밀도는 다음 조건을 만족해야 합니다.

 

식 1.29, 식 1.30

 

식 1.30에서의 적분은 전체 $x$ 공간에 대해서 시행하는 것이며, 이산형 변수와 연속형 변수가 조합된 경우에 대해서도 결합 확률 분포를 고려하는 것이 가능합니다.

 

 

만약 $x$가 이산 변수일 경우 $p(x)$를 때때로 확률 질량 함수(probability mass function)라고 부르기도 합니다. 

 

연속 변수의 확률 밀도와 이산형 변수/연속형 변수가 조합된 경우의 확률 밀도에서도 합의 법칙, 곱의 법칙, 베이지안 정리를 적용할 수 있습니다. 예를 들어 $x, y$가 실수 변수인 경우, 합과 곱의 법칙은 다음과 같이 표현할 수 있습니다.

 

식 1.31, 식 1.32

 

 

1.2.2 기댓값과 공분산

 

 

 

확률 밀도 $p(x)$ 하에서 어떤 함수 $f(x)$의 평균값은 기댓값(expectation)이라 하며, $E[f]$라 적습니다.

 

 

이산 분포의 경우 기댓값은 다음과 같이 주어집니다.

 

식 1.33

쉽게 생각하면, 각 $x$ 값에 대해 해당 확률을 가중치로 사용해서 가중치 x 값을 이용해서 가중 평균을 구하는 것입니다.

 

연속 변수의 경우에는 확률 밀도에 대해 적분을 해서 기댓값을 구합니다.

 

식 1.34

 

만약 유한한 $N$개의 포인트를 확률 분포 또는 확률 밀도에서 추출했다면, 포인트들의 합으로 기댓값을 근사할 수 있습니다.

 

식 1.35

 

다변수 함수의 기댓값을 구할 경우에는 어떤 변수에 대해 평균을 내는지를 지정해서 계산할 수 있습니다.

 

식 1.36

식 1.36은 함수 $f(x, y)$의 평균값을 $x$의 분포에 대해 구하라는 의미입니다. 이는 $y$에 대한 함수가 될 것입니다. ($x$에 대해서 계산한 것이므로)

 

 

또한, 조건부 분포에 해당하는 조건부 기댓값(conditional expectation)도 생각해 볼 수 있습니다.

 

식 1.37

 

 

$f(x)$의 분산(variance)은 다음과 같이 정의됩니다.

 

식 1.38

분산은 $f(x)$가 평균값으로부터 전반적으로 얼마나 멀리 분포되어 있는지를 나타내는 값입니다. 위 식을 전개하면 다음과 같이 표현할 수 있습니다.

 

식 1.39

고등학교 수학에서는 보통 제곱의 평균 - 평균의 제곱으로 많이 외우곤 합니다.

 

 

두 개의 확률 변수 $x$와 $y$에 대해서 공분산(covariance)은 다음과 같이 정의됩니다.

 

식 1.41

 

공분산은 $x$ 값과 $y$ 값이 얼마나 함께 같이 변동하는가에 대한 지표이며, 서로 독립이면 공분산은 0입니다.

 

 

두 확률 변수 $x$와 $y$가 벡터일 경우에는 공분산은 행렬이 됩니다.

 

식 1.42

 

 

 

1.2.3. 베이지안 확률

 

 

지금까지 우리는 확률을 '반복 가능한 임의의 사건의 빈도수'라는 측면에서 살펴보았습니다. 이러한 해석을 고전적(classical) 또는 빈도적(frequentist) 관점이라고 일컫는데요. 이보다 더 포괄적인 베이지안(Bayesian) 관점에 대해서 살펴봅니다. 베이지안 관점을 이용하면 확률을 이용해 불확실성을 정량화하는 것이 가능합니다.

 

 

어떤 불확실한 사건에 대해서 생각해보겠습니다. 예를 들어 '북극의 빙하가 이번 세기까지 다 녹아 없어진다'는 사건을 생각해보면, 이런 사건들은 여러 번 반복할 수 없습니다. 따라서 빈도적 관점에서 확률을 정의하는 것이 불가능합니다. 물론, 우리는 이러한 사건들에 대해 어떤 견해를 가지고 있긴 할 텐데요. 예를 들면 '북극의 얼음이 이 정도 속도로 녹는다'와 같은 의견이 될 수 있습니다.

 

 

만약 우리가 새로운 증거를 추가할 수 있다면 얼음이 녹는 속도에 대한 우리의 의견을 수정할 수 있을 것입니다. 이런 증거가 강력하다면 우리의 판단에 따라 취할 행동이 바뀔 수도 있죠. 예를 들어서, 얼음이 녹는 속도가 빠르다는 증거를 관측했다면 기후 변화를 늦추기 위해 노력할 수 있습니다. 이런 상황들에서 주어진 불확실성을 정량화하고, 새로운 증거가 주어질 때마다 불확실성을 수정하고 그 결과에 따라 최적의 선택을 내리고 싶을 때, 이것이 가능하게 해주는 방법론이 바로 확률의 베이지안 해석입니다. 즉 확률을 불확실성을 나타내는 도구로 활용하는 것이죠.

 

 

확률에 대한 개념을 더 일반적으로 확장하는 것은 패턴 인식 분야에서도 큰 도움이 됩니다. 1.1절의 다항 곡선 피팅 예시를 다시 생각해보겠습니다. 적합한 모델 매개변수 $\bf{w}$를 정하는 데 있어서 불확실성을 수치화하고 표현하려면 어떻게 해야 할까요? 이때 베이지안 관점을 사용하면 확률론의 다양한 장치들을 활용하여 $\bf{w}$와 같은 모델 매개변수의 불확실성을 설명할 수 있습니다. 더 나아가, 베이지안 관점은 모델 그 자체를 선택하는 데 있어서도 유용합니다.

 

 

앞의 과일 상자 예시에서 어떤 과일이 선택되었는지에 대한 관측 결과가 선택된 상자가 어떤 것이었을지에 대한 확률을 바꾸었던 것을 기억해 봅시다. 해당 예시에서 베이지안 정리는 관측값들을 이용하여 사전 확률을 사후 확률로 바꾸는 역할을 했습니다. 다항 곡선 피팅 예시의 매개변수 $\bf{w}$와 같은 값들을 추론해 내는 데 있어서도 비슷한 방식을 사용할 수 있습니다. 

 

 

일단, 첫 번째로 데이터를 관측하기 전의 $\bf{w}$에 대한 우리의 가정을 사전 확률 분포 $p(\bf{w})$로 표현할 수 있습니다. 관측된 데이터 $D$ = {${t_1, ... t_N}$}은 조건부 확률 $p(D|\bf{w})$로써 작용하게 됩니다. 이 경우 베이지안 정리는 다음의 형태를 띱니다.

 

식 1.43

$D$를 관측한 후의 $\bf{w}$에 대한 불확실성을 사후 확률 $p(\bf{w}$$|D)$로 표현한 것입니다.

 

베이지안 정리의 오른쪽에 있는 값 $p(D|\bf{w})$은 관측 데이터 집합 $D$를 바탕으로 계산됩니다. 이 값은 매개변수 벡터 $\bf{w}$의 함수로 볼 수 있으며, 가능도 함수(likelihood function)라고 불립니다. 가능도 함수는 각각의 다른 매개변수 벡터 $\bf{w}$에 대해 관측된 데이터 집합이 얼마나 '그렇게 나타날 가능성이 있었는지'를 표현합니다. 가능도 함수는 $\bf{w}$에 대한 확률 분포가 아니며, 따라서 $\bf{w}$에 대해 가능도 함수를 적분하여도 1이 될 필요가 없습니다.

 

 

가능도 함수에 대한 정의를 바탕으로 베이지안 정리를 다음처럼 적을 수 있습니다.

 

식 1.44

posterior = 사후 확률, likelihood = 가능도, prior = 사전 확률입니다.

 

 

식 1.44의 각 값은 전부 $\bf{w}$에 대한 함수입니다. 

 

 

식 1.43 오른쪽 변의 분모는 식 왼쪽 변의 사후 분포가 적절한 확률 분포가 되고 적분 값이 1이 되도록 하기 위한 정규화 상수입니다. 식 1.43 오른쪽 변의 분모는 다음과 같이 구할 수 있습니다.

 

식 1.45

 

 

가능도 함수 $p(D|\bf{w})$는 베이지안 확률 관점과 빈도적 확률 관점 모두에게 굉장히 중요한 역할을 합니다. 하지만 가능도 함수가 사용되는 방식은 양 접근법에서 근본적으로 다릅니다.

 

먼저, 빈도적 확률 관점에서는 $\bf{w}$가 고정된 매개변수로 여겨지며, 그 값은 어떤 형태의 '추정 값'을 통해서 결정됩니다. 그리고 추정에서의 오류는 가능한 데이터 집합들 $D$의 분포를 고려함으로써 구할 수 있습니다.

 

 

이와는 대조적으로 베이지안 확률 관점에서는 오직 하나의 데이터 집합 $D$만이 존재하고 매개변수의 불확실성은 $\bf{w}$의 확률 분포를 통해 표현됩니다.

 

 

빈도적 확률 관점에서 널리 사용되는 추정 값 중 하나는 바로 최대 가능도(maximum likelihood)입니다. 최대 가능도를 사용할 경우에 $\bf{w}$는 가능도 함수 $p(D|\bf{w})$를 최대화하는 값으로 선택됩니다. 머신러닝 문헌에서는 종종 음의 로그 가능도 함숫값을 오차 함수(error function)이라고 합니다. 음의 로그 함수는 단조 감소하는 함수이기 때문에 가능도의 최댓값을 찾는 것이 오차를 최소화하는 것과 동일하기 때문입니다.

 

 

빈도적 확률론자들이 오차를 측정하는 방법 중 하나가 바로 부트스트랩(bootstrap) 방법입니다. 원 데이터 집합이 $N$개의 데이터 포인트 $X$ = {$\bf{x_1}, ..., \bf{x_N}$}이라고 가정해봅니다. $\bf{X}$에서 $N$개의 데이터 포인트를 임의로 추출하여 데이터 집합 $\bf{X_B}$를 만드는데, 이때 한번 추출된 값도 다시 추출 대상으로 고려될 수 있도록 하는 방식을 사용합니다. 즉 어떤 값은 중복될 수도 있고, 아예 포함되지 않을 수도 있습니다. 이 과정을 $L$번 반복하면 원래 데이터 집합의 표본에 해당하는 크기 $N$의 데이터 집합을 $L$개 만들 수 있습니다. 각각의 부트스트랩 데이터 집합에서의 예측치와 실제 매개변수 값과의 차이를 바탕으로 매개변수 추정 값의 통계적 정확도를 판단할 수 있습니다.

 

 

베이지안 관점의 장점 중 하나는 사전 지식을 추론 과정에 자연스럽게 포함시킬 수 있다는 점입니다. 예를 들어 멀쩡하게 생긴 동전 하나를 세 번 던졌는데, 세 번 다 앞면이 나왔다고 해봅시다. 고전적인 최대 가능도 추정을 통해 추론한다면 앞으로는 앞면이 나올 확률이 1일 것입니다. 미래의 모든 동전 던지기에서 앞면만 나올 것이라고 예측한다는 말입니다. 대조적으로 베이지안적으로 접근할 경우 적당히 합리적인 사전 확률을 사용한다면 이렇게까지 과도한 결론이 나오지는 않을 것입니다.

 

 

빈도적 확률 관점과 베이지안 확률 관점 중 어떤 것이 더 상대적으로 우수한지에 대해서는 끊임없는 논쟁이 있습니다. 베이지안 접근법에 대한 비판 중 하나는 사전 분포가 실제 사전의 믿음을 반영하기보다는 수학적인 편리성을 위해서 선택된다는 것입니다. 베이지안 관점에서는 사전 확률의 선택에 따라 결론이 나기 때문에 추론 과정에 주관이 포함될 수밖에 없습니다. 이 때문에 적절한 사전 확률을 선택하는 것이 어려운 경우도 있습니다. 사전 분포에 대한 의존도를 낮추기 위해 무정보적(noninformative) 사전 분포를 사용하는 경우도 있지만, 이는 서로 다른 모델을 비교하기 어렵게 만듭니다. 그리고 실제로 좋지 않은 사전 분포를 바탕으로 한 베이지안 방법은 부족한 결과물을 높은 확신으로 내놓기도 합니다. 

 

 

 

 

1.2 확률론 내용이 많아서, 여기서 끊고 1.2.4 가우시안 분포부터 다음 글에서 다루도록 하겠습니다.

 

 

 

 

 

 

 

이번 내용은 다항 곡선 피팅에 대한 내용입니다. 즉, 데이터가 주어졌을 때 이를 잘 표현할 수 있는 다항 곡선을 만들어내는 것입니다. 

 

 

실숫값의 입력 변수인 $x$를 관찰한 후 이 값을 바탕으로 실숫값인 타깃 변수 $t$를 예측하려 한다고 가정합니다.

 

 

예시에서 사용된 데이터는 $sin(2\pi{x})$ 함수를 사용하여 만들었으며, 타깃 변수에는 약간의 랜덤 노이즈를 포함했는데 이는 가우시안 분포 기반으로 표준편차 0.3을 사용하여 만들었습니다.

 

 

$N$개의 관찰값 $x$로 이루어진 훈련 집합 $\bf{x} \equiv$ $(x_1, ... x_N)^T$와 그에 해당하는 타깃 값 $\bf{t} \equiv$ $(t_1, ... t_N)^T$가 주어졌다고 생각해봅니다. 만약 N = 10이라면 다음과 같은 그림으로 표현할 수 있습니다.

 

그림 1.2 N = 10일 때 데이터 그림

입력 데이터 집합 $\bf{x}$는 서로 간에 같은 거리를 가지도록 균등하게  $x_n$값들을 선택해서 만들었다고 합니다.

 

 

우리가 패턴 인식 알고리즘을 통해 알아내고 싶은 실제 데이터 집합은 어떤 특정한 규칙성을 가지지만, 각각의 관찰 값들은 보통 랜덤한 노이즈에 의해 변질되곤 합니다. 이러한 노이즈는 본질적으로 확률적인 과정을 통해서 발생할 수 있으나, 더 많은 경우에는 관찰되지 않은 변수의 가변성에 기인한다고 합니다.

 

 

우리의 목표는 훈련 집합들을 통해 어떤 새로운 입력값 $\hat{x}$가 주어졌을 때 타깃 변수 $\hat{t}$를 예측하는 것입니다. 결국에는 기저에 있는 함수인 $sin(2\pi{x})$를 찾아내는 것이 예측 과정에 포함된다고 생각할 수 있습니다. 이는 한정적인 데이터 집합으로부터 일반화를 하는 과정이니 본질적으로 어려운 문제이며, 관측된 값이 노이즈에 의해 변질되어 있어서 더더욱 어려운 상황입니다.

 

 

곡선을 피팅하는데 있어서 다음과 같은 형태의 다항식을 활용합니다.

 

 

(식 1.1)

 

$M$은 다항식의 차수(order), 다항식의 계수인 $w_0, ... w_M$을 함께 모아서 벡터 $\bf{w}$로 표현할 수 있습니다. 다항 함수 $y(x, \bf{w}$$)$는 $x$에 대해서는 비선형이지만, 계수 $\bf{w}$에 대해서는 선형입니다. (선형이냐 아니냐는 1차 함수인지 아닌지를 생각해보시면 쉽습니다. 즉, $x$에 대해서는 2차, 3차... M차로 구성되지만, 계수인 $w_0$ 등은 차수가 1이기 때문에 선형이라고 부르는 것입니다. 계수를 기준으로 볼 때는 $x$는 실수 값처럼 생각해주시면 됩니다.)

 

 

우리가 식 1.1에서 정의한 다항식을 훈련 집합 데이터에 fitting해서 계수의 값들을 정할 수 있습니다. 훈련 집합의 타깃 값과 함숫값(다항식을 이용해서 예측한 값) $y(x, \bf{w}$$)$와의 오차를 측정하는 오차 함수(error function)를 정의하고 이 함수의 값을 최소화하는 방식으로 fitting 할 수 있습니다. 즉, 최대한 예측값이 실제 타깃 값과 가까워지게끔 하겠다는 것입니다. 가장 널리 쓰이는 간단한 오차 함수 중 하나는 식 1.2에서 보이는 것처럼 타깃 값과 함숫값 사이의 오차를 제곱해서 합산하는 함수를 사용합니다.

 

(식 1.2)

 

식 1.2의 함수에서 왜 갑자기 1/2이 추가되었나 라고 한다면, 추후 이 함수를 미분해야 하기 때문에 계산상으로 조금 더 편하게 하기 위해서 곱했다 정도로 생각해주시면 됩니다.(제곱 형태기 때문에, 미분 하면 2가 앞으로 나오게 되어 1/2를 상쇄하게 됩니다.) 해당 오차 함수는 결괏값이 0보다 크거나 같은 값이 나오게 되며, 오직 함수 $y(x, \bf{w}$$)$가 정확히 데이터 포인트들을 지날 때만 값이 0이 된다는 사실을 알 수 있습니다. 

 

 

$E(\bf{w}$$)$를 최소화하는 $\bf{w}$를 선택함으로써 이 곡선 피팅 문제를 해결할 수 있으며, 식 1.2에서 주어진 오차 함수가 이차 다항식의 형태를 지니고 있으므로 이를 계수에 대해서 미분하면 $\bf{w}$에 대해 선형인 식이 나오게 됩니다. 이를 통해 오차 함수를 최소화하는 유일한 값인 $\bf{w}^*$를 찾아낼 수 있습니다. 

 

 

아직 우리가 고려하지 않은 사항이 있습니다. 바로 다항식의 차수 $M$을 결정해야 합니다. 이 문제는 모델 비교(model comparison) 혹은 모델 결정(model selection)이라 불리는 중요한 내용입니다. 그림 1.4에서는 $M$ = 0, 1, 3, 9인 네 가지 경우에 대해 앞에서 언급한 데이터를 바탕으로 다항식을 피팅하는 예시를 보여줍니다.

 

 

그림 1.4 다양한 차수 M에 따른 곡선 피팅

 

우리가 피팅한 다항식은 빨간색으로, 실제 데이터에 사용된 기저 함수인 $sin(2\pi{x})$은 연두색으로, 데이터 포인트는 파란색 원으로 표시되어 있습니다.

 

 

여기서는 우리가 다항식의 차수를 어떻게 결정하는지에 따라 피팅된 곡선이 어떤 모습을 하게 되는지를 보시면 됩니다.

 

 

먼저 상수($M$ = 0)일 때와 1차($M$ = 1) 다항식을 사용할 때는 피팅이 잘 되지 않고 있는 모습을 확인할 수 있습니다. 이는 우리가 사용하려는 다항식이 너무 단순해서, 데이터 포인트를 충분히 표현하기에 어렵다는 것을 의미합니다. 책에는 이렇게 쓰여있지는 않지만, 모델의 복잡도가 낮다 라는 용어로도 표현합니다. 복잡도는 보통 영어로 complexity라고 표현하니 이 용어를 알아두시면 좋습니다.

 

 

3차($M$ = 3) 다항식의 경우는, 기저 함수를 잘 표현하고 있음을 확인할 수 있습니다.

 

 

차수를 더 높여 9차($M$ = 9) 다항식을 사용할 경우, 훈련 집합에 대한 완벽한 피팅이 가능합니다. 이 경우 피팅된 곡선이 모든 데이터 포인트를 지나가며 앞에서 정의한 오차 함수 기준으로 $E(\bf{w^*}$$) = 0$입니다. 하지만 피팅된 곡선은 심하게 진동하고 있고, 함수 $sin(2\pi{x})$를 적절하게 표현하지 못하고 있습니다. 이것이 바로 과적합(over-fitting)의 예시입니다. 

 

 

앞에서 언급했듯이, 우리의 목표는 새로운 데이터가 주어졌을 때, 정확한 결괏값을 예측할 수 있는 좋은 일반화를 달성하는 것입니다. 따라서, 이러한 과적합을 피해야 합니다.

 

 

앞과 같은 과정을 사용하되, 랜덤한 노이즈 값만 다르게 적용해 100개의 새 데이터 포인트로 이루어진 시험 집합을 만들어 봅니다. 이 시험 집합에서의 성능을 확인해 보면 $M$의 값에 따라 일반화의 성능이 어떻게 변화하는지 정량적으로 살펴볼 수 있습니다. 

 

 

훈련 집합과 시험 집합 각각에 대해서 평균 제곱근 오차(root mean square error, RMSE)를 이용해 일반화의 성능을 확인해봅시다. RMSE는 다음과 같이 정의됩니다.

 

 

(수식 1.3)

 

$N$으로 나눠 데이터 사이즈가 다른 경우에도 비교할 수 있으며, 제곱근을 취해 RMSE가 정답 값 $t$와 같은 크기를 가지게 됩니다.

 

 

각각의 $M$ 값에 대한 훈련 집합과 시험 집합의 RMSE를 그림 1.5에서 확인할 수 있습니다.

 

 

그림 1.5

 

$M$ 값이 작은 경우는 시험 오차가 상대적으로 큽니다. 즉 낮은 차수의 다항식은 비교적 융통성이 없어 피팅된 다항식이 기저인 함수를 충분히 표현할 수 없습니다.

 

$3 \leq M \leq 8$의 경우, 시험 집합의 오차가 작고 피팅된 해당 다항식이 기저인 함수를 충분히 잘 표현하고 있습니다.

 

 

$M = 9$일 경우는 훈련 집합의 오차가 0이지만, 시험 집합의 오차가 굉장히 큽니다. 

 

 

차수 $M$에 따른 피팅 함수의 계수 $\bf{w}^*$의 값들을 적은 표 1.1도 한번 봅시다.

 

표 1.1

$M$이 커짐에 따라 계숫값의 단위 역시 커지는 것을 확인할 수 있습니다. 특히, $M = 9$ 다항식의 경우는 상당히 큰 양숫값의 계수와 음숫값의 계수가 번갈아 나타나는 것을 볼 수 있습니다. 이는 각각의 데이터 포인트에 정확하게 맞도록 피팅한 결과죠.  

 

 

사용되는 데이터 집합의 크기가 달라지는 경우에는 어떤 일이 일어나는지 확인해 봅시다. 그림 1.6를 보면 이에 대해 생각해볼 수 있습니다. 

 

그림 1.6

 

모델의 복잡도를 일정하게 유지시킬 때는 사용하는 데이터 집합의 수가 늘어날수록 과적합 문제가 완화되는 것을 확인할 수 있습니다. 이를 다르게 표현하면 데이터 집합의 수가 클수록 더 복잡한 모델을 활용하여 피팅할 수 있다는 의미도 됩니다.

 

 

비교적 복잡하고 유연한 모델을 제한적인 숫자의 데이터 집합을 활용하여 피팅하려면 어떻게 하는게 좋을까요?

 

 

과적합 문제를 해결하기 위해 자주 사용되는 기법 중 하나가 바로 정규화(regularization) 입니다. 기존의 오차 함수에 피팅 함수의 계수 크기가 커지는 것을 막기 위해 페널티항을 추가하는 것입니다. 이러한 페널티항 중 가장 단순한 형태는 각각의 계수들을 제곱하여 합하는 것이며, 이는 다음 식 1.4처럼 표현할 수 있습니다.

 

 

식 1.4

여기서 패널티항은 $\parallel w \parallel^2$ $\equiv$ $\bf{w}^T{w}$ = $w^2_0 + w^2_1 + .... w^2_M$이며, 여기서 사용된 계수 $\lambda$는 제곱합 오류항에 대한 상대적인 중요도를 결정합니다. 

 

 

통계학 문헌들에서는 이 방법을 수축법(shrinkage method)라고 합니다. 방법 자체가 계수의 크기를 수축시키는 방식을 이용하기 때문이죠. 이차 형식(quadratic) 정규화는 리지 회귀(ridge regression)이라고 부릅니다. 딥러닝에서는 이를 가중치 감쇠(weight decay)라고 합니다.

 

 

그림 1.7을 보면 계수 $\lambda$의 값에 따른 $M = 9$ 다항식을 피팅한 결과를 확인할 수 있습니다.

 

그림 1.7

$ln\lambda$ = -18의 경우 과적합이 많이 줄어들었고, 그 결과 다항식이 기저 함수에 훨씬 더 가까워진 것을 확인할 수 있습니다. 하지만, 너무 큰 $\lambda$ 값을 사용하면 좋지 않은 피팅 결과를 가져옵니다. 이는 그림 1.7의 오른쪽 그림을 통해서 확인할 수 있습니다.

 

 

표 1.2에는 해당 피팅 다항식의 계숫값들이 나타나 있습니다.

 

표 1.2

 

$ln\lambda$ = $- \infty$ 인 경우는 $\lambda$ = 0인 경우이고, $ln\lambda$ = 0인 경우는 $\lambda$ = $e$인 경우입니다. 즉, 왼쪽에서 오른쪽으로 갈수록 $\lambda$가 더 커지는 것으로 이해할 수 있습니다.

 

 

확실히 $\lambda$가 커질수록, 정규화의 효과가 나타나는 것을 확인할 수 있습니다. 계수가 엄청나게 줄어들었죠.

 

 

그림 1.8에는 서로 다른 $ln\lambda$ 값에 따라서 훈련 집합과 시험 집합의 RMSE의 변화를 확인할 수 있습니다. $\lambda$가 모델의 복잡도를 조절해서 과적합 정도를 통제하는 것을 확인할 수 있습니다.

 

 

그림 1.8

 

지금까지의 내용을 정리해보면, 결국 오차 함수를 줄이는 방식으로 다항식을 피팅하는 경우에는 과적합 문제를 해결하기 위해서는 적합한 정도의 모델 복잡도를 선택하는 방법을 잘 활용해야 한다는 것입니다.

 

 

지금까지의 결과를 바탕으로 모델 복잡도를 잘 선택하는 단순한 방법 하나를 생각해 볼 수 있는데, 이는 바로 데이터 집합을 훈련 집합(training set)과 검증 집합(validation set, hold-out set)으로 나누는 것입니다. 

 

 

훈련 지합은 계수 $\bf{w}$를 결정하는 데 활용하고, 검증 집합은 모델 복잡도($M$나 $\lambda$)를 최적화하는 데 활용하는 방식입니다. 하지만 많은 경우에 이 방식은 소중한 훈련 집합 데이터를 낭비하게 됩니다. 따라서 더 좋은 방법을 고려할 필요가 있습니다.

 

 

지금까지 다항 곡선을 피팅하는 문제에 대해서 논의해보았으며, 다음 절에서는 확률론에 대해서 살펴보도록 합니다. 

 

 

 

1.1절 간단하게 정리

 

 

- 데이터를 잘 표현하기 위해서, 오차 함수를 정의하고 오차 함수가 최소가 되는 가중치 $\bf{w}$를 결정합니다.

 

- 이 때, 적절하게 다항 함수의 차수와 가중치의 크기를 조절해야만 과적합이 일어나지 않습니다. 주로 정규화를 이용해서 이를 조절할 수 있습니다. 

 

- 아니면 학습을 할 때, 더 많은 데이터를 사용하면 과적합을 피할 수 있습니다. 

+ Recent posts