📌 목차
1. Auto Encoder
2. Variational Auto Encoder
3. 요약
1. Auto Encoder
Intro.
Encoding: Data를 저차원 latent space의 특정 point에 mapping시키는 것.
Decoding: 이 point의 위치를 받아 해당 Data를 다시 생성하려 시도하는 것.
encoder는 decoder가 정확하게 재구성하도록 가능한 많은 정보를 내포시키려 하는데, 이 벡터를 embedding이라 한다.
Auto Encoder: encoding과 decoding작업을 수행하도록 훈련된 신경망.
→ 원본에 가까워지도록 train시킨다.
보통 Loss function으론 원본과 재구성 img의 pixel간의 RMSE나 BCE를 사용한다.
🤔 이미 갖고 있는 img를 왜 재구성?
이미 갖고있는 img를 왜 재구성해야할까?
"Auto Encoder 사용이유": Embedding공간(latent space) 때문!
latent space에서 sampling하여 새로운 img를 생성할 수 있기 때문.
cf) test_set을 encoder에 통과시켜 만들어진 embedding을 그래프(scatter 등)로 나타내면, img가 latent space에 어떻게 embedding되는지 시각화가능하다.
import torch import torch.nn as nn import matplotlib.pyplot as plt from torchvision import datasets, transforms from torch.utils.data import DataLoader # 예시: Autoencoder 모델 정의 class Autoencoder(nn.Module): def __init__(self, input_size, hidden_size): super(Autoencoder, self).__init__() self.encoder = nn.Linear(input_size, hidden_size) self.decoder = nn.Linear(hidden_size, input_size) def forward(self, x): encoded = self.encoder(x) decoded = self.decoder(encoded) return encoded, decoded # 예시: 잠재 공간 시각화 함수 def visualize_latent_space(data_loader, model, device): model.eval() latent_space, labels = [], [] with torch.no_grad(): for images, labels_batch in data_loader: images = images.view(images.size(0), -1).to(device) encoded, _ = model(images) latent_space.append(encoded.cpu().numpy()) labels.append(labels_batch.numpy()) latent_space = torch.cat(latent_space, dim=0) labels = torch.cat(labels, dim=0) plt.figure(figsize=(10, 8)) plt.scatter(latent_space[:, 0], latent_space[:, 1], c=labels, cmap='viridis') plt.colorbar() plt.title('Latent Space Visualization') plt.show() # 예시: 데이터 로딩 transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))]) mnist_data = datasets.MNIST(root='./data', train=True, transform=transform, download=True) data_loader = DataLoader(mnist_data, batch_size=64, shuffle=True) # 예시: 모델 및 학습 설정 input_size = 28 * 28 # MNIST 이미지 크기 hidden_size = 2 # 잠재 공간 차원 autoencoder_model = Autoencoder(input_size, hidden_size) device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') autoencoder_model.to(device) # 예시: 학습된 모델의 가중치 로드 autoencoder_model.load_state_dict(torch.load('autoencoder_model.pth')) autoencoder_model.eval() # 예시: 잠재 공간 시각화 visualize_latent_space(data_loader, autoencoder_model, device)
이에대해, Decoder를 사용해 다시 pixel공간으로 변환하면 새로운 img를 생성할 수 있다.import torch import torch.nn as nn import matplotlib.pyplot as plt from torchvision import datasets, transforms from torch.utils.data import DataLoader # 예시: Autoencoder 모델 정의 class Autoencoder(nn.Module): def __init__(self, input_size, hidden_size): super(Autoencoder, self).__init__() self.encoder = nn.Linear(input_size, hidden_size) self.decoder = nn.Linear(hidden_size, input_size) def forward(self, x): encoded = self.encoder(x) decoded = self.decoder(encoded) return encoded, decoded # 예시: 이미지 생성 함수 def generate_image_from_latent_space(latent_vector, model, device): model.eval() with torch.no_grad(): latent_vector = torch.tensor(latent_vector).float().to(device) reconstructed_image = model.decoder(latent_vector) reconstructed_image = reconstructed_image.view(1, 1, 28, 28) # MNIST 이미지 크기 return reconstructed_image # 예시: 이미지 시각화 함수 def visualize_image(image): plt.imshow(image.squeeze().cpu().numpy(), cmap='gray') plt.axis('off') plt.show() # 예시: 데이터 로딩 transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))]) mnist_data = datasets.MNIST(root='./data', train=True, transform=transform, download=True) data_loader = DataLoader(mnist_data, batch_size=64, shuffle=True) # 예시: 모델 및 학습 설정 input_size = 28 * 28 # MNIST 이미지 크기 hidden_size = 2 # 잠재 공간 차원 autoencoder_model = Autoencoder(input_size, hidden_size) device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') autoencoder_model.to(device) # 예시: 학습된 모델의 가중치 로드 autoencoder_model.load_state_dict(torch.load('autoencoder_model.pth')) autoencoder_model.eval() # 예시: 잠재 공간에서 샘플링하여 이미지 생성 및 시각화 latent_vector_sample = [0.5, 0.5] # 잠재 공간에서 샘플링한 값 generated_image = generate_image_from_latent_space(latent_vector_sample, autoencoder_model, device) visualize_image(generated_image)
2. Variational Auto Encoder
Auto Encoder의 문제점.
Auto Encoder는 latent space가 연속적인지 강제하지 않기에, 다룰 수 있는 차원 수가 적다.
그렇기에, 더 복잡한 img생성 시, latent space에 더 많은 차원 사용 시, 이 문제는 뚜렷해진다.
즉, encoding 시, latent space를 자유롭게 사용하면 비슷한 point가 모인 그룹간의 큰 간격이 발생한다.
이런 간격이 발생한 공간에서는 잘 형성된 img를 생성할 가능성이 낮다.
이를 위해 Auto Encoder 대신, Variational Auto Encoder로 바꿔야한다.
VAE
AE: 각 img가 latent space의 한 point에 직접 mapping됨.
VAE: 각 img가 latent space point주변의 다변량정규분포에 mapping됨.
다변량 정규분포 N(0,I)는 평균벡터가 0, 공분산행렬이 단위벡터이다.
즉, 요약해보면 Encoder는 input을 받아 latent space의 다변량정규분포를 정의하는 2개의 벡터로 encoding한다.
∙ z_mean: 이 분포의 평균벡터
∙ z_log_var: 차원별 분산의 로그값
z = z_mean + z_sigma*epsilon z_sigma = exp(z_log_var * 0.5) epsilon ~ N(0, I)
🧐 Main Contribution of VAE
이런 작은 변화로 어떻게 Encoder를 향상시킬 수 있었을까?
이전 AE: latent space를 연속적으로 만들필요❌
VAE: z_mean주변영역에서 random point를 sampling하기에
재구성손실이 작게 유지되도록 동일영역의 Point의 img와 매우 비슷하게 decoding.
cf) reparameterization trick이란?
mean과 log_var로 정의된 정규분포에서 직접 sampling하는 대신,
표준정규분포에서 epsilon을 sampling → 올바른 평균과 분산을 갖도록 sample수동조정.
이 방법은 epsilon을 포함함으로써 출력의 편도함수를 결정론적(= random epsilon과 무관함)으로 표시할 수 있어 역전파를 위해 필수적이다.
🧐 Loss Function
AE의 손실함수: Src_img와 AE통과한 출력간의 Reconstruction Loss
VAE: Reconstruction Loss + KL-Divergence (Latent Loss)
KL Divergence는 z_mean과 z_log_var가 표준정규분포와 얼마나 다른지를 측정한다.
KL_Loss = -0.5 * sum(1 + z_log_var - z_mean**2 - exp(z_log_var))
# the Binary Cross Entropy between the target and the output reconstruction_function = nn.BCELoss(size_average=False) def loss_function(recon_x, x, mu, logvar): BCE = reconstruction_function(recon_x, x) KLD_element = mu.pow(2).add_(logvar.exp()).mul_(-1).add_(1).add_(logvar) KLD = torch.sum(KLD_element).mul_(-0.5) return BCE + KLD
이제, VAE에 대한 전반적인 구현을 진행해보자.
import torch import torch.nn as nn import torch.Functional as F class VAE(nn.Module): def __init__(self): super (VAE, self).__init_() self.fc1 = nn.Linear(784, 400) self.fc21 = nn.Linear (400, 20) self.fc22 = nn.Linear (400, 20) self.fc3 = nn.Linear (20, 400) self. fc4 = nn.Linear (400, 784) def encode(self, x): h1 = F.relu(self.fc1(x)) return self.fc21(h1), self.fc22(h1) def reparameterize(self, mu, logvar): if self.training: std = torch.exp(0.5*logvar) eps = torch.randn_like(std) return eps.mul(std).add_(mu) else: return mu def decode(self, z): h3 = F.relu(self.fc3(z)) return F.sigmoid(self.fc4(h3)) def forward(self, x): mu, logvar = self.encode(x.view(-1, 784)) z = self.reparameterize(mu, logvar) return self.decode(z), mu, logvar # Reconstruction + KL divergence losses summed over all elements and batch def loss_function (recon_x, x, mu, logvar): BCE = F.binary_cross_entropy (recon_x, x.view(-1, 784), size_average=False) KLD = -0.5 * torch.sum(1 + logvar - mu.pow(2) - logvar.exp()) return BCE + KLD
3. 요약
VAE의 장단점
[장점]
이전에 말했듯, VAE는 명시적으로 밀도함수를 모델링한다.
따라서 VAE는 GAN에 비해 학습이 안정적인 편이다.
손실함수에서 확인할 수 있듯 reconstruction error과 같이 평가 기준이 명확하기 때문이다.
아울러 데이터뿐 아니라 데이터에 내재한 잠재변수 𝑧도 함께 학습할 수 있다는 장점이 있다 (feature learning).
[단점]
하지만, 출력이 선명하지 않고 평균값 형태로 표시되는 문제와
reparameterization trick이 모든 경우에 적용되지 않는 문제 등이 존재한다.
AE vs VAE
AE를 사용해 고차원 img를 저차원 Latent space로 mapping하는데, 1가지 단점이 존재한다.
학습된 latent space에서 sampling시 문제가 발생한다.
VAE는 밀도함수를 쉽게 다루기 위해(= 계산가능하도록) 모델에 무작위성을 주입하고
point가 latent space에 분포되는 방식을 제약하여 AE의 문제를 해결한다.
'Gain Study > Generation' 카테고리의 다른 글
[G]Part 2-5. Energy-based Model (0) | 2024.01.29 |
---|---|
[G]Part 2-4. Normalizing Flows (2) | 2024.01.29 |
[G]Part 2-3. Auto Regressive Models (0) | 2024.01.26 |
[G]Part 2-2. GAN (0) | 2024.01.26 |
[G]Part 1. Intro. Generative Deep Learning (0) | 2024.01.25 |