Autoencoders: Principles, Examples, and Python Implementation

Autoencodeurs : Guide Complet — Principes, Exemples et Implémentation Python

Autoencoders: Complete Guide — Principles, Examples, and Python Implementation

Summary

Autoencoders are artificial neural networks designed to learn compact representations of data in an unsupervised manner. Their primary goal is nonlinear dimensionality reduction: they compress input data into a lower-dimensional space (the “bottleneck”), then attempt to reconstruct the original data from this compressed representation. Unlike Principal Component Analysis (PCA), autoencoders capture complex nonlinear relationships, making them extremely powerful for structured data such as images, sound, or text.

Mathematical Principle

An autoencoder consists of two main sub-networks:

1. The Encoder

The encoder $E$ transforms the input $x$ into a latent representation $h$ of reduced dimension:

$$h = \sigma(W_e x + b_e)$$

where:
– $x \in \mathbb{R}^n$ is the input vector of dimension $n$,
– $W_e \in \mathbb{R}^{d \times n}$ is the encoder weight matrix (with $d < n$),
– $b_e \in \mathbb{R}^d$ is the bias vector,
– $\sigma$ is a nonlinear activation function (ReLU, sigmoid, etc.),
– $h \in \mathbb{R}^d$ is the latent representation (the bottleneck), of dimension $d$ strictly less than $n$.

2. The Bottleneck

The bottleneck is the layer with the smallest dimension of the network. This is where the entire essence of dimensionality reduction lies: the network is forced to select the most relevant information and encode it in a restricted space. The bottleneck dimension $d$ is a critical hyperparameter: too large, the network only learns to copy; too small, it loses essential information.

3. The Decoder

The decoder $D$ attempts to reconstruct the original input from the latent representation:

$$x’ = \sigma(W_d h + b_d)$$

where:
– $W_d \in \mathbb{R}^{n \times d}$ is the decoder weight matrix,
– $b_d \in \mathbb{R}^n$ is the decoder bias vector,
– $x’ \in \mathbb{R}^n$ is the reconstruction of the original input.

4. Loss function: reconstruction minimization

The autoencoder is trained by minimizing the reconstruction error between the original input and its reconstruction:

$$\mathcal{L}(x, x’) = ||x – x’||^2 = \sum_{i=1}^{n} (x_i – x’_i)^2$$

This cost function (mean squared error, MSE) penalizes each deviation between the reconstructed value and the original value. The network thus learns to preserve the most important information through the bottleneck. Optimization is performed via gradient descent (or variants such as Adam), backpropagating the loss gradient through both sub-networks.

Intuition: the smart zip file

Imagine you want to compress a zip file. A conventional compression algorithm reduces file size by encoding information more efficiently. An autoencoder works in a similar but intelligent way:

  • The encoder compresses: it analyzes the data and transforms it into a compact representation in the bottleneck, retaining only the essential information needed to reconstruct the original.
  • The decoder decompresses: it takes this compact representation and attempts to reconstitute the original data as faithfully as possible.

If the autoencoder works well, the bottleneck contains a faithful but compact representation of the data. It’s as if the network had discovered the “fundamental characteristics” of the data: for faces, this could be face shape, eye position, skin color; for texts, the general theme; for signals, the dominant frequencies.

The crucial difference from a classical zip is that the autoencoder learns the compression from the data itself, without predefined rules. It adapts to the specific structure of the dataset.

Python Implementation

Example 1: Simple autoencoder on MNIST with Keras/TensorFlow

Let’s start with a basic autoencoder trained on MNIST handwritten digits. The 28×28 (=784 pixels) images will be compressed into a 32-dimensional space.

import numpy as np
import matplotlib.pyplot as plt
from tensorflow.keras.layers import Input, Dense
from tensorflow.keras.models import Model
from tensorflow.keras.datasets import mnist

# Load data
(x_train, _), (x_test, _) = mnist.load_data()

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

# Bottleneck dimension
encoding_dim = 32

# Autoencoder architecture
input_img = Input(shape=(784,))

# Encoder: 784 -> 128 -> 32
encodeur = Dense(128, activation='relu')(input_img)
encodeur = Dense(encoding_dim, activation='relu', name='bottleneck')(encodeur)

# Decoder: 32 -> 128 -> 784
decodeur = Dense(128, activation='relu')(encodeur)
decodeur = Dense(784, activation='sigmoid', name='output')(decodeur)

# Complete model
autoencodeur = Model(inputs=input_img, outputs=decodeur)
autoencodeur.compile(optimizer='adam', loss='mse')

# Training
autoencodeur.fit(x_train, x_train,
                 epochs=50,
                 batch_size=256,
                 shuffle=True,
                 validation_split=0.1,
                 verbose=1)

# Visualize results
encodeur_modele = Model(inputs=input_img, outputs=encodeur)
reconstructions = autoencodeur.predict(x_test[:10])

fig, axes = plt.subplots(2, 10, figsize=(20, 4))
for i in range(10):
    axes[0, i].imshow(x_test[i].reshape(28, 28), cmap='gray')
    axes[0, i].axis('off')
    axes[1, i].imshow(reconstructions[i].reshape(28, 28), cmap='gray')
    axes[1, i].axis('off')

axes[0, 0].set_ylabel('Original', fontsize=14)
axes[1, 0].set_ylabel('Reconstruit', fontsize=14)
plt.tight_layout()
plt.show()

This simple autoencoder learns to reduce images from 784 dimensions to just 32, a compression factor of 24.5, while preserving enough information to recognize the digits.

Example 2: Comparison with PCA in 2D

Let’s compare autoencoder dimensionality reduction with PCA (Principal Component Analysis). PCA is a linear method, while the autoencoder is nonlinear.

from sklearn.decomposition import PCA
from tensorflow.keras.datasets import mnist
import matplotlib.pyplot as plt

# Load
(x_train, y_train), _ = mnist.load_data()
x_train = x_train.reshape(-1, 784).astype('float32') / 255.0

# --- PCA ---
pca = PCA(n_components=2)
x_pca = pca.fit_transform(x_train[:5000])

plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
scatter = plt.scatter(x_pca[:, 0], x_pca[:, 1], c=y_train[:5000],
                      cmap='tab10', alpha=0.5, s=5)
plt.colorbar(scatter, ticks=range(10))
plt.title('ACP : réduction linéaire en 2D')

# --- Autoencoder to 2D ---
from tensorflow.keras.layers import Input, Dense
from tensorflow.keras.models import Model

input_img = Input(shape=(784,))
encodeur = Dense(128, activation='relu')(input_img)
encodeur = Dense(64, activation='relu')(encodeur)
encodeur = Dense(2, activation='linear', name='latente_2d')(encodeur)

decodeur = Dense(64, activation='relu')(encodeur)
decodeur = Dense(128, activation='relu')(decodeur)
decodeur = Dense(784, activation='sigmoid')(decodeur)

ae = Model(inputs=input_img, outputs=decodeur)
ae.compile(optimizer='adam', loss='mse')
ae.fit(x_train, x_train, epochs=30, batch_size=256, verbose=1)

encodeur_2d = Model(inputs=input_img, outputs=encodeur)
x_ae = encodeur_2d.predict(x_train[:5000])

plt.subplot(1, 2, 2)
scatter = plt.scatter(x_ae[:, 0], x_ae[:, 1], c=y_train[:5000],
                      cmap='tab10', alpha=0.5, s=5)
plt.colorbar(scatter, ticks=range(10))
plt.title('Autoencodeur : réduction non linéaire en 2D')
plt.tight_layout()
plt.show()

We typically observe that the autoencoder separates digit classes better than PCA, as it captures nonlinear boundaries between clusters. Handwritten digits group into distinct clusters in the latent space, while PCA overlaps them more.

Example 3: Denoising Autoencoder

A denoising autoencoder receives noise-corrupted data as input and must reconstruct the clean data. This forces the network to learn robust, generalizable representations.

import numpy as np
from tensorflow.keras.layers import Input, Dense
from tensorflow.keras.models import Model
from tensorflow.keras.datasets import mnist
import matplotlib.pyplot as plt

# Load and normalize
(x_train, _), (x_test, _) = mnist.load_data()
x_train = x_train.astype('float32') / 255.0
x_test = x_test.astype('float32') / 255.0
x_train = x_train.reshape(-1, 784)
x_test = x_test.reshape(-1, 784)

# Add Gaussian noise
bruit = 0.5
x_train_bruite = x_train + bruit * np.random.normal(size=x_train.shape)
x_test_bruite = x_test + bruit * np.random.normal(size=x_test.shape)

# Clamp between 0 and 1
x_train_bruite = np.clip(x_train_bruite, 0., 1.)
x_test_bruite = np.clip(x_test_bruite, 0., 1.)

# Architecture
input_img = Input(shape=(784,))
x = Dense(256, activation='relu')(input_img)
x = Dense(128, activation='relu')(x)
encoded = Dense(64, activation='relu', name='latent')(x)
x = Dense(128, activation='relu')(encoded)
x = Dense(256, activation='relu')(x)
decoded = Dense(784, activation='sigmoid')(x)

denoiser = Model(inputs=input_img, outputs=decoded)
denoiser.compile(optimizer='adam', loss='mse')

# Training: noisy input -> clean output
denoiser.fit(x_train_bruite, x_train,
             epochs=50,
             batch_size=256,
             shuffle=True,
             validation_split=0.1)

# Results
n = 8
idx = np.random.choice(len(x_test), n, replace=False)
bruitees = x_test_bruite[idx]
nettoyees = denoiser.predict(bruitees)

fig, axes = plt.subplots(3, n, figsize=(20, 6))
for i in range(n):
    axes[0, i].imshow(x_test[idx[i]].reshape(28, 28), cmap='gray')
    axes[0, i].set_title('Original')
    axes[1, i].imshow(bruitees[i].reshape(28, 28), cmap='gray')
    axes[1, i].set_title('Bruité')
    axes[2, i].imshow(nettoyees[i].reshape(28, 28), cmap='gray')
    axes[2, i].set_title('Nettoyé')
    for j in range(3):
        axes[j, i].axis('off')
plt.tight_layout()
plt.show()

The denoising autoencoder is particularly useful in image processing, where it removes noise while preserving important structural details.

Key Hyperparameters

Choosing hyperparameters is crucial for obtaining an effective autoencoder:

Hyperparameter Role Recommendation
Bottleneck size Latent representation dimension 2-64 for visualization, 32-256 for general applications
Number of layers Depth of encoder and decoder 2-4 layers for tabular data, more for images
Activation function Introduced nonlinearity ReLU for hidden layers, sigmoid for output with normalized images
Regularization Prevent overfitting Dropout (0.2-0.5), L1/L2 on weights, or sparse activation
Learning rate Learning speed 0.001 with Adam is a good starting point
Batch size Number of samples per update 128-256 for MNIST, 32-64 for large images
Epochs Number of complete passes over data 20-100 depending on complexity; use early stopping

An important empirical rule: the bottleneck size determines the trade-off between reconstruction fidelity and compression. If the bottleneck is too large, the autoencoder may learn a simple identity (blindly copying input to output). To avoid this, one can add sparse regularization (penalizing hidden neuron activations) or use a denoising autoencoder.

Advantages and Limitations

Advantages

  • Nonlinear reduction: captures complex relationships that PCA cannot model
  • Unsupervised learning: no labels required; raw data is sufficient
  • Architectural flexibility: you can stack as many layers as needed, use convolutions for images, or recurrent networks for sequences
  • Versatility: beyond dimensionality reduction, autoencoders serve for denoising, generation (VAE), and anomaly detection
  • Rich representations: the latent space often encodes semantic features useful for downstream tasks

Limitations

  • Computational cost: training requires more resources than classical methods like PCA
  • Difficult interpretation: unlike principal components, latent axes are rarely directly interpretable
  • Random reproduction: initial weights are random, making results slightly variable from one run to another
  • Overfitting risk: a powerful autoencoder may learn the identity function instead of compressing
  • No structural guarantee: the latent space is not always continuous or well-structured (unlike VAEs)

4 Concrete Use Cases

Case 1 — Industrial Anomaly Detection

In a factory, an autoencoder is trained on sensor measurements during normal operation. In production, a high reconstruction error signals unusual behavior: a worn bearing, abnormal temperature, suspicious vibration. The advantage is that this approach requires no examples of failures for training.

Case 2 — Image Compression and Preprocessing

Convolutional autoencoders can reduce high-resolution images to a compact latent representation used as input for smaller networks (classification, segmentation). This significantly reduces training time and memory requirements while preserving semantic information.

Case 3 — Biomedical Signal Denoising

In electrocardiography (ECG) or electroencephalography (EEG), signals are often contaminated by electrical noise, motion artifacts, or 50 Hz interference. A denoising autoencoder trained on pairs (noisy signal, clean signal) restores signal quality much better than a classical linear filter.

Case 4 — Creative Content Generation

Variational Autoencoders (VAEs) extend the concept by imposing a probabilistic distribution on the latent space (typically Gaussian). This structure allows generating new data by sampling from the latent space and passing through the Decoder. Applications: face generation, music creation, pharmaceutical molecule design, or creative interpolation between two concepts.

Conclusion

Autoencoders represent a powerful and flexible method for nonlinear dimensionality reduction. Where PCA is limited to linear combinations, autoencoders capture complex relationships through their nonlinear layers and flexible architecture. They are highly adaptable: the same encoder-bottleneck-decoder idea extends to denoising autoencoders, sparse autoencoders, variational autoencoders, and many other variants.

To get started, begin with a simple autoencoder on MNIST with Keras, as shown in the examples above. Experiment with different bottleneck sizes and architectures to understand the compression/fidelity trade-off. Then explore the variants (denoising, convolutional, variational) based on your application.

See also

About Salah YAHIAOUI

Salah YAHIAOUI publie sur The Pingouin des tutoriels Python et des décryptages sur l’intelligence artificielle. Les cas pratiques récents précisent leurs données, leurs tests et leurs limites. À propos, méthode éditoriale et contact.

View all posts by Salah YAHIAOUI →