Skip to content

Commit 4f34c8e

Browse files
author
bghira
committed
krea2: CREPA self-flow, LayerSync, TREAD, block swap, cfg-zero star, skip layer guidance, and flowmap generation
1 parent f44caf2 commit 4f34c8e

10 files changed

Lines changed: 475 additions & 19 deletions

File tree

documentation/OPTIONS.es.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ Donde `foo` es tu entorno de configuración; o simplemente usa `config/config.js
129129

130130
### `--musubi_blocks_to_swap`
131131

132-
- **Qué**: Intercambio de bloques Musubi para LongCat-Video, Wan, LTXVideo, Kandinsky5-Video, Qwen-Image, Flux, Flux.2, zlab i1, Cosmos2Image y HunyuanVideo: mantiene los últimos N bloques del transformer en CPU y transmite pesos por bloque durante el forward.
132+
- **Qué**: Intercambio de bloques Musubi para LongCat-Video, Wan, LTXVideo, Kandinsky5-Video, Qwen-Image, Flux, Flux.2, zlab i1, Cosmos2Image, HunyuanVideo y Krea 2: mantiene los últimos N bloques del transformer en CPU y transmite pesos por bloque durante el forward.
133133
- **Predeterminado**: `0` (desactivado)
134134
- **Notas**: Offload de pesos estilo Musubi; reduce VRAM con coste en rendimiento y se omite cuando los gradientes están habilitados.
135135

documentation/OPTIONS.hi.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ simpletuner configure config/foo/config.json
129129

130130
### `--musubi_blocks_to_swap`
131131

132-
- **What**: LongCat‑Video, Wan, LTXVideo, Kandinsky5‑Video, Qwen‑Image, Flux, Flux.2, zlab i1, Cosmos2Image, और HunyuanVideo के लिए Musubi block swap — आख़िरी N transformer blocks को CPU पर रखें और forward के दौरान प्रति block weights stream करें।
132+
- **What**: LongCat‑Video, Wan, LTXVideo, Kandinsky5‑Video, Qwen‑Image, Flux, Flux.2, zlab i1, Cosmos2Image, HunyuanVideo, और Krea 2 के लिए Musubi block swap — आख़िरी N transformer blocks को CPU पर रखें और forward के दौरान प्रति block weights stream करें।
133133
- **Default**: `0` (disabled)
134134
- **Notes**: Musubi‑style weight offload; throughput लागत पर VRAM कम करता है और gradients सक्षम होने पर skip हो जाता है।
135135

documentation/OPTIONS.ja.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ simpletuner configure config/foo/config.json
129129

130130
### `--musubi_blocks_to_swap`
131131

132-
- **内容**: LongCat-Video、Wan、LTXVideo、Kandinsky5-Video、Qwen-Image、Flux、Flux.2、zlab i1、Cosmos2Image、HunyuanVideo の Musubi ブロックスワップ。最後の N 個の Transformer ブロックを CPU に置き、forward 中にブロック単位で重みをストリーミングします。
132+
- **内容**: LongCat-Video、Wan、LTXVideo、Kandinsky5-Video、Qwen-Image、Flux、Flux.2、zlab i1、Cosmos2Image、HunyuanVideo、Krea 2 の Musubi ブロックスワップ。最後の N 個の Transformer ブロックを CPU に置き、forward 中にブロック単位で重みをストリーミングします。
133133
- **既定**: `0`(無効)
134134
- **注記**: Musubi 方式の重みオフロードで、スループット低下と引き換えに VRAM を削減します。勾配が有効な場合はスキップされます。
135135
- **注記**: Musubi 方式の重みオフロードで、スループット低下と引き換えに VRAM を削減します。勾配が有効な場合はスキップされます。

documentation/OPTIONS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ Where `foo` is your config environment - or just use `config/config.json` if you
129129

130130
### `--musubi_blocks_to_swap`
131131

132-
- **What**: Musubi block swap for LongCat-Video, Wan, LTXVideo, Kandinsky5-Video, Qwen-Image, Flux, Flux.2, zlab i1, Cosmos2Image, and HunyuanVideo — keep the last N transformer blocks on CPU and stream weights per block during forward.
132+
- **What**: Musubi block swap for LongCat-Video, Wan, LTXVideo, Kandinsky5-Video, Qwen-Image, Flux, Flux.2, zlab i1, Cosmos2Image, HunyuanVideo, and Krea 2 — keep the last N transformer blocks on CPU and stream weights per block during forward.
133133
- **Default**: `0` (disabled)
134134
- **Notes**: Musubi-style weight offload; reduces VRAM at a throughput cost and is skipped when gradients are enabled.
135135

documentation/OPTIONS.pt-BR.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ Onde `foo` e seu ambiente de config — ou use `config/config.json` se nao estiv
129129

130130
### `--musubi_blocks_to_swap`
131131

132-
- **O que**: Musubi block swap para LongCat-Video, Wan, LTXVideo, Kandinsky5-Video, Qwen-Image, Flux, Flux.2, zlab i1, Cosmos2Image e HunyuanVideo — mantem os ultimos N blocos transformer na CPU e faz streaming de pesos por bloco durante o forward.
132+
- **O que**: Musubi block swap para LongCat-Video, Wan, LTXVideo, Kandinsky5-Video, Qwen-Image, Flux, Flux.2, zlab i1, Cosmos2Image, HunyuanVideo e Krea 2 — mantem os ultimos N blocos transformer na CPU e faz streaming de pesos por bloco durante o forward.
133133
- **Padrao**: `0` (desabilitado)
134134
- **Notas**: Offload de pesos estilo Musubi; reduz VRAM com custo de throughput e e ignorado quando gradientes estao habilitados.
135135

documentation/OPTIONS.zh.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ simpletuner configure config/foo/config.json
129129

130130
### `--musubi_blocks_to_swap`
131131

132-
- **内容**:为 LongCat-Video、Wan、LTXVideo、Kandinsky5-Video、Qwen-Image、Flux、Flux.2、zlab i1、Cosmos2Image、HunyuanVideo 提供 Musubi 块交换。将最后 N 个 Transformer 块保留在 CPU,并在前向中按块流式加载权重。
132+
- **内容**:为 LongCat-Video、Wan、LTXVideo、Kandinsky5-Video、Qwen-Image、Flux、Flux.2、zlab i1、Cosmos2Image、HunyuanVideo、Krea 2 提供 Musubi 块交换。将最后 N 个 Transformer 块保留在 CPU,并在前向中按块流式加载权重。
133133
- **默认**`0`(禁用)
134134
- **说明**:Musubi 风格权重卸载,会降低吞吐以换取显存节省,且在启用梯度时会跳过。
135135
- **说明**:Musubi 风格权重卸载,通过降低吞吐换取显存节省;启用梯度时会跳过。

simpletuner/helpers/models/krea2/model.py

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from simpletuner.helpers.models.krea2.transformer import Krea2Transformer2DModel
1919
from simpletuner.helpers.models.registry import ModelRegistry
2020
from simpletuner.helpers.models.tae.types import VideoTAESpec
21+
from simpletuner.helpers.musubi_block_swap import apply_musubi_pretrained_defaults
2122
from simpletuner.helpers.training.state_tracker import StateTracker
2223

2324
logger = logging.getLogger(__name__)
@@ -63,6 +64,26 @@ class Krea2(ImageModelFoundation):
6364
DEFAULT_LORA_TARGET = ["to_k", "to_q", "to_v", "to_out.0"]
6465
FUSED_LORA_TARGET = ["to_qkv", "to_out.0"]
6566

67+
def tread_init(self):
68+
from simpletuner.helpers.training.tread import TREADRouter
69+
70+
tread_cfg = getattr(self.config, "tread_config", None)
71+
if not isinstance(tread_cfg, dict) or tread_cfg == {} or tread_cfg.get("routes") is None:
72+
logger.error("TREAD training requires you to configure the routes in the TREAD config")
73+
import sys
74+
75+
sys.exit(1)
76+
77+
self.unwrap_model(model=self.model).set_router(
78+
TREADRouter(
79+
seed=getattr(self.config, "seed", None) or 42,
80+
device=self.accelerator.device,
81+
),
82+
tread_cfg["routes"],
83+
)
84+
85+
logger.info("TREAD training is enabled for Krea 2")
86+
6687
@classmethod
6788
def max_swappable_blocks(cls, config=None) -> Optional[int]:
6889
return 27
@@ -78,6 +99,24 @@ def _uses_reference_latents(self) -> bool:
7899
def supports_conditioning_dataset(self) -> bool:
79100
return True
80101

102+
def supports_crepa_self_flow(self) -> bool:
103+
return True
104+
105+
def _prepare_crepa_self_flow_batch(self, batch: dict, state: dict) -> dict:
106+
return self._prepare_image_crepa_self_flow_batch(batch, state, patch_size=self._patch_size())
107+
108+
def _select_crepa_hidden_states(self, prepared_batch: dict, hidden_states_buffer):
109+
if hidden_states_buffer is None:
110+
return None
111+
crepa = getattr(self, "crepa_regularizer", None)
112+
capture_layer = prepared_batch.get(
113+
"crepa_capture_block_index",
114+
getattr(crepa, "block_index", None),
115+
)
116+
if capture_layer is None:
117+
return None
118+
return hidden_states_buffer.get(f"layer_{int(capture_layer)}")
119+
81120
def requires_conditioning_dataset(self) -> bool:
82121
return self._uses_reference_latents()
83122

@@ -419,6 +458,11 @@ def _prepare_model_predict_timesteps(self, raw_timesteps, batch_size: int) -> to
419458
raise ValueError(f"Krea 2 expected scalar or 1D timesteps, got shape {tuple(timesteps.shape)}.")
420459
return timesteps / 1000.0
421460

461+
def _apply_krea2_experimental_timestep_kwargs(self, call_kwargs: dict, prepared_batch: dict) -> None:
462+
if getattr(self.config, "twinflow_enabled", False):
463+
call_kwargs["timestep_sign"] = prepared_batch.get("twinflow_time_sign")
464+
self._apply_flowmap_r_timestep_kwargs(call_kwargs, prepared_batch)
465+
422466
def _prepare_reference_latents(self, prepared_batch: dict, batch_size: int, channels: int, height: int, width: int):
423467
reference_latents = prepared_batch.get("conditioning_latents")
424468
if isinstance(reference_latents, list):
@@ -477,20 +521,35 @@ def model_predict(self, prepared_batch):
477521
timesteps = self._prepare_model_predict_timesteps(prepared_batch["timesteps"], batch_size)
478522
position_ids = self._position_ids_for_grids(prompt_embeds.shape[1], grids, self.accelerator.device)
479523

524+
hidden_states_buffer = self._new_hidden_state_buffer()
525+
call_kwargs = {}
526+
if hidden_states_buffer is not None:
527+
call_kwargs["hidden_states_buffer"] = hidden_states_buffer
528+
self._apply_krea2_experimental_timestep_kwargs(call_kwargs, prepared_batch)
529+
480530
noise_pred = self.model(
481531
hidden_states=hidden_states,
482532
encoder_hidden_states=prompt_embeds,
483533
timestep=timesteps,
484534
position_ids=position_ids,
485535
encoder_attention_mask=prompt_embeds_mask,
536+
**call_kwargs,
486537
return_dict=False,
487538
)[0]
488539
noise_pred = noise_pred[:, :target_token_count]
489540
noise_pred = self._unpack_latents(noise_pred, latent_height, latent_width)
490541

491542
if target_ndim == 5:
492543
noise_pred = noise_pred.unsqueeze(2)
493-
return {"model_prediction": noise_pred}
544+
return {
545+
"model_prediction": noise_pred,
546+
"crepa_hidden_states": self._select_crepa_hidden_states(prepared_batch, hidden_states_buffer),
547+
"hidden_states_buffer": hidden_states_buffer,
548+
}
549+
550+
def pretrained_load_args(self, pretrained_load_args: dict) -> dict:
551+
args = super().pretrained_load_args(pretrained_load_args)
552+
return apply_musubi_pretrained_defaults(self.config, args)
494553

495554

496555
ModelRegistry.register("krea2", Krea2)

simpletuner/helpers/models/krea2/pipeline.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,16 @@
5959
"""
6060

6161

62+
@torch.amp.autocast(
63+
"mps" if torch.backends.mps.is_available() else "cuda" if torch.cuda.is_available() else "cpu",
64+
dtype=torch.float32,
65+
)
66+
def optimized_scale(positive_flat: torch.Tensor, negative_flat: torch.Tensor):
67+
dot_product = torch.sum(positive_flat * negative_flat, dim=1, keepdim=True)
68+
squared_norm = torch.sum(negative_flat**2, dim=1, keepdim=True) + 1e-8
69+
return dot_product / squared_norm
70+
71+
6272
# Copied from diffusers.pipelines.flux.pipeline_flux.calculate_shift
6373
def calculate_shift(
6474
image_seq_len,
@@ -578,6 +588,13 @@ def __call__(
578588
callback_on_step_end_tensor_inputs: list[str] = ["latents"],
579589
attention_kwargs: dict[str, Any] | None = None,
580590
max_sequence_length: int = 512,
591+
skip_guidance_layers: list[int] | None = None,
592+
skip_layer_guidance_start: float = 0.01,
593+
skip_layer_guidance_stop: float = 0.2,
594+
skip_layer_guidance_scale: float = 2.8,
595+
use_cfg_zero_star: bool = True,
596+
zero_steps: int = 1,
597+
use_zero_init: bool = True,
581598
):
582599
r"""
583600
Function invoked when calling the pipeline for generation.
@@ -796,7 +813,39 @@ def __call__(
796813
return_dict=False,
797814
)[0]
798815
neg_noise_pred = neg_noise_pred[:, :target_image_seq_len]
799-
noise_pred = noise_pred + guidance_scale * (noise_pred - neg_noise_pred)
816+
pos_noise_pred = noise_pred
817+
if use_cfg_zero_star:
818+
pos_flat = pos_noise_pred.view(pos_noise_pred.shape[0], -1)
819+
neg_flat = neg_noise_pred.view(neg_noise_pred.shape[0], -1)
820+
alpha = optimized_scale(pos_flat, neg_flat).view(
821+
pos_noise_pred.shape[0], *([1] * (pos_noise_pred.dim() - 1))
822+
)
823+
if i <= zero_steps and use_zero_init:
824+
noise_pred = pos_noise_pred * 0.0
825+
else:
826+
noise_pred = neg_noise_pred * alpha + guidance_scale * (pos_noise_pred - neg_noise_pred * alpha)
827+
else:
828+
noise_pred = pos_noise_pred + guidance_scale * (pos_noise_pred - neg_noise_pred)
829+
830+
should_skip_layers = (
831+
skip_guidance_layers is not None
832+
and len(skip_guidance_layers) > 0
833+
and i > num_inference_steps * skip_layer_guidance_start
834+
and i < num_inference_steps * skip_layer_guidance_stop
835+
)
836+
if should_skip_layers:
837+
skip_noise_pred = self.transformer(
838+
hidden_states=transformer_latents,
839+
encoder_hidden_states=prompt_embeds,
840+
timestep=timestep,
841+
position_ids=position_ids,
842+
encoder_attention_mask=prompt_embeds_mask,
843+
attention_kwargs=self.attention_kwargs,
844+
skip_layers=skip_guidance_layers,
845+
return_dict=False,
846+
)[0]
847+
skip_noise_pred = skip_noise_pred[:, :target_image_seq_len]
848+
noise_pred = noise_pred + (pos_noise_pred - skip_noise_pred) * skip_layer_guidance_scale
800849

801850
# compute the previous noisy sample x_t -> x_t-1
802851
latents_dtype = latents.dtype

0 commit comments

Comments
 (0)