Skip to content

fix(lora): only drop adapter from _merged_adapters when unfused from all components - #14385

Open
AloysJehwin wants to merge 7 commits into
huggingface:mainfrom
AloysJehwin:fix/unfuse-lora-merged-adapters-sync
Open

fix(lora): only drop adapter from _merged_adapters when unfused from all components#14385
AloysJehwin wants to merge 7 commits into
huggingface:mainfrom
AloysJehwin:fix/unfuse-lora-merged-adapters-sync

Conversation

@AloysJehwin

Copy link
Copy Markdown

Fixes #14214

When unfuse_lora(components=[...]) is called with a subset of components, the current code removes the adapter from _merged_adapters as soon as it's unfused from the first component — even if it's still physically fused into the others. So num_fused_loras/fused_loras report nothing fused while the remaining components silently still have the LoRA baked in.

The fix: call module.unmerge() as before, then recompute _merged_adapters by scanning all loadable components for adapters that are still physically merged (via BaseTunerLayer.merged_adapters). That way the set only loses an adapter once it's gone from everything.

Verified with the reproduction script from the issue — num_fused_loras correctly stays at 1 after unfusing only text_encoder, and the unet is confirmed still merged at the PEFT level.

…all components

Signed-off-by: Aloys Jehwin <aloysjehwin@gmail.com>

@sayakpaul sayakpaul left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this PR. Should we also have a test for this?

@HuggingFaceDocBuilderDev

Copy link
Copy Markdown

The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.

Signed-off-by: Aloys Jehwin <aloysjehwin@gmail.com>
@AloysJehwin
AloysJehwin force-pushed the fix/unfuse-lora-merged-adapters-sync branch from ff8dbce to 8e1ca01 Compare August 6, 2026 04:05
@github-actions github-actions Bot added tests size/S PR with diff < 50 LOC size/M PR with diff < 200 LOC and removed size/S PR with diff < 50 LOC labels Aug 6, 2026
@AloysJehwin

Copy link
Copy Markdown
Author

Added a test in tests/lora/test_lora_loader_utils.py. Let me know if anything else is needed.

@sayakpaul

Copy link
Copy Markdown
Member

It shouldn't be a regression test. The test should live under tests/lora/utils.py.

…lone unit)

Signed-off-by: Aloys Jehwin <aloysjehwin@gmail.com>
@AloysJehwin
AloysJehwin force-pushed the fix/unfuse-lora-merged-adapters-sync branch from 9085ff6 to 9145c5f Compare August 6, 2026 04:45
@AloysJehwin

Copy link
Copy Markdown
Author

The test uses a minimal mock pipeline (no real weights needed), so it lives in test_lora_loader_utils.py alongside similar loader utils tests. Happy to move it into utils.py as a method on PeftLoraLoaderMixinTests if you prefer — just let me know.

@BenjaminBossan BenjaminBossan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR, generally looks good. I just added some smaller comments. As for where to put the test, please follow the guidance from the mtaintainers.

Comment thread src/diffusers/loaders/lora_base.py Outdated
remaining_merged: set[str] = set()
for component_name in self._lora_loadable_modules:
component_model = getattr(self, component_name, None)
if component_model is not None and issubclass(component_model.__class__, (ModelMixin, PreTrainedModel)):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why check issubclass instead of isinstance? I also think isinstance(component_model, nn.Module) would be sufficient here, but I'll leave that to the maintainers to decide.

Comment thread src/diffusers/loaders/lora_base.py Outdated
module.unmerge()

# Only remove an adapter from _merged_adapters once it is no longer
# physically merged in any remaining loadable component. Removing it

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO the second sentence is not needed.

Comment thread tests/lora/utils.py Outdated
# Clean up the hooks to prevent state leak
if hasattr(denoiser, "_diffusers_hook"):
denoiser._diffusers_hook.remove_hook(_GROUP_OFFLOADING, recurse=True)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove unrelated changes.

Comment thread tests/lora/test_lora_loader_utils.py Outdated
Unfusing only a subset of components must keep _merged_adapters in sync
with the adapters still physically fused in the remaining components.
"""
import torch.nn as nn

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why local imports? Only PEFT needs to be local or guarded behind is_peft_available().

Signed-off-by: Aloys Jehwin <aloysjehwin@gmail.com>
@AloysJehwin
AloysJehwin force-pushed the fix/unfuse-lora-merged-adapters-sync branch from c7d8593 to 5ec11b4 Compare August 6, 2026 21:06
@AloysJehwin

Copy link
Copy Markdown
Author

Addressed all four points — swapped to isinstance(component_model, nn.Module), trimmed the comment, removed the stray change in utils.py, and moved imports to the top. Test still passes.

Signed-off-by: Aloys Jehwin <aloysjehwin@gmail.com>
@AloysJehwin

Copy link
Copy Markdown
Author

Fixed — restored utils.py to main.

@BenjaminBossan BenjaminBossan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the updates, LGTM. About where to put the test, let's wait for @sayakpaul's response.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

unfuse_lora(components=[...]) desyncs _merged_adapters/num_fused_loras from actual per-component PEFT merge state

4 participants