Skip to content
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

[Enhancement] distinguish v1 from v2 LoRA models #3175

Merged
merged 16 commits into from
Apr 25, 2023

Conversation

lstein
Copy link
Collaborator

@lstein lstein commented Apr 11, 2023

Distinguish LoRA/LyCORIS files based on what version of SD they were built on top of

  • Attempting to run a prompt with a LoRA based on SD v1.X against a model based on v2.X will now throw an IncompatibleModelException. To import this exception:
    from ldm.modules.lora_manager import IncompatibleModelException (maybe this should be defined in ModelManager?)

  • Enhance LoraManager.list_loras() to accept an optional integer argument, token_vector_length. This will filter the returned LoRA models to return only those that match the indicated length. Use:
    768 => for models based on SD v1.X 1024 => for models based on SD v2.X
    Note that this filtering requires each LoRA file to be opened by torch.safetensors. It will take ~8s to scan a directory of 40 files.

  • Added new static methods to ldm.modules.kohya_lora_manager:
    - check_model_compatibility()
    - vector_length_from_checkpoint()
    - vector_length_from_checkpoint_file()

  • You can now create subdirectories within the loras directory and organize the model files.

- Previously the user's preferred precision was used to select which
  version branch of a diffusers model would be downloaded. Half-precision
  would try to download the 'fp16' branch if it existed.

- Turns out that with waifu-diffusion this logic doesn't work, as
  'fp16' gets you waifu-diffusion v1.3, while 'main' gets you
  waifu-diffusion v1.4. Who knew?

- This PR adds a new optional "revision" field to `models.yaml`. This
  can be used to override the diffusers branch version. In the case of
  Waifu diffusion, INITIAL_MODELS.yaml now specifies the "main" branch.

- This PR also quenches the NSFW nag that downloading diffusers sometimes
  triggers.

- Closes #3160
- Attempting to run a prompt with a LoRA based on SD v1.X against a
  model based on v2.X will now throw an
  `IncompatibleModelException`. To import this exception:
  `from ldm.modules.lora_manager import IncompatibleModelException`
  (maybe this should be defined in ModelManager?)

- Enhance `LoraManager.list_loras()` to accept an optional integer
  argument, `token_vector_length`. This will filter the returned LoRA
  models to return only those that match the indicated length. Use:
  ```
  768 => for models based on SD v1.X
  1024 => for models based on SD v2.X
  ```

  Note that this filtering requires each LoRA file to be opened
  by `torch.safetensors`. It will take ~8s to scan a directory of
  40 files.

- Added new static methods to `ldm.modules.kohya_lora_manager`:
  - check_model_compatibility()
  - vector_length_from_checkpoint()
  - vector_length_from_checkpoint_file()
@StAlKeR7779
Copy link
Contributor

Maybe create file with info about lora like sha file for checkpoint models?
So, first check will take a bit time, but if lora loaded in past, then just load info file.

@lstein
Copy link
Collaborator Author

lstein commented Apr 11, 2023

Maybe create file with info about lora like sha file for checkpoint models? So, first check will take a bit time, but if lora loaded in past, then just load info file.

H'mmm, makes sense. But we'd have to update the file everytime someone added or removed a LoRA file.

@StAlKeR7779
Copy link
Contributor

StAlKeR7779 commented Apr 11, 2023

Maybe create file with info about lora like sha file for checkpoint models? So, first check will take a bit time, but if lora loaded in past, then just load info file.

H'mmm, makes sense. But we'd have to update the file everytime someone added or removed a LoRA file.

No, i mean creating <lora_file>.info for every lora file.
At least i thinks about it at first, but to be more user friendly we can write it somewhere as a cache, hidden from loras folder. And how it will be done - single or multiple files - i'm not sure which option better.

I saw logic as:
On lora loading if no info exists, then write it.
On model loading check all loras info and if some lora doesn't have info file, then load it to create/get info.
If be paranoid about situation - user deleted lora file and pasted new with same name, i think we can include file length in info, so on checking we will compare this value with current file length and discard info if it's mismatch.(Can be checksum of file, but i don't know impact of calculating file checksum on performance)

@lstein
Copy link
Collaborator Author

lstein commented Apr 13, 2023

I've created a lora cache file in the top-level loras directory that holds information on the lora vector length. It speeds up scanning of the directory from 8s to 0.1s, and uses file locking to avoid one invokeai process overwriting the file while another invokeai has it open. It does not address the possibility that the user will delete the original lora file and replace it with another one of the exact same name derived from a different sized model.

@blessedcoolant I also implemented a routine called list_compatible_loras() that will return a dictionary of all the LoRAs that are compatible with the current model. The keys are the LoRA base name, and the values are the full path to the .safetensors file. I modified invokeai_web_server so that it calls this method in its getLoraModels event, and it works to the extent that when the loras first load they are filtered to be compatible with the initial model. However, I don't know how to force the list of loras to be reloaded whenever the model changes. Is this something you can do?

@blessedcoolant
Copy link
Collaborator

However, I don't know how to force the list of loras to be reloaded whenever the model changes. Is this something you can do?

You could try the following.

In listeners.ts find the onModelChanged function. Prior to dispatch(setIsProcessing(false)); add dispatch(getLoraModels()); and auto-import the getLoraModels function into the ifle..

This should effectively call the lora models again when a new model is loaded. And if the backend sends back only compatible models, it should work as intended.

@lstein
Copy link
Collaborator Author

lstein commented Apr 14, 2023

However, I don't know how to force the list of loras to be reloaded whenever the model changes. Is this something you can do?

You could try the following.

In listeners.ts find the onModelChanged function. Prior to dispatch(setIsProcessing(false)); add dispatch(getLoraModels()); and auto-import the getLoraModels function into the ifle..

This should effectively call the lora models again when a new model is loaded. And if the backend sends back only compatible models, it should work as intended.

Worked like a charm on the first try. Thanks so much!

I think this is PR is ready for a review now.

@blessedcoolant
Copy link
Collaborator

blessedcoolant commented Apr 14, 2023

@lstein The frontend is not built correctly. index.js is missing.

@lstein lstein requested a review from GreggHelt2 as a code owner April 20, 2023 16:25
@lstein
Copy link
Collaborator Author

lstein commented Apr 20, 2023

@lstein The frontend is not built correctly. index.js is missing.

I've rebuilt the frontend and added the missing .js file. @blessedcoolant Do you mind giving this PR a review? It should be working now.

@lstein lstein enabled auto-merge April 20, 2023 16:49
@lstein lstein disabled auto-merge April 25, 2023 02:55
@lstein lstein merged commit 994a76a into v2.3 Apr 25, 2023
@lstein lstein deleted the bugfix/lora-incompatibility-handling branch April 25, 2023 02:57
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.

3 participants