TensorFlow Autoencoders

An autoencoder is a neural network that learns to compress data into a compact representation and then reconstruct the original from that compressed form. It trains without any labels — the input itself is the target output. Autoencoders discover the essential structure of data automatically and apply it to denoising images, detecting anomalies, reducing dimensionality, and generating new samples through their latent space.

The Packing and Unpacking Analogy

Imagine packing a suitcase for a long trip. You cannot bring everything, so you carefully fold, roll, and compress your clothes to fit as much as possible into a small bag. When you arrive, you unpack and restore your wardrobe to its original form. An autoencoder does the same: the encoder "packs" the input into a small latent vector, and the decoder "unpacks" it back to the original size. The quality of reconstruction depends on how well the packing preserved the essential information.

Autoencoder Architecture

Input (784 dimensions — a 28×28 MNIST digit image)
         │
         ▼
ENCODER — compresses
  Dense(256, relu)   → (256,)
  Dense(128, relu)   → (128,)
  Dense(64,  relu)   → (64,)
         │
         ▼
LATENT SPACE (bottleneck)
  Dense(32)          → (32,)   ← Compressed representation
         │
         ▼
DECODER — reconstructs
  Dense(64,  relu)   → (64,)
  Dense(128, relu)   → (128,)
  Dense(256, relu)   → (256,)
  Dense(784, sigmoid) → (784,) ← Reconstructed image
         │
         ▼
Output (784 dimensions — reconstructed image)

Loss: MSE or Binary Crossentropy between Input and Output
The network receives NO labels — it just tries to reconstruct its own input.

Building an Autoencoder in TensorFlow

import tensorflow as tf

LATENT_DIM = 32

# Encoder
encoder_input = tf.keras.Input(shape=(784,))
x = tf.keras.layers.Dense(256, activation='relu')(encoder_input)
x = tf.keras.layers.Dense(128, activation='relu')(x)
x = tf.keras.layers.Dense(64,  activation='relu')(x)
latent = tf.keras.layers.Dense(LATENT_DIM, name='latent')(x)

encoder = tf.keras.Model(encoder_input, latent, name='encoder')

# Decoder
decoder_input = tf.keras.Input(shape=(LATENT_DIM,))
x = tf.keras.layers.Dense(64,  activation='relu')(decoder_input)
x = tf.keras.layers.Dense(128, activation='relu')(x)
x = tf.keras.layers.Dense(256, activation='relu')(x)
reconstructed = tf.keras.layers.Dense(784, activation='sigmoid')(x)

decoder = tf.keras.Model(decoder_input, reconstructed, name='decoder')

# Full autoencoder: encoder + decoder chained
autoencoder_input = tf.keras.Input(shape=(784,))
encoded   = encoder(autoencoder_input)
decoded   = decoder(encoded)
autoencoder = tf.keras.Model(autoencoder_input, decoded, name='autoencoder')

# Compile with reconstruction loss
autoencoder.compile(
    optimizer='adam',
    loss='binary_crossentropy'   # or 'mse' for continuous-valued inputs
)
autoencoder.summary()

Training the Autoencoder

import numpy as np
import tensorflow as tf

# Load MNIST
(x_train, _), (x_test, _) = tf.keras.datasets.mnist.load_data()
# Note: labels are ignored — unsupervised learning

# Preprocess
x_train = x_train.reshape(-1, 784).astype('float32') / 255.0
x_test  = x_test.reshape(-1, 784).astype('float32') / 255.0

# Train: input = output = the image itself
history = autoencoder.fit(
    x_train, x_train,          # ← Input and target are the SAME
    epochs=30,
    batch_size=256,
    validation_data=(x_test, x_test)
)

Application 1 — Image Denoising

A denoising autoencoder receives noisy images as input and learns to output clean images. This forces the encoder to learn which features are true signal and which are noise.

# Add noise to training images
noise_factor = 0.4
x_train_noisy = x_train + noise_factor * np.random.normal(size=x_train.shape)
x_train_noisy = np.clip(x_train_noisy, 0.0, 1.0)

x_test_noisy  = x_test + noise_factor * np.random.normal(size=x_test.shape)
x_test_noisy  = np.clip(x_test_noisy, 0.0, 1.0)

# Train: input=noisy, target=clean
denoiser = build_autoencoder()   # Same architecture
denoiser.fit(
    x_train_noisy, x_train,     # ← Noisy input, clean output
    epochs=30,
    batch_size=256,
    validation_data=(x_test_noisy, x_test)
)

# Denoise new images
clean_predictions = denoiser.predict(x_test_noisy)

Application 2 — Anomaly Detection

An autoencoder trained on normal data learns to reconstruct normal patterns efficiently. Anomalous data — which the model has never seen — reconstructs poorly. High reconstruction error signals an anomaly.

# Train on normal credit card transactions only
autoencoder.fit(normal_transactions, normal_transactions, epochs=20)

# Compute reconstruction error on all transactions
reconstructions = autoencoder.predict(all_transactions)
mse_per_sample = np.mean((all_transactions - reconstructions) ** 2, axis=1)

# Set threshold at 95th percentile of normal reconstruction errors
threshold = np.percentile(mse_per_sample[normal_indices], 95)

# Flag high-error transactions as anomalies
anomalies = mse_per_sample > threshold
print(f"Detected {anomalies.sum()} potential fraudulent transactions")

Convolutional Autoencoder for Images

import tensorflow as tf

# Encoder with Conv2D layers
encoder_in = tf.keras.Input(shape=(28, 28, 1))
x = tf.keras.layers.Conv2D(32, 3, activation='relu', padding='same')(encoder_in)
x = tf.keras.layers.MaxPooling2D(2)(x)   # (14, 14, 32)
x = tf.keras.layers.Conv2D(64, 3, activation='relu', padding='same')(x)
x = tf.keras.layers.MaxPooling2D(2)(x)   # (7, 7, 64)
encoded = tf.keras.layers.Conv2D(16, 3, activation='relu', padding='same')(x)

# Decoder with Conv2DTranspose (upsampling)
decoder_in = tf.keras.Input(shape=(7, 7, 16))
x = tf.keras.layers.Conv2DTranspose(64, 3, activation='relu', padding='same')(decoder_in)
x = tf.keras.layers.UpSampling2D(2)(x)   # (14, 14, 64)
x = tf.keras.layers.Conv2DTranspose(32, 3, activation='relu', padding='same')(x)
x = tf.keras.layers.UpSampling2D(2)(x)   # (28, 28, 32)
decoded = tf.keras.layers.Conv2DTranspose(1, 3, activation='sigmoid', padding='same')(x)

conv_autoencoder = tf.keras.Model(encoder_in, decoded)
conv_autoencoder.compile(optimizer='adam', loss='binary_crossentropy')

Variational Autoencoder (VAE) — Generating New Data

A standard autoencoder encodes each input to a fixed point in latent space. A Variational Autoencoder encodes each input to a distribution (mean and variance). Sampling from this distribution during decoding generates new, realistic samples that were never in the training set.

# VAE adds a sampling layer and a KL divergence loss
class Sampling(tf.keras.layers.Layer):
    """Sample z = mean + std × epsilon."""
    def call(self, inputs):
        z_mean, z_log_var = inputs
        epsilon = tf.random.normal(shape=tf.shape(z_mean))
        return z_mean + tf.exp(0.5 * z_log_var) * epsilon

# Encoder outputs mean AND log-variance
encoder_in = tf.keras.Input(shape=(784,))
x = tf.keras.layers.Dense(256, activation='relu')(encoder_in)
z_mean    = tf.keras.layers.Dense(LATENT_DIM, name='z_mean')(x)
z_log_var = tf.keras.layers.Dense(LATENT_DIM, name='z_log_var')(x)
z = Sampling()([z_mean, z_log_var])

vae_encoder = tf.keras.Model(encoder_in, [z_mean, z_log_var, z])

# Generate a new digit by sampling from the latent space
random_latent = tf.random.normal([1, LATENT_DIM])
generated_digit = decoder.predict(random_latent)

Autoencoder Applications Summary

Application              How Autoencoder Helps
──────────────────────────────────────────────────────────────────────
Image denoising          Train on noisy→clean pairs; outputs clean images
Anomaly detection        High reconstruction error = anomaly
Dimensionality reduction Use encoder as feature extractor (like PCA but non-linear)
Data compression         Encode to small latent, decode on demand
Generative modeling      VAE samples new examples from latent distribution
Pre-training             Use encoder weights to initialize supervised model
Data imputation          Reconstruct missing values in partial inputs
──────────────────────────────────────────────────────────────────────

Autoencoders are one of the most versatile unsupervised learning tools in deep learning. They learn the essential structure of data without labels, compress it into a dense latent representation, and reconstruct it faithfully. The latent space they create is a powerful compressed summary of what the model considers important — a foundation that powers everything from image generation to fraud detection. The next topic covers Generative Adversarial Networks, which take generative modeling further by training two networks in competition to produce strikingly realistic outputs.

Leave a Comment

Your email address will not be published. Required fields are marked *