How to Construct Autoencoders and VAEs for Image Compression: A Complete Guide
Autoencoders compress images by learning compact latent representations through encoder-decoder neural networks, while Variational Autoencoders (VAEs) add probabilistic sampling to create smooth, generative latent spaces suitable for robust compression.
This guide explains how to build autoencoders and VAEs for image compression using the curriculum from the microsoft/AI-For-Beginners repository. You will learn the architectural components, implementation differences between deterministic and probabilistic models, and practical code patterns for training these networks on image datasets.
Core Architecture of Image Compression Autoencoders
An autoencoder consists of three mandatory components that work together to compress and reconstruct images. When the latent vector dimensionality is significantly smaller than the input, the network functions as a learned compressor.
The Encoder Network
The encoder maps high-dimensional images to low-dimensional latent vectors using convolutional layers followed by dense projections. In the TensorFlow implementation from lessons/4-ComputerVision/09-Autoencoders/AutoencodersTF.ipynb, the encoder uses stacked Conv2D layers with MaxPooling2D to reduce spatial dimensions before flattening to a dense latent representation.
The Bottleneck (Latent Space)
The bottleneck holds the compressed information. In a vanilla autoencoder, this is a deterministic vector produced by a dense layer. In a Variational Autoencoder (VAE), the bottleneck represents a probability distribution with two dense heads outputting z_mean (μ) and z_log_var (log σ²), enabling probabilistic sampling.
The Decoder Network
The decoder reconstructs images from latent vectors using transposed convolutions or upsampling layers. As implemented in the PyTorch notebook AutoEncodersPyTorch.ipynb, the decoder typically uses ConvTranspose2d layers to upsample the latent representation back to the original image dimensions, ending with a sigmoid activation for pixel values.
Deterministic vs. Variational: Key Differences
Deterministic Autoencoder Structure
A deterministic autoencoder learns a direct mapping from input to latent space. The model minimizes reconstruction loss (MSE or binary cross-entropy) to force the decoder to reproduce the input from the compressed code. This architecture excels at compression but may produce discontinuous latent spaces unsuitable for generation.
VAE Probabilistic Framework
Variational Autoencoders treat the latent space probabilistically. The encoder outputs distribution parameters (mean and log-variance) rather than fixed values. The reparameterization trick samples z = μ + σ * ε where ε ∼ N(0, I), allowing gradients to flow through the stochastic node. The loss function combines reconstruction loss with KL-divergence, regularizing the latent distribution toward a standard normal.
TensorFlow/Keras Implementation
The AutoencodersTF.ipynb notebook provides a complete deterministic autoencoder implementation. Below is the core architecture:
import tensorflow as tf
from tensorflow.keras import layers, models
# Encoder
inputs = layers.Input(shape=(28, 28, 1))
x = layers.Conv2D(32, 3, activation='relu', padding='same')(inputs)
x = layers.MaxPooling2D(2, padding='same')(x)
x = layers.Conv2D(64, 3, activation='relu', padding='same')(x)
x = layers.MaxPooling2D(2, padding='same')(x)
shape_before_flatten = tf.keras.backend.int_shape(x)
x = layers.Flatten()(x)
latent = layers.Dense(32, name='latent_vector')(x) # 32-dim compression
# Decoder
x = layers.Dense(tf.math.reduce_prod(shape_before_flatten[1:]))(latent)
x = layers.Reshape(shape_before_flatten[1:])(x)
x = layers.Conv2DTranspose(64, 3, strides=2, activation='relu', padding='same')(x)
x = layers.Conv2DTranspose(32, 3, strides=2, activation='relu', padding='same')(x)
outputs = layers.Conv2D(1, 3, activation='sigmoid', padding='same')(x)
autoencoder = models.Model(inputs, outputs, name='autoencoder')
autoencoder.compile(optimizer='adam', loss='mse')
Training requires passing the input as both x and y (self-supervised learning):
autoencoder.fit(x_train, x_train,
epochs=30,
batch_size=128,
validation_data=(x_val, x_val))
PyTorch VAE Implementation
For probabilistic compression, the AutoEncodersPyTorch.ipynb notebook demonstrates a VAE with the reparameterization trick:
import torch, torch.nn as nn, torch.nn.functional as F
class VAE(nn.Module):
def __init__(self, latent_dim=20):
super().__init__()
# Encoder
self.enc_conv1 = nn.Conv2d(1, 32, 3, stride=2, padding=1) # 28→14
self.enc_conv2 = nn.Conv2d(32, 64, 3, stride=2, padding=1) # 14→7
self.fc_mu = nn.Linear(64*7*7, latent_dim)
self.fc_logvar = nn.Linear(64*7*7, latent_dim)
# Decoder
self.fc_dec = nn.Linear(latent_dim, 64*7*7)
self.dec_conv1 = nn.ConvTranspose2d(64, 32, 3, stride=2, padding=1, output_padding=1)
self.dec_conv2 = nn.ConvTranspose2d(32, 1, 3, stride=2, padding=1, output_padding=1)
def encode(self, x):
x = F.relu(self.enc_conv1(x))
x = F.relu(self.enc_conv2(x))
x = x.view(x.size(0), -1)
return self.fc_mu(x), self.fc_logvar(x)
def reparameterize(self, mu, logvar):
std = torch.exp(0.5*logvar)
eps = torch.randn_like(std)
return mu + eps*std
def decode(self, z):
x = F.relu(self.fc_dec(z))
x = x.view(-1, 64, 7, 7)
x = F.relu(self.dec_conv1(x))
x = torch.sigmoid(self.dec_conv2(x))
return x
def forward(self, x):
mu, logvar = self.encode(x)
z = self.reparameterize(mu, logvar)
return self.decode(z), mu, logvar
The VAE requires a custom loss function combining reconstruction and KL-divergence:
def vae_loss(recon_x, x, mu, logvar):
recon_loss = F.mse_loss(recon_x, x, reduction='sum')
kl = -0.5 * torch.sum(1 + logvar - mu.pow(2) - logvar.exp())
return recon_loss + kl
Compression Workflow
To use these models as practical image compressors:
- Encode: Pass the image through the encoder to obtain the latent vector (32 floats ≈ 128 bytes).
- Store: Save the latent representation (e.g., as
.npyfiles or quantized integers) instead of the full pixel array (784 bytes for 28×28 grayscale). - Decode: Feed the stored latent vector through the decoder to reconstruct the image when needed.
This achieves order-of-magnitude compression ratios while maintaining perceptual quality through end-to-end optimization.
Summary
- Autoencoders compress images by learning encoder-decoder mappings that reduce dimensionality through convolutional bottlenecks.
- Deterministic autoencoders use fixed latent vectors and minimize reconstruction loss (MSE/binary cross-entropy).
- Variational Autoencoders introduce probabilistic latent spaces with μ and log-variance outputs, enabling smooth interpolation via the reparameterization trick.
- The
microsoft/AI-For-Beginnersrepository provides complete implementations in both TensorFlow (AutoencodersTF.ipynb) and PyTorch (AutoEncodersPyTorch.ipynb). - Practical compression involves encoding images to compact latent vectors, storing the low-dimensional representations, and decoding on demand.
Frequently Asked Questions
What is the difference between an autoencoder and a VAE for image compression?
A standard autoencoder learns a deterministic mapping to a fixed latent vector, optimizing purely for reconstruction accuracy. A VAE learns a probability distribution over the latent space, adding a KL-divergence regularization term that forces the distribution toward a standard normal. This creates a smoother, more continuous latent space that is robust to perturbations and suitable for generative tasks, though it may sacrifice some reconstruction fidelity compared to deterministic models.
How does the reparameterization trick work in VAEs?
The reparameterization trick enables gradient descent through the stochastic sampling process. Instead of sampling directly from the latent distribution (which would block gradients), the model samples ε from a standard normal distribution and computes z = μ + σ * ε. This moves the randomness to an input variable while keeping the path from parameters to z differentiable, allowing backpropagation to update the encoder weights.
What loss functions are used to train autoencoders for compression?
Deterministic autoencoders typically use Mean Squared Error (MSE) or Binary Cross-Entropy between the input and reconstructed output. VAEs use a composite loss function: the reconstruction loss (MSE) plus the KL-divergence between the learned latent distribution and a prior (usually standard normal). The KL term acts as a regularizer, preventing overfitting and ensuring the latent space maintains desirable geometric properties.
How much compression can autoencoders achieve compared to traditional codecs?
Autoencoders can achieve significant compression ratios depending on the latent dimensionality. For example, compressing a 28×28 grayscale image (784 bytes) to a 32-dimensional latent vector (128 bytes) yields a 6:1 compression ratio. However, learned codecs differ from traditional methods like JPEG because they optimize for reconstruction fidelity on specific datasets rather than general-purpose entropy coding, often producing better perceptual quality at low bit-rates for in-distribution images but potentially poorer generalization to out-of-distribution data.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →