Normalize negative_prompt in LongCatAudioDiT pipeline for CFG symmetry#13525
Normalize negative_prompt in LongCatAudioDiT pipeline for CFG symmetry#13525Ricardo-M-L wants to merge 1 commit into
Conversation
The LongCatAudioDiT pipeline pre-processes positive prompts through
`_normalize_text` before encoding:
```python
normalized_prompts = [_normalize_text(text) for text in prompt]
...
prompt_embeds, prompt_embeds_len = self.encode_prompt(normalized_prompts, device)
```
`_normalize_text` lowercases the string, strips ASCII and curly quote
characters (`"“”‘’`), and collapses whitespace. The upstream reference
implementation applies the same normalization (`normalize_text` in
`utils.py`) to every piece of text it feeds to the UMT5 text encoder.
However, when a user passes a `negative_prompt`, the pipeline encodes
it raw:
```python
negative_prompt_embeds, negative_prompt_embeds_len = self.encode_prompt(
negative_prompt, device
)
```
That creates a distribution mismatch between the conditional and
unconditional text embeddings used in CFG — the model was trained
against normalized text on both branches, so a raw negative prompt
lands off-distribution and weakens/distorts guidance rather than
guiding away from the described audio. Empty/`None` `negative_prompt`
is unaffected (that path uses a zero tensor to match the reference).
This change applies the same `_normalize_text` pass to user-supplied
negative prompts before encoding, restoring symmetry with the positive
branch and with the reference pipeline.
|
Polite bump — negative_prompt currently bypasses the same `_normalize_text` cleanup that's applied to positive prompt above (lines ~270). This causes a small CFG asymmetry: positive prompt is whitespace/case-normalized but the negative isn't, so they disagree on how identical-but-differently-formatted text gets embedded. cc @dg845 — you merged the original LongCat-AudioDiT pipeline (#13390), so this is in your area. Happy to back out if you'd prefer to keep the asymmetry intentional. CI is green, sitting since 2026-04-21. 🙏 |
|
@yiyixuxu friendly bump 🙏 — 14 days, no review yet. Tiny CFG-symmetry fix in LongCatAudioDiT: |
|
Hi team, just following up on this PR. Happy to make any changes or address feedback if needed. Thanks for considering it! |
What this PR does
In
LongCatAudioDiTPipeline.__call__, positive prompts are normalized via_normalize_textbefore being encoded:_normalize_textlowercases the string, strips ASCII and curly quote characters ("“”‘’), and collapses whitespace. The upstream reference implementationmeituan-longcat/LongCat-AudioDiTapplies the samenormalize_textpass to every piece of text it feeds to the UMT5 text encoder.However, when a user passes a
negative_prompt, the pipeline currently encodes it raw:Why this is a bug
The model was trained with normalized text on both branches, so a raw negative prompt lands off-distribution relative to the normalized positive prompt. In classifier-free guidance
the
(pos - neg)delta no longer represents a clean "target − avoid" direction — it's partly absorbing normalization noise (case, quote glyphs, whitespace). That weakens/distorts guidance rather than guiding away from what the user described.Examples that currently fail to behave symmetrically:
negative_prompt="Loud Noise"encodes differently from the positive path's"loud noise"negative_prompt='"speech"'keeps the quote glyphs that the positive path would stripnegative_prompt="multiple speakers"keeps the extra whitespaceThe empty/
Nonebranch is unaffected — it already uses a zero tensor to match the reference model.Fix
Apply the same
_normalize_textpass to user-supplied negative prompts before encoding, restoring symmetry with the positive branch and with the reference pipeline.if negative_prompt is None or (isinstance(negative_prompt, str) and negative_prompt == ""): negative_prompt_embeds = torch.zeros(...) negative_prompt_embeds_len = torch.tensor([1] * batch_size, device=device) else: - negative_prompt_embeds, negative_prompt_embeds_len = self.encode_prompt(negative_prompt, device) + normalized_negative_prompts = [_normalize_text(text) for text in negative_prompt] + negative_prompt_embeds, negative_prompt_embeds_len = self.encode_prompt( + normalized_negative_prompts, device + )Before submitting
Who can review?
@yiyixuxu @sayakpaul