Skip to content

Commit 33b0326

Browse files
authored
Merge pull request #3085 from bghira/bugfix/anyflow-uncond-batch-aliases
anyflow: swap unconditional aliases instead of popping them
2 parents 8e04bdf + d733661 commit 33b0326

4 files changed

Lines changed: 51 additions & 8 deletions

File tree

simpletuner/helpers/distillation/anyflow/distiller.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -950,10 +950,24 @@ def _unconditional_batch(prepared_batch: Dict[str, Any]) -> Dict[str, Any]:
950950
batch.pop("encoder_attention_mask", None)
951951
if torch.is_tensor(negative_mask):
952952
batch["encoder_attention_mask"] = negative_mask
953-
# Some families (e.g. Ideogram) read model-specific conditioning aliases ahead of the
954-
# generic keys; drop them so the swapped unconditional embeds/mask take effect.
955-
for alias in ("prompt_embeds", "attention_mask", "attention_masks"):
956-
batch.pop(alias, None)
953+
# Some families read model-specific conditioning aliases ahead of (or instead of) the
954+
# generic keys: Ideogram prefers `prompt_embeds` when present, and Flux requires it.
955+
# Swap the aliases to the unconditional tensors rather than popping them.
956+
if "prompt_embeds" in batch:
957+
batch["prompt_embeds"] = negative
958+
for alias in ("attention_mask", "attention_masks"):
959+
if alias in batch:
960+
if torch.is_tensor(negative_mask):
961+
batch[alias] = negative_mask
962+
else:
963+
batch.pop(alias)
964+
negative_pooled = prepared_batch.get("negative_add_text_embeds")
965+
if torch.is_tensor(negative_pooled):
966+
if "add_text_embeds" in batch:
967+
batch["add_text_embeds"] = negative_pooled
968+
added_cond_kwargs = batch.get("added_cond_kwargs")
969+
if isinstance(added_cond_kwargs, dict) and "text_embeds" in added_cond_kwargs:
970+
batch["added_cond_kwargs"] = {**added_cond_kwargs, "text_embeds": negative_pooled}
957971
# Lets families with a dedicated unconditional model (e.g. Ideogram) dispatch to it.
958972
batch["is_unconditional_pass"] = True
959973
return batch

simpletuner/helpers/models/common.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5456,6 +5456,9 @@ def prepare_batch(self, batch: dict, state: dict) -> dict:
54565456
negative_attention_mask = batch.get("negative_encoder_attention_mask")
54575457
if negative_attention_mask is not None and hasattr(negative_attention_mask, "to"):
54585458
batch["negative_encoder_attention_mask"] = negative_attention_mask.to(device=self.accelerator.device)
5459+
negative_pooled_embeds = batch.get("negative_add_text_embeds")
5460+
if negative_pooled_embeds is not None and hasattr(negative_pooled_embeds, "to"):
5461+
batch["negative_add_text_embeds"] = negative_pooled_embeds.to(**target_device_kwargs)
54595462

54605463
# Process additional conditioning if provided
54615464
pooled_embeds = batch.get("add_text_embeds")

simpletuner/helpers/training/collate.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1335,6 +1335,7 @@ def resolve_local_source_audio_path(path: str | None) -> str | None:
13351335
"negative_prompt_embeds": unconditional_text_encoder_outputs.get("prompt_embeds"),
13361336
"negative_text_token_tags": unconditional_text_encoder_outputs.get("text_token_tags"),
13371337
"negative_encoder_attention_mask": unconditional_text_encoder_outputs.get("attention_masks"),
1338+
"negative_add_text_embeds": unconditional_text_encoder_outputs.get("pooled_prompt_embeds"),
13381339
"add_text_embeds": all_text_encoder_outputs.get("pooled_prompt_embeds"),
13391340
"t5xxl_ids": all_text_encoder_outputs.get("t5xxl_ids"),
13401341
"t5xxl_weights": all_text_encoder_outputs.get("t5xxl_weights"),

tests/helpers/distillation/test_anyflow_distiller.py

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -751,18 +751,30 @@ def test_unconditional_batch_drops_positive_attention_mask_when_negative_has_non
751751

752752
self.assertNotIn("encoder_attention_mask", unconditional_batch)
753753

754-
def test_unconditional_batch_drops_model_specific_conditioning_aliases(self):
754+
def test_unconditional_batch_swaps_model_specific_conditioning_aliases(self):
755755
batch = _prepared_batch()
756756
batch["prompt_embeds"] = torch.ones(2, 7, 4)
757757
batch["attention_mask"] = torch.ones(2, 7, dtype=torch.bool)
758758
batch["attention_masks"] = torch.ones(2, 7, dtype=torch.bool)
759759

760760
unconditional_batch = AnyFlowDistiller._unconditional_batch(batch)
761761

762-
self.assertNotIn("prompt_embeds", unconditional_batch)
762+
self.assertIs(unconditional_batch["prompt_embeds"], batch["negative_encoder_hidden_states"])
763763
self.assertNotIn("attention_mask", unconditional_batch)
764764
self.assertNotIn("attention_masks", unconditional_batch)
765765

766+
def test_unconditional_batch_swaps_alias_masks_when_negative_mask_present(self):
767+
batch = _prepared_batch()
768+
batch["prompt_embeds"] = torch.ones(2, 7, 4)
769+
batch["attention_mask"] = torch.ones(2, 7, dtype=torch.bool)
770+
batch["attention_masks"] = torch.ones(2, 7, dtype=torch.bool)
771+
batch["negative_encoder_attention_mask"] = torch.zeros(2, 5, dtype=torch.bool)
772+
773+
unconditional_batch = AnyFlowDistiller._unconditional_batch(batch)
774+
775+
self.assertIs(unconditional_batch["attention_mask"], batch["negative_encoder_attention_mask"])
776+
self.assertIs(unconditional_batch["attention_masks"], batch["negative_encoder_attention_mask"])
777+
766778
def test_onpolicy_initializes_separate_discriminator_adapter_and_optimizer(self):
767779
model = _FlowModel()
768780
distiller = AnyFlowDistiller(
@@ -1071,7 +1083,7 @@ def test_get_scheduler_wraps_conditional_transformer_pipeline(self):
10711083

10721084

10731085
class AnyFlowUnconditionalBatchTests(unittest.TestCase):
1074-
def test_unconditional_batch_drops_stale_mask_and_model_specific_aliases(self):
1086+
def test_unconditional_batch_drops_stale_mask_and_swaps_model_specific_aliases(self):
10751087
batch = _prepared_batch()
10761088
batch["encoder_attention_mask"] = torch.ones(2, 5, dtype=torch.long)
10771089
batch["prompt_embeds"] = torch.ones(2, 7, 4)
@@ -1082,7 +1094,7 @@ def test_unconditional_batch_drops_stale_mask_and_model_specific_aliases(self):
10821094

10831095
self.assertIs(unconditional_batch["encoder_hidden_states"], batch["negative_encoder_hidden_states"])
10841096
self.assertNotIn("encoder_attention_mask", unconditional_batch)
1085-
self.assertNotIn("prompt_embeds", unconditional_batch)
1097+
self.assertIs(unconditional_batch["prompt_embeds"], batch["negative_encoder_hidden_states"])
10861098
self.assertNotIn("attention_mask", unconditional_batch)
10871099
self.assertNotIn("attention_masks", unconditional_batch)
10881100

@@ -1162,3 +1174,16 @@ def __init__(self):
11621174
)
11631175
with tempfile.TemporaryDirectory() as temp_dir:
11641176
save_file(state, str(Path(temp_dir) / "adapter.safetensors"))
1177+
1178+
def test_unconditional_batch_swaps_negative_pooled_embeds(self):
1179+
batch = _prepared_batch()
1180+
batch["add_text_embeds"] = torch.ones(2, 8)
1181+
batch["added_cond_kwargs"] = {"text_embeds": batch["add_text_embeds"], "time_ids": torch.zeros(2, 6)}
1182+
batch["negative_add_text_embeds"] = torch.zeros(2, 8)
1183+
1184+
unconditional_batch = AnyFlowDistiller._unconditional_batch(batch)
1185+
1186+
self.assertIs(unconditional_batch["add_text_embeds"], batch["negative_add_text_embeds"])
1187+
self.assertIs(unconditional_batch["added_cond_kwargs"]["text_embeds"], batch["negative_add_text_embeds"])
1188+
self.assertIs(unconditional_batch["added_cond_kwargs"]["time_ids"], batch["added_cond_kwargs"]["time_ids"])
1189+
self.assertIs(batch["added_cond_kwargs"]["text_embeds"], batch["add_text_embeds"])

0 commit comments

Comments
 (0)