| --- |
| library_name: pytorch |
| pipeline_tag: text-to-image |
| license: mit |
| datasets: |
| - ylecun/mnist |
| tags: |
| - diffusion |
| - ddim |
| - mnist |
| - handwritten-digits |
| - tiny-model |
| --- |
| |
| # Tiny Digit Diffusion 3M |
|
|
| Tiny Digit Diffusion 3M is a **2,767,529-parameter** conditional diffusion |
| model that renders a prompt containing one to eight ASCII digits as a |
| handwritten grayscale image. It was trained from scratch on dynamically |
| composed MNIST digit strings and contains no weights from another generative |
| model. |
|
|
| This is a deliberately narrow tiny-model experiment, not a general-purpose |
| text-to-image model. Its complete prompt language is a numeric string such as |
| `7`, `2026`, or `31415926`. |
|
|
|  |
|
|
| ## Repository contents |
|
|
| - `model/model.safetensors`: FP32 EMA inference weights |
| - `model/config.json`: architecture and diffusion configuration |
| - `tiny_digit_diffusion.py`: model, schedule, DDIM sampler, and weight loader |
| - `generate_tiny_digits.py`: command-line generation example |
| - `train_tiny_digit_diffusion.py`: training script |
| - `artifact_metadata.json`: training settings and evaluation summary |
| - `sample_grid.png`: generations from the final checkpoint |
|
|
| Optimizer checkpoints, MNIST files, per-epoch samples, and the training log are |
| not included in the distribution package. |
|
|
| ## Quick start |
|
|
| Create an environment and install the inference dependencies: |
|
|
| ```bash |
| python -m venv .venv |
| source .venv/bin/activate |
| pip install -r requirements.txt |
| ``` |
|
|
| Generate an eight-digit image: |
|
|
| ```bash |
| python generate_tiny_digits.py 31415926 --output 31415926.png |
| ``` |
|
|
| On the first run, the script downloads `model/config.json` and |
| `model/model.safetensors` from `shibatch/tinydigitdiffusion3m` on Hugging |
| Face. Later runs reuse the local Hugging Face cache. |
|
|
| The script accepts the following useful options: |
|
|
| ```bash |
| python generate_tiny_digits.py 2026 \ |
| --steps 50 \ |
| --guidance-scale 1.0 \ |
| --seed 0 \ |
| --device auto \ |
| --output 2026.png |
| ``` |
|
|
| Use a different Hub revision with `--revision`, or override the repository |
| with `--repo-id`. To use model files already stored locally (including the |
| `model/` directory included in this distribution package), pass |
| `--model-dir`: |
|
|
| ```bash |
| python generate_tiny_digits.py 2026 --model-dir ./model --output 2026.png |
| ``` |
|
|
| The output is a fixed `32 x 256` grayscale PNG. Short prompts are centered on |
| the eight available 32-pixel slots. |
|
|
| ## Python example |
|
|
| ```python |
| import sys |
| from pathlib import Path |
| |
| import torch |
| from huggingface_hub import snapshot_download |
| from PIL import Image |
| |
| repo_dir = Path(snapshot_download( |
| repo_id="shibatch/tinydigitdiffusion3m", |
| allow_patterns=[ |
| "model/config.json", |
| "model/model.safetensors", |
| "tiny_digit_diffusion.py", |
| ], |
| )) |
| sys.path.insert(0, str(repo_dir)) |
| |
| from tiny_digit_diffusion import ddim_sample, load_model |
| |
| device = torch.device("cuda" if torch.cuda.is_available() else "cpu") |
| model = load_model(repo_dir / "model", device) |
| |
| image = ddim_sample( |
| model, |
| prompts=["31415926"], |
| device=device, |
| sampling_steps=50, |
| guidance_scale=1.0, |
| seed=0, |
| )[0, 0] |
| |
| pixels = ((image.cpu() + 1) * 127.5).round().clamp(0, 255).byte().numpy() |
| Image.fromarray(pixels, mode="L").save("31415926.png") |
| ``` |
|
|
| Prompts must contain only one to eight ASCII digits. Spaces, signs, decimal |
| points, letters, and strings longer than eight digits are rejected. |
|
|
| ## Recommended inference settings |
|
|
| ```yaml |
| sampling_method: deterministic DDIM |
| sampling_steps: 50 |
| guidance_scale: 1.0 |
| output_size: 32 x 256 |
| ``` |
|
|
| Use `guidance_scale=1.0`. Larger classifier-free-guidance values are not |
| helpful for this checkpoint. In particular, the original diagnostic setting |
| of `3.0` over-amplifies the conditioning difference and creates digit-like |
| artifacts in padding slots. At `1.0`, the sampler uses the conditional model |
| directly and skips the unnecessary unconditional pass. |
|
|
| ## Architecture |
|
|
| ```yaml |
| model: custom conditional diffusion U-Net |
| parameter_count: 2,767,529 |
| weight_dtype: float32 |
| weight_file_size: approximately 11 MB |
| |
| image_channels: 1 |
| image_height: 32 |
| image_width: 256 |
| maximum_digits: 8 |
| slot_width: 32 |
| |
| base_channels: 48 |
| channel_multipliers: [1, 1.5, 2, 2.6667] |
| embedding_dim: 192 |
| token_embedding_dim: 32 |
| spatial_condition_channels: 8 |
| attention_heads: 4 |
| diffusion_steps: 400 |
| ``` |
|
|
| The denoiser has four resolution levels with residual blocks, skip |
| connections, a bottleneck self-attention layer, sinusoidal timestep |
| conditioning, global prompt conditioning, and slot-aligned spatial digit |
| conditioning. Prompt tokens consist of digits `0` through `9`, a padding token, |
| and a classifier-free null token. |
|
|
| ## Training |
|
|
| Each training example was assembled dynamically from MNIST: |
|
|
| 1. Sample a prompt length uniformly from one to eight digits. |
| 2. Sample an MNIST image independently for every digit. |
| 3. Center the string on a `32 x 256` canvas. |
| 4. Apply small horizontal and vertical offsets and an intensity variation. |
| 5. Train the model to predict noise at a random cosine-schedule timestep. |
|
|
| Training configuration: |
|
|
| ```yaml |
| dtype: float32 |
| epochs: 30 |
| steps: 28,110 |
| batch_size: 64 |
| samples_processed: 1,799,040 |
| optimizer: AdamW |
| learning_rate: 2.0e-4 |
| warmup_steps: 500 |
| minimum_learning_rate: 2.0e-5 |
| weight_decay: 0.0 |
| gradient_clip: 1.0 |
| condition_dropout: 0.1 |
| ema_decay: 0.999 |
| final_recent_noise_mse: 0.015345 |
| training_time: approximately 70 minutes |
| ``` |
|
|
| The distributed checkpoint is the EMA model, not the raw final optimizer |
| weights. |
|
|
| To train from scratch, install the additional dependencies and choose an output |
| directory: |
|
|
| ```bash |
| pip install -r requirements-train.txt |
| python train_tiny_digit_diffusion.py \ |
| --output-dir runs/tiny_digit_diffusion_3m \ |
| --data-dir data/mnist \ |
| --epochs 30 \ |
| --batch-size 64 \ |
| --device cuda |
| ``` |
|
|
| ## Evaluation |
|
|
| A separate small MNIST classifier was trained only for automated readability |
| measurement. It reached 98.68% accuracy on the MNIST test split. The final |
| generator was then evaluated on 240 random prompts: 30 prompts for every length |
| from one through eight digits, using 50 DDIM steps and guidance 1.0. |
|
|
| | Prompt length | Exact string | Per-digit accuracy | |
| |---:|---:|---:| |
| | 1 | 29/30 (96.7%) | 96.7% | |
| | 2 | 26/30 (86.7%) | 91.7% | |
| | 3 | 26/30 (86.7%) | 95.6% | |
| | 4 | 22/30 (73.3%) | 93.3% | |
| | 5 | 22/30 (73.3%) | 93.3% | |
| | 6 | 14/30 (46.7%) | 88.9% | |
| | 7 | 22/30 (73.3%) | 95.7% | |
| | 8 | 13/30 (43.3%) | 90.4% | |
| | **Overall** | **174/240 (72.5%)** | **92.6%** | |
|
|
| These are OCR-proxy measurements, not human ratings. A generated digit can be |
| legible to a person while being classified differently, and the classifier can |
| also be confidently wrong. The samples should therefore be considered |
| alongside the numeric results. |
|
|
| ## Limitations |
|
|
| - Longer strings compound individual digit errors; eight-digit exact match is |
| substantially lower than single-digit accuracy. |
| - Some handwritten `3`, `5`, `7`, `8`, and `9` shapes can be ambiguous. |
| - The output canvas and maximum string length are fixed by the architecture. |
| - The model supports only ASCII digits and does not render signs, punctuation, |
| decimal values, mathematical expressions, or arbitrary text. |
| - This checkpoint reproduces MNIST-like handwriting only. It is not suitable |
| for OCR security testing, document generation, or realistic typography. |
| - Results vary with the random seed even though each DDIM trajectory is |
| deterministic for a fixed seed. |
|
|
| ## Scope |
|
|
| This checkpoint is intended for education, architecture experiments, tests, |
| and demonstrations of a complete conditional image generator at a very small |
| parameter count. It should not be interpreted as an official MNIST model or as |
| an image-generation counterpart of any production diffusion system. |
|
|
| ## License |
|
|
| The source code and model checkpoint in this package are released under the |
| MIT License. See `LICENSE` for the complete terms. |
|
|
| The MNIST images are not redistributed in this package. Training used MNIST |
| through `torchvision`; the [MNIST dataset card](https://huggingface.co/datasets/ylecun/mnist) |
| identifies its license as MIT and credits Yann LeCun, Corinna Cortes, and |
| Christopher J. C. Burges as the dataset curators. |
|
|