Consistency Models — Complete Guide to One-Step Generation

Consistency Models : Guide Complet — Génération en un Seul Pas

Consistency Models: Complete Guide — One-Step Generation

Summary — Consistency Models, introduced by Song, Dhariwal, and Chen (OpenAI, 2023), are an ultra-fast generation approach that learns a function capable of mapping any point on a diffusion ODE trajectory directly to the final point (the generated sample) in a single pass. Unlike diffusion models that require 50 to 1000 integration steps, Consistency Models can generate in 1 step (instantaneously) or in a few steps for superior quality. It is a form of distillation: the model is trained to be consistent with itself along diffusion trajectories.


Mathematical Principle

1. The Consistency Function

A Consistency Model learns a function fθ(x_t, t) that, for any point x_t on a diffusion ODE trajectory (regardless of the step t), always returns the same final result x_0. This is the consistency constraint:

fθ(x_t, t) = fθ(x_{t'}, t') for all t, t' on the same trajectory

In particular, fθ(x_T, T) = x_0 where T is the final time (pure noise) and x_0 is the generated sample.

2. Consistency Training

The key to training is enforcing consistency between two adjacent points on the same ODE trajectory. The consistency loss is:

L(θ) = E[||fθ(x_{t_{n+1}}, t_{n+1}) - fθ^{µ}(x_{t_n}, t_n)||²]

Where fθ^{µ} is an exponential moving average (EMA) of parameters that serves as a stable target. The ODE trajectory is discretized into N times t_1 < t_2 < … < t_N and the model is trained to be consistent between adjacent times.

3. Parameterization

To satisfy the boundary condition (f(x_0, 0) = x_0), we parameterize:

f_θ(x_t, t) = c_{skip}(t) · x_t + c_{out}(t) · F_θ(x_t, t)

Where c_{skip}(0) = 1 and c_{out}(0) = 0 ensure that f_θ(x_0, 0) = x_0 automatically.

4. Generation

Generation is trivial:
1 step: fθ(x_T, T) where x_T ~ N(0,I) — instantaneous
Few steps: iteration is possible to improve quality

5. Diffusion Model Distillation

An alternative approach is the direct distillation of a pre-trained diffusion model: the diffusion model is used to generate trajectories and the consistency model is trained to reproduce the final result from any intermediate point. This approach yields better results because it benefits from the quality of the source diffusion model.


Intuition

Gradient descent in a diffusion model is like going down a mountain with 1,000 steps, one by one. It’s slow but it works. A Consistency Model is like having a cable car: no matter where you are on the mountain (at the top in the fog or halfway up), the cable car always takes you to the same place at the bottom — in a single trip.

The network learns a function that says “no matter what denoising step you’re at, here’s what the final image looks like.” By trading a tiny amount of quality for an enormous speed gain (1000 steps → 1 step), you get near-instantaneous generation.

It’s like the difference between a GPS that tells you “turn right in 200m, then left in 500m” (diffusion, step by step) and an autopilot that says “the destination is there, let’s go directly” (consistency model, 1 step).


Python Implementation

import torch
import torch.nn as nn
import torch.nn.functional as F
import math


class ConsistencyModel(nn.Module):
    def __init__(self, data_dim=2, hidden_dim=256):
        super().__init__()
        self.data_dim = data_dim
        self.register_buffer('ema_params', None)

        # Feature extraction
        self.t_embed = nn.Sequential(
            nn.Linear(1, hidden_dim), nn.SiLU(),
            nn.Linear(hidden_dim, hidden_dim), nn.SiLU()
        )
        self.x_embed = nn.Linear(data_dim, hidden_dim)

        # Backbone
        self.net = nn.Sequential(
            nn.Linear(hidden_dim * 2, hidden_dim), nn.SiLU(),
            nn.Linear(hidden_dim, hidden_dim), nn.SiLU(),
            nn.Linear(hidden_dim, hidden_dim), nn.SiLU(),
            nn.Linear(hidden_dim, data_dim)
        )

    def c_skip(self, t):
        return torch.ones_like(t)

    def c_skip_at_zero(self, t):
        """Returns c_skip(t) with c_skip(0) = 1"""
        sigma = t
        return 0.25 / (sigma + 0.25)

    def c_out(self, t):
        """Returns c_out(t) with c_out(0) = 0"""
        sigma = t
        return 0.25 * sigma / (sigma + 0.25)

    def forward(self, x, t):
        t = t.view(-1, 1)
        c_skip = self.c_skip_at_zero(t)
        c_out = self.c_out(t)
        F_theta = self.net(
            torch.cat([self.x_embed(x), self.t_embed(t)], dim=1)
        )
        return c_skip * x + c_out * F_theta


class ConsistencyTrainer:
    def __init__(self, model, lr=1e-3, timesteps=40):
        self.model = model
        self.ema_model = None
        self.lr = lr
        self.timesteps = timesteps
        self.optimizer = torch.optim.Adam(model.parameters(), lr=lr)
        self.ts = torch.linspace(0.002, 1.0, timesteps)

    def consistency_loss(self, x_0):
        """Consistency loss between adjacent time steps"""
        batch = x_0.size(0)
        # Pick a random time step
        n = torch.randint(0, self.timesteps - 1, (batch,))
        t_n = self.ts[n]
        t_np1 = self.ts[n + 1]

        # Add noise to simulate the forward process
        sigma_n = t_n.view(-1, 1)
        sigma_np1 = t_np1.view(-1, 1)

        z = torch.randn_like(x_0)
        x_tn = x_0 + sigma_n * z
        x_tnp1 = x_0 + sigma_np1 * z

        # Predictions
        f_n = self.model(x_tn, t_n)
        f_np1 = self.model(x_tnp1, t_np1)

        # Consistency loss: the two should be equal
        loss = F.mse_loss(f_n, f_np1)
        return loss

    def train_step(self, x_0):
        self.optimizer.zero_grad()
        loss = self.consistency_loss(x_0)
        loss.backward()
        torch.nn.utils.clip_grad_norm_(self.model.parameters(), 1.0)
        self.optimizer.step()

        # EMA update
        if self.ema_model is None:
            self.ema_model = self.deepcopy_model(self.model)
        else:
            self.update_ema(0.995)

        return loss.item()

    @torch.no_grad()
    def sample(self, n, steps=1):
        """1-step or few-step generation"""
        x = torch.randn(n, self.model.data_dim)
        t = torch.ones(n)
        x_gen = self.model(x, t)
        return x_gen

    def deepcopy_model(self, src):
        dst = type(src)()
        dst.load_state_dict(src.state_dict())
        return dst

    def update_ema(self, decay):
        for p, ema_p in zip(self.model.parameters(),
                            self.ema_model.parameters()):
            ema_p.data.mul_(decay).add_(p.data, alpha=1 - decay)


# Training on 2D data (spirals)
def make_spiral(n=5000):
    r = torch.rand(n) * 2
    t = torch.rand(n) * 2 * math.pi
    x = r * torch.cos(t + r)
    y = r * torch.sin(t + r)
    return torch.stack([x, y], dim=1)

data = make_spiral()
model = ConsistencyModel(data_dim=2, hidden_dim=256)
trainer = ConsistencyTrainer(model, lr=1e-3, timesteps=40)

for epoch in range(500):
    idx = torch.randint(0, data.size(0), (512,))
    batch = data[idx]
    loss = trainer.train_step(batch)
    if epoch % 50 == 0:
        samples = trainer.sample(100)
        print(f'Epoch {epoch} | Loss: {loss:.4f}')

# Instantaneous generation
samples_1step = trainer.sample(100, steps=1)
print(f'Generated {samples_1step.size(0)} samples in 1 step')

Hyperparameters

Hyperparameter Typical value Description
timesteps 18-404 Number of discretized time steps (more = quality but slower)
ema_decay 0.995 EMA decay rate for the stable target
lr 1e-3 Adam learning rate
sigma_min 0.002 Minimum noise (boundary condition at x_0)

Advantages

  1. Ultra-fast generation: 1 step vs. 50-1000 for conventional diffusion, yielding a 50x to 1000x speedup.
  2. Preserved quality: With advanced Consistency Models (CD), quality reaches 95% of diffusion quality at a fraction of the cost.
  3. Flexible tradeoff: You can choose between 1 step (fast) and a few steps (balanced) depending on the need.
  4. Compatibility: Can be trained by distillation from any existing diffusion model.
  5. No noise scheduling needed: No need for a sophisticated noise schedule unlike diffusion.

Limitations

  1. Unstable training: The consistency loss between adjacent points can be difficult to optimize.
  2. Lower quality at 1 step: Single-step generation loses fine details compared to multi-step diffusion.
  3. Requires a source model: The best approach requires a pre-trained diffusion model that is expensive to produce.
  4. Young field of research: Less mature and tested than diffusion models or Flow Matching.

4 Concrete Use Cases

1. Real-Time Image Generation

For interactive applications such as AI image editors, generation must be instantaneous. Consistency Models enable generating images in 1-4 steps instead of the usual 50-100 steps, making real-time editing possible.

2. Low-Latency Speech Synthesis

In voice assistants, latency is critical. CMs reduce audio generation time from several seconds to a few milliseconds, making conversation more natural.

3. High-Throughput Molecule Design

In computational chemistry, millions of candidate molecules need to be generated and evaluated. CMs enable ultra-fast generation of valid 3D molecular structures.

4. Online Data Augmentation

Consistency Models can generate synthetic data on the fly during the training of other models, without the prohibitive cost of conventional diffusion.


See Also