You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: .ai/references/models.md
+9-4Lines changed: 9 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -87,13 +87,18 @@ if num_key_value_groups > 1:
87
87
88
88
`dim=2` because tensors are `(batch_size, seq_len, num_heads, head_dim)` here. Must be `repeat_interleave`, not `repeat` — the groups are contiguous, and `repeat` gives a silently wrong pairing no shape check catches.
89
89
90
-
Both compute the same thing, so weigh the two on compatibility and performance and recommend whichever fits the model better.
90
+
Both compute the same thing. What differs is which kernel runs. Under the default backend (`native`, a plain `F.scaled_dot_product_attention`), torch has four kernels:
91
91
92
-
-**Compatibility.** Most backends do not implement `enable_gqa` yet — flash, FA3, sage, cuDNN and the hub kernels raise on it, as does the context-parallel path. Grep `enable_gqa` in `attention_dispatch.py` for the current list rather than trusting this one; it changes as support lands. The flag limits the model to whichever backends still accept it, while repeating works on all of them.
| math | ✓ | ✓ — materializes the full `[batch_size, num_heads, seq_len_q, seq_len_kv]` score matrix |
97
+
| cuDNN | ✓ | ✓ |
93
98
94
-
-**Performance.** Depends on whether the model passes a mask. With a mask, no fused kernel takes a mask *and* mismatched head counts, so SDPA falls back to math and materializes the full `[batch_size, num_heads, seq_len_q, seq_len_kv]` score matrix — no error, no warning, only memory. Without a mask, flash broadcasts inside the kernel and the flag saves the key/value copy. Both effects scale with sequence length and head count, so measure at the model's real shape; `torch.backends.cuda.can_use_flash_attention(params, debug=True)` and `can_use_efficient_attention` print why a kernel was rejected, which is the fastest way to see which one you actually got.
99
+
It tries them in a priority order and takes the first that accepts the call; the order changes across torch versions and GPUs.
95
100
96
-
-**Recommendation.** Repeat by default — it is portable and never pathological. Reach for `enable_gqa=True` only when the model never passes a mask*and* the measured saving justifies the narrower backend support. For scale: on Krea 2 at 1024×1024, masked, the flag cost 9.02 GiB and 26.7 ms per call against 0.16 GiB and 4.1 ms repeated; unmasked at the same shape it saved 0.11 GiB and 0.1 ms. `transformer_cosmos3.py` is the in-repo case where it is defensible — causal, never masked.
101
+
If the model passes a mask, repeat the key/value heads: (1) `enable_gqa` is rejected by the context-parallel path, and (2) with a mask it can only land on math or cuDNN — if math comes first, it silently materializes the full score matrix.
Copy file name to clipboardExpand all lines: .ai/references/testing.md
+22-2Lines changed: 22 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -9,7 +9,7 @@ Two test layers must be added for any new pipeline: pipeline-level tests, and (i
9
9
- Keep component sizes tiny so the suite runs fast — small `num_layers`, small hidden/attention dims, low resolution, few frames. Reference `tests/pipelines/wan/test_wan.py` (`get_dummy_components` and `get_dummy_inputs`) for the size scale to target.
10
10
- Build dummy components from the **real classes** at tiny config — a real VAE with tiny dims, a real tokenizer from an `hf-internal-testing/tiny-random-*` repo. Don't substitute a hand-rolled mock (a bare `nn.Module` with a `SimpleNamespace` config, a fake tokenizer) without a good reason: a mock is written by copying whatever the pipeline reads from the component today, so it can only confirm the pipeline against itself — the test stays green when the component renames a config field or the pipeline starts reading one the component doesn't have, and catching exactly that pipeline↔component contract is what a pipeline test is for. A good reason to stub: the component is impractical to instantiate and only its I/O matters to the pipeline (e.g. `DummyCosmosSafetyChecker` standing in for the huge Cosmos guardrail) — then make it a shared, purpose-built class honoring the real interface.
11
11
- The same applies to test doubles at the call level: don't monkeypatch a component method (e.g. the scheduler's `set_timesteps`) just to capture what the code under test passed to it — that only verifies the caller against itself, not against the real method's contract. Call the real component and assert on its resulting state.
12
-
- No LoRA tests in the initial PR (no `LoraTesterMixin`, no `tests/lora/test_lora_layers_<model>.py`).
12
+
- No LoRA tests in the initial PR — don't compose the LoRA tester mixins into the pipeline or model test file (see [LoRA tests](#lora-tests)), and don't add a `tests/lora/test_lora_layers_<model>.py`.
13
13
- No integration / slow tests in the initial PR — don't add anything gated on `@slow` / `RUN_SLOW=1` yet.
14
14
15
15
## Pipeline-level tests
@@ -30,8 +30,28 @@ Follow the style introduced in [#14113](https://github.com/huggingface/diffusers
30
30
-`MemoryTesterMixin` — CPU offload, group offload, layerwise casting.
31
31
- Cache mixins — `PyramidAttentionBroadcastTesterMixin`, `FasterCacheTesterMixin`, `FirstBlockCacheTesterMixin`, `TaylorSeerCacheTesterMixin`, `MagCacheTesterMixin`. Guidance-distilled models override the cache config (e.g. `FASTER_CACHE_CONFIG = {... "is_guidance_distilled": True}`). Don't introduce caching related tests in the first iteration. These tests are added on a case-by-case basis.
32
32
- In the first pass, just add tests related to `PipelineTesterMixin` and `MemoryTesterMixin`.
33
+
- **Declare a component that can't be offloaded — don't hand-write a skip.** Leaf-level offloading hooks only the supported leaf types (`nn.Linear`, `nn.Conv*`, `nn.Embedding` — see `_GO_LC_SUPPORTED_PYTORCH_LAYERS` in `src/diffusers/hooks/_common.py`) and onloads each on its own `forward`, so any code that reads a leaf's `.weight` instead of calling the leaf bypasses that leaf's hook and computes against offloaded weights. Which fix applies depends on who owns the component. For a diffusers model, set `_supports_group_offloading = False` on the `ModelMixin` subclass (as `HunyuanDiT2DModel` does) — both offload mixins honor the flag and skip themselves, so the gap is declared on the model instead of buried in a test file. For a third-party component you can't annotate, such as a `transformers` encoder, list it in `group_offloading_leaf_level_exclude_modules` on the config class; `enable_group_offload` keeps excluded components on the accelerator, so every other component stays covered — including the VAE, which the component-scoped `test_group_offloading_inference` leaves out. Block-level offloading is usually unaffected, hence the level in the name — a component that fails at both levels does need a skip.
34
+
-`torch.nn.MultiheadAttention` is the common instance: it passes `self.out_proj.weight` straight to `torch.nn.functional.multi_head_attention_forward` instead of calling `self.out_proj`, so the hook on `out_proj` never fires. `SiglipVisionModel`'s attention pooling head wraps one — see `tests/pipelines/hunyuan_video/test_hunyuan_video_framepack.py`, whose `image_encoder` is excluded for this reason.
35
+
-`HunyuanDiTAttentionPool` (`src/diffusers/models/embeddings.py`) shows the same failure without an MHA module: a plain `nn.Module` that hands its `q_proj` / `k_proj` / `v_proj` / `c_proj` weights to `torch.nn.functional.multi_head_attention_forward`, so all four projections stay offloaded rather than just one. `HunyuanDiT2DModel` opts out of group offloading entirely with `_supports_group_offloading = False`.
36
+
- Before adding a skip or an exclusion, confirm the failure still reproduces — several existing skips are stale, having outlived the upstream cause.
33
37
-**IP-Adapter tests** live in their own class decorated with `@is_ip_adapter`, subclassing only the config (not `PipelineTesterMixin`).
34
38
39
+
#### LoRA tests
40
+
41
+
Since [#14268](https://github.com/huggingface/diffusers/pull/14268), a standard pipeline's LoRA tests live **next to its pipeline tests** — another mixin composed with the same `<Pipeline>PipelineTesterConfig` — not in `tests/lora/test_lora_layers_<model>.py`. Reference: `TestFluxPipelineLoRA` / `TestFluxPipelineLoRAMemory` in `tests/pipelines/flux/test_pipeline_flux.py`.
42
+
43
+
- Mixins live in `tests/pipelines/testing_utils/lora.py` and are exported from `..testing_utils`. One test class each, named `Test<Pipeline>LoRA...`:
44
+
-`LoraTesterMixin` — adapter attach/detach, LoRA scale and attention-kwargs, fuse/unfuse, multi-adapter (set/delete/weight), save/load round-trips, adapter metadata. Runs on CPU.
45
+
-`LoraMemoryTesterMixin` — LoRA × memory optimizations (group offload, model CPU offload, deleting adapters while offloaded). Accelerator-only.
-**Give each mixin its own test class.** They are marked `@is_lora`, and a mark applies to every test in the class that inherits it — mixing one into `Test<Pipeline>` would mark those tests as LoRA tests too.
48
+
- Run them with `pytest tests/pipelines/ -m "lora"` (what CI does); `pytest -m "not lora"` skips them.
49
+
-**Nothing LoRA-specific goes on the config class.** The mixins read the same contract as every other mixin — `pipeline_class`, `get_dummy_components()`, `get_dummy_inputs()` with `output_type="pt"` — and self-skip when `pipeline_class` isn't a `LoraBaseMixin` subclass.
50
+
- Components to adapt are derived from `pipeline_class._lora_loadable_modules`. Override `denoiser_target_modules` on the test class only when the denoiser's attention modules aren't named `to_q` / `to_k` / `to_v` / `to_out.0`. A text encoder architecture that isn't registered yet needs an entry in `TEXT_ENCODER_TARGET_MODULES` in `tests/pipelines/testing_utils/lora.py` — not a per-class override.
51
+
-**Pipeline-specific LoRA tests are methods on the `Test<Pipeline>LoRA` class**, written against the shared helpers: `self.get_pipeline()`, `self.add_adapters_to_pipeline(pipe, components=[...], **lora_config_kwargs)`, `self.run_pipe(pipe)`, and the class-scoped `base_pipe_output` fixture (baseline output of the un-adapted pipeline). `run_pipe` produces `base_pipe_output`, so the two are directly comparable — don't hand-roll a forward pass to compare against it. See `test_with_alpha_in_state_dict` and `test_lora_expansion_works_for_{absent,extra}_keys` on `TestFluxPipelineLoRA`.
52
+
- Load and save through the public API (`pipe.load_lora_weights`, `pipeline_class.save_lora_weights`, `pipe.set_adapters`, `pipe.unload_lora_weights`), and assert the adapter landed with `check_if_lora_correctly_set` from `...models.testing_utils.lora`.
53
+
- Nightly LoRA-checkpoint integration tests (loading real Hub LoRAs) go in the same file, in their own `@nightly @require_big_accelerator @require_peft_backend` class — see `TestFluxLoRAIntegration`. Still not part of an initial PR.
54
+
35
55
### Modular pipelines
36
56
37
57
- Location: `tests/modular_pipelines/<model>/test_modular_pipeline_<model>.py` (one config class + set of test classes per blockset / pipeline variant).
- Run with **no `--include` flags** initially. The generator auto-detects mixins/attributes and emits the always-on testers (`ModelTesterMixin`, `MemoryTesterMixin`, `TorchCompileTesterMixin`, plus `AttentionTesterMixin` / `ContextParallelTesterMixin` / `TrainingTesterMixin` as applicable). Optional testers (quantization, caching, single-file, IP adapter, etc.) are added later, after maintainer discussion.
61
81
- The generator writes to `tests/models/transformers/test_models_transformer_<model>.py` (or the matching `unets/` / `autoencoders/` subdir).
62
82
- Fill in the `TODO`s in the generated `<Model>TesterConfig`: `pretrained_model_name_or_path`, `get_init_dict()` (tiny config), `get_dummy_inputs()`, `input_shape`, `output_shape`. Keep init dims small for speed.
63
-
- Do **not** add `LoraTesterMixin` at the start, even if the model subclasses `PeftAdapterMixin` — strip it from the generated file for the initial PR.
83
+
- Do **not** add the model-level `LoraTesterMixin` (from `tests/models/testing_utils/lora.py`, distinct from the pipeline-level one) at the start, even if the model subclasses `PeftAdapterMixin` — strip it from the generated file for the initial PR.
Prepare a mask for the final outpainted image. To create a more natural transition between the original image and the outpainted background, blur the mask to help it blend better.
0 commit comments