Commit graph

1,269 commits

Author SHA1 Message Date
Daniel Han
c7477ea236 Add the Krea 2 per-arch eager fusion (Krea2TransformerBlock addcmul patch)
Fuses the block's two inline modulations (1 + scale) * norm(x) + shift and two
gated residuals x + gate * out to torch.addcmul, matching the existing qwen /
z-image / flux fusions (compile-safe, 1-ULP more accurate, body-drift guarded).
Stock-vs-patched equivalence test included; install count is now 7.
2026-07-03 14:50:35 +00:00
Daniel Han
190b45c178 Merge branch 'diffusion-train-tab-2' into diffusion-krea2 2026-07-03 13:26:08 +00:00
Daniel Han
cc8133513a Merge remote-tracking branch 'origin/diffusion-train-tab-2' into diffusion-krea2 2026-07-03 13:26:08 +00:00
Daniel Han
fca32d5a5b Add Krea 2 Turbo: diffusion family, inference loader, LoRA training (diffusers 0.39)
Inference:
- krea-2 DiffusionFamily (Krea2Pipeline / Krea2Transformer2DModel, base
  krea/Krea-2-Turbo, bf16 only, no GGUF/sd.cpp mapping yet)
- Per-component pipeline loader (core/inference/diffusion_krea2.py): the krea
  repo is exported with transformers 5.2, so the tokenizer config
  (extra_special_tokens as a list, no slow-tokenizer vocab files) and the
  text encoder rope settings (rope_parameters vs rope_scaling) need explicit
  compat on the 4.x line; values are copied verbatim and equal the 4.x
  Qwen3-VL defaults, so the math is unchanged. from_pretrained also
  type-checks the tokenizer against the declared slow class, so the pipeline
  is assembled through its constructor with the model_index init config
  (is_distilled carries Turbo's fixed mu=1.15 schedule)
- Trust allowlist entry, curated picker entry + 8 step / cfg 0 defaults,
  int8 exclusion token for the M=1 Krea2TimestepEmbedding projection

Training:
- krea-2 _FamilySpec in the DiT trainer: phased conditioning/transformer
  load through the compat loader, shared Qwen-Image VAE latent path,
  fixed-512 text embeds (static shapes, plain concat collate), inline 2x2
  latent packing + shared position grid, the authors' recommended LoRA
  target set and rank/alpha 32, lr 3e-4, 512px presets
- GPU smokes on B200: nf4 2.9 steps/s at 11.5 GB, bf16 3.4 steps/s at
  30.1 GB, bf16 + regional compile 5.2 steps/s; adapter round-trip
  generation verified
2026-07-03 13:25:54 +00:00
Daniel Han
df1ecee819 Train tab: epochs run length + stop dialog wrap fix
- num_epochs on the diffusion train request and config: > 0 overrides
  train_steps with epochs x ceil(N / (batch x grad_accum)) optimizer steps,
  resolved against the dataset size in both the DiT and SDXL trainers
- Train settings: run length control with a Steps / Epochs unit select
- Stop dialog: flex-wrap footer so Stop and save wraps instead of clipping
  out of frame at narrow window widths
2026-07-03 13:25:16 +00:00
pre-commit-ci[bot]
44c6984f73 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-07-03 11:58:28 +00:00
Daniel Han
4215035a83 Merge pre-commit formatting into diffusion-train-tab-2 2026-07-03 11:57:55 +00:00
Daniel Han
32c5855742 Train tab: Stop inside the run card, adapter card above charts, persisted run history with re-plottable logs, clearer stop dialog 2026-07-03 11:52:25 +00:00
pre-commit-ci[bot]
bc0d10f759 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-07-03 11:14:50 +00:00
Daniel Han
0fbdd743a0 Report grad norm from the trainers and chart it instead of LR; celebrate completion in the run header 2026-07-03 11:10:40 +00:00
pre-commit-ci[bot]
bebbda797d [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-07-03 09:43:06 +00:00
Daniel Han
982b977296 Address review: auto int8 requires the dense-load transient to fit, dense modes are CUDA-only, auto respects bf16 compute, exact cudnn SDPA restore 2026-07-03 09:42:28 +00:00
Daniel Han
f2c2ff9a2b Merge diffusion-train-perf (pre-commit formatting + strict TF32 opt-out) into diffusion-train-precision
# Conflicts:
#	studio/backend/core/training/diffusion_train_common.py
2026-07-03 09:40:23 +00:00
Daniel Han
fabd930c39 Clear TF32 flags when enable_tf32 is off so the opt-out is strict fp32 2026-07-03 09:39:43 +00:00
Daniel Han
5f725bacf2 Add base_precision speed modes to DiT training: bf16 2.3-2.6x, int8, fp8
New base_precision config for the DiT trainers: nf4 (unchanged default) |
bf16 | int8 | fp8 | auto, advertised per family + per machine through
/api/train/diffusion/info (precision_modes, recommended_precision,
supports_compile) so the UI can gate the selector.

- bf16: dense transformer + regional torch.compile (auto-armed). The
  measured speed mode: 2.3x nf4 on FLUX (1.81 -> 4.12 steps/s), 2.6x on
  Z-Image (2.5 -> 6.38 steps/s) on B200, at dense-weight VRAM
  (FLUX 24.7 GB / Z-Image 13.6 GB peak vs 10.4 / 4.7 for nf4).
- int8: torchao weight-only int8 on the frozen base, quantized AFTER
  add_adapter (quantizing first trips peft 0.18's TorchaoLoraLinear,
  which is incompatible with the torchao 0.16 config API). Runs eager:
  inductor rejects the int8 subclass training graph (aliased subclass
  outputs), so compile is force-disabled for it.
- fp8: torchao convert_to_float8_training on the frozen linears
  (filter skips lora_ modules, proj_out, non-divisible-by-16 dims,
  pad_inner_dim), applied after add_adapter, compile auto-armed.
  Works and round-trips, but measured SLOWER than compiled bf16 at
  LoRA-training shapes (FLUX 3.15 vs 4.12 steps/s; Z-Image similar),
  so it is an explicit opt-in and auto never picks it.
- auto: free VRAM (measured before load) + dense-size table -> bf16
  when it fits with headroom, int8 in the middle band, else nf4.
  Prequant bnb repos always resolve to nf4; dense modes on them are
  rejected at validation with a pointer to the family's dense base.

Two crashes found and fixed along the way:
- The cuDNN SDPA backend's training graph fails on the FLUX attention
  shapes (torch 2.10 + cu130, B200): mha_graph.execute errors, then the
  context degrades into illegal memory accesses. The perf-flag guard now
  pins flash/mem-efficient SDPA for the run (mathematically equivalent,
  snapshot/restored). nf4 escaped it by routing attention differently.
- Regional compile now uses dynamic=True (the inference layer's proven
  default): dynamic=False specialisation fused a gemm_and_bias epilogue
  that failed with CUBLAS_STATUS_EXECUTION_FAILED on the FLUX training
  graph; dynamic=True is also faster (Z-Image 3.84 -> 6.38 steps/s).

Verified: 98 backend tests green (new test_diffusion_base_precision.py:
validation, auto policy table, fp8 filter, compile gating, /info fields);
per-mode 40-step runs on FLUX + Z-Image with loss means inside the nf4
envelope and adapter round-trip generation through the normal LoRA path
for bf16-, fp8-, and int8-trained adapters.
2026-07-03 09:26:05 +00:00
pre-commit-ci[bot]
07128c784a [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-07-03 08:46:19 +00:00
Daniel Han
9d967d2c5b Speed up diffusion LoRA training and cut DiT peak VRAM by a third
Perf core for the diffusion trainers, defaults preserving the training math:

- Phased model loading: the pipeline now loads without its transformer
  (conditioning only), captions are encoded and the text encoders freed,
  the VAE latent cache is built and the VAE freed, and only then does the
  transformer load. The multi-GB denoiser never shares VRAM with the
  encoders, cutting measured peak VRAM on B200: FLUX 17.1 -> 10.4 GB,
  Qwen-Image 19.1 -> 12.8 GB, Z-Image 7.3 -> 4.7 GB.
- Latent cache (cache_latents, default on): per-image crop/flip variants
  (cache_variants, default 4 vs the single frozen variant of the diffusers
  --cache_latents) store the VAE posterior's affine parameters, so every
  step still draws a fresh VAE sample; a cached center-crop Z-Image run
  matches the uncached one at the bf16 nondeterminism floor.
- True batching: train_batch_size now actually batches the transformer
  forward (it was silently 1). nf4 dequant dominates the step cost, so
  batch 4 lands near batch-1 step time: 4.0x samples/s on Qwen-Image,
  3.1x on FLUX, 2.1x on Z-Image, with multi-seed loss envelopes
  overlapping batch-1.
- LR scheduler support in the DiT loop (lr_scheduler / lr_warmup_steps
  were accepted but ignored); progress events now report the real
  per-step LR.
- TF32 + high fp32 matmul precision under enable_tf32 (default on),
  snapshot/restored around the run. cudnn.benchmark is scoped to a
  caller opt-in only: autotuning the fp32 VAE convs doubled peak VRAM
  on the DiT families for zero steady-state gain.
- Vectorized sigma gathering (drops a per-step Python search loop),
  cached FLUX img_ids/guidance, fused torch AdamW fallback, steady-state
  samples_per_second (excludes the first-step warmup).
- Regional torch.compile plumbing (compile_transformer off/on/auto with
  eager fallback): auto stays off over a bitsandbytes base where compile
  is a net loss (27 s warmup, slightly slower steady on Z-Image); it
  arms automatically for the dense/quantized speed modes that follow.
- Stop parity with the LLM trainer: /api/train/diffusion/stop accepts an
  optional {save} body and the service forwards save=False as a
  no-save cancel; a new preparing event surfaces cache-build progress.
- SDXL trainer gets the same latent cache, perf flags, and fused
  fallback; its batching, LR schedule, and min-SNR stay as they were.

Verified: 83 backend tests green; per-family 30-40 step runs with
adapter round-trip generation through the normal LoRA path (FLUX,
Qwen-Image, Z-Image all pass).
2026-07-03 08:44:57 +00:00
Daniel Han
4ed839e553 Merge branch 'diffusion-train-datasets' of https://github.com/unslothai/unsloth into diffusion-train-datasets 2026-07-03 06:18:03 +00:00
Daniel Han
ca2104d481 Add Smithsonian Butterflies and Nouns example datasets
Two permissive ~100-image sets for the Train tab: huggan/smithsonian_butterflies_subset
(CC0, the classic diffusers-docs training set, imported as a subject set with a trigger
prompt since its metadata columns are species names not captions) and m1guelpf/nouns
(CC0, captioned pixel-art avatars via the text column). Both cap at 100 images.
2026-07-03 06:17:54 +00:00
pre-commit-ci[bot]
074f5f51a7 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-07-02 16:08:26 +00:00
Daniel Han
d0ffab4f24 Merge remote-tracking branch 'origin/diffusion-train-dit' into diffusion-train-datasets 2026-07-02 16:06:04 +00:00
Daniel Han
83a5d52e7b Wrap the DiT training forward in bf16 autocast
The fp32 LoRA parameters and the bnb 4-bit base matmuls need a single
compute dtype during the forward, exactly like the diffusers dreambooth
scripts run under accelerator.autocast. Without it the 4-bit backward on
FLUX.1-dev fails with an illegal-address CUBLAS error partway into the
first step. Z-Image and Qwen-Image smokes are unaffected and the SDXL
path (its own trainer) is untouched.
2026-07-02 16:05:56 +00:00
Daniel Han
8547fd14f7 Merge remote-tracking branch 'origin/diffusion-train-dit' into diffusion-train-datasets 2026-07-02 16:03:25 +00:00
Daniel Han
60268a77b2 Tests for DiT trainers, family resolution, info families, gated preflight
Cover the DiT spec table, the QLoRA prequant heuristic, the Z-Image bf16-only
guard, the gated-repo name check, family resolution now that FLUX/Qwen/Z-Image
are trainable (and GGUF repos are rejected as inference-only), the families list
in /diffusion/info, and the gated-base 400 preflight that leaves the GPU
untouched.
2026-07-02 15:26:05 +00:00
Daniel Han
afd93591d6 Expose trainable families in /diffusion/info and preflight gated bases
The training info endpoint now returns the trainable model families (name,
label, default + allowed base repos, recommended defaults, and a VRAM/access
note) so the Train UI can offer a base picker with realistic guidance. The start
route preflights a gated base repo (HEAD model_index.json with the user's token)
BEFORE freeing resident GPU workloads, so a missing FLUX.1-dev license/token
fails fast with an actionable 400 instead of evicting the loaded model and then
hitting a confusing mid-load 401.
2026-07-02 15:26:05 +00:00
Daniel Han
b28793418d Speed up + shrink SDXL LoRA training (precompute text embeds, 8-bit AdamW)
SDXL re-encoded every caption with both CLIP text encoders on every step (pure
waste, since captions are constant) and kept the encoders resident. Precompute
each unique caption's embeddings once, then free the text encoders before the
loop: numerically identical (embeddings are deterministic and this consumes no
torch RNG, so the noise/timestep stream is unchanged) but faster and ~1.5 GB
lighter. Default the optimizer to 8-bit AdamW (bitsandbytes) with an fp32
fallback, halving optimizer state with no meaningful LoRA quality cost. Env
toggles (UNSLOTH_DIFFUSION_NO_PRECOMPUTE / _FP32_OPTIM) let the accuracy guard
A/B the paths.
2026-07-02 15:25:55 +00:00
Daniel Han
fa2cb600ee Add flow-matching DiT LoRA trainers (FLUX.1-dev, Qwen-Image, Z-Image)
Extends diffusion LoRA training beyond SDXL to the three popular DiT families
via a single shared flow-matching loop parameterised by small per-family specs
(loading, prompt/latent encoding, transformer forward, save). Verified against
diffusers 0.38.0:

- FLUX.1-dev: 2x2 latent packing + image ids, guidance-embed forward, on-the-fly
  nf4 QLoRA of the 12B transformer (the dev repo is gated, so training needs the
  user's HF token).
- Qwen-Image: 5D VAE latents normalised by the per-channel latents_mean/std,
  img_shapes forward, prequant nf4 base by default (on-the-fly nf4 for the bf16
  base).
- Z-Image: list I/O with the reversed timestep convention and a negated
  prediction, bf16 only.

The registry (get_trainer) and DiffusionFamily.trainable / train_base_repos now
route these families to the DiT trainer; the SDXL blocklist guard is replaced by
a positive family resolution that also rejects GGUF repos (inference-only) and
still-unsupported families. Per-family defaults + labels + VRAM notes are exposed
via family_train_infos for the Train UI.

Memory: caption embeddings are precomputed once and the text encoders freed
before the loop; gradient checkpointing (non-reentrant, required for bnb 4-bit)
and 8-bit AdamW are on by default.
2026-07-02 15:25:43 +00:00
Daniel Han
c32bda109b Test diffusion dataset labeling and example-import endpoints
Cover caption precedence, thumbnail generation and .thumbs exclusion,
caption write/clear, image delete cleanup, path-traversal rejection on
names and filenames, and example import with a mocked datasets.load_dataset
(files plus sidecars written, idempotent second call, cap respected, load
failure mapped to 502).
2026-07-02 15:14:54 +00:00
Daniel Han
60056c13eb Add diffusion dataset labeling and example-import endpoints
The Train tab needs to let users caption small datasets in the browser and
pull in a ready-made set to see training work end to end, neither of which
the upload-only endpoint supported.

Add, under /api/train/diffusion/dataset:
- GET {name}/images lists every image with its resolved caption (metadata
  beats a per-image sidecar, matching the trainer's discovery order) so
  uncaptioned images are visible and flaggable.
- GET {name}/image/{filename} serves an image, with ?thumb=<px> returning a
  cached downscaled JPEG kept in a hidden .thumbs subdir (regenerated when
  the source is newer) so the labeling grid stays light.
- PUT {name}/caption/{filename} writes, or when blank clears, the .txt
  sidecar; DELETE {name}/image/{filename} removes the image plus its
  sidecars and thumbnails.
- GET dataset-examples lists a curated, license-labelled registry, and
  POST dataset/import-example materializes one into a dataset folder as
  numbered images + .txt captions. Two loaders cover the shapes seen in the
  wild: streaming rows from datasets.load_dataset (dog-example, Tuxemon) and
  a snapshot + jsonl walk for imagefolder repos whose captions live in a
  non-standard *.jsonl (the public-domain tarot set). Imports are idempotent
  and cap the image count.

Filenames and dataset names are validated against path traversal and pinned
inside the datasets root.
2026-07-02 15:14:47 +00:00
Daniel Han
e801bc37a8 Tests for the diffusion training platform
Cover the trainer registry (get_trainer resolves SDXL, unknown family raises),
family resolution (explicit model_family validation, resolved_family on the config),
the metadata sidecar write + scan read with family gating, and the service loss-history
folding (append, bad-point skipping, decimation at cap, family/perf fields) plus the
status route nesting metric_history.
2026-07-02 14:55:27 +00:00
Daniel Han
76520bb553 Retain diffusion training loss history and expose it in status
The training service kept only the latest loss, so a live loss chart could show a
single point. Fold each progress event into bounded (step, loss, lr) history arrays
(capped at 4000 points, decimated when full) plus the latest throughput and peak VRAM,
and record the family / base model / catalog path on completion. The status endpoint
returns these as a nested metric_history object the UI can chart directly, and the
start request accepts an optional model_family override.
2026-07-02 14:55:17 +00:00
Daniel Han
7f0a9ebd2f Refactor diffusion LoRA training into a family-aware platform
Split the SDXL trainer into a shared, architecture-agnostic layer so more model
families can be trained without duplicating the plumbing:

- New core/training/diffusion_train_common.py holds the config + validation, dataset
  discovery, event emission, stop protocol, adapter publishing, and a lazy trainer
  registry (get_trainer). diffusion_lora_trainer.py keeps the SDXL-specific loop and
  re-exports the moved names so existing imports are unchanged.
- The SDXL-only base-model blocklist becomes a positive check: the family is resolved
  from the base model (or an explicit model_family) via the diffusion family registry,
  and a known-but-not-yet-trainable family is refused with a clear message. Unknown
  custom names still default to the SDXL trainer.
- DiffusionFamily gains a trainable flag and train_base_repos; SDXL is marked trainable.
  DiT families flip on when their trainers land.
- Trained adapters now write a <name>.json metadata sidecar (family, base model, rank,
  trigger prompt, ...) that the LoRA scanner reads to family-gate the adapter in the
  picker instead of showing it as unknown for every model.
- The training base-model trust allowlist adds the official FLUX.1-dev, Z-Image-Turbo,
  and Qwen-Image repos (safetensors-only, no remote code).
2026-07-02 14:55:09 +00:00
pre-commit-ci[bot]
3aebcf45ee [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-07-02 10:01:45 +00:00
Daniel Han
4be3d4c93d Merge remote-tracking branch 'origin/diffusion-lora-training-api' into diffusion-lora-training-ui 2026-07-02 10:00:26 +00:00
Daniel Han
8cbdd88c0b Merge branch 'diffusion-lora-training-api' of https://github.com/unslothai/unsloth into diffusion-lora-training-api 2026-07-02 10:00:08 +00:00
Daniel Han
373edae1c0 Validate diffusion training config before freeing the GPU
The start route freed resident GPU workloads (export, Images pipeline, chat)
before the service validated the config, so a start that was then refused,
now including a non-SDXL base model, tore down the user's loaded model for
nothing. Run the same cheap normalise pass first; the LLM path already
follows this rule via its before_spawn hook.
2026-07-02 09:59:58 +00:00
pre-commit-ci[bot]
0aab029639 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-07-02 09:58:56 +00:00
pre-commit-ci[bot]
9a6d4ad38f [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-07-02 09:58:24 +00:00
Daniel Han
cfde12451c Add diffusion dataset upload and training info endpoints
Training an image LoRA required knowing the Studio home layout and copying
files onto the server by hand, which is the most confusing step of the whole
flow. Two small endpoints fix that:

- GET /api/train/diffusion/info reports the datasets and outputs roots plus
  every dataset folder that contains images (with image/caption counts), so
  the UI can offer a picker instead of a blind free-text path.
- POST /api/train/diffusion/dataset uploads images and optional caption
  .txt / metadata.jsonl files into a named folder under the datasets root,
  creating it on first use and accumulating on repeat uploads so large sets
  can arrive in batches. Names are validated to a single path component and
  files stream to disk under the same per-upload size cap as LLM dataset
  uploads. The returned name is a valid data_dir for /diffusion/start.
2026-07-02 09:57:26 +00:00
Daniel Han
c5a0ad59cf Merge remote-tracking branch 'origin/diffusion-lora-training' into diffusion-lora-training-api 2026-07-02 09:56:56 +00:00
Daniel Han
e1f82b4446 Refuse non-SDXL base models at diffusion training start
The trainer only supports the SDXL U-Net, but a FLUX / Qwen-Image / Z-Image
repo or a GGUF filename passed as base_model was accepted and then failed
minutes later inside StableDiffusionXLPipeline.from_pretrained with an
unrelated-looking error. Add a name-based guard in normalized() so known
DiT-family names and .gguf checkpoints are rejected up front, which the API
start route surfaces as an immediate 400 with a message that says exactly
which bases are trainable. Unrecognisable names still pass through so custom
local SDXL checkpoints keep working.
2026-07-02 09:56:49 +00:00
Daniel Han
1df030e325 Merge remote-tracking branch 'origin/diffusion-lora-training' into diffusion-lora-training-api 2026-07-02 06:42:34 +00:00
Daniel Han
0c68c402d0 Merge remote-tracking branch 'origin/diffusion-lora-ux' into diffusion-lora-training 2026-07-02 06:42:32 +00:00
Daniel Han
138b796218 Merge remote-tracking branch 'origin/diffusion-controlnet' into diffusion-sdxl 2026-07-02 06:41:44 +00:00
Daniel Han
47a64f3819 Merge remote-tracking branch 'origin/diffusion-lora' into diffusion-controlnet
# Conflicts:
#	studio/backend/core/inference/sd_cpp_backend.py
2026-07-02 06:40:54 +00:00
Daniel Han
38aa96a19c Merge remote-tracking branch 'origin/diffusion-image-workflows' into diffusion-lora 2026-07-02 06:39:42 +00:00
pre-commit-ci[bot]
5a221223e6 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-07-02 06:39:23 +00:00
Daniel Han
8bfa236798 Merge remote-tracking branch 'origin/diffusion-image-workflows' into diffusion-lora
# Conflicts:
#	studio/backend/core/inference/sd_cpp_backend.py
2026-07-02 06:38:47 +00:00
pre-commit-ci[bot]
ffed150070 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-07-02 06:37:36 +00:00
Daniel Han
eaf968bfc1 Merge remote-tracking branch 'origin/image-generation' into diffusion-image-workflows
# Conflicts:
#	studio/backend/core/inference/diffusion.py
#	studio/backend/core/inference/diffusion_families.py
#	studio/backend/routes/inference.py
#	studio/backend/tests/test_diffusion_backend.py
#	studio/frontend/src/features/images/images-page.tsx
2026-07-02 06:36:45 +00:00