Post

Auto-Encoding Variational Bayes

Auto-Encoding Variational Bayes

Auto-Encoding Variational Bayes

Auto-Encoding Variational Bayes

Introduction

이전에 GAN에선, 모델 $G$가 생성하는 분포 $p_{g}$를 실제 데이터의 분포 $p_{\text{data}}$에 가까워지도록(근사하도록) 학습 목표를 잡았었다. 여기서 이미지라는 시각 데이터를 왜 분포로 나타내는지, 이 분포의 형태가 어떤 의미를 가지는지 딱히 별 언급은 없었지만, 여기서 자세히 살펴보도록 하자.

한 예시로 세상에는 수많은 인물 사진, 풍경 사진, 그림, 인포그래픽 등의 다양한 종류의 이미지들이 존재하고, 그 중에서도 도로롱 이미지들이 존재한다.

DOROKING

수많은 도로롱 이미지들의 본질의 어떠한 의미론적 공통된 성질이 있을 것이다. 물론 이게 어떤 것인지는 알 수 없다. 하지만 분명 도로롱 이미지들이 다른 이미지들과는 차별화되는 무언가가 분명히 존재하기에, 우리가 이걸 도로롱이라고 인식할 수 있을 것이다.

즉, 고차원의 전체 이미지 데이터 공간이 존재하고, 이 안에서 도로롱 이미지들만 가질 수 있는 독특한 성질이 모여 이루어진 특정 부분공간이 존재할 것이다. 그러면 이 전체 공간에서 도로롱 이미지를 선택할 확률을 도로롱 이미지의 확률 분포가 $p_{\text{data}} = p(x)$ 가 되는 것이다.

따라서, 우리가 보는 각각의 도로롱 이미지($x$)들은 이 거대한 분포에서 샘플링된 것으로 볼 수 있고, 이 분포 $p(x)$를 알면, 우리가 직접 도로롱 이미지를 생성해낼 수 있는 것이다.

하지만 도로롱 이미지들을 통해 봤을 때, 도로롱 분포가 어떤 파라미터를 가지는지, 어떤 형태인지, 전~혀 알 수가 없다.

변분 베이지안 (Variational Bayesian, VB) 방식에선 이러한 미지의 사후분포 $p(z\vert x)$ 를 알 수 없기 때문에, 가짜 분포 $q(z)$ 를 최대한 가깝게 만들어 근사하고자 했다. 이에 평균 장(Mean-field) 가정을 통해 각 가짜 분포 $q_{j}(z_{j})$ 를 업데이트를 다음과 같이 유도할 수 있었다.

\[\log q_{j}^{*}(z_{j}) = \mathbf{E}_{q_{-j}}[\log p(x,z)] + \text{const}\]

이때 $\mathbf{E}_{q_{-j}}$ 는 $j$ 번째 변수를 제외한 다른 모든 변수들의 가짜분포 $q$ 를 기준으로 한 기댓값을 의미한다. 이 식은 다음과 같이 나타낼 수 있다.

\[\log q_{j}^{*}(z_{j}) = \int q(z_{1})q(z_{2}) \cdots \log p(x, z_{1}, z_{2},...)dz_{1}dz_{2}\cdots + \text{const}\]

딱봐도 계산하기 더럽게 힘들어보인다. 그냥 한마디로 기존 통계학에서의 VB 방식은 전혀 쓸 수 없다는 것이다.

이 논문에서는 이러한 문제를 ELBO(Evidence Lower Bound)Reparaameterization Trick 을 통해 접근하였는데, 한번 살펴보자.

논문에선 다음과 같은 가정을 한다.

  • i.i.d. 데이터셋으로 데이터 하나당 고유한 잠재 변수가 1:1로 매칭된다.
  • 모델 전체 파라미터 $\theta$ 에 대해서 최대 우도 추정(ML)과 최대 사후 확률 추정(MAP)을 수행한다.
  • 스트리밍 데이터에도 가능은 하지만 간단하게 고정된 크기 데이터셋을 기준으로 한다.

정리하자면 딥러닝에서 가장 일반적인 학습환경으로 가정한다는 것이다.


Architecture & 손실함수(ELBO)

일단 VAE에서는 Varialtional auto-encoder로, 인코더와 디코더의 구조를 가지고 있다.

사후분포 $p_{\theta}(z\vert x)$ 에서 잠재 변수 $z$ 를 뽑고, 이 잠재변수 $z$ 를 통해서, $p_{\theta}(x\vert z)$ 에서 데이터 $x$를 뽑아낸다.

근데 여기서 진짜 사후분포 $p_{\theta}(z\vert x) = \frac{p_{\theta}(x\vert z)p_{\theta}(z)}{p_{\theta}(x)}$ 는 계산할 수가 없다. 왜냐하면 분모가 $p_{\theta}(x) = \int p(z)p(x\vert z)\,dz$ 로, 존재할 수 있는 모든 $z$ 를 고려하여 적분을 해야한다.

그래서 VAE에서는 이에 대한 근사값을 뱉는 가짜 인코더 $q_{\phi}(z\vert x)$ 를 사용하여 $x$를 입력받아 잠재 공간의 $z$ 가 가질 수 있는 확률 분포를 출력한다. 즉, 가짜 인코더 $q_{\phi}(z\vert x)$ 의 파라미터을 조절함으로써, 복잡한 적분의 필요 없이 인코더 파라미터 $\phi$ 와 디코더 파라미터 $\theta$ 를 동시에 최적화하는 딥러닝 문제가 된 것이다.

정리하자면,

  • 인코더 $q_{\phi}(z\vert x)$ 는 데이터 $x$를 입력받아 데이터의 특징 분포를 출력하고, $\phi$는 진짜 사후 분포인 $p_{\theta}(z\vert x)$ 에 가까워지도록 갱신한다.
  • 디코더 $p_{\theta}(x\vert z)$ 는 인코더에서 출력된 분포에서 샘플링된 잠재 변수 $z$를 입력받아 원래 데이터 $x$ 를 복원한다. $\theta$ 는 기존 데이터 $x$ 에 최대한 똑같이 복원되도록 갱신된다.

그걸 어떻게 하는지! 이제부터 알아보자.

손실함수

우리가 생성하는 이미지가 실제 데이터 $x$ 와 가장 비슷하도록 하기 위해서 전체 데이터셋의 확률을 알아야 한다. 전체 데이터들의 확률은 다음과 같다.

\[\log p_{\theta}(x^{(1)},...,x^{(N)}) = \sum_{i=1}^{N}\log p_{\theta}(x^{(i)})\]

여기서 각 데이터의 확률은 다음과 같이 나타낼 수 있다.

\[\begin{align} \log p_{\theta}(x^{(i)}) &= \int q_{\phi}(z\vert x^{(i)})\log p_{\theta}(x^{(i)})\,dz~~~\cdots \left( \int q(z\vert x^{(i)})\,dz = 1 \right) \\ &= \int q_{\phi}(z\vert x^{(i)})\log\left[ \frac{p_{\theta}(x^{(i)},z)}{q_{\phi}(z\vert x^{(i)})} * \frac{q_{\phi}(z\vert x^{(i)})}{p_{\theta}(z\vert x^{(i)})} \right]\,dz \\ &= \int q_{\phi}(z\vert x^{(i)})\log\left[ \frac{q_{\phi}(z\vert x^{(i)})}{p_{\theta}(z\vert x^{(i)})} \right]\,dz + \int q_{\phi}(z\vert x^{(i)})\log\left[ \frac{p_{\theta}(x^{(i)},z)}{q_{\phi}(z\vert x^{(i)})} \right] \, dz \\ &= D_{KL}(q_{\phi}(z\vert x^{(i)}) || p_{\theta}(z\vert x^{(i)})) + \mathbf{E}_{q_{\phi}(z\vert x^{(i)})}[-\log q_{\phi}(z\vert x^{(i)}) + \log p_{\theta}(x^{(i)},z)] \\ \therefore \log p_{\theta}(x^{(i)}) &= D_{KL}(q_{\phi}(z\vert x^{(i)})||p_{\theta}(z\vert x^{(i)})) + \mathcal{L}(\theta,\phi;x^{(i)}) \end{align}\]

여기서 첫번째 항은 KL-divergence로 항상 0 이상의 값을 가지므로, $\mathcal{L}(\theta,\phi;x^{(i)})$ 이 항이 로그 우도 함수의 최솟값, 즉, ELBO(Evidence Loswer Bound) 가 된다. 다시 말해서, 다음과 같이 나타낼 수 있다.

\[\log p_{\theta}(x^{(i)}) \geq \mathcal{L}(\theta,\phi;x^{(i)}) = \mathbf{E}_{q_{\phi}(z\vert x)}[-\log q_{\phi}(z\vert x) + \log p_{\theta}(x,z)]\]

이 ELBO 식을 디코더 조건부 확률 공식( $p(x,z) = p(z)p(x\vert z)$ )을 이용해 다시 정리하면 다음과 같이 나타난다.

\[\begin{align} \mathcal{L}(\theta,\phi;x) &= \int q_{\phi}(z\vert x)\log\left[ \frac{p_{\theta}(x,z)}{q_{\phi}(z\vert x)} \right]\,dz \\ &= \int q_{\phi}(z\vert x)\log\left[ \frac{p_{\theta}(z) * p_{\theta}(x\vert z)}{q_{\phi}(z\vert x)} \right]\,dz \\ &= \int q_{\phi}(z\vert x)\log\left[ \frac{p_{\theta}(z)}{q_{\phi}(z\vert x)} \right]\,dz + \int q_{\phi}(z\vert x)\log p_{\theta}(x\vert z)\,dz \\ &= -\int q_{\phi}(z\vert x)\log\left[ \frac{q_{\phi}(z\vert x)}{p_{\theta}(z)} \right]\,dz + \int q_{\phi}(z\vert x)\log p_{\theta}(x\vert z)\,dz \\ \therefore \mathcal{L}(\theta,\phi;x^{(i)}) &= -D_{KL}(q_{\phi}(z\vert x^{(i)}) || p_{\theta}(z)) + \mathbf{E}_{q_{\phi}(z\vert x^{(i)})}[\log p_{\theta}(x^{(i)}|z)] \tag{3} \end{align}\]

이게 바로 손실함수다! 이제 이를 토대로 기울기에 기반한 파라미터 최적화를 수행할 수 있다! 하지만 아쉽게도 이 손실함수는 $\phi$ 에 대해 그대로 미분했을 때 문제가 생긴다.

\[\begin{align} \nabla_{\phi}\mathbf{E}_{q_{\phi}(z)}[f(z)] &= \nabla_{\phi}\int q_{\phi}(z)f(z)\, dz = \int \nabla_{\phi}q_{\phi}(z)f(z)\,dz \\ &= \int q_{\phi}(z)\nabla_{\phi}\log q_{\phi}(z)f(z)\,dz \\ &\cdots \left( \nabla \log q = \frac{\nabla q}{q} \implies q\nabla \log q = \nabla q \right) \\ &= \mathbf{E}_{q_{\phi}(z)}[f(z)\nabla_{q_{\phi}(z)}\log q_{\phi}(z)] \\ &\simeq \frac{1}{L}\sum_{l=1}^{L} f(z^{(l)})\nabla_{\phi}\log q_{\phi}(z^{(l)}) \end{align}\]

식을 보면 알 수 있지만, Monte-Carlo 샘플링 $z \sim q_{\phi}$ 를 하는 순간, 복원 오차 $f(z)$ 가 곱해지기 때문에 학습하기 위해 샘플링 $z \sim q_{\phi}$ 를 갈기는 순간 높은 분산으로 인해 학습이 우주로 가버린다.

이러한 문제에 논문에서는 여기서 Reparameterization Trick 을 사용한다.

Reparameterization Trick

임의의 미분가능한 함수 $g_{\phi}(\epsilon, x)$ 와 노이즈 변수 $\epsilon$ 을 도입하여, 기존 $z$ 를 다음과 같이 나타낸다.

\[\begin{align} \tilde{z} &= g_{\phi}(\epsilon, x) \\ \epsilon &\sim p(\epsilon) \end{align}\]

이렇게 나타내면 다음과 같이 나타낼 수 있다.

\[\int q_{\phi}(z\vert x)f(z)\,dz = \int p(\epsilon)f(z)\,d\epsilon = \int p(\epsilon)f(g_{\phi}(\epsilon, x))\, d\epsilon\]

이렇게 하면? 기존 식 $\mathbf{E}_{q_{\phi}(z\vert x)}[f(z)]$ 에선 미분 대상인 $\phi$ 가 분포 $q_{\phi}$ 에 위치하여 앞서 살펴봤듯이 높은 분산이 나오지만, 새로운 식 $\mathbf{E}_{p(\epsilon)}[f(g_{\phi}(\epsilon, x))]$ 에선 미분 대상 $\phi$ 가 기댓값 안쪽의 함수로 쫓겨난다. 그러면 즉,

\[\begin{align} \mathbf{E}_{q_{\phi}(z\vert x^{(i)})}[f(z)] &= \mathbf{E}_{p(\epsilon)}[f(g_{\phi}(\epsilon, x^{(i)}))] \simeq \frac{1}{L} \sum_{l=1}^{L} f(g_{\phi}(\epsilon^{(l)}, x^{(i)})) \\ \epsilon^{(l)} &\sim p(\epsilon) \end{align}\]

이제 샘플링은 $\phi$ 와 무관한 표준 노이즈 분포 $p(\epsilon)$ 가 전담하므로, 역전파 기울기는 사칙연산 함수 $g_{\phi}$ 를 타고 인코더 끝까지 삼뽕하게 흐를 수 있다. 이 식을 $\phi$ 에 대해 미분하면 다음과 같다.

\[\begin{align} \nabla_{\phi}\mathbf{E}_{p(\epsilon)}[f(g_{\phi}(\epsilon, x^{(i)}))] &= \mathbf{E}_{p(\epsilon)}[\nabla_{z}f(z) \cdot \nabla_{\phi}g_{\phi}(\epsilon, x^{(i)})] \\ &\simeq \frac{1}{L} \sum_{l=1}^{L} \nabla_{z}f(z) \cdot \nabla_{\phi}g_{\phi}(\epsilon^{(l)}, x^{(i)}) \end{align}\]

그래서 기존 미분식에 디코더 복원 오차인 $f(z)$ 가 곱해진 것과 달리, 오차 함수 자체를 미분한 $\nabla_{z} f(z)$ 라는 기울기가 직접 곱해져, 잠재 공간 $z$ 가 어느 방향으로 가야할지 그대로 전해진다고 볼 수 있다. 따라서 분산이 낮아져 안정적인 학습이 가능해진다.

그래서 이 Reparameterization Trick을 우리가 앞서 살펴본 손실함수에 적용하면? 다음과 같이 나타난다.

\[\begin{align} &\tilde{\mathcal{L}}(\theta,\phi;x^{(i)}) = -D_{KL}(q_{\phi}(z\vert x^{(i)}) || p_{\theta}(z)) + \frac{1}{L}\sum_{l=1}^{L} (\log p_{\theta}(x^{(i)} | z^{(i,l)})) \\ &z^{(i,l)} = g_{\phi}(\epsilon^{(i,l)}, x^{(i)}),~~~\epsilon^{(l)} \sim p(\epsilon) \end{align}\]

이게 우리의 정말 최종 손실함수이다. 이제 이걸 구현하면 된다!

그러면 다음 두 가지 문제를 풀어야 한다.

  • 함수 $g_{\phi}()$ 와 $p(\epsilon)$ 의 형태 정의
  • 손실함수 각 항 계산

$g_{\phi}()$, $p(\epsilon)$의 형태

논문에 다양한 경우에서 각 함수 $g_{\phi}, p(\epsilon)$ 을 어떻게 선택해야할 지 나와있긴 하지만, 논문에서는 잠재 변수의 분포 $z \sim p(z\vert x) = \mathcal{\mu, \sigma^{2}}$ 를 그냥 가우시안으로 퉁쳐버린다. 그래서 $z \sim q_{\phi}(z\vert x)$ 도 가우시안이므로, $\epsilon \sim p(\epsilon) = \mathcal{N}(0, 1)$ 이 된다. 이를 통해서 함수 $g_{\phi}$ 는 다음과 같이 나타난다.

\[g_{\phi}(x, \epsilon) = \mu_{x} + \sigma_{x} \odot \epsilon\]

근데 그냥 가우시안으로 퉁친 이유는 내 생각엔 신경망으로 표현하기 가장 편하고, 바로 뒤에서 후술하겠지만 손실함수의 각 항의 값을 깔끔하게 계산할 수 있기 때문이 아닐까 싶다.

손실함수 계산

KL-divergence 항 계산

먼저 KL-divergence는 가우시안의 경우 논문에서 아주 친절하게 식의 유도를 넣어놨다. 먼저 $J$ 는 잠재 변수 $z$의 차원이고, $\mu,\sigma$는 데이터 $i$ 에서 인코더가 근사한 분포의 평균, 표준 편차이고,

\[\begin{align} \int q_{\phi}(z\vert x^{(i)}) \log p_{\theta}(z)\,dz &= \int \mathcal{N}(z; \mu, \sigma^{2}) \log \mathcal{N}(z; 0, \mathbb{I})\,dz \\ &= - \frac{J}{2}\log(2\pi) - \frac{1}{2}\sum_{j=1}^{J}(\mu_{j}^{2}+\sigma_{j}^{2}) \end{align}\] \[\begin{align} \int q_{\phi}(z\vert x^{(i)})\log q_{\phi}(z\vert x)\,dz &= \int \mathcal{N}(z; \mu, \sigma^{2}) \log \mathcal{N}(z; \mu, \sigma^{2}) \,dz \\ &= - \frac{J}{2}\log(2\pi) - \frac{1}{2}\sum_{j=1}^{J}(1 + \log \sigma_{j}^{2}) \end{align}\]

위 두 식을 통해 최종 KL항은 다음과 같이 계산된다.

\[\begin{align} -D_{KL}((q_{\phi}(z\vert x^{(i)}) || p_{\theta}(z))) &= \int q_{\phi}(z\vert x^{(i)})(\log p_{\theta}(z) - \log q_{\phi}(z\vert x^{(i)}))\,dz \\ &= \frac{1}{2}\sum_{j=1}^{J}(1 + \log(\sigma_{j}^{2}) - \mu_{j}^{2} - \sigma_{j}^{2}) \end{align}\]

매~우 간단하다. 인코더의 출력인, 근사한 분포의 평균, 표준편차만 알고 있으면 된다.

Reconstruction 복원 오차 항 계산

다음으로 두번째 항은 앞서서 복원오차에 대한 항 $f(g_{\phi}(\epsilon, x^{i})) = \log p_{\theta}(x^{i}\vert g_{\phi}(\epsilon, x^{i}))$ 이라고 했었다. 이는 디코더가 이미지를 얼마나 잘 복원했는지를 나타낸다.

논문에서는 $L=1$ 로 설정하기 때문에, 한개의 샘플에 대해서만 처리하면 된다.

\[\mathbf{E}_{q_{\phi}(z|\mathbf{x}^{(i)})}[\log p_{\theta}(\mathbf{x}^{(i)}|z)] \simeq \log p_{\theta}(\mathbf{x}^{(i)}|\tilde{z}^{(i)}) \quad \left( \text{where } \tilde{z}^{(i)} = \mu^{(i)} + \sigma^{(i)} \odot \epsilon \right)\]

즉, 디코더의 로그 우도 함수를 구하기만 하면 된다. 근데 이는 어떤 데이터셋인지에 따라 다른 형태를 가지는데, 이진 데이터의 경우 베르누이 분포, 실수형의 데이터의 경우 가우시안을 사용한다.

먼저 가우시안의 경우, 다음과 같다.

\[\begin{align} \log p_{\theta}(x\vert z) &\approx \sum_{j=1}^{J}-\frac{(x_{j} - \hat{x}_{j})^{2}}{2\sigma^{2}} \\ &= -\frac{1}{2\sigma^{2}}\sum_{j=1}^{J} (x_{j} - \hat{x}_{j})^{2} \end{align}\]

즉, MSE와 같다.

베르누이의 경우, 다음과 같다.

\[\begin{align} \log p_{\theta}(x\vert z) &= \log\sum_{j=1}^{J}\hat{x}_{j}^{x_{j}}(1-\hat{x}_{j})^{(1-x_{j})} \\ &= \sum_{j=1}^{J} x_{j} \log \hat{x}_{j} + (1-x_{j}) \log (1-\hat{x}_{j}) \end{align}\]

즉, BCE(Binary Cross Entropy)와 같다.

Implementation

전체적인 모델의 구조는 DCGAN과의 비교를 위해서, DCGAN과 똑같이 설정하였고, 같은 데이터셋 MNIST와 LSUN bedroom을 사용하였다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Encoder(nn.Module):
    def __init__(self):
        ...
        self.blocks = nn.ModuleList([...])
        ...

        # in_features = current_channels * kernel_size * kernel_size
        self.to_mu = nn.Linear(in_features=1024 * 4 * 4, out_features=d_z)
        self.to_logvar = nn.Linear(in_features=1024 * 4 * 4, out_features=d_z)

    def forward(self, x):
        h = x
        for block in self.blocks:
            h = block(h)
        
        h = h.view(h.size(0), -1)
        return self.to_mu(h), self.to_logvar(h)

위와 같이 인코더는 이미지 ( $\text{batch_size}, C, H, W$ ) 를 입력 받아 이를 ( $\text{batch_size}, d_z$ ) 의 잠재 공간의 평균과 로그분산 $\log(\sigma^{2})$ 벡터를 출력한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Decoder(nn.Module):
    def __init__(self):
        ...
        self.blocks = nn.ModuleList([...])
        ...
        self.tail = nn.Tanh()

        if dataset=="MNIST":
            self.tail = nn.Sigmoid()
        elif dataset=="LSUN":
            self.tail = nn.Tanh()
        
    def forward(self, x):
        h = x.view(x.size(0), -1, 1, 1)
        for block in self.blocks:
            h = block(h)
        
        h = self.tail(h)
         
        return h 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class VAE(nn.Module):
    def __init__(self):
        self.encoder = Encoder()
        self.decoder = Decoder()
        ...
    ...
    ...
    def get_noise(self, batch_size):
            return torch.randn(batch_size, self.d_z, device=self.device)
        
    def reparameterization(self, mu, logvar):
        eps = self.get_noise(mu.size(0))
        std = torch.exp(0.5 * logvar)
        return mu + std * eps
        

    def forward(self, x):
        mu, logvar = self.encoder(x)
        z = self.reparameterization(mu, logvar)
        
        outputs = []
        for _ in range(self.L):
            outputs.append(self.decoder(z))
            
        return outputs, mu, logvar

학습 코드도 나름 간단하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    def get_loss(self, criterion, model_outputs, target, mu, logvar, beta=1.0):
        kl_loss = (1 + logvar - mu**2 - torch.exp(logvar)) * -0.5
        kl_loss = kl_loss.sum(dim=1).mean()
        
        assert criterion is not None
        
        recon_loss = 0
        for output in model_outputs:
            recon_loss += criterion(output, target) / target.size(0)
            
        recon_loss = recon_loss / self.L
        
        loss = kl_loss + recon_loss

        return loss
1
2
3
4
if ds_name == "LSUN":
        criterion = nn.MSELoss(reduction='sum')
elif ds_name == "MNIST":
    criterion = nn.BCELoss(reduction='sum')

이때 Loss(reduction='mean')의 경우, 과소적합(Underfitting)이 발생했는데, 전체 제곱 오차의 총합을 $\text{batch_size} \times \text{channels} \times H \times W$ 으로 나누게 된다. 그렇기에, Loss(reduction='sum')으로 설정하여 따로 $\text{batch_size}$ 로만 나누도록 한다.

Outputs

recon_test

이 사진은 입력 이미지를 얼마나 잘 복원했는지를 보여준다. 위쪽 행이 기존 입력 이미지, 아래 행이 모델이 생성한(복원한) 이미지 출력이다.

잠재 공간 보간잠재 공간 특정 차원 값 변화
latentlatent_dim

.vs (DC)GAN

(DC)GANVAE
DCGAN_outputsVAE_outputs

결과를 보면 VAE가 비교적 많이 뿌연데(빨간약..), 그 이유에 대해서, 외국인 형님들의 키보드 배틀들을 유심히 본 결과 다음과 같이 정리할 수 있었다.

  • 손실 함수, 유사도 측정 기준 차이

VAE는 주로 정답 이미지와 예측 이미지의 유클리드 거리(픽셀 단위의 오차)를 줄이도록 학습된다. MSEBCE처럼. 근데 이 픽셀 단위의 오차는 사람이 느끼는 이미지의 유사도를 반영하지 않는다. 예를 들어서 정답의 특정 사물이 특정 방향으로 1픽셀만 이동해도 오차가 매우 커지게 된다. 그래서 모델은 오차를 제일 줄일 수 있게끔 사물을 대강 평균값으로 뿌옇게 그리도록 학습된다는 것이다. 결국 디코더 모델이 뱉는 출력 이미지는 이미지 그 자체가 아니라 각 픽셀이 가질 가우시안 분포의 평균을 예측한다. 이러한 평균값들은 주변 픽셀들과의 오차를 부드럽게 완충하기 위해 급격한 변화를 피하려는 성질을 가지는데, 디지털 이미지에서 주변 픽셀의 값을 평균 내서 경계선을 뭉개버리는 연산이 바로 가우시안 블러 필터이다. 즉, 디코더의 출력물 자체가 이러한 필터를 먹인 것이라는 것이다!

이에 반해 (DC)GAN에선 고정된 수식이 아니라 판별모델 $D$ 가 손실 함수의 역할을 하면서 실제 데이터 분포 $p_{\text{data}}$ 를 찾도록 모델 $G$ 를 조련(?)한다.

의문의 형님들이 다른걸로도 싸우시던데, 내가 이해한건 여기까지다. 내공을 더 쌓고 와야겠다.

DOROKING

찌라시

사실 MNIST에 대한 건 생략했다. 한번만 봐달라.

This post is licensed under CC BY 4.0 by the author.

Trending Tags