Library / SDK
lucidrains/denoising-diffusion-pytorch avatar
lucidrains/denoising-diffusion-pytorch

denoising-diffusion-pytorch: A Readable DDPM Baseline You Can Actually Modify

Implementation of Denoising Diffusion Probabilistic Model in Pytorch

10,686 stars1,280 forksPythonMIT

At a glance

What is it?
This is a single-author PyTorch implementation of Denoising Diffusion Probabilistic Models, packaged with a Unet, a GaussianDiffusion wrapper and a Trainer. It is a good fit for people who want to read and edit the diffusion loop, and a poor fit for anyone who wants a production sampling service.
Who is it for?
Adopt it if you need to read, fork or modify the DDPM training and sampling loop itself, or if you are training a small image or 1D sequence model on your own data and want a Trainer that takes a folder path. Do not adopt it if you need a maintained serving stack, a scheduler catalogue, or any evaluation of 1D outputs, because Trainer1D performs none.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 14 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The problem it solves is reading the diffusion loop, not deploying it

Denoising diffusion is easy to describe and fiddly to implement. The forward process adds noise on a schedule, a network predicts something about that noise, and sampling runs the reverse chain step by step. Getting the schedule, the loss parameterisation and the sampling loop consistent is where most reimplementations go wrong. This repository exists to give one compact, readable version of that loop in PyTorch, credited in the README as inspired by the official TensorFlow implementation. The audience is narrow and specific: researchers, students and engineers who want to see the whole thing in one place and change it. The README even links to an annotated version by Hugging Face engineers, which tells you the author expects people to read the code rather than treat it as a black box. If you want a library that hides the loop, this is the wrong starting point by design.

Unet plus GaussianDiffusion: where the noise schedule lives

The architecture splits into two objects. Unet is the denoising network, configured with dim, dim_mults and an optional flash_attn flag. GaussianDiffusion wraps that network and owns the diffusion process itself, taking image_size and timesteps. The README example uses timesteps=1000. Training is a single call: passing a batch of images normalised from 0 to 1 into diffusion returns a loss, and loss.backward() is left to the caller. Sampling is diffusion.sample(batch_size=4), which returns a tensor shaped (4, 3, 128, 128) for the 128 pixel configuration shown. The sampling_timesteps argument is the interesting one. Setting it to 250 while timesteps stays at 1000 switches inference to DDIM for faster sampling, per the README's own comment. That separation matters: the training schedule and the inference schedule are decoupled, so you can train at 1000 steps and sample at 250 without retraining. The README does not document what happens if you set sampling_timesteps above timesteps, so treat that as unverified.

Trainer takes a folder path and writes to ./results

The Trainer class removes the data loading work. You pass it the diffusion object and a string path such as 'path/to/your/images', then set train_batch_size, train_lr, train_num_steps, gradient_accumulate_every, ema_decay, amp and calculate_fid. The README's example uses train_batch_size=32, train_lr=8e-5, train_num_steps=700000, gradient_accumulate_every=2, ema_decay=0.995 and amp=True. Samples and checkpoints are logged to ./results periodically, according to the README. Multi-GPU training goes through Hugging Face Accelerate: run accelerate config at the project root, then accelerate launch train.py in the same directory. The calculate_fid flag is worth pausing on. Enabling it means the training loop computes FID during training, which pulls in additional dependencies and adds per-checkpoint cost. The README states the flag exists but does not list those dependencies, so check them before turning it on for a long run.

Two things the README does not settle: XMWrapper cost and 1D evaluation

The XMWrapper, added for Explorative Modeling, wraps any diffusion model and generates multiple candidates per sample, picking the minimum loss. The README example sets candidates=4. What it does not state is the memory and compute multiplier, though generating four candidates per sample and selecting one implies roughly four forward passes per training step. That is a real budget decision the documentation leaves to you. The 1D path is more explicit about its gap. Unet1D, GaussianDiffusion1D, Trainer1D and Dataset1D mirror the 2D classes, with seq_length=128, channels=32 and objective='pred_v' in the example. The README says plainly that Trainer1D does not evaluate the generated samples in any way, because the type of data is not known. It suggests adding a metric yourself after an editable install via pip install -e . That is an honest limitation rather than a hidden one, and it means any 1D project needs its own evaluation code before results mean anything.

Where this is the wrong tool

The README's own update line is the sharpest caveat in the repository: it points to the Cold Diffusion paper with the remark that none of the technicalities really matters at all. That is the author telling you the specific DDPM formulation is one choice among several, not a requirement. The practical consequence is that this codebase is a baseline, not a platform. There is no model hub, no scheduler registry, no pipeline abstraction for swapping samplers, and no inference server. The Unet is defined here rather than imported from a shared library, so you cannot drop in a pretrained checkpoint from elsewhere without matching its configuration. If your requirement is to run an existing diffusion checkpoint, or to serve sampling behind an API, this repository gives you none of that scaffolding and you would be rebuilding it. The 1D case compounds this: with no built-in evaluation, you cannot tell whether a training run improved without writing the metric yourself.

diffusers is the alternative, and the difference is architectural

Hugging Face diffusers is the obvious comparison, and the same organisation's annotated walkthrough of this code is linked in this README, which is a fair signal about the relationship between the two. The difference is where the abstraction sits. diffusers separates the scheduler, the model and the pipeline into independently swappable components, so you can change the noise schedule without touching the network, and load published weights through a hub. This repository fuses the schedule and the sampling loop into GaussianDiffusion, which is exactly why it is readable and exactly why it is inflexible. Choosing between them is not a quality judgement. If you want to modify the loss or the reverse process, the fused version is easier to edit because there is one file to follow. If you want to compose pretrained parts, the separated version is the only one of the two that supports it. Note also that the version numbers here run 2.2.4 through 2.2.6 across 2025 and 2026, so the package is still receiving releases.

Licence, upgrade surface and what a fork costs you

The licence is MIT, which permits commercial use and modification provided the copyright notice and permission notice are retained. That is the standard permissive arrangement, and it is the reason a fork is a realistic option here: you can vendor the code into your own repository and edit the diffusion loop directly. The upgrade cost is the flip side. The public surface is broad and flat, with Unet, GaussianDiffusion, Trainer, Unet1D, GaussianDiffusion1D, Trainer1D, Dataset1D and XMWrapper all exported from the same package, and releases arriving as patch versions (2.2.4, 2.2.5, 2.2.6) rather than major ones. Nothing in the supplied material describes a deprecation policy or a stability guarantee for these names, so a fork that diverges from upstream will have to reconcile changes by hand. Pinning to a specific version is the low-effort option, and it is only viable if you do not need later fixes. This is a description of the licence and the release pattern, not legal advice; check the MIT terms against your own distribution model.

Editorial conclusion

Adopt it if you need to read, fork or modify the DDPM training and sampling loop itself, or if you are training a small image or 1D sequence model on your own data and want a Trainer that takes a folder path. Do not adopt it if you need a maintained serving stack, a scheduler catalogue, or any evaluation of 1D outputs, because Trainer1D performs none. Before committing, verify three things against the current main branch: that the 1D path still uses objective='pred_v' by default, that calculate_fid=True actually resolves its FID dependencies on your machine, and that the XMWrapper candidate loop fits your memory budget, since the README example sets candidates=4 without stating the cost multiplier.

Official sources

  1. Issues
  2. License: MIT
  3. lucidrains/denoising-diffusion-pytorch on GitHub
  4. README
  5. Releases
Community notes

Community notes