Uh oh!
There was an error while loading. Please reload this page.
Move IP Adapter Face ID to core - #7186
Conversation
jfischoff
commented
Mar 9, 2024
I'm getting the error when I try to use this. |
you cannot use Face ID with SDXL, the current changes only affect the Stable Diffusion pipeline |
fabiorigano
commented
Mar 9, 2024
@jfischoff you can use it now, I also updated the example code |
yiyixuxu
commented
Mar 9, 2024
@fabiorigano is this ready for a review? |
fabiorigano
commented
Mar 9, 2024
@yiyixuxu I have to add some checks on the inputs, but I would appreciate your feedback. thanks :) |
Since both Face ID adapter and Face ID XL don't use an image encoder, I tested the multi-adapter feature by separately extracting and then concatenating the image embeddings of Face ID XL and another IP Adapter, Plus Face SDXL. I think that Here it is the code of the test: # Create a SDXL pipeline# ...# Load sample imagesimage1=load_image("https://huggingface.co/datasets/YiYiXu/testing-images/resolve/main/ai_face2.png")
image2=load_image("https://huggingface.co/datasets/YiYiXu/testing-images/resolve/main/women_input.png")
# Extract Face features using insightfaceref_images= []
app=FaceAnalysis(name="buffalo_l", providers=['CUDAExecutionProvider', 'CPUExecutionProvider'])
app.prepare(ctx_id=0, det_size=(640, 640))
forimin [image1, image2]:
image=cv2.cvtColor(np.asarray(im), cv2.COLOR_BGR2RGB)
faces=app.get(image)
image=torch.from_numpy(faces[0].normed_embedding)
ref_images.append(image.unsqueeze(0))
ref_images=torch.cat(ref_images, dim=0)
# Load Face ID XL adapter into the pipelinepipeline.load_ip_adapter("h94/IP-Adapter-FaceID", subfolder=None, weight_name="ip-adapter-faceid_sdxl.bin", image_encoder_folder=None
)
# Generate Face ID image embeddings and save them locallyimage_embeds=pipeline.prepare_ip_adapter_image_embeds(
ip_adapter_image=ref_images,
ip_adapter_image_embeds=None,
device="cuda",
num_images_per_prompt=1,
do_classifier_free_guidance=True,
)
torch.save(image_embeds, "faceid_xl.ipadpt")
# Unload ip adapter and lora# ...# Load Plus SDXL adapter into the pipelinepipeline.load_ip_adapter("h94/IP-Adapter", subfolder="sdxl_models", weight_name="ip-adapter-plus-face_sdxl_vit-h.safetensors")
# Generate Plus SDXL image embeddings and save them locallyip_images=[[image1, image2]]
image_embeds=pipeline.prepare_ip_adapter_image_embeds(
ip_adapter_image=ip_images,
ip_adapter_image_embeds=None,
device="cuda",
num_images_per_prompt=1,
do_classifier_free_guidance=True,
)
torch.save(image_embeds, "plus_face_xl.ipadpt")
# Unload the IP adapter# ...# Load both IP Adapterspipeline.load_ip_adapter(["h94/IP-Adapter", "h94/IP-Adapter-FaceID"], subfolder=["sdxl_models", None], weight_name=["ip-adapter-plus-face_sdxl_vit-h.safetensors", "ip-adapter-faceid_sdxl.bin"]
)
pipeline.set_ip_adapter_scale([0.7]*2)
# Load image embeddings and run inferencegenerator=torch.Generator(device="cpu").manual_seed(42)
t1=torch.load("plus_face_xl.ipadpt")
t2=torch.load("faceid_xl.ipadpt")
t= [t1[0], t2[0]]
images=pipeline(
prompt="A photo of a girl wearing a black dress, holding red roses in hand, upper body, behind is the Eiffel Tower",
ip_adapter_image_embeds=t, guidance_scale=7.5,
negative_prompt="monochrome, lowres, bad anatomy, worst quality, low quality", num_inference_steps=30, num_images_per_prompt=num_images, width=1024, height=1024, generator=generator
).images |
yiyixuxu
commented
Mar 10, 2024
good news is that we do not want to support also, to make it easier to test, can you upload the |
can you combine face-id with other ip-adaper models? I thought it required its own attention processor |
fabiorigano
commented
Mar 10, 2024
@yiyixuxu I used PEFT to load the LoRA weights, so we don't need additional attention processors :) |
fabiorigano
commented
Mar 10, 2024
I uploaded some tensors here https://huggingface.co/datasets/fabiorigano/testing-images/tree/main Some of my tests and the results (input image embeddings are computed from "https://huggingface.co/datasets/YiYiXu/testing-images/resolve/main/ai_face2.png"): Face ID SD 1.5 onlypipeline.load_ip_adapter("h94/IP-Adapter-FaceID", subfolder=None, weight_name="ip-adapter-faceid_sd15.bin", image_encoder_folder=None)
pipeline.set_ip_adapter_scale(0.6)
image_embeds=load_pt("https://huggingface.co/datasets/fabiorigano/testing-images/resolve/main/ai_face2.ipadpt")
images=pipeline(
prompt="A photo of a girl wearing a black dress, holding red roses in hand, upper body, behind is the Eiffel Tower",
ip_adapter_image_embeds=image_embeds,
negative_prompt="monochrome, lowres, bad anatomy, worst quality, low quality", num_inference_steps=20, num_images_per_prompt=1, width=512, height=704, generator=torch.Generator(device="cpu").manual_seed(0)
).imagesPlus Face SD 1.5 onlypipeline.load_ip_adapter("h94/IP-Adapter", subfolder="models", weight_name="ip-adapter-plus-face_sd15.bin")
pipeline.set_ip_adapter_scale(0.6)
image_embeds=load_pt("https://huggingface.co/datasets/fabiorigano/testing-images/resolve/main/clip_ai_face2.ipadpt")
images=pipeline(
prompt="A photo of a girl wearing a black dress, holding red roses in hand, upper body, behind is the Eiffel Tower",
ip_adapter_image_embeds=image_embeds,
negative_prompt="monochrome, lowres, bad anatomy, worst quality, low quality", num_inference_steps=20, num_images_per_prompt=1, width=512, height=704, generator=torch.Generator(device="cpu").manual_seed(0)
).imagesPlus Face SD 1.5 + Face ID SD 1.5pipeline.load_ip_adapter(["h94/IP-Adapter", "h94/IP-Adapter-FaceID"], subfolder=["models", None], weight_name=["ip-adapter-plus-face_sd15.safetensors", "ip-adapter-faceid_sd15.bin"])
pipeline.set_ip_adapter_scale([0.5, 0.5])
t1=load_pt("https://huggingface.co/datasets/fabiorigano/testing-images/resolve/main/clip_ai_face2.ipadpt")
t2=load_pt("https://huggingface.co/datasets/fabiorigano/testing-images/resolve/main/ai_face2.ipadpt")
image_embeds= [t1[0], t2[0]]
images=pipeline(
prompt="A photo of a girl wearing a black dress, holding red roses in hand, upper body, behind is the Eiffel Tower",
ip_adapter_image_embeds=image_embeds,
negative_prompt="monochrome, lowres, bad anatomy, worst quality, low quality", num_inference_steps=20, num_images_per_prompt=1, width=512, height=704, generator=torch.Generator(device="cpu").manual_seed(0)
).images |
fabiorigano
commented
Mar 11, 2024
@yiyixuxu it is ready for review |
HuggingFaceDocBuilderDev
commented
Mar 12, 2024
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. |
yiyixuxu
left a comment
There was a problem hiding this comment.
thnaks!
I left some comments and questions
Uh oh!
There was an error while loading. Please reload this page.
| logger.warning( | ||
| "image_encoder is not loaded since `image_encoder_folder=None` passed. You will not be able to use `ip_adapter_image` when calling the pipeline with IP-Adapter." | ||
| "Use `ip_adapter_image_embeds` to pass pre-generated image embedding instead." | ||
| "image_encoder is not loaded since `image_encoder_folder=None` passed. `ip_adapter_image` is allowed only if you are loading an IP-Adapter Face ID model." |
There was a problem hiding this comment.
a little bit confused here - I thought it was the opposite, i.e. we do not allow using ip_adapter_image with the Face ID model.
There was a problem hiding this comment.
conceptually Face ID embeddings are image embeddings, but the tensor as it is doesn't have the unconditioned part, so in encode_image it is updated as is expected.
do you think it is better to leave this to the user?
There was a problem hiding this comment.
yes - let's make it clear on the doc how to create the ip_adapter_image_embedding for face-id
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
| ] | ||
| } | ||
| ) | ||
| key_id += 1 |
There was a problem hiding this comment.
are there more than one face-id checkpoints right now? does it make sense for us to support more than one?
There was a problem hiding this comment.
Face ID and Face ID XL are both supported by this PR
Face ID Plus models have different image projection layers
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
| """Forward pass. | ||
| Args: | ||
| ---- | ||
| id_embeds (torch.Tensor): Input Tensor (ID embeds). | ||
| Returns: | ||
| ------- | ||
| torch.Tensor: Output Tensor. | ||
| """ |
There was a problem hiding this comment.
I think this needs to follow our doc-string format?
There was a problem hiding this comment.
ok, I will update it (also IPAdapterPlusImageProjection for code consistency)?
| nn.LayerNorm(embed_dims), | ||
| nn.LayerNorm(embed_dims), | ||
| Attention( | ||
| query_dim=embed_dims, | ||
| dim_head=dim_head, | ||
| heads=heads, | ||
| out_bias=False, | ||
| ), | ||
| nn.Sequential( | ||
| nn.LayerNorm(embed_dims), | ||
| FeedForward(embed_dims, embed_dims, activation_fn="gelu", mult=ffn_ratio, bias=False), | ||
| ), |
There was a problem hiding this comment.
I don't have strong opinions here but perhaps we could create a small block consisting of these layers and use that block here instead. Then
for ln0, ln1, attn, ff in self.layers:
residual = latents
encoder_hidden_states = ln0(x)
latents = ln1(latents)
encoder_hidden_states = torch.cat([encoder_hidden_states, latents], dim=-2)
latents = attn(latents, encoder_hidden_states) + residual
latents = ff(latents) + latents
could become:
forblockinself.blocks:
...If the checkpoint needs to be rejigged to match this structure, we could have a load state dict hook to deal with the modifications. But I would wait for @yiyixuxu to comment further before making any changes.
There was a problem hiding this comment.
nice but I don't think it is a big deal
if it requires a lot of effort from @fabiorigano I don't think it's worth it
There was a problem hiding this comment.
Yeah totally fine by me. It was just a suggestion.
There was a problem hiding this comment.
hi, I added IPAdapterPlusImageProjectionBlock, let me know if it works for you
| # load ip-adapter into unet | ||
| unet = getattr(self, self.unet_name) if not hasattr(self, "unet") else self.unet | ||
| unet._load_ip_adapter_weights(state_dicts, low_cpu_mem_usage=low_cpu_mem_usage) | ||
| extra_loras = unet._load_ip_adapter_weights(state_dicts, low_cpu_mem_usage=low_cpu_mem_usage) |
There was a problem hiding this comment.
Interesting. To reduce the maintenance burden and to promote better readability, perhaps we could separate out the LoRA-related code from _load_ip_adapter_weights()?
sayakpaul
left a comment
There was a problem hiding this comment.
Thank you! Left a couple of comments.
Co-authored-by: Sayak Paul <spsayakpaul@gmail.com>
| extra_loras = unet._load_ip_adapter_loras(state_dicts) | ||
| if extra_loras != {}: | ||
| # apply the IP Adapter Face ID LoRA weights | ||
| peft_config = getattr(unet, "peft_config", {}) | ||
| for k, lora in extra_loras.items(): | ||
| if f"faceid_{k}" not in peft_config: | ||
| self.load_lora_weights(lora, adapter_name=f"faceid_{k}") | ||
| self.set_adapters([f"faceid_{k}"], adapter_weights=[1.0]) |
| heads=heads, | ||
| id_embeddings_dim=id_embeddings_dim, | ||
| ) | ||
| print(state_dict.keys()) |
| print(updated_state_dict.keys()) | ||
| print(image_projection.state_dict().keys()) |
| max_diff = numpy_cosine_similarity_distance(image_slice, expected_slice) | ||
| assert max_diff < 5e-4 | ||
| def test_text_to_image_face_id(self): |
There was a problem hiding this comment.
The PR is looking quite nice to me. Thanks a lot for working on it. Also, do we need to add a check like so
diffusers/src/diffusers/loaders/lora.py
Line 108 in cf6e040
when there's a call to use the IP Adapter Face ID weights?
I will defer to @yiyixuxu to merge this. I would just run the concerned slow tests on our CI infrastructure as well to ensure nothing's breaking. @yiyixuxu could you do that before merging?
fabiorigano
commented
Apr 16, 2024
I will add it |
yiyixuxu
commented
Apr 19, 2024
great work as always! thanks a lot :) @fabiorigano |
* Switch to peft and multi proj layers * Move Face ID loading and inference to core --------- Co-authored-by: Sayak Paul <spsayakpaul@gmail.com>



What does this PR do?
Fixes#7014#6935
@yiyixuxu@sayakpaul
Create face embeddings
IP Adapter Face ID (SD 1.5)
IP Adapter Face ID XL (SDXL)