File size: 15,639 Bytes
8a5ad73
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
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

        # set attention processor when inference
        if self.adapter_modules is None:
            self.set_ms_adapter()

        # dummy image tokens
        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)  # (bsz*rn, num_tokens, cross_attention_dim)
        else:
            ip_tokens = self.image_proj_model(image_embeds, grounding_kwargs=grounding_kwargs)
        # concat multiple images tokens
        ip_tokens = ip_tokens.view(bsz, -1, ip_tokens.shape[-2], ip_tokens.shape[-1])  # (bsz, rn, num_tokens, cross_attention_dim)
        total_num_tokens = ip_tokens.shape[-2]  # num_tokens
        ip_tokens = ip_tokens.view(bsz, ip_tokens.shape[-3] * ip_tokens.shape[-2], ip_tokens.shape[-1])  # (bsz, total_num_tokens*rn, cross_attention_dim)
        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)
        # Predict the noise residual
        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")

        # Calculate original checksums
        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")

        # Load state dict for image_proj_model and adapter_modules when using resampler
        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)

        # Calculate new checksums
        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()]))

        # Verify if the weights have changed
        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):
        # set attention processor
        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):
        # get image embeds
        # processed_images: [bsz, rn, ...]
        processed_images = processed_images.view(-1, processed_images.shape[-3], processed_images.shape[-2],
                                                 processed_images.shape[-1])  # (bsz*rn, ...)
        if image_proj_type == "resampler":
            image_embeds = image_encoder(processed_images.to(self.device, dtype=weight_dtype),
                                         output_hidden_states=True).hidden_states[-2]  # (bsz*rn, num_tokens, embedding_dim)
        else:
            image_embeds = image_encoder(processed_images.to(self.device, dtype=weight_dtype)).image_embeds  # (bsz*rn, embedding_dim)

        return image_embeds  # [bsz*rn, ...]

    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 = []  # clear 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):
        # generate images (validation&inference)
        self.pipe = pipe
        self.set_scale(scale, subject_scales)
        if mask_threshold is not None:
            self.enable_psuedo_attention_mask(mask_threshold, start_step)

        # pil_images: [[xxx, xxx, xxx], [xxx, xxx, xxx], ...]
        bsz = len(pil_images)  # only support bsz=1 now
        if processed_images is None:
            # write in this way to promise it can be extended to batch in the future
            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"  # duplicate
        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)
                # write in this way to promise it can be extended to batch in the future
                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])  # (bsz, rn, num_tokens, cross_attention_dim)
            image_prompt_embeds = image_prompt_embeds.view(bsz, image_prompt_embeds.shape[-3] * image_prompt_embeds.shape[-2],
                                                           image_prompt_embeds.shape[-1])  # (bsz, total_num_tokens*rn, cross_attention_dim)
            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