CoolFace
Modelpublic

Sebasdi/nanodiff-350m-base

sourceHugging Facemitupdated 4mo agoView on Hugging Face
0likes
Model Card

nanoDiff 350M — base

A masked diffusion language model trained with the LLaDA recipe, from the nanoDiff project — a minimal, hackable "nanoGPT for diffusion LLMs".

The next rung in the scaling ladder after nanodiff-50m-base and nanodiff-150m-base. This is the base model — pretrained on raw web text only, no instruction tuning. It continues documents; it does not answer questions.

TL;DR

ArchitectureMasked diffusion LM (LLaDA recipe), bidirectional Transformer
Parameters~382M total · ~317M non-embedding (tied input/output embeddings)
Training dataFineWeb-Edu, ~10B tokens
ValidationNLL-bound 3.38 nats/token · perplexity 29.3
LAMBADA accuracy36.95%
HardwareNVIDIA DGX-Spark (GB10), ~6 days

Scaling family

This run completes a 4-point capacity scaling sweep on the same FineWeb-Edu shard, same masked-diffusion recipe, same WSD schedule structure:

ModelTokensVal NLLPerplexityLAMBADA acc
50M2B3.9250.619.83%
50M (matched-token control)3B3.9150.1—
150M3B3.7843.821.89%
350M10B3.3829.336.95%

The 150M→350M step is the largest gain in the family — -0.38 nats / ~31% PPL reduction — consistent with continuing to scale capacity and training tokens together (10B tokens is Chinchilla-optimal for 350M at ~20 tokens/param, overshooting slightly).

Provenance

bash
  python scripts/prepare_data.py --out-dir data/fineweb_edu_10b \
      --num-tokens 10_000_000_000
  python pretrain/train.py --config pretrain/configs/350m.py

Architecture

A decoder-style Transformer with bidirectional self-attention — the one architectural change from an autoregressive GPT, since a diffusion LM denoises all positions at once.

HyperparameterValue
Layers16
Attention heads20
Embedding dim (n_embd)1280
Head dim64
Context length (block_size)512
Positional encodingRoPE
Vocabulary50304 (GPT-2 BPE 50257 + [MASK]@50257 + padding)
Embeddingstied (input = output)
Total parameters~382M (~317M non-embedding)

Training objective

Masked (absorbing-state) diffusion, following LLaDA: each token is independently replaced by [MASK] with probability t ~ U(0, 1); the model predicts the clean tokens at all masked positions in one pass; the loss is a 1/t-weighted cross-entropy over masked positions (an upper bound on the negative log-likelihood). Time-free parameterization — t is not fed to the model.

Training setup

DatasetFineWeb-Edu, GPT-2 BPE, uint16 memmap
Tokens seen~10.0B (76,300 iters × batch 64 × 4 grad-accum × 512 ctx)
OptimizerAdamW (β1 0.9, β2 0.95, weight decay 0.1, grad clip 1.0)
LR scheduleWSD — warmup 1000, stable, linear decay over the final 25000 iters
Peak / min LR6e-4 → 1e-5
Batch size64 sequences × 4 grad-accum = effective 256 sequences/step
Precisionbf16 autocast
Compilationtorch.compile(mode="default")
HardwareNVIDIA DGX-Spark (GB10), single device
Wall-clock~6 days (~6.85 sec/iter steady-state)

Results

Offline evaluation (eval.py, 500 batches) on the FineWeb-Edu validation split:

  • —NLL-bound: 3.38 nats/token
  • —Perplexity: ~29.3

LAMBADA last-word prediction (single-pass diffusion scoring, full 5153-example test split):

  • —Accuracy: 36.95% (1904/5153)
  • —Perplexity (last-word): 55.3

Notable asymmetry vs the 150M: val PPL improved 33% (43.8 → 29.3), but LAMBADA PPL improved 84% (358 → 55.3) and LAMBADA accuracy jumped +15.06 pp (21.89 → 36.95). The larger jump on the discourse-dependent benchmark suggests the added capacity is being spent on long-range representations rather than sharper local statistics — a capability signal, not just a fitting one.

Intended use

A research / learning artifact, not a product.

  • —✅ A base model to supervised-fine-tune, or to study masked-diffusion training and sampling dynamics at the next-larger-than-toy scale.
  • —❌ Not an instruction-following model. Prompt it document-style ("The history of Rome is"), not question-style.

Sampling — important

Small diffusion LMs collapse into repetition loops under the default low-confidence remasking sampler. Use the frequency repetition penalty (on by default in chat.py / sample.py):

bash
python sample.py --ckpt nanodiff-350m-base.pt \
    --prompt "The history of Rome is" --rep-penalty 3.0

chat.py and sample.py enable a number of speed defaults (compile, reduced steps, persistent inductor cache). See the repo README for the full table.

Limitations

  • —Confabulates. At ~350M params on ~10B tokens, factual recall is unreliable. It learned English structure, not a dependable world model.
  • —Coherence degrades on harder prompts.
  • —English only; FineWeb-Edu domain.
  • —Base model — no alignment, no safety tuning.
  • —The training was still improving when it ended — the val curve hadn't flattened. Extending to ~15B tokens would likely give another 0.05-0.10 nats; this checkpoint is not at its capacity ceiling.

Citation

Built on the LLaDA recipe:

bibtex
@article{nie2025llada,
  title   = {Large Language Diffusion Models},
  author  = {Nie, Shen and others},
  journal = {arXiv preprint arXiv:2502.09992},
  year    = {2025}
}