-
Notifications
You must be signed in to change notification settings - Fork 6k
[LoRA] restrict certain keys to be checked for peft config update. #10808
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
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. |
The original peft config is like
After adjustment
Similar for control loras that this was added for
Becomes
Except in that case, It seems like we already have the correct rank from the original config, no? In the original case are adjusting the config to have
If we change |
Your deducations are correct, thank you! Help me understand this:
We infer this in
Feel free to push it directly to my branch and we can run the tests to confirm nothing broke. |
It would be a change in peft (and removing As another example for this lora we originally have default https://github.com/huggingface/peft/blob/1e2d6b5832401e07e917604dfb080ec474818f2b/src/peft/tuners/lora/model.py#L175-L190 |
This was done with suggestions from @BenjaminBossan here:
I think it's a bit hard to gauge but I will let @BenjaminBossan comment further. In my experience, I would say this is fairly divided. |
Always this cursèd problem of missing metadata :-/
In general, I think we have to accept that without metadata, we cannot guarantee that the identical config is restored, but it should of course still lead to the same adapter model. Do we know why
We can't change the base logic here in PEFT, as it could potentially break existing code. Theoretically, we could think about having a flag on FULLY_QUALIFIED_KEY_PREFIX = "FULL-NAME-"
def get_pattern_key(pattern_keys, key_to_match):
"""Match a substring of key_to_match in pattern keys"""
for key in pattern_keys:
if key.startswith(FULLY_QUALIFIED_KEY_PREFIX) and key[len(FULLY_QUALIFIED_KEY_PREFIX):] == key_to_match:
return key
return next(filter(lambda key: re.match(rf".*\.{key}$", key_to_match), pattern_keys), key_to_match) Diffusers would have to be adjusted to use this new prefix. We would also need to ensure that the PEFT and diffusers version match, otherwise adding the prefix in diffusers would result in no match on the PEFT side. WDYT, would it solve the issue? I'm also open to different proposals. |
Thanks for the discussions! Appreciate that.
It's this line that is the culprit I think: diffusers/src/diffusers/loaders/peft.py Line 77 in c14057c
This sounds like a bit too much of |
I wouldn't say it's diffusers-specific, there could be other model architectures where users may want to add a specific rank pattern but where the target module key is not unique. Therefore, I think a more general solution would be preferable, even if it's more complicated.
Isn't this error prone? |
It is error-prone, yes.
How do we decide a reasonable default for this variable? API-wise could you walk me through the changes that might needed on the |
If I'm not missing something, the change would be that instead of creating a
the dict would be
This would signal to PEFT that we should consider this key to be the full key, not a pattern to match. I'm not sure if this could lead to bad performance, given that |
Thanks. I am still wondering about a realistic example for |
If I understood the issue correctly, it is caused by trying to set a special rank pattern for a certain key but the same key ( |
To be clear, if you think the proposed solution in this PR is good enough to work for most users, I'd also be fine with going that way and not having to change PEFT, as it's less work overall :) |
I see. This can work. Will just have to figure out the right API calls inside of
My solution was admittedly a dirty hack. But after the discussions, I am leaning towards your proposal. |
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.
I created a draft PR to implement this: huggingface/peft#2382. Let's check that, with the right adjustments on the diffusers code, this is solving the original issue before proceeding. |
There is no missing metadata, right? we're detecting the correct default rank and "fully qualified" keys to set as a different rank. Adding a magic key seems like a weird solution to me. |
I just meant that in general, many LoRA training frameworks don't save any
It's not beautiful, but what would be your suggested solution? |
I think it's being inferred for both loras mentioned in this issue. Are there known circumstances when the metadata cannot be inferred correctly?
Maybe just a flag in |
As far as I can tell from my experience of dealing with several LoRA checkpoints from the community, metadata (rank, alpha, etc.) can always be inferred from the LoRA checkpoints.
Yes, this is true, especially when we're loading non-diffusers LoRA checkpoints. However, I think @BenjaminBossan wanted a more general solution to deal with this in @BenjaminBossan, in huggingface/peft#2382, what if we add a flag instead like @hlky suggested. What would be the downside of it? |
It seems there are 2 separate paths for peft/ A flag would feel more natural but I have no major issues with a magic key prefix. |
I can then base this PR off of huggingface/peft#2382 until the regex flag is landed in the future. Is that fine by @hlky @BenjaminBossan? |
Well, we're going through hurdles because the checkpoint under consideration is a non-diffusers and non-peft checkpoint. So, I think we're good for now. LMK if the current changes are good for you.
Thanks for the headsup, yeah I thought of about it too but didn't reflect on it here as we had already found a better solution. |
What I meant is that it would be cool if we could do: config = LoraConfig(..., rank_pattern={"my-string-key": 8, re.compile("my-regex-key"): 16}) That way, we would inverse control so that users can decide how they want to match if the pass a regex as key. However, we would not be able to JSON-serialize this config.
You mean something like: config = LoraConfig(..., rank_pattern={"foo": 8, "bar": 16}, fully_qualified_rank_keys=["foo"])? I'm not a big fan of this pattern, as it requires 2 independent data structures to be in sync. It's easier to make mistake as a user and PEFT would need to implement checks for consistency.
I agree.
I mean if this adapter is saved in the PEFT format, it will contain the magic string. If anyone wants to load this without PEFT, they need to be aware of that. As mentioned, I don't think it's a big deal but I just wanted to mention it in case I'm missing something.
In the PEFT PR, I went with |
Oh, we will need to handle that then. |
Yeah, I didn't version-guard yet. I think we may have to support both codepaths based on the
Do you see any easy way other than this? Or we could |
Hmm, I would have added a check for PEFT > 0.14.0 and
|
Well, it would certainly fail without |
Yes, it would mean that for the error to go away, users would need the next PEFT version. But we could also adopt the workaround for older PEFT versions and the new prefix-based solution if the installed PEFT version supports it. |
Yeah for the older peft version, I will defer to what I had in this PR (constant the attention modules and checking against them in the config update method). Do you have any other ideas? |
No, unfortunately not :( |
@BenjaminBossan ready for another review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
src/diffusers/loaders/peft.py
Outdated
@@ -54,6 +54,7 @@ | |||
"SanaTransformer2DModel": lambda model_cls, weights: weights, | |||
"Lumina2Transformer2DModel": lambda model_cls, weights: weights, | |||
} | |||
_NO_CONFIG_UPDATE_KEYS = ["to_k", "to_q", "to_v"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_NO_CONFIG_UPDATE_KEYS = ["to_k", "to_q", "to_v"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need it when PEFT version doesn't contain the required prefix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it
a possible fix for the issue in that function is storing deleted keys to ensure they aren't re-added by other iterations
If this issue reoccurs with other keys before minimum PEFT version is increased this can be applied
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a bit of a messy situation, but I think this PR finds a nice solution to the issue. LGTM. If the test script passes both with and without the new feature in PEFT, it's good to be merged.
src/diffusers/loaders/peft.py
Outdated
@@ -71,30 +74,27 @@ def _maybe_adjust_config(config): | |||
key_rank = rank_pattern[key] | |||
|
|||
# try to detect ambiguity | |||
# `target_modules` can also be a str, in which case this loop would loop |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any specific reason why this was removed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me add this back.
@AmericanPresidentJimmyCarter can you check if this solves #10804 |
after install diffusers@b0550a66cc3c882a1b88470df7e26103208b13de and trying to load Comfy LoRA (https://civitai.com/models/631986/xlabs-flux-realism-lora) - still getting
|
Could you provide a new issue thread as I just ran the xlabs integration tests and they are passing: diffusers/tests/lora/test_lora_layers_flux.py Line 911 in cc7b5b8
diffusers/tests/lora/test_lora_layers_flux.py Line 933 in cc7b5b8
(I ensured I pulled in the latest changes in |
Confirmed that it works. |
What does this PR do?
Fixes: #10804
Relies on huggingface/peft#2382
Code: