Skip to content

ENH Allow rank/alpha keys to be "fully qualified" #2382

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

BenjaminBossan
Copy link
Member

@BenjaminBossan BenjaminBossan commented Feb 17, 2025

See huggingface/diffusers#10808 for context.

Right now, if we have a key in rank_pattern or alpha_pattern, it is interpreted as a pattern to be matched against the module names of the model (basically it is an endswith match). The issue with this logic is that we may sometimes have false matches. E.g. if we have a model with modules "foo" and "bar", and if "bar" also has a sub-module called "foo", it is impossible to target just the outer "foo" but not "bar.foo". (Note: It is already possible target only "bar.foo" and not "foo" by using "bar.foo" as key)

This PR adds the possibility to indicate to PEFT that a key should be considered to be the "fully qualified" key, i.e. a strict match should be performed instead of a pattern match. For this, users need to prefix the string "FULL-NAME-" before the actual key. In our example, that would be "FULL-NAME-foo".

Notice that since the prefix contains "-", there is no possibility that it could accidentally match a valid module name.

For now, this is a draft PR, until we can verify that it is sufficient to solve the original diffusers issue.

See huggingface/diffusers#10808 for context.

Right now, if we have a key in rank_pattern or alpha_pattern, it is
interpreted as a pattern to be matched against the module names of the
model (basically it is an endswith match). The issue with this logic is
that we may sometimes have false matches. E.g. if we have a model with
modules "foo" and "bar", and if "bar" also has a sub-module called
"foo", it is impossible to target just the outer "foo" but not
"bar.foo". (Note: It is already possible target only "bar.foo" and not
"foo" by using "bar.foo" as key)

This PR adds the possibility to indicate to PEFT that a key should be
considered to be the "fully qualified" key, i.e. a strict match should
be performed instead of a pattern match. For this, users need to prefix
the string "FULL-NAME-" before the actual key. In our example, that
would be "FULL-NAME-foo".

Notice that since the prefix contains "-", there is no possibility that
it could accidentally match a valid module name.
@HuggingFaceDocBuilderDev

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.

@BenjaminBossan BenjaminBossan marked this pull request as ready for review March 3, 2025 14:21
@BenjaminBossan
Copy link
Member Author

@sayakpaul I forgot about this PR, is it good from the diffusers point of view?

Copy link
Member

@sayakpaul sayakpaul left a comment

Choose a reason for hiding this comment

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

It slipped through the crack from my end too. All good.

Copy link
Collaborator

@githubnemo githubnemo left a comment

Choose a reason for hiding this comment

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

We can go forward with this but I think there's a solution with less change possible, maybe I'm wrong. Let me know what you think.

Comment on lines +1058 to 1068
# handling of a special case: Users can prefix the key in rank_pattern or alpha_pattern with this special prefix to
# indicate that this key is supposed to be the full name, not a pattern. That way, the key "foo" can be matched
# without inadvertently matching bar.foo as well.
for key in pattern_keys:
if (
key.startswith(FULLY_QUALIFIED_PATTERN_KEY_PREFIX)
and key[len(FULLY_QUALIFIED_PATTERN_KEY_PREFIX) :] == key_to_match
):
return key

return next(filter(lambda key: re.match(rf".*\.{key}$", key_to_match), pattern_keys), key_to_match)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not sure if the introduction of a special prefix is necessary. We're already utilizing regex here, why not use the caret operator to force a full match? The current pattern doesn't allow this but I think it is easily changed.

Proof of concept:

import re

def match(pattern, name):
    return re.match(rf".*(^|\.){pattern}$", name)

assert match("foo", "model.foo")
assert not match("foo", "model.bar")
assert not match("foo", "bofoo")
assert not match("foo", "model.bofoo")

assert match("^foo", "foo")
assert not match("^foo", "model.foo")

@BenjaminBossan
Copy link
Member Author

@sayakpaul I have converted this PR to draft to put it on hold for now, given what @githubnemo suggested and the discussion in #2413. Sorry for the inconvenience, but I think it's worth it to invest a bit more time to figure out the best way forward.

@sayakpaul
Copy link
Member

sayakpaul commented Mar 7, 2025

Then I guess the diffusers PR should be reverted as well?

@BenjaminBossan
Copy link
Member Author

There is no final decision yet, but maybe we can check if the solution proposed above is enough or not.

why not use the caret operator to force a full match?

@sayakpaul
Copy link
Member

I will temporarily remove the conditional branch and settle with what we have in the other branch in diffusers. Once there's a decision and this PR is merged, we can revisit that. Would that work for you?

@BenjaminBossan
Copy link
Member Author

Whatever works best for you is fine with me.

BenjaminBossan added a commit to BenjaminBossan/peft that referenced this pull request Mar 11, 2025
Supersedes huggingface#2382

Right now, the regex used to match the keys passed for rank_pattern and
alpha_pattern requires that either:

1. The module name is identical to the key
2. The module name having a prefix and then ending on the key

This is restrictive, since it doesn't allow to disambiguate between all
cases. E.g. if we have a model with these attributes:

- model.foo
- model.bar.foo

We cannot currently target just model.foo. (We can already target only
model.bar.foo by passing "bar.foo" as a key to the rank_pattern /
alpha_pattern dict).

This PR makes it possible to pass "^foo" as a key. This way,
model.bar.foo is not targeted, as the key does not start with "foo".

As a general rule for users, if they intend to have a full match, they
should pass the full name of the module preceded by a ^. This is the
least ambigious way.

When running the test case with the old code, all the test cases with ^
will fail, which is fine, since ^ was not working anyway. At the same
time, all test cases not using ^ pass, which means they are backwards
compatible.
BenjaminBossan added a commit that referenced this pull request Mar 13, 2025
Supersedes #2382

Right now, the regex used to match the keys passed for rank_pattern and
alpha_pattern requires that either:

1. The module name is identical to the key
2. The module name having a prefix and then ending on the key

This is restrictive, since it doesn't allow to disambiguate between all
cases. E.g. if we have a model with these attributes:

- model.foo
- model.bar.foo

We cannot currently target just model.foo. (We can already target only
model.bar.foo by passing "bar.foo" as a key to the rank_pattern /
alpha_pattern dict).

This PR makes it possible to pass "^foo" as a key. This way,
model.bar.foo is not targeted, as the key does not start with "foo".

As a general rule for users, if they intend to have a full match, they
should pass the full name of the module preceded by a ^. This is the
least ambigious way.

When running the test case with the old code, all the test cases with ^
will fail, which is fine, since ^ was not working anyway. At the same
time, all test cases not using ^ pass, which means they are backwards
compatible.
@BenjaminBossan
Copy link
Member Author

Superseded by #2419.

Guy-Bilitski pushed a commit to Guy-Bilitski/peft that referenced this pull request May 13, 2025
Supersedes huggingface#2382

Right now, the regex used to match the keys passed for rank_pattern and
alpha_pattern requires that either:

1. The module name is identical to the key
2. The module name having a prefix and then ending on the key

This is restrictive, since it doesn't allow to disambiguate between all
cases. E.g. if we have a model with these attributes:

- model.foo
- model.bar.foo

We cannot currently target just model.foo. (We can already target only
model.bar.foo by passing "bar.foo" as a key to the rank_pattern /
alpha_pattern dict).

This PR makes it possible to pass "^foo" as a key. This way,
model.bar.foo is not targeted, as the key does not start with "foo".

As a general rule for users, if they intend to have a full match, they
should pass the full name of the module preceded by a ^. This is the
least ambigious way.

When running the test case with the old code, all the test cases with ^
will fail, which is fine, since ^ was not working anyway. At the same
time, all test cases not using ^ pass, which means they are backwards
compatible.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants