Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/peft/tuners/tuners_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1167,7 +1167,8 @@ def _replace_module(self, parent, child_name, new_module, child) -> None:
child = child.base_layer

if not hasattr(new_module, "base_layer"):
new_module.weight = child.weight
if hasattr(child, "weight"):
new_module.weight = child.weight
if hasattr(child, "bias"):
new_module.bias = child.bias

Expand Down
31 changes: 31 additions & 0 deletions tests/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,37 @@ def test_transient_attribute_access_non_existing_adapter(self, mlp):
model.lin1.weight


class TestModulesToSaveMergeAndUnload:
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.

The test passes with or without the fix. Therefore, it doesn't check the fix. I also don't think that the diagnostic is correct: _replace_module is not involved in the merging path at all.

def test_merge_and_unload_without_weight(self):
# Regression test for #3213: merge_and_unload should not crash when modules_to_save
# targets a module without a weight attribute (e.g., a transformer encoder block)
class ModuleWithoutWeight(nn.Module):
def __init__(self):
super().__init__()
self.foo = nn.Linear(1, 1)

def forward(self, x):
return self.foo(x)

class Model(nn.Module):
def __init__(self):
super().__init__()
self.lin0 = nn.Linear(1, 2)
self.mod1 = ModuleWithoutWeight()

def forward(self, x):
return self.mod1(self.lin0(x))

from peft import LoraConfig, get_peft_model
config = LoraConfig(target_modules=["lin0"], modules_to_save=["mod1"])
model = get_peft_model(Model(), config)

# This should not raise an AttributeError
merged = model.merge_and_unload()
assert not hasattr(merged.mod1, "weight")



class TestModulesToSaveKwargsOnlyForward:
"""Regression test for #3191: modules listed in `modules_to_save` whose parent calls them with keyword arguments
only (e.g. Gemma's `vision_tower(pixel_values=...)`) used to crash with `TypeError: forward() missing 1 required
Expand Down