| import os |
| import math |
| from typing import List |
|
|
| import torch |
| import torch.nn as nn |
| from diffusers.pipelines.controlnet import MultiControlNetModel |
|
|
| from .attention_processor import MaskedIPAttnProcessor2_0 as IPAttnProcessor, AttnProcessor2_0 as AttnProcessor, CNAttnProcessor2_0 as CNAttnProcessor |
|
|
|
|
| class MSAdapter(torch.nn.Module): |
| def __init__(self, unet, image_proj_model, adapter_modules=None, ckpt_path=None, |
| num_tokens=4, text_tokens=77, max_rn=4, num_dummy_tokens=4, |
| device="cuda", controlnet=None): |
| super().__init__() |
| self.unet = unet |
| self.image_proj_model = image_proj_model |
| self.adapter_modules = adapter_modules |
| self.num_tokens = num_tokens |
| self.num_dummy_tokens = num_dummy_tokens |
| self.text_tokens = text_tokens |
| self.max_rn = max_rn |
| self.device = device |
| self.controlnet = controlnet |
| self.cross_attention_dim = self.unet.config.cross_attention_dim |
|
|
| |
| if self.adapter_modules is None: |
| self.set_ms_adapter() |
|
|
| |
| self.dummy_image_tokens = nn.Parameter(torch.randn(1, self.num_dummy_tokens, self.cross_attention_dim)) |
|
|
| if ckpt_path is not None: |
| self.load_from_checkpoint(ckpt_path) |
|
|
| def forward(self, noisy_latents, timesteps, encoder_hidden_states, unet_added_cond_kwargs, image_embeds, |
| rf_attention_mask=None, cross_attention_kwargs=None, grounding_kwargs=None): |
| bsz = encoder_hidden_states.shape[0] |
| if grounding_kwargs is None: |
| ip_tokens = self.image_proj_model(image_embeds) |
| else: |
| ip_tokens = self.image_proj_model(image_embeds, grounding_kwargs=grounding_kwargs) |
| |
| ip_tokens = ip_tokens.view(bsz, -1, ip_tokens.shape[-2], ip_tokens.shape[-1]) |
| total_num_tokens = ip_tokens.shape[-2] |
| ip_tokens = ip_tokens.view(bsz, ip_tokens.shape[-3] * ip_tokens.shape[-2], ip_tokens.shape[-1]) |
| dummy_image_tokens = self.dummy_image_tokens.repeat(bsz, 1, 1) |
| ip_tokens = torch.cat([dummy_image_tokens, ip_tokens], dim=1) |
| encoder_hidden_states = torch.cat([encoder_hidden_states, ip_tokens], dim=1) |
| encoder_attention_mask = None |
| if rf_attention_mask is not None: |
| attention_mask = torch.ones((bsz, self.text_tokens)).cuda() |
| rf_attention_mask = torch.repeat_interleave(rf_attention_mask, repeats=total_num_tokens, dim=1) |
| encoder_attention_mask = torch.cat([attention_mask, rf_attention_mask], dim=1) |
| |
| noise_pred = self.unet(noisy_latents, timesteps, encoder_hidden_states, |
| added_cond_kwargs=unet_added_cond_kwargs, |
| encoder_attention_mask=encoder_attention_mask, |
| cross_attention_kwargs=cross_attention_kwargs).sample |
| return noise_pred |
|
|
| def save_to_checkpoint(self, output_path: str): |
| if os.path.isdir(output_path): |
| os.makedirs(output_path, exist_ok=True) |
| output_path = os.path.join(output_path, "ms_adapter.bin") |
|
|
| state_dict = { |
| "image_proj": self.image_proj_model.state_dict(), |
| "ms_adapter": self.adapter_modules.state_dict(), |
| "dummy_image_tokens": self.dummy_image_tokens, |
| } |
| torch.save(state_dict, output_path) |
| print(f"Successfully saved weights to checkpoint {output_path}") |
|
|
| def load_from_checkpoint(self, ckpt_path: str): |
| if os.path.isdir(ckpt_path): |
| ckpt_path = os.path.join(ckpt_path, "ms_adapter.bin") |
|
|
| |
| orig_ip_proj_sum = torch.sum(torch.stack([torch.sum(p) for p in self.image_proj_model.parameters()])) |
| orig_adapter_sum = torch.sum(torch.stack([torch.sum(p) for p in self.adapter_modules.parameters()])) |
|
|
| state_dict = torch.load(ckpt_path, map_location="cpu") |
|
|
| |
| self.image_proj_model.load_state_dict(state_dict["image_proj"], strict=True) |
| self.adapter_modules.load_state_dict(state_dict["ms_adapter"], strict=True) |
| self.load_state_dict({"dummy_image_tokens": state_dict["dummy_image_tokens"]}, strict=False) |
|
|
| |
| new_ip_proj_sum = torch.sum(torch.stack([torch.sum(p) for p in self.image_proj_model.parameters()])) |
| new_adapter_sum = torch.sum(torch.stack([torch.sum(p) for p in self.adapter_modules.parameters()])) |
|
|
| |
| assert orig_ip_proj_sum != new_ip_proj_sum, "Weights of image_proj_model did not change!" |
| assert orig_adapter_sum != new_adapter_sum, "Weights of adapter_modules did not change!" |
|
|
| print(f"Successfully loaded weights from checkpoint {ckpt_path}") |
|
|
| def set_ms_adapter(self, weight_dtype=torch.float16, cache_attention_maps=True): |
| |
| attn_procs = {} |
| for name in self.unet.attn_processors.keys(): |
| cross_attention_dim = None if name.endswith("attn1.processor") else self.unet.config.cross_attention_dim |
| if name.startswith("mid_block"): |
| hidden_size = self.unet.config.block_out_channels[-1] |
| elif name.startswith("up_blocks"): |
| block_id = int(name[len("up_blocks.")]) |
| hidden_size = list(reversed(self.unet.config.block_out_channels))[block_id] |
| elif name.startswith("down_blocks"): |
| block_id = int(name[len("down_blocks.")]) |
| hidden_size = self.unet.config.block_out_channels[block_id] |
| if cross_attention_dim is None: |
| attn_procs[name] = AttnProcessor() |
| else: |
| attn_procs[name] = IPAttnProcessor( |
| hidden_size=hidden_size, |
| cross_attention_dim=cross_attention_dim, |
| num_tokens=self.num_tokens, |
| text_tokens=self.text_tokens, |
| ).to(self.device, dtype=weight_dtype) |
| self.unet.set_attn_processor(attn_procs) |
| self.adapter_modules = torch.nn.ModuleList(self.unet.attn_processors.values()) |
| if self.controlnet is not None: |
| if isinstance(self.controlnet, MultiControlNetModel): |
| for controlnet in self.controlnet.nets: |
| controlnet.set_attn_processor(CNAttnProcessor(text_tokens=self.text_tokens, num_tokens=self.num_tokens)) |
| else: |
| self.controlnet.set_attn_processor(CNAttnProcessor(text_tokens=self.text_tokens, num_tokens=self.num_tokens)) |
|
|
| @torch.inference_mode() |
| def get_image_embeds(self, processed_images, image_encoder=None, image_proj_type="linear", |
| image_encoder_type="clip", weight_dtype=torch.float16): |
| |
| |
| processed_images = processed_images.view(-1, processed_images.shape[-3], processed_images.shape[-2], |
| processed_images.shape[-1]) |
| if image_proj_type == "resampler": |
| image_embeds = image_encoder(processed_images.to(self.device, dtype=weight_dtype), |
| output_hidden_states=True).hidden_states[-2] |
| else: |
| image_embeds = image_encoder(processed_images.to(self.device, dtype=weight_dtype)).image_embeds |
|
|
| return image_embeds |
|
|
| def set_scale(self, scale, subject_scales): |
| for attn_processor in self.pipe.unet.attn_processors.values(): |
| if isinstance(attn_processor, IPAttnProcessor): |
| attn_processor.scale = scale |
| attn_processor.subject_scales = subject_scales |
|
|
| def enable_psuedo_attention_mask(self, mask_threshold=0.5, start_step=5): |
| for attn_processor in self.pipe.unet.attn_processors.values(): |
| if isinstance(attn_processor, IPAttnProcessor): |
| attn_processor.mask_threshold = mask_threshold |
| attn_processor.start_step = start_step |
| attn_processor.use_psuedo_attention_mask = True |
| attn_processor.need_text_attention_map = True |
| attn_processor.attention_maps = [] |
|
|
| def generate(self, pipe, pil_images=None, processed_images=None, prompt=None, negative_prompt=None, scale=1.0, |
| num_samples=4, seed=None, guidance_scale=7.5, num_inference_steps=50, image_processor=None, |
| image_encoder=None, image_proj_type="linear", image_encoder_type="clip", weight_dtype=torch.float16, |
| boxes=None, phrases=None, drop_grounding_tokens=None, phrase_idxes=None, |
| eot_idxes=None, height=1024, width=1024, subject_scales=None, mask_threshold=None, start_step=5, |
| **kwargs): |
| |
| self.pipe = pipe |
| self.set_scale(scale, subject_scales) |
| if mask_threshold is not None: |
| self.enable_psuedo_attention_mask(mask_threshold, start_step) |
|
|
| |
| bsz = len(pil_images) |
| if processed_images is None: |
| |
| processed_images = [] |
| for pil_image in pil_images: |
| processed_image = image_processor(images=pil_image, return_tensors="pt").pixel_values |
| processed_images.append(processed_image) |
| processed_images = torch.stack(processed_images, dim=0) |
|
|
| num_prompts = bsz |
| if prompt is None: |
| prompt = "best quality, high quality" |
| if negative_prompt is None: |
| negative_prompt = "monochrome, lowres, bad anatomy, worst quality, low quality" |
| if not isinstance(prompt, List): |
| prompt = [prompt] * num_prompts |
| if not isinstance(negative_prompt, List): |
| negative_prompt = [negative_prompt] * num_prompts |
|
|
| cross_attention_kwargs = None |
| grounding_kwargs = None |
| if boxes is not None: |
| boxes = torch.tensor(boxes).to(self.device, weight_dtype) |
| if phrases is not None: |
| drop_grounding_tokens = drop_grounding_tokens if drop_grounding_tokens is not None else [0]*bsz |
| batch_boxes = boxes.view(bsz*boxes.shape[1], -1) |
| |
| phrase_input_ids = [] |
| for phrase in phrases: |
| phrase_input_id = pipe.tokenizer(phrase, max_length=pipe.tokenizer.model_max_length, |
| padding="max_length", truncation=True, |
| return_tensors="pt").input_ids |
| phrase_input_ids.append(phrase_input_id) |
| phrase_input_ids = torch.stack(phrase_input_ids) |
| phrase_input_ids = phrase_input_ids.view(-1, phrase_input_ids.shape[-1]) |
| phrase_embeds = pipe.text_encoder(phrase_input_ids.to(self.device)).pooler_output |
| grounding_kwargs = {"boxes": batch_boxes, "phrase_embeds": phrase_embeds, "drop_grounding_tokens": drop_grounding_tokens} |
| else: |
| grounding_kwargs = None |
| boxes = torch.repeat_interleave(boxes, repeats=num_samples, dim=0) |
| uncond_boxes = torch.zeros_like(boxes) |
| boxes = torch.cat([uncond_boxes, boxes], dim=0) |
| cross_attention_kwargs = {"boxes": boxes} |
|
|
| if phrase_idxes is not None: |
| phrase_idxes = torch.tensor(phrase_idxes).to(self.device, torch.int) |
| eot_idxes = torch.tensor(eot_idxes).to(self.device, torch.int) |
| phrase_idxes = torch.repeat_interleave(phrase_idxes, repeats=num_samples, dim=0) |
| eot_idxes = torch.repeat_interleave(eot_idxes, repeats=num_samples, dim=0) |
| uncond_phrase_idxes = torch.zeros_like(phrase_idxes) |
| uncond_eot_idxes = torch.zeros_like(eot_idxes) |
| phrase_idxes = torch.cat([uncond_phrase_idxes, phrase_idxes], dim=0) |
| eot_idxes = torch.cat([uncond_eot_idxes, eot_idxes], dim=0) |
| if cross_attention_kwargs is None: |
| cross_attention_kwargs = {"phrase_idxes": phrase_idxes, "eot_idxes": eot_idxes} |
| else: |
| cross_attention_kwargs["phrase_idxes"] = phrase_idxes |
| cross_attention_kwargs["eot_idxes"] = eot_idxes |
|
|
| with torch.inference_mode(): |
| image_embeds = self.get_image_embeds(processed_images, image_encoder, image_proj_type=image_proj_type, |
| image_encoder_type=image_encoder_type, weight_dtype=weight_dtype) |
| image_prompt_embeds = self.image_proj_model(image_embeds, grounding_kwargs=grounding_kwargs) |
| image_prompt_embeds = image_prompt_embeds.view(bsz, -1, image_prompt_embeds.shape[-2], image_prompt_embeds.shape[-1]) |
| image_prompt_embeds = image_prompt_embeds.view(bsz, image_prompt_embeds.shape[-3] * image_prompt_embeds.shape[-2], |
| image_prompt_embeds.shape[-1]) |
| image_prompt_embeds = torch.cat([self.dummy_image_tokens, image_prompt_embeds], dim=1) |
| uncond_image_prompt_embeds = torch.zeros_like(image_prompt_embeds) |
| bs_embed, seq_len, _ = image_prompt_embeds.shape |
| image_prompt_embeds = image_prompt_embeds.repeat(1, num_samples, 1) |
| image_prompt_embeds = image_prompt_embeds.view(bs_embed * num_samples, seq_len, -1) |
| uncond_image_prompt_embeds = uncond_image_prompt_embeds.repeat(1, num_samples, 1) |
| uncond_image_prompt_embeds = uncond_image_prompt_embeds.view(bs_embed * num_samples, seq_len, -1) |
|
|
| prompt_embeds_, negative_prompt_embeds_, pooled_prompt_embeds, negative_pooled_prompt_embeds = pipe.encode_prompt( |
| prompt, |
| device=self.device, |
| num_images_per_prompt=num_samples, |
| do_classifier_free_guidance=True, |
| negative_prompt=negative_prompt, |
| ) |
| prompt_embeds = torch.cat([prompt_embeds_, image_prompt_embeds], dim=1) |
| negative_prompt_embeds = torch.cat([negative_prompt_embeds_, uncond_image_prompt_embeds], dim=1) |
|
|
| generator = torch.Generator(self.device).manual_seed(seed) if seed is not None else None |
| images = pipe( |
| prompt_embeds=prompt_embeds, |
| negative_prompt_embeds=negative_prompt_embeds, |
| pooled_prompt_embeds=pooled_prompt_embeds, |
| negative_pooled_prompt_embeds=negative_pooled_prompt_embeds, |
| guidance_scale=guidance_scale, |
| num_inference_steps=num_inference_steps, |
| generator=generator, |
| cross_attention_kwargs=cross_attention_kwargs, |
| height=height, |
| width=width, |
| **kwargs, |
| ).images |
|
|
| return images |
|
|