├── LICENSE ├── README.md ├── bounded_attention.py ├── images ├── example.jpg └── teaser.jpg ├── injection_utils.py ├── pipeline_stable_diffusion_opt.py ├── pipeline_stable_diffusion_xl_opt.py ├── requirements.txt ├── run_sd.py ├── run_xl.py └── utils.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Omer Dahary 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Be Yourself: Bounded Attention for Multi-Subject Text-to-Image Generation (ECCV 2024) 2 | 3 | > **Omer Dahary, Or Patashnik, Kfir Aberman, Daniel Cohen-Or** 4 | > 5 | > Text-to-image diffusion models have an unprecedented ability to generate diverse and high-quality images. However, they often struggle to faithfully capture the intended semantics of complex input prompts that include multiple subjects. Recently, numerous layout-to-image extensions have been introduced to improve user control, aiming to localize subjects represented by specific tokens. Yet, these methods often produce semantically inaccurate images, especially when dealing with multiple semantically or visually similar subjects. In this work, we study and analyze the causes of these limitations. Our exploration reveals that the primary issue stems from inadvertent semantic leakage between subjects in the denoising process. This leakage is attributed to the diffusion model’s attention layers, which tend to blend the visual features of different subjects. To address these issues, we introduce Bounded Attention, a training-free method for bounding the information flow in the sampling process. Bounded Attention prevents detrimental leakage among subjects and enables guiding the generation to promote each subject's individuality, even with complex multi-subject conditioning. Through extensive experimentation, we demonstrate that our method empowers the generation of multiple subjects that better align with given prompts and layouts. 6 | 7 | 8 | 9 | [![Hugging Face Spaces](https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Spaces-blue)](https://huggingface.co/spaces/omer11a/bounded-attention) 10 | 11 |

12 | 13 |

14 | 15 | ## Description 16 | Official implementation of our "Be Yourself: Bounded Attention for Multi-Subject Text-to-Image Generation" paper. 17 | 18 | ## Setup 19 | 20 | ### Environment 21 | 22 | To set up the environment, run: 23 | 24 | ``` 25 | conda create --name bounded-attention python=3.11.4 26 | conda activate bounded-attention 27 | pip install -r requirements.txt 28 | ``` 29 | 30 | Then, run in Python: 31 | 32 | ``` 33 | import nltk 34 | nltk.download('averaged_perceptron_tagger') 35 | ``` 36 | 37 | ### Demo 38 | 39 | This project has a gradio [demo](https://huggingface.co/spaces/omer11a/bounded-attention) deployed in HuggingFace. 40 | To run the demo locally, run the following: 41 | ```shell 42 | gradio app.py 43 | ``` 44 | Then, you can connect to the local demo by browsing to `http://localhost:7860/`. 45 | 46 | ## Usage 47 | 48 |

49 | 50 |
51 | Example generations by SDXL with and without Bounded Attention. 52 |

53 | 54 | ### Basics 55 | 56 | To generate images, you can run `run_xl.py` for our SDXL version, and `run_sd.py` for our Stable Diffusion version. 57 | In each script, we call the `run` function to generate the images. E.g., 58 | ``` 59 | boxes = [ 60 | [0.35, 0.4, 0.65, 0.9], 61 | [0, 0.6, 0.3, 0.9], 62 | [0.7, 0.55, 1, 0.85], 63 | ] 64 | 65 | prompt = "3 D Pixar animation of a cute unicorn and a pink hedgehog and a nerdy owl traveling in a magical forest" 66 | subject_token_indices = [[7, 8, 17], [11, 12, 17], [15, 16, 17]] 67 | 68 | run(boxes, prompt, subject_token_indices, init_step_size=25, final_step_size=10) 69 | ``` 70 | 71 | The `run` function receives the following parameters: 72 | - boxes: the bounding box of each subject in the format [(x0, y0, x1, y1), ...], where x=0,y=0 represents the top-left corner of the image, and x=1,y=1 represents the bottom-right corner. 73 | - prompt: the textual prompt. 74 | - subject_token_indices: The indices of each token belonging to each subject, where the indices start from 1. Tokens can be shared between subjects. 75 | - out_dir: The output directory. Defaults to "out". 76 | - seed: The random seed. 77 | - batch_size: The number of generated images. 78 | - filter_token_indices: The indices of the tokens to ignore. This is automatically inferred, but we recommend explicitly ignoring prepositions, numbers and positional relations. 79 | - eos_token_index: The index of the EOS token (the first padding token appended to the end of the prompt). This is automatically inferred, but we recommend explicitly passing it, as we use it to verify you have correctly counted the number of tokens. 80 | 81 | ### Advanced options 82 | 83 | The `run` function also supports the following optional hyperparameters: 84 | 85 | - init_step_size: The initial step size of the linear step size scheduler when performing guidance. 86 | - final_step_size: The final step size of the linear step size scheduler when performing guidance. 87 | - num_clusters_per_subject: The number of clusters computed when clustering the self-attention maps (#clusters = #subject x #clusters_per_subject). Changing this value might improve semantics (adherence to the prompt), especially when the subjects exceed their bounding boxes. 88 | - cross_loss_scale: The scale factor of the cross-attention loss term. Increasing it will improve semantic control (adherence to the prompt), but may reduce image quality. 89 | - self_loss_scale: The scale factor of the self-attention loss term. Increasing it will improve layout control (adherence to the bounding boxes), but may reduce image quality. 90 | - classifier_free_guidance_scale: The scale factor of classifier-free guidance. 91 | - num_guidance_steps: The number of timesteps in which to perform guidance. Decreasing this also decreases the runtime. 92 | - first_refinement_step: The timestep from which subject mask refinement is performed. 93 | - num_gd_iterations: The number of Gradient Descent iterations for each timestep when performing guidance. 94 | - loss_threshold: If the loss is below the threshold, Gradient Descent stops for that timestep. 95 | 96 | ## Acknowledgements 97 | 98 | This code was built using the code from the following repositories: 99 | - [diffusers](https://github.com/huggingface/diffusers) 100 | - [Prompt-to-Prompt](https://github.com/google/prompt-to-prompt/) 101 | - [MasaCtrl](https://github.com/TencentARC/MasaCtrl) 102 | 103 | ## Citation 104 | 105 | If you use this code for your research, please cite our paper: 106 | 107 | ``` 108 | @misc{dahary2024yourself, 109 | title={Be Yourself: Bounded Attention for Multi-Subject Text-to-Image Generation}, 110 | author={Omer Dahary and Or Patashnik and Kfir Aberman and Daniel Cohen-Or}, 111 | year={2024}, 112 | eprint={2403.16990}, 113 | archivePrefix={arXiv}, 114 | primaryClass={cs.CV} 115 | } 116 | ``` 117 | -------------------------------------------------------------------------------- /bounded_attention.py: -------------------------------------------------------------------------------- 1 | import nltk 2 | import einops 3 | import torch 4 | import torch.nn.functional as F 5 | import torchvision.utils 6 | from torch_kmeans import KMeans 7 | 8 | import os 9 | 10 | import injection_utils 11 | import utils 12 | 13 | 14 | class BoundedAttention(injection_utils.AttentionBase): 15 | EPSILON = 1e-5 16 | FILTER_TAGS = { 17 | 'CC', 'CD', 'DT', 'EX', 'IN', 'LS', 'MD', 'PDT', 'POS', 'PRP', 'PRP$', 'RP', 'TO', 'UH', 'WDT', 'WP', 'WRB'} 18 | TAG_RULES = {'left': 'IN', 'right': 'IN', 'top': 'IN', 'bottom': 'IN'} 19 | 20 | def __init__( 21 | self, 22 | boxes, 23 | prompts, 24 | subject_token_indices, 25 | cross_loss_layers, 26 | self_loss_layers, 27 | cross_mask_layers=None, 28 | self_mask_layers=None, 29 | eos_token_index=None, 30 | filter_token_indices=None, 31 | leading_token_indices=None, 32 | mask_cross_during_guidance=True, 33 | mask_eos=True, 34 | cross_loss_coef=1.5, 35 | self_loss_coef=0.5, 36 | max_guidance_iter=15, 37 | max_guidance_iter_per_step=5, 38 | start_step_size=18, 39 | end_step_size=5, 40 | loss_stopping_value=0.2, 41 | min_clustering_step=15, 42 | cross_mask_threshold=0.2, 43 | self_mask_threshold=0.2, 44 | delta_refine_mask_steps=5, 45 | pca_rank=None, 46 | num_clusters=None, 47 | num_clusters_per_box=3, 48 | max_resolution=None, 49 | map_dir=None, 50 | debug=False, 51 | delta_debug_attention_steps=20, 52 | delta_debug_mask_steps=5, 53 | debug_layers=None, 54 | saved_resolution=64, 55 | ): 56 | super().__init__() 57 | self.boxes = boxes 58 | self.prompts = prompts 59 | self.subject_token_indices = subject_token_indices 60 | self.cross_loss_layers = set(cross_loss_layers) 61 | self.self_loss_layers = set(self_loss_layers) 62 | self.cross_mask_layers = self.cross_loss_layers if cross_mask_layers is None else set(cross_mask_layers) 63 | self.self_mask_layers = self.self_loss_layers if self_mask_layers is None else set(self_mask_layers) 64 | 65 | self.eos_token_index = eos_token_index 66 | self.filter_token_indices = filter_token_indices 67 | self.leading_token_indices = leading_token_indices 68 | self.mask_cross_during_guidance = mask_cross_during_guidance 69 | self.mask_eos = mask_eos 70 | self.cross_loss_coef = cross_loss_coef 71 | self.self_loss_coef = self_loss_coef 72 | self.max_guidance_iter = max_guidance_iter 73 | self.max_guidance_iter_per_step = max_guidance_iter_per_step 74 | self.start_step_size = start_step_size 75 | self.step_size_coef = (end_step_size - start_step_size) / max_guidance_iter 76 | self.loss_stopping_value = loss_stopping_value 77 | self.min_clustering_step = min_clustering_step 78 | self.cross_mask_threshold = cross_mask_threshold 79 | self.self_mask_threshold = self_mask_threshold 80 | 81 | self.delta_refine_mask_steps = delta_refine_mask_steps 82 | self.pca_rank = pca_rank 83 | num_clusters = len(boxes) * num_clusters_per_box if num_clusters is None else num_clusters 84 | self.clustering = KMeans(n_clusters=num_clusters, num_init=100) 85 | self.centers = None 86 | 87 | self.max_resolution = max_resolution 88 | self.map_dir = map_dir 89 | self.debug = debug 90 | self.delta_debug_attention_steps = delta_debug_attention_steps 91 | self.delta_debug_mask_steps = delta_debug_mask_steps 92 | self.debug_layers = self.cross_loss_layers | self.self_loss_layers if debug_layers is None else debug_layers 93 | self.saved_resolution = saved_resolution 94 | 95 | self.optimized = False 96 | self.cross_foreground_values = [] 97 | self.self_foreground_values = [] 98 | self.cross_background_values = [] 99 | self.self_background_values = [] 100 | self.mean_cross_map = 0 101 | self.num_cross_maps = 0 102 | self.mean_self_map = 0 103 | self.num_self_maps = 0 104 | self.self_masks = None 105 | 106 | def clear_values(self, include_maps=False): 107 | lists = ( 108 | self.cross_foreground_values, 109 | self.self_foreground_values, 110 | self.cross_background_values, 111 | self.self_background_values, 112 | ) 113 | 114 | for values in lists: 115 | values.clear() 116 | 117 | if include_maps: 118 | self.mean_cross_map = 0 119 | self.num_cross_maps = 0 120 | self.mean_self_map = 0 121 | self.num_self_maps = 0 122 | 123 | def before_step(self): 124 | self.clear_values() 125 | if self.cur_step == 0: 126 | self._determine_tokens() 127 | 128 | def reset(self): 129 | self.clear_values(include_maps=True) 130 | super().reset() 131 | 132 | def forward(self, q, k, v, is_cross, place_in_unet, num_heads, **kwargs): 133 | batch_size = q.size(0) // num_heads 134 | n = q.size(1) 135 | d = k.size(1) 136 | dtype = q.dtype 137 | device = q.device 138 | if is_cross: 139 | masks = self._hide_other_subjects_from_tokens(batch_size // 2, n, d, dtype, device) 140 | else: 141 | masks = self._hide_other_subjects_from_subjects(batch_size // 2, n, dtype, device) 142 | 143 | resolution = int(n ** 0.5) 144 | if (self.max_resolution is not None) and (resolution > self.max_resolution): 145 | return super().forward(q, k, v, is_cross, place_in_unet, num_heads, mask=masks) 146 | 147 | sim = torch.einsum('b i d, b j d -> b i j', q, k) * kwargs['scale'] 148 | attn = sim.softmax(-1) 149 | self._display_attention_maps(attn, is_cross, num_heads) 150 | sim = sim.reshape(batch_size, num_heads, n, d) + masks 151 | attn = sim.reshape(-1, n, d).softmax(-1) 152 | self._save(attn, is_cross, num_heads) 153 | self._display_attention_maps(attn, is_cross, num_heads, prefix='masked') 154 | self._debug_hook(q, k, v, sim, attn, is_cross, place_in_unet, num_heads, **kwargs) 155 | out = torch.bmm(attn, v) 156 | return einops.rearrange(out, '(b h) n d -> b n (h d)', h=num_heads) 157 | 158 | def update_loss(self, forward_pass, latents, i): 159 | if i >= self.max_guidance_iter: 160 | return latents 161 | 162 | step_size = self.start_step_size + self.step_size_coef * i 163 | 164 | self.optimized = True 165 | normalized_loss = torch.tensor(10000) 166 | with torch.enable_grad(): 167 | latents = latents.clone().detach().requires_grad_(True) 168 | for guidance_iter in range(self.max_guidance_iter_per_step): 169 | if normalized_loss < self.loss_stopping_value: 170 | break 171 | 172 | latent_model_input = torch.cat([latents] * 2) 173 | cur_step = self.cur_step 174 | forward_pass(latent_model_input) 175 | self.cur_step = cur_step 176 | 177 | loss, normalized_loss = self._compute_loss() 178 | grad_cond = torch.autograd.grad(loss, [latents])[0] 179 | latents = latents - step_size * grad_cond 180 | if self.debug: 181 | print(f'Loss at step={i}, iter={guidance_iter}: {normalized_loss}') 182 | grad_norms = grad_cond.flatten(start_dim=2).norm(dim=1) 183 | grad_norms = grad_norms / grad_norms.max(dim=1, keepdim=True)[0] 184 | self._save_maps(grad_norms, 'grad_norms') 185 | 186 | self.optimized = False 187 | return latents 188 | 189 | def _tokenize(self): 190 | ids = self.model.tokenizer.encode(self.prompts[0]) 191 | tokens = self.model.tokenizer.convert_ids_to_tokens(ids, skip_special_tokens=True) 192 | return [token[:-4] for token in tokens] # remove ending 193 | 194 | def _tag_tokens(self): 195 | tagged_tokens = nltk.pos_tag(self._tokenize()) 196 | return [type(self).TAG_RULES.get(token, tag) for token, tag in tagged_tokens] 197 | 198 | def _determine_eos_token(self): 199 | tokens = self._tokenize() 200 | eos_token_index = len(tokens) + 1 201 | if self.eos_token_index is None: 202 | self.eos_token_index = eos_token_index 203 | elif eos_token_index != self.eos_token_index: 204 | raise ValueError(f'Wrong EOS token index. Tokens are: {tokens}.') 205 | 206 | def _determine_filter_tokens(self): 207 | if self.filter_token_indices is not None: 208 | return 209 | 210 | tags = self._tag_tokens() 211 | self.filter_token_indices = [i + 1 for i, tag in enumerate(tags) if tag in type(self).FILTER_TAGS] 212 | 213 | def _determine_leading_tokens(self): 214 | if self.leading_token_indices is not None: 215 | return 216 | 217 | tags = self._tag_tokens() 218 | leading_token_indices = [] 219 | for indices in self.subject_token_indices: 220 | subject_noun_indices = [i for i in indices if tags[i - 1].startswith('NN')] 221 | leading_token_candidates = subject_noun_indices if len(subject_noun_indices) > 0 else indices 222 | leading_token_indices.append(leading_token_candidates[-1]) 223 | 224 | self.leading_token_indices = leading_token_indices 225 | 226 | def _determine_tokens(self): 227 | self._determine_eos_token() 228 | self._determine_filter_tokens() 229 | self._determine_leading_tokens() 230 | 231 | def _split_references(self, tensor, num_heads): 232 | tensor = tensor.reshape(-1, num_heads, *tensor.shape[1:]) 233 | unconditional, conditional = tensor.chunk(2) 234 | 235 | num_subjects = len(self.boxes) 236 | batch_unconditional = unconditional[:-num_subjects] 237 | references_unconditional = unconditional[-num_subjects:] 238 | batch_conditional = conditional[:-num_subjects] 239 | references_conditional = conditional[-num_subjects:] 240 | 241 | batch = torch.cat((batch_unconditional, batch_conditional)) 242 | references = torch.cat((references_unconditional, references_conditional)) 243 | batch = batch.reshape(-1, *batch_unconditional.shape[2:]) 244 | references = references.reshape(-1, *references_unconditional.shape[2:]) 245 | return batch, references 246 | 247 | def _hide_other_subjects_from_tokens(self, batch_size, n, d, dtype, device): # b h i j 248 | resolution = int(n ** 0.5) 249 | subject_masks, background_masks = self._obtain_masks(resolution, batch_size=batch_size, device=device) # b s n 250 | include_background = self.optimized or (not self.mask_cross_during_guidance and self.cur_step < self.max_guidance_iter_per_step) 251 | subject_masks = torch.logical_or(subject_masks, background_masks.unsqueeze(1)) if include_background else subject_masks 252 | min_value = torch.finfo(dtype).min 253 | sim_masks = torch.zeros((batch_size, n, d), dtype=dtype, device=device) # b i j 254 | for token_indices in (*self.subject_token_indices, self.filter_token_indices): 255 | sim_masks[:, :, token_indices] = min_value 256 | 257 | for batch_index in range(batch_size): 258 | for subject_mask, token_indices in zip(subject_masks[batch_index], self.subject_token_indices): 259 | for token_index in token_indices: 260 | sim_masks[batch_index, subject_mask, token_index] = 0 261 | 262 | if self.mask_eos and not include_background: 263 | for batch_index, background_mask in zip(range(batch_size), background_masks): 264 | sim_masks[batch_index, background_mask, self.eos_token_index] = min_value 265 | 266 | return torch.cat((torch.zeros_like(sim_masks), sim_masks)).unsqueeze(1) 267 | 268 | def _hide_other_subjects_from_subjects(self, batch_size, n, dtype, device): # b h i j 269 | resolution = int(n ** 0.5) 270 | subject_masks, background_masks = self._obtain_masks(resolution, batch_size=batch_size, device=device) # b s n 271 | min_value = torch.finfo(dtype).min 272 | sim_masks = torch.zeros((batch_size, n, n), dtype=dtype, device=device) # b i j 273 | for batch_index, background_mask in zip(range(batch_size), background_masks): 274 | all_subject_mask = ~background_mask.unsqueeze(0) * ~background_mask.unsqueeze(1) 275 | sim_masks[batch_index, all_subject_mask] = min_value 276 | 277 | for batch_index in range(batch_size): 278 | for subject_mask in subject_masks[batch_index]: 279 | subject_sim_mask = sim_masks[batch_index, subject_mask] 280 | condition = torch.logical_or(subject_sim_mask == 0, subject_mask.unsqueeze(0)) 281 | sim_masks[batch_index, subject_mask] = torch.where(condition, 0, min_value).to(dtype=dtype) 282 | 283 | return torch.cat((sim_masks, sim_masks)).unsqueeze(1) 284 | 285 | def _save(self, attn, is_cross, num_heads): 286 | _, attn = attn.chunk(2) 287 | attn = attn.reshape(-1, num_heads, *attn.shape[-2:]) # b h n k 288 | 289 | self._save_mask_maps(attn, is_cross) 290 | self._save_loss_values(attn, is_cross) 291 | 292 | def _save_mask_maps(self, attn, is_cross): 293 | if ( 294 | (self.optimized) or 295 | (is_cross and self.cur_att_layer not in self.cross_mask_layers) or 296 | ((not is_cross) and (self.cur_att_layer not in self.self_mask_layers)) 297 | ): 298 | return 299 | 300 | if is_cross: 301 | attn = attn[..., self.leading_token_indices] 302 | mean_map = self.mean_cross_map 303 | num_maps = self.num_cross_maps 304 | else: 305 | mean_map = self.mean_self_map 306 | num_maps = self.num_self_maps 307 | 308 | num_maps += 1 309 | attn = attn.mean(dim=1) # mean over heads 310 | mean_map = ((num_maps - 1) / num_maps) * mean_map + (1 / num_maps) * attn 311 | if is_cross: 312 | self.mean_cross_map = mean_map 313 | self.num_cross_maps = num_maps 314 | else: 315 | self.mean_self_map = mean_map 316 | self.num_self_maps = num_maps 317 | 318 | def _save_loss_values(self, attn, is_cross): 319 | if ( 320 | (not self.optimized) or 321 | (is_cross and (self.cur_att_layer not in self.cross_loss_layers)) or 322 | ((not is_cross) and (self.cur_att_layer not in self.self_loss_layers)) 323 | ): 324 | return 325 | 326 | resolution = int(attn.size(2) ** 0.5) 327 | boxes = self._convert_boxes_to_masks(resolution, device=attn.device) # s n 328 | background_mask = boxes.sum(dim=0) == 0 329 | 330 | if is_cross: 331 | saved_foreground_values = self.cross_foreground_values 332 | saved_background_values = self.cross_background_values 333 | contexts = [indices + [self.eos_token_index] for indices in self.subject_token_indices] # TODO: fix EOS loss term 334 | else: 335 | saved_foreground_values = self.self_foreground_values 336 | saved_background_values = self.self_background_values 337 | contexts = boxes 338 | 339 | foreground_values = [] 340 | background_values = [] 341 | for i, (box, context) in enumerate(zip(boxes, contexts)): 342 | context_attn = attn[:, :, :, context] 343 | 344 | # sum over heads, pixels and contexts 345 | foreground_values.append(context_attn[:, :, box].sum(dim=(1, 2, 3))) 346 | background_values.append(context_attn[:, :, background_mask].sum(dim=(1, 2, 3))) 347 | 348 | saved_foreground_values.append(torch.stack(foreground_values, dim=1)) 349 | saved_background_values.append(torch.stack(background_values, dim=1)) 350 | 351 | def _compute_loss(self): 352 | cross_losses = self._compute_loss_term(self.cross_foreground_values, self.cross_background_values) 353 | self_losses = self._compute_loss_term(self.self_foreground_values, self.self_background_values) 354 | b, s = cross_losses.shape 355 | 356 | # sum over samples and subjects 357 | total_cross_loss = cross_losses.sum() 358 | total_self_loss = self_losses.sum() 359 | 360 | loss = self.cross_loss_coef * total_cross_loss + self.self_loss_coef * total_self_loss 361 | normalized_loss = loss / b / s 362 | return loss, normalized_loss 363 | 364 | def _compute_loss_term(self, foreground_values, background_values): 365 | # mean over layers 366 | mean_foreground = torch.stack(foreground_values).mean(dim=0) 367 | mean_background = torch.stack(background_values).mean(dim=0) 368 | iou = mean_foreground / (mean_foreground + len(self.boxes) * mean_background) 369 | return (1 - iou) ** 2 370 | 371 | def _obtain_masks(self, resolution, return_boxes=False, return_existing=False, batch_size=None, device=None): 372 | return_boxes = return_boxes or (return_existing and self.self_masks is None) 373 | if return_boxes or self.cur_step < self.min_clustering_step: 374 | masks = self._convert_boxes_to_masks(resolution, device=device).unsqueeze(0) 375 | if batch_size is not None: 376 | masks = masks.expand(batch_size, *masks.shape[1:]) 377 | else: 378 | masks = self._obtain_self_masks(resolution, return_existing=return_existing) 379 | if device is not None: 380 | masks = masks.to(device=device) 381 | 382 | background_mask = masks.sum(dim=1) == 0 383 | return masks, background_mask 384 | 385 | def _convert_boxes_to_masks(self, resolution, device=None): # s n 386 | boxes = torch.zeros(len(self.boxes), resolution, resolution, dtype=bool, device=device) 387 | for i, box in enumerate(self.boxes): 388 | x0, x1 = box[0] * resolution, box[2] * resolution 389 | y0, y1 = box[1] * resolution, box[3] * resolution 390 | 391 | boxes[i, round(y0) : round(y1), round(x0) : round(x1)] = True 392 | 393 | return boxes.flatten(start_dim=1) 394 | 395 | def _obtain_self_masks(self, resolution, return_existing=False): 396 | if ( 397 | (self.self_masks is None) or 398 | ( 399 | (self.cur_step % self.delta_refine_mask_steps == 0) and 400 | (self.cur_att_layer == 0) and 401 | (not return_existing) 402 | ) 403 | ): 404 | self.self_masks = self._fix_zero_masks(self._build_self_masks()) 405 | 406 | b, s, n = self.self_masks.shape 407 | mask_resolution = int(n ** 0.5) 408 | self_masks = self.self_masks.reshape(b, s, mask_resolution, mask_resolution).float() 409 | self_masks = F.interpolate(self_masks, resolution, mode='nearest-exact') 410 | return self_masks.flatten(start_dim=2).bool() 411 | 412 | def _build_self_masks(self): 413 | c, clusters = self._cluster_self_maps() # b n 414 | cluster_masks = torch.stack([(clusters == cluster_index) for cluster_index in range(c)], dim=2) # b n c 415 | cluster_area = cluster_masks.sum(dim=1, keepdim=True) # b 1 c 416 | 417 | n = clusters.size(1) 418 | resolution = int(n ** 0.5) 419 | cross_masks = self._obtain_cross_masks(resolution) # b s n 420 | cross_mask_area = cross_masks.sum(dim=2, keepdim=True) # b s 1 421 | 422 | intersection = torch.bmm(cross_masks.float(), cluster_masks.float()) # b s c 423 | min_area = torch.minimum(cross_mask_area, cluster_area) # b s c 424 | score_per_cluster, subject_per_cluster = torch.max(intersection / min_area, dim=1) # b c 425 | subjects = torch.gather(subject_per_cluster, 1, clusters) # b n 426 | scores = torch.gather(score_per_cluster, 1, clusters) # b n 427 | 428 | s = cross_masks.size(1) 429 | self_masks = torch.stack([(subjects == subject_index) for subject_index in range(s)], dim=1) # b s n 430 | scores = scores.unsqueeze(1).expand(-1 ,s, n) # b s n 431 | self_masks[scores < self.self_mask_threshold] = False 432 | self._save_maps(self_masks, 'self_masks') 433 | return self_masks 434 | 435 | def _cluster_self_maps(self): # b s n 436 | self_maps = self._compute_maps(self.mean_self_map) # b n m 437 | if self.pca_rank is not None: 438 | dtype = self_maps.dtype 439 | _, _, eigen_vectors = torch.pca_lowrank(self_maps.float(), self.pca_rank) 440 | self_maps = torch.matmul(self_maps, eigen_vectors.to(dtype=dtype)) 441 | 442 | clustering_results = self.clustering(self_maps, centers=self.centers) 443 | self.clustering.num_init = 1 # clustering is deterministic after the first time 444 | self.centers = clustering_results.centers 445 | clusters = clustering_results.labels 446 | num_clusters = self.clustering.n_clusters 447 | self._save_maps(clusters / num_clusters, f'clusters') 448 | return num_clusters, clusters 449 | 450 | def _obtain_cross_masks(self, resolution, scale=10): 451 | maps = self._compute_maps(self.mean_cross_map, resolution=resolution) # b n k 452 | maps = F.sigmoid(scale * (maps - self.cross_mask_threshold)) 453 | maps = self._normalize_maps(maps, reduce_min=True) 454 | maps = maps.transpose(1, 2) # b k n 455 | existing_masks, _ = self._obtain_masks( 456 | resolution, return_existing=True, batch_size=maps.size(0), device=maps.device) 457 | maps = maps * existing_masks.to(dtype=maps.dtype) 458 | self._save_maps(maps, 'cross_masks') 459 | return maps 460 | 461 | def _fix_zero_masks(self, masks): 462 | b, s, n = masks.shape 463 | resolution = int(n ** 0.5) 464 | boxes = self._convert_boxes_to_masks(resolution, device=masks.device) # s n 465 | 466 | for i in range(b): 467 | for j in range(s): 468 | if masks[i, j].sum() == 0: 469 | print('******Found a zero mask!******') 470 | for k in range(s): 471 | masks[i, k] = boxes[j] if (k == j) else masks[i, k].logical_and(~boxes[j]) 472 | 473 | return masks 474 | 475 | def _compute_maps(self, maps, resolution=None): # b n k 476 | if resolution is not None: 477 | b, n, k = maps.shape 478 | original_resolution = int(n ** 0.5) 479 | maps = maps.transpose(1, 2).reshape(b, k, original_resolution, original_resolution) 480 | maps = F.interpolate(maps, resolution, mode='bilinear', antialias=True) 481 | maps = maps.reshape(b, k, -1).transpose(1, 2) 482 | 483 | maps = self._normalize_maps(maps) 484 | return maps 485 | 486 | @classmethod 487 | def _normalize_maps(cls, maps, reduce_min=False): # b n k 488 | max_values = maps.max(dim=1, keepdim=True)[0] 489 | min_values = maps.min(dim=1, keepdim=True)[0] if reduce_min else 0 490 | numerator = maps - min_values 491 | denominator = max_values - min_values + cls.EPSILON 492 | return numerator / denominator 493 | 494 | def _save_maps(self, maps, prefix): 495 | if self.map_dir is None or self.cur_step % self.delta_debug_mask_steps != 0: 496 | return 497 | 498 | resolution = int(maps.size(-1) ** 0.5) 499 | maps = maps.reshape(-1, 1, resolution, resolution).float() 500 | maps = F.interpolate(maps, self.saved_resolution, mode='bilinear', antialias=True) 501 | path = os.path.join(self.map_dir, f'map_{prefix}_{self.cur_step}_{self.cur_att_layer}.png') 502 | torchvision.utils.save_image(maps, path) 503 | 504 | def _display_attention_maps(self, attention_maps, is_cross, num_heads, prefix=None): 505 | if (not self.debug) or (self.cur_step == 0) or (self.cur_step % self.delta_debug_attention_steps > 0) or (self.cur_att_layer not in self.debug_layers): 506 | return 507 | 508 | dir_name = self.map_dir 509 | if prefix is not None: 510 | splits = list(os.path.split(dir_name)) 511 | splits[-1] = '_'.join((prefix, splits[-1])) 512 | dir_name = os.path.join(*splits) 513 | 514 | resolution = int(attention_maps.size(-2) ** 0.5) 515 | if is_cross: 516 | attention_maps = einops.rearrange(attention_maps, 'b (r1 r2) k -> b k r1 r2', r1=resolution) 517 | attention_maps = F.interpolate(attention_maps, self.saved_resolution, mode='bilinear', antialias=True) 518 | attention_maps = einops.rearrange(attention_maps, 'b k r1 r2 -> b (r1 r2) k') 519 | 520 | utils.display_attention_maps( 521 | attention_maps, 522 | is_cross, 523 | num_heads, 524 | self.model.tokenizer, 525 | self.prompts, 526 | dir_name, 527 | self.cur_step, 528 | self.cur_att_layer, 529 | resolution, 530 | ) 531 | 532 | def _debug_hook(self, q, k, v, sim, attn, is_cross, place_in_unet, num_heads, **kwargs): 533 | pass 534 | -------------------------------------------------------------------------------- /images/example.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omer11a/bounded-attention/aeb6580e2f203f5f78e532b9c97f103219849e5c/images/example.jpg -------------------------------------------------------------------------------- /images/teaser.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omer11a/bounded-attention/aeb6580e2f203f5f78e532b9c97f103219849e5c/images/teaser.jpg -------------------------------------------------------------------------------- /injection_utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | import torch 4 | import torch.nn as nn 5 | import torch.nn.functional as F 6 | 7 | from typing import Optional, Union, Tuple, List, Callable, Dict 8 | 9 | from torchvision.utils import save_image 10 | from einops import rearrange, repeat 11 | 12 | 13 | class AttentionBase: 14 | def __init__(self): 15 | self.cur_step = 0 16 | self.num_att_layers = -1 17 | self.cur_att_layer = 0 18 | 19 | def before_step(self): 20 | pass 21 | 22 | def after_step(self): 23 | pass 24 | 25 | def __call__(self, q, k, v, is_cross, place_in_unet, num_heads, **kwargs): 26 | if self.cur_att_layer == 0: 27 | self.before_step() 28 | 29 | out = self.forward(q, k, v, is_cross, place_in_unet, num_heads, **kwargs) 30 | self.cur_att_layer += 1 31 | if self.cur_att_layer == self.num_att_layers: 32 | self.cur_att_layer = 0 33 | self.cur_step += 1 34 | self.after_step() 35 | 36 | return out 37 | 38 | def forward(self, q, k, v, is_cross, place_in_unet, num_heads, **kwargs): 39 | batch_size = q.size(0) // num_heads 40 | n = q.size(1) 41 | d = k.size(1) 42 | 43 | q = q.reshape(batch_size, num_heads, n, -1) 44 | k = k.reshape(batch_size, num_heads, d, -1) 45 | v = v.reshape(batch_size, num_heads, d, -1) 46 | out = F.scaled_dot_product_attention(q, k, v, attn_mask=kwargs['mask']) 47 | out = out.reshape(batch_size * num_heads, n, -1) 48 | out = rearrange(out, '(b h) n d -> b n (h d)', h=num_heads) 49 | return out 50 | 51 | def reset(self): 52 | self.cur_step = 0 53 | self.cur_att_layer = 0 54 | 55 | 56 | def register_attention_editor_diffusers(model, editor: AttentionBase): 57 | """ 58 | Register a attention editor to Diffuser Pipeline, refer from [Prompt-to-Prompt] 59 | """ 60 | def ca_forward(self, place_in_unet): 61 | def forward(x, encoder_hidden_states=None, attention_mask=None, context=None, mask=None): 62 | """ 63 | The attention is similar to the original implementation of LDM CrossAttention class 64 | except adding some modifications on the attention 65 | """ 66 | if encoder_hidden_states is not None: 67 | context = encoder_hidden_states 68 | if attention_mask is not None: 69 | mask = attention_mask 70 | 71 | to_out = self.to_out 72 | if isinstance(to_out, nn.modules.container.ModuleList): 73 | to_out = self.to_out[0] 74 | else: 75 | to_out = self.to_out 76 | 77 | h = self.heads 78 | q = self.to_q(x) 79 | is_cross = context is not None 80 | context = context if is_cross else x 81 | k = self.to_k(context) 82 | v = self.to_v(context) 83 | q, k, v = map(lambda t: rearrange(t, 'b n (h d) -> (b h) n d', h=h), (q, k, v)) 84 | out = editor( 85 | q, k, v, is_cross, place_in_unet, 86 | self.heads, scale=self.scale, mask=mask) 87 | 88 | return to_out(out) 89 | 90 | return forward 91 | 92 | def register_editor(net, count, place_in_unet): 93 | for name, subnet in net.named_children(): 94 | if net.__class__.__name__ == 'Attention': # spatial Transformer layer 95 | net.original_forward = net.forward 96 | net.forward = ca_forward(net, place_in_unet) 97 | return count + 1 98 | elif hasattr(net, 'children'): 99 | count = register_editor(subnet, count, place_in_unet) 100 | return count 101 | 102 | cross_att_count = 0 103 | for net_name, net in model.unet.named_children(): 104 | if "down" in net_name: 105 | cross_att_count += register_editor(net, 0, "down") 106 | elif "mid" in net_name: 107 | cross_att_count += register_editor(net, 0, "mid") 108 | elif "up" in net_name: 109 | cross_att_count += register_editor(net, 0, "up") 110 | 111 | editor.num_att_layers = cross_att_count 112 | editor.model = model 113 | model.editor = editor 114 | 115 | 116 | def unregister_attention_editor_diffusers(model): 117 | def unregister_editor(net): 118 | for name, subnet in net.named_children(): 119 | if net.__class__.__name__ == 'Attention': # spatial Transformer layer 120 | net.forward = net.original_forward 121 | net.original_forward = None 122 | elif hasattr(net, 'children'): 123 | unregister_editor(subnet) 124 | 125 | for net_name, net in model.unet.named_children(): 126 | if "down" in net_name: 127 | unregister_editor(net) 128 | elif "mid" in net_name: 129 | unregister_editor(net) 130 | elif "up" in net_name: 131 | unregister_editor(net) 132 | 133 | editor.model = None 134 | model.editor = None 135 | -------------------------------------------------------------------------------- /pipeline_stable_diffusion_opt.py: -------------------------------------------------------------------------------- 1 | """ 2 | Util functions based on Diffuser framework. 3 | """ 4 | 5 | 6 | import os 7 | import torch 8 | import numpy as np 9 | 10 | import torch.nn.functional as F 11 | from tqdm import tqdm 12 | from PIL import Image 13 | from torchvision.utils import save_image 14 | from torchvision.io import read_image 15 | 16 | from diffusers import StableDiffusionPipeline 17 | 18 | from pytorch_lightning import seed_everything 19 | 20 | 21 | class StableDiffusionPipeline(StableDiffusionPipeline): 22 | def next_step( 23 | self, 24 | model_output: torch.FloatTensor, 25 | timestep: int, 26 | x: torch.FloatTensor, 27 | eta=0., 28 | verbose=False 29 | ): 30 | """ 31 | Inverse sampling for DDIM Inversion 32 | """ 33 | if verbose: 34 | print("timestep: ", timestep) 35 | next_step = timestep 36 | timestep = min(timestep - self.scheduler.config.num_train_timesteps // self.scheduler.num_inference_steps, 999) 37 | alpha_prod_t = self.scheduler.alphas_cumprod[timestep] if timestep >= 0 else self.scheduler.final_alpha_cumprod 38 | alpha_prod_t_next = self.scheduler.alphas_cumprod[next_step] 39 | beta_prod_t = 1 - alpha_prod_t 40 | pred_x0 = (x - beta_prod_t**0.5 * model_output) / alpha_prod_t**0.5 41 | pred_dir = (1 - alpha_prod_t_next)**0.5 * model_output 42 | x_next = alpha_prod_t_next**0.5 * pred_x0 + pred_dir 43 | return x_next, pred_x0 44 | 45 | def step( 46 | self, 47 | model_output: torch.FloatTensor, 48 | timestep: int, 49 | x: torch.FloatTensor, 50 | eta: float=0.0, 51 | verbose=False, 52 | ): 53 | """ 54 | predict the sampe the next step in the denoise process. 55 | """ 56 | prev_timestep = timestep - self.scheduler.config.num_train_timesteps // self.scheduler.num_inference_steps 57 | alpha_prod_t = self.scheduler.alphas_cumprod[timestep] 58 | alpha_prod_t_prev = self.scheduler.alphas_cumprod[prev_timestep] if prev_timestep > 0 else self.scheduler.final_alpha_cumprod 59 | beta_prod_t = 1 - alpha_prod_t 60 | pred_x0 = (x - beta_prod_t**0.5 * model_output) / alpha_prod_t**0.5 61 | pred_dir = (1 - alpha_prod_t_prev)**0.5 * model_output 62 | x_prev = alpha_prod_t_prev**0.5 * pred_x0 + pred_dir 63 | return x_prev, pred_x0 64 | 65 | @torch.no_grad() 66 | def image2latent(self, image): 67 | DEVICE = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") 68 | if type(image) is Image: 69 | image = np.array(image) 70 | image = torch.from_numpy(image).float() / 127.5 - 1 71 | image = image.permute(2, 0, 1).unsqueeze(0).to(DEVICE) 72 | # input image density range [-1, 1] 73 | latents = self.vae.encode(image)['latent_dist'].mean 74 | latents = latents * 0.18215 75 | return latents 76 | 77 | @torch.no_grad() 78 | def latent2image(self, latents, return_type='np'): 79 | latents = latents.half() 80 | latents = 1 / 0.18215 * latents.detach() 81 | image = self.vae.decode(latents)['sample'] 82 | if return_type == 'np': 83 | image = (image / 2 + 0.5).clamp(0, 1) 84 | image = image.cpu().permute(0, 2, 3, 1).numpy()[0] 85 | image = (image * 255).astype(np.uint8) 86 | elif return_type == "pt": 87 | image = (image / 2 + 0.5).clamp(0, 1) 88 | 89 | return image 90 | 91 | def latent2image_grad(self, latents): 92 | latents = 1 / 0.18215 * latents 93 | image = self.vae.decode(latents)['sample'] 94 | 95 | return image # range [-1, 1] 96 | 97 | def update_loss(self, latents, text_embeddings, i, t): 98 | def forward_pass(latent_model_input): 99 | self.unet( 100 | latent_model_input, 101 | t, 102 | encoder_hidden_states=text_embeddings, 103 | ) 104 | self.unet.zero_grad() 105 | 106 | return self.editor.update_loss(forward_pass, latents, i) 107 | 108 | @torch.no_grad() 109 | def __call__( 110 | self, 111 | prompt, 112 | batch_size=1, 113 | height=512, 114 | width=512, 115 | num_inference_steps=50, 116 | guidance_scale=7.5, 117 | eta=0.0, 118 | latents=None, 119 | unconditioning=None, 120 | neg_prompt=None, 121 | ref_intermediate_latents=None, 122 | return_intermediates=False, 123 | **kwds): 124 | DEVICE = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") 125 | if isinstance(prompt, list): 126 | batch_size = len(prompt) 127 | elif isinstance(prompt, str): 128 | if batch_size > 1: 129 | prompt = [prompt] * batch_size 130 | 131 | # text embeddings 132 | text_input = self.tokenizer( 133 | prompt, 134 | padding="max_length", 135 | max_length=77, 136 | return_tensors="pt" 137 | ) 138 | 139 | text_embeddings = self.text_encoder(text_input.input_ids.to(DEVICE))[0] 140 | print("input text embeddings :", text_embeddings.shape) 141 | if kwds.get("dir"): 142 | dir = text_embeddings[-2] - text_embeddings[-1] 143 | u, s, v = torch.pca_lowrank(dir.transpose(-1, -2), q=1, center=True) 144 | text_embeddings[-1] = text_embeddings[-1] + kwds.get("dir") * v 145 | print(u.shape) 146 | print(v.shape) 147 | 148 | # define initial latents 149 | latents_shape = (batch_size, self.unet.in_channels, height//8, width//8) 150 | if latents is None: 151 | latents = torch.randn(latents_shape, device=DEVICE) 152 | else: 153 | assert latents.shape == latents_shape, f"The shape of input latent tensor {latents.shape} should equal to predefined one." 154 | 155 | # unconditional embedding for classifier free guidance 156 | if guidance_scale > 1.: 157 | max_length = text_input.input_ids.shape[-1] 158 | if neg_prompt: 159 | uc_text = neg_prompt 160 | else: 161 | uc_text = "" 162 | # uc_text = "ugly, tiling, poorly drawn hands, poorly drawn feet, body out of frame, cut off, low contrast, underexposed, distorted face" 163 | unconditional_input = self.tokenizer( 164 | [uc_text] * batch_size, 165 | padding="max_length", 166 | max_length=77, 167 | return_tensors="pt" 168 | ) 169 | # unconditional_input.input_ids = unconditional_input.input_ids[:, 1:] 170 | unconditional_embeddings = self.text_encoder(unconditional_input.input_ids.to(DEVICE))[0] 171 | text_embeddings = torch.cat([unconditional_embeddings, text_embeddings], dim=0) 172 | 173 | print("latents shape: ", latents.shape) 174 | # iterative sampling 175 | self.scheduler.set_timesteps(num_inference_steps) 176 | # print("Valid timesteps: ", reversed(self.scheduler.timesteps)) 177 | latents_list = [latents] 178 | pred_x0_list = [latents] 179 | latents = latents.half() 180 | text_embeddings = text_embeddings.half() 181 | for i, t in enumerate(tqdm(self.scheduler.timesteps, desc="DDIM Sampler")): 182 | if ref_intermediate_latents is not None: 183 | # note that the batch_size >= 2 184 | latents_ref = ref_intermediate_latents[-1 - i] 185 | _, latents_cur = latents.chunk(2) 186 | latents = torch.cat([latents_ref, latents_cur]) 187 | 188 | if unconditioning is not None and isinstance(unconditioning, list): 189 | _, text_embeddings = text_embeddings.chunk(2) 190 | text_embeddings = torch.cat([unconditioning[i].expand(*text_embeddings.shape), text_embeddings]) 191 | 192 | #with torch.enable_grad(): 193 | latents = self.update_loss(latents, text_embeddings, i, t) 194 | 195 | if guidance_scale > 1.: 196 | model_inputs = torch.cat([latents] * 2) 197 | else: 198 | model_inputs = latents 199 | #model_inputs = self.scheduler.scale_model_input(model_inputs, t) 200 | 201 | # predict tghe noise 202 | noise_pred = self.unet(model_inputs, t, encoder_hidden_states=text_embeddings).sample 203 | if guidance_scale > 1.: 204 | noise_pred_uncon, noise_pred_con = noise_pred.chunk(2, dim=0) 205 | noise_pred = noise_pred_uncon + guidance_scale * (noise_pred_con - noise_pred_uncon) 206 | # compute the previous noise sample x_t -> x_t-1 207 | #print('noise_pred.shape', noise_pred.shape) 208 | #print('latents.shape', latents.shape) 209 | latents, pred_x0 = self.step(noise_pred, t, latents) 210 | #print('new_latents.shape', latents.shape) 211 | #print('pred_x0.shape', pred_x0.shape) 212 | latents_list.append(latents) 213 | pred_x0_list.append(pred_x0) 214 | 215 | image = self.latent2image(latents, return_type="pt") 216 | if return_intermediates: 217 | pred_x0_list = [self.latent2image(img, return_type="pt") for img in pred_x0_list] 218 | latents_list = [self.latent2image(img, return_type="pt") for img in latents_list] 219 | return image, pred_x0_list, latents_list 220 | return image 221 | 222 | @torch.no_grad() 223 | def invert( 224 | self, 225 | image: torch.Tensor, 226 | prompt, 227 | num_inference_steps=50, 228 | guidance_scale=7.5, 229 | eta=0.0, 230 | return_intermediates=False, 231 | **kwds): 232 | """ 233 | invert a real image into noise map with determinisc DDIM inversion 234 | """ 235 | DEVICE = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") 236 | batch_size = image.shape[0] 237 | if isinstance(prompt, list): 238 | if batch_size == 1: 239 | image = image.expand(len(prompt), -1, -1, -1) 240 | elif isinstance(prompt, str): 241 | if batch_size > 1: 242 | prompt = [prompt] * batch_size 243 | 244 | # text embeddings 245 | text_input = self.tokenizer( 246 | prompt, 247 | padding="max_length", 248 | max_length=77, 249 | return_tensors="pt" 250 | ) 251 | text_embeddings = self.text_encoder(text_input.input_ids.to(DEVICE))[0] 252 | print("input text embeddings :", text_embeddings.shape) 253 | # define initial latents 254 | latents = self.image2latent(image) 255 | start_latents = latents 256 | # print(latents) 257 | # exit() 258 | # unconditional embedding for classifier free guidance 259 | if guidance_scale > 1.: 260 | max_length = text_input.input_ids.shape[-1] 261 | unconditional_input = self.tokenizer( 262 | [""] * batch_size, 263 | padding="max_length", 264 | max_length=77, 265 | return_tensors="pt" 266 | ) 267 | unconditional_embeddings = self.text_encoder(unconditional_input.input_ids.to(DEVICE))[0] 268 | text_embeddings = torch.cat([unconditional_embeddings, text_embeddings], dim=0) 269 | 270 | print("latents shape: ", latents.shape) 271 | # interative sampling 272 | self.scheduler.set_timesteps(num_inference_steps) 273 | print("Valid timesteps: ", reversed(self.scheduler.timesteps)) 274 | # print("attributes: ", self.scheduler.__dict__) 275 | latents_list = [latents] 276 | pred_x0_list = [latents] 277 | for i, t in enumerate(tqdm(reversed(self.scheduler.timesteps), desc="DDIM Inversion")): 278 | if guidance_scale > 1.: 279 | model_inputs = torch.cat([latents] * 2) 280 | else: 281 | model_inputs = latents 282 | 283 | # predict the noise 284 | noise_pred = self.unet(model_inputs, t, encoder_hidden_states=text_embeddings).sample 285 | if guidance_scale > 1.: 286 | noise_pred_uncon, noise_pred_con = noise_pred.chunk(2, dim=0) 287 | noise_pred = noise_pred_uncon + guidance_scale * (noise_pred_con - noise_pred_uncon) 288 | # compute the previous noise sample x_t-1 -> x_t 289 | latents, pred_x0 = self.next_step(noise_pred, t, latents) 290 | latents_list.append(latents) 291 | pred_x0_list.append(pred_x0) 292 | 293 | if return_intermediates: 294 | # return the intermediate laters during inversion 295 | # pred_x0_list = [self.latent2image(img, return_type="pt") for img in pred_x0_list] 296 | return latents, latents_list 297 | return latents, start_latents 298 | -------------------------------------------------------------------------------- /pipeline_stable_diffusion_xl_opt.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The HuggingFace Team. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import inspect 16 | import os 17 | from typing import Any, Callable, Dict, List, Optional, Tuple, Union 18 | 19 | import torch 20 | from transformers import CLIPTextModel, CLIPTextModelWithProjection, CLIPTokenizer 21 | 22 | from diffusers.image_processor import VaeImageProcessor 23 | from diffusers.loaders import FromSingleFileMixin, LoraLoaderMixin, TextualInversionLoaderMixin 24 | from diffusers.models import AutoencoderKL, UNet2DConditionModel 25 | from diffusers.models.attention_processor import ( 26 | AttnProcessor2_0, 27 | LoRAAttnProcessor2_0, 28 | LoRAXFormersAttnProcessor, 29 | XFormersAttnProcessor, 30 | ) 31 | from diffusers.schedulers import KarrasDiffusionSchedulers 32 | from diffusers.utils import ( 33 | is_accelerate_available, 34 | is_accelerate_version, 35 | is_invisible_watermark_available, 36 | logging, 37 | randn_tensor, 38 | replace_example_docstring, 39 | ) 40 | from diffusers.pipeline_utils import DiffusionPipeline 41 | from diffusers.pipelines.stable_diffusion_xl import StableDiffusionXLPipelineOutput 42 | 43 | 44 | if is_invisible_watermark_available(): 45 | from .watermark import StableDiffusionXLWatermarker 46 | 47 | 48 | logger = logging.get_logger(__name__) # pylint: disable=invalid-name 49 | 50 | EXAMPLE_DOC_STRING = """ 51 | Examples: 52 | ```py 53 | >>> import torch 54 | >>> from diffusers import StableDiffusionXLPipeline 55 | 56 | >>> pipe = StableDiffusionXLPipeline.from_pretrained( 57 | ... "stabilityai/stable-diffusion-xl-base-1.0", torch_dtype=torch.float16 58 | ... ) 59 | >>> pipe = pipe.to("cuda") 60 | 61 | >>> prompt = "a photo of an astronaut riding a horse on mars" 62 | >>> image = pipe(prompt).images[0] 63 | ``` 64 | """ 65 | 66 | 67 | # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.rescale_noise_cfg 68 | def rescale_noise_cfg(noise_cfg, noise_pred_text, guidance_rescale=0.0): 69 | """ 70 | Rescale `noise_cfg` according to `guidance_rescale`. Based on findings of [Common Diffusion Noise Schedules and 71 | Sample Steps are Flawed](https://arxiv.org/pdf/2305.08891.pdf). See Section 3.4 72 | """ 73 | std_text = noise_pred_text.std(dim=list(range(1, noise_pred_text.ndim)), keepdim=True) 74 | std_cfg = noise_cfg.std(dim=list(range(1, noise_cfg.ndim)), keepdim=True) 75 | # rescale the results from guidance (fixes overexposure) 76 | noise_pred_rescaled = noise_cfg * (std_text / std_cfg) 77 | # mix with the original results from guidance by factor guidance_rescale to avoid "plain looking" images 78 | noise_cfg = guidance_rescale * noise_pred_rescaled + (1 - guidance_rescale) * noise_cfg 79 | return noise_cfg 80 | 81 | 82 | class StableDiffusionXLPipeline(DiffusionPipeline, FromSingleFileMixin, LoraLoaderMixin): 83 | r""" 84 | Pipeline for text-to-image generation using Stable Diffusion XL. 85 | 86 | This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods the 87 | library implements for all the pipelines (such as downloading or saving, running on a particular device, etc.) 88 | 89 | In addition the pipeline inherits the following loading methods: 90 | - *LoRA*: [`StableDiffusionXLPipeline.load_lora_weights`] 91 | - *Ckpt*: [`loaders.FromSingleFileMixin.from_single_file`] 92 | 93 | as well as the following saving methods: 94 | - *LoRA*: [`loaders.StableDiffusionXLPipeline.save_lora_weights`] 95 | 96 | Args: 97 | vae ([`AutoencoderKL`]): 98 | Variational Auto-Encoder (VAE) Model to encode and decode images to and from latent representations. 99 | text_encoder ([`CLIPTextModel`]): 100 | Frozen text-encoder. Stable Diffusion XL uses the text portion of 101 | [CLIP](https://huggingface.co/docs/transformers/model_doc/clip#transformers.CLIPTextModel), specifically 102 | the [clip-vit-large-patch14](https://huggingface.co/openai/clip-vit-large-patch14) variant. 103 | text_encoder_2 ([` CLIPTextModelWithProjection`]): 104 | Second frozen text-encoder. Stable Diffusion XL uses the text and pool portion of 105 | [CLIP](https://huggingface.co/docs/transformers/model_doc/clip#transformers.CLIPTextModelWithProjection), 106 | specifically the 107 | [laion/CLIP-ViT-bigG-14-laion2B-39B-b160k](https://huggingface.co/laion/CLIP-ViT-bigG-14-laion2B-39B-b160k) 108 | variant. 109 | tokenizer (`CLIPTokenizer`): 110 | Tokenizer of class 111 | [CLIPTokenizer](https://huggingface.co/docs/transformers/v4.21.0/en/model_doc/clip#transformers.CLIPTokenizer). 112 | tokenizer_2 (`CLIPTokenizer`): 113 | Second Tokenizer of class 114 | [CLIPTokenizer](https://huggingface.co/docs/transformers/v4.21.0/en/model_doc/clip#transformers.CLIPTokenizer). 115 | unet ([`UNet2DConditionModel`]): Conditional U-Net architecture to denoise the encoded image latents. 116 | scheduler ([`SchedulerMixin`]): 117 | A scheduler to be used in combination with `unet` to denoise the encoded image latents. Can be one of 118 | [`DDIMScheduler`], [`LMSDiscreteScheduler`], or [`PNDMScheduler`]. 119 | """ 120 | 121 | def __init__( 122 | self, 123 | vae: AutoencoderKL, 124 | text_encoder: CLIPTextModel, 125 | text_encoder_2: CLIPTextModelWithProjection, 126 | tokenizer: CLIPTokenizer, 127 | tokenizer_2: CLIPTokenizer, 128 | unet: UNet2DConditionModel, 129 | scheduler: KarrasDiffusionSchedulers, 130 | force_zeros_for_empty_prompt: bool = True, 131 | add_watermarker: Optional[bool] = None, 132 | ): 133 | super().__init__() 134 | 135 | self.register_modules( 136 | vae=vae, 137 | text_encoder=text_encoder, 138 | text_encoder_2=text_encoder_2, 139 | tokenizer=tokenizer, 140 | tokenizer_2=tokenizer_2, 141 | unet=unet, 142 | scheduler=scheduler, 143 | ) 144 | self.register_to_config(force_zeros_for_empty_prompt=force_zeros_for_empty_prompt) 145 | self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1) 146 | self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor) 147 | self.default_sample_size = self.unet.config.sample_size 148 | 149 | add_watermarker = add_watermarker if add_watermarker is not None else is_invisible_watermark_available() 150 | 151 | if add_watermarker: 152 | self.watermark = StableDiffusionXLWatermarker() 153 | else: 154 | self.watermark = None 155 | 156 | # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.enable_vae_slicing 157 | def enable_vae_slicing(self): 158 | r""" 159 | Enable sliced VAE decoding. When this option is enabled, the VAE will split the input tensor in slices to 160 | compute decoding in several steps. This is useful to save some memory and allow larger batch sizes. 161 | """ 162 | self.vae.enable_slicing() 163 | 164 | # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.disable_vae_slicing 165 | def disable_vae_slicing(self): 166 | r""" 167 | Disable sliced VAE decoding. If `enable_vae_slicing` was previously enabled, this method will go back to 168 | computing decoding in one step. 169 | """ 170 | self.vae.disable_slicing() 171 | 172 | # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.enable_vae_tiling 173 | def enable_vae_tiling(self): 174 | r""" 175 | Enable tiled VAE decoding. When this option is enabled, the VAE will split the input tensor into tiles to 176 | compute decoding and encoding in several steps. This is useful for saving a large amount of memory and to allow 177 | processing larger images. 178 | """ 179 | self.vae.enable_tiling() 180 | 181 | # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.disable_vae_tiling 182 | def disable_vae_tiling(self): 183 | r""" 184 | Disable tiled VAE decoding. If `enable_vae_tiling` was previously enabled, this method will go back to 185 | computing decoding in one step. 186 | """ 187 | self.vae.disable_tiling() 188 | 189 | def enable_model_cpu_offload(self, gpu_id=0): 190 | r""" 191 | Offloads all models to CPU using accelerate, reducing memory usage with a low impact on performance. Compared 192 | to `enable_sequential_cpu_offload`, this method moves one whole model at a time to the GPU when its `forward` 193 | method is called, and the model remains in GPU until the next model runs. Memory savings are lower than with 194 | `enable_sequential_cpu_offload`, but performance is much better due to the iterative execution of the `unet`. 195 | """ 196 | if is_accelerate_available() and is_accelerate_version(">=", "0.17.0.dev0"): 197 | from accelerate import cpu_offload_with_hook 198 | else: 199 | raise ImportError("`enable_model_cpu_offload` requires `accelerate v0.17.0` or higher.") 200 | 201 | device = torch.device(f"cuda:{gpu_id}") 202 | 203 | if self.device.type != "cpu": 204 | self.to("cpu", silence_dtype_warnings=True) 205 | torch.cuda.empty_cache() # otherwise we don't see the memory savings (but they probably exist) 206 | 207 | model_sequence = ( 208 | [self.text_encoder, self.text_encoder_2] if self.text_encoder is not None else [self.text_encoder_2] 209 | ) 210 | model_sequence.extend([self.unet, self.vae]) 211 | 212 | hook = None 213 | for cpu_offloaded_model in model_sequence: 214 | _, hook = cpu_offload_with_hook(cpu_offloaded_model, device, prev_module_hook=hook) 215 | 216 | # We'll offload the last model manually. 217 | self.final_offload_hook = hook 218 | 219 | def encode_prompt( 220 | self, 221 | prompt: str, 222 | prompt_2: Optional[str] = None, 223 | device: Optional[torch.device] = None, 224 | num_images_per_prompt: int = 1, 225 | do_classifier_free_guidance: bool = True, 226 | negative_prompt: Optional[str] = None, 227 | negative_prompt_2: Optional[str] = None, 228 | prompt_embeds: Optional[torch.FloatTensor] = None, 229 | negative_prompt_embeds: Optional[torch.FloatTensor] = None, 230 | pooled_prompt_embeds: Optional[torch.FloatTensor] = None, 231 | negative_pooled_prompt_embeds: Optional[torch.FloatTensor] = None, 232 | lora_scale: Optional[float] = None, 233 | ): 234 | r""" 235 | Encodes the prompt into text encoder hidden states. 236 | 237 | Args: 238 | prompt (`str` or `List[str]`, *optional*): 239 | prompt to be encoded 240 | prompt_2 (`str` or `List[str]`, *optional*): 241 | The prompt or prompts to be sent to the `tokenizer_2` and `text_encoder_2`. If not defined, `prompt` is 242 | used in both text-encoders 243 | device: (`torch.device`): 244 | torch device 245 | num_images_per_prompt (`int`): 246 | number of images that should be generated per prompt 247 | do_classifier_free_guidance (`bool`): 248 | whether to use classifier free guidance or not 249 | negative_prompt (`str` or `List[str]`, *optional*): 250 | The prompt or prompts not to guide the image generation. If not defined, one has to pass 251 | `negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is 252 | less than `1`). 253 | negative_prompt_2 (`str` or `List[str]`, *optional*): 254 | The prompt or prompts not to guide the image generation to be sent to `tokenizer_2` and 255 | `text_encoder_2`. If not defined, `negative_prompt` is used in both text-encoders 256 | prompt_embeds (`torch.FloatTensor`, *optional*): 257 | Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not 258 | provided, text embeddings will be generated from `prompt` input argument. 259 | negative_prompt_embeds (`torch.FloatTensor`, *optional*): 260 | Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt 261 | weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input 262 | argument. 263 | pooled_prompt_embeds (`torch.FloatTensor`, *optional*): 264 | Pre-generated pooled text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. 265 | If not provided, pooled text embeddings will be generated from `prompt` input argument. 266 | negative_pooled_prompt_embeds (`torch.FloatTensor`, *optional*): 267 | Pre-generated negative pooled text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt 268 | weighting. If not provided, pooled negative_prompt_embeds will be generated from `negative_prompt` 269 | input argument. 270 | lora_scale (`float`, *optional*): 271 | A lora scale that will be applied to all LoRA layers of the text encoder if LoRA layers are loaded. 272 | """ 273 | device = device or self._execution_device 274 | 275 | # set lora scale so that monkey patched LoRA 276 | # function of text encoder can correctly access it 277 | if lora_scale is not None and isinstance(self, LoraLoaderMixin): 278 | self._lora_scale = lora_scale 279 | 280 | if prompt is not None and isinstance(prompt, str): 281 | batch_size = 1 282 | elif prompt is not None and isinstance(prompt, list): 283 | batch_size = len(prompt) 284 | else: 285 | batch_size = prompt_embeds.shape[0] 286 | 287 | # Define tokenizers and text encoders 288 | tokenizers = [self.tokenizer, self.tokenizer_2] if self.tokenizer is not None else [self.tokenizer_2] 289 | text_encoders = ( 290 | [self.text_encoder, self.text_encoder_2] if self.text_encoder is not None else [self.text_encoder_2] 291 | ) 292 | 293 | if prompt_embeds is None: 294 | prompt_2 = prompt_2 or prompt 295 | # textual inversion: procecss multi-vector tokens if necessary 296 | prompt_embeds_list = [] 297 | prompts = [prompt, prompt_2] 298 | for prompt, tokenizer, text_encoder in zip(prompts, tokenizers, text_encoders): 299 | if isinstance(self, TextualInversionLoaderMixin): 300 | prompt = self.maybe_convert_prompt(prompt, tokenizer) 301 | 302 | text_inputs = tokenizer( 303 | prompt, 304 | padding="max_length", 305 | max_length=tokenizer.model_max_length, 306 | truncation=True, 307 | return_tensors="pt", 308 | ) 309 | 310 | text_input_ids = text_inputs.input_ids 311 | untruncated_ids = tokenizer(prompt, padding="longest", return_tensors="pt").input_ids 312 | 313 | if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal( 314 | text_input_ids, untruncated_ids 315 | ): 316 | removed_text = tokenizer.batch_decode(untruncated_ids[:, tokenizer.model_max_length - 1 : -1]) 317 | logger.warning( 318 | "The following part of your input was truncated because CLIP can only handle sequences up to" 319 | f" {tokenizer.model_max_length} tokens: {removed_text}" 320 | ) 321 | 322 | prompt_embeds = text_encoder( 323 | text_input_ids.to(device), 324 | output_hidden_states=True, 325 | ) 326 | 327 | # We are only ALWAYS interested in the pooled output of the final text encoder 328 | pooled_prompt_embeds = prompt_embeds[0] 329 | ### TODO: remove 330 | null_text_inputs = tokenizer( 331 | ['a realistic photo of an empty background'] * batch_size, 332 | padding="max_length", 333 | max_length=tokenizer.model_max_length, 334 | truncation=True, 335 | return_tensors="pt", 336 | ) 337 | null_input_ids = null_text_inputs.input_ids 338 | null_prompt_embeds = text_encoder( 339 | null_input_ids.to(device), 340 | output_hidden_states=True, 341 | ) 342 | pooled_prompt_embeds = null_prompt_embeds[0] 343 | ### TODO: remove 344 | prompt_embeds = prompt_embeds.hidden_states[-2] 345 | 346 | prompt_embeds_list.append(prompt_embeds) 347 | 348 | prompt_embeds = torch.concat(prompt_embeds_list, dim=-1) 349 | 350 | # get unconditional embeddings for classifier free guidance 351 | zero_out_negative_prompt = negative_prompt is None and self.config.force_zeros_for_empty_prompt 352 | if do_classifier_free_guidance and negative_prompt_embeds is None and zero_out_negative_prompt: 353 | negative_prompt_embeds = torch.zeros_like(prompt_embeds) 354 | negative_pooled_prompt_embeds = torch.zeros_like(pooled_prompt_embeds) 355 | elif do_classifier_free_guidance and negative_prompt_embeds is None: 356 | negative_prompt = negative_prompt or "" 357 | negative_prompt_2 = negative_prompt_2 or negative_prompt 358 | 359 | uncond_tokens: List[str] 360 | if prompt is not None and type(prompt) is not type(negative_prompt): 361 | raise TypeError( 362 | f"`negative_prompt` should be the same type to `prompt`, but got {type(negative_prompt)} !=" 363 | f" {type(prompt)}." 364 | ) 365 | elif isinstance(negative_prompt, str): 366 | uncond_tokens = [negative_prompt, negative_prompt_2] 367 | elif batch_size != len(negative_prompt): 368 | raise ValueError( 369 | f"`negative_prompt`: {negative_prompt} has batch size {len(negative_prompt)}, but `prompt`:" 370 | f" {prompt} has batch size {batch_size}. Please make sure that passed `negative_prompt` matches" 371 | " the batch size of `prompt`." 372 | ) 373 | else: 374 | uncond_tokens = [negative_prompt, negative_prompt_2] 375 | 376 | negative_prompt_embeds_list = [] 377 | for negative_prompt, tokenizer, text_encoder in zip(uncond_tokens, tokenizers, text_encoders): 378 | if isinstance(self, TextualInversionLoaderMixin): 379 | negative_prompt = self.maybe_convert_prompt(negative_prompt, tokenizer) 380 | 381 | max_length = prompt_embeds.shape[1] 382 | uncond_input = tokenizer( 383 | negative_prompt, 384 | padding="max_length", 385 | max_length=max_length, 386 | truncation=True, 387 | return_tensors="pt", 388 | ) 389 | 390 | negative_prompt_embeds = text_encoder( 391 | uncond_input.input_ids.to(device), 392 | output_hidden_states=True, 393 | ) 394 | # We are only ALWAYS interested in the pooled output of the final text encoder 395 | negative_pooled_prompt_embeds = negative_prompt_embeds[0] 396 | negative_prompt_embeds = negative_prompt_embeds.hidden_states[-2] 397 | 398 | negative_prompt_embeds_list.append(negative_prompt_embeds) 399 | 400 | negative_prompt_embeds = torch.concat(negative_prompt_embeds_list, dim=-1) 401 | 402 | prompt_embeds = prompt_embeds.to(dtype=self.text_encoder_2.dtype, device=device) 403 | bs_embed, seq_len, _ = prompt_embeds.shape 404 | # duplicate text embeddings for each generation per prompt, using mps friendly method 405 | prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1) 406 | prompt_embeds = prompt_embeds.view(bs_embed * num_images_per_prompt, seq_len, -1) 407 | 408 | if do_classifier_free_guidance: 409 | # duplicate unconditional embeddings for each generation per prompt, using mps friendly method 410 | seq_len = negative_prompt_embeds.shape[1] 411 | negative_prompt_embeds = negative_prompt_embeds.to(dtype=self.text_encoder_2.dtype, device=device) 412 | negative_prompt_embeds = negative_prompt_embeds.repeat(1, num_images_per_prompt, 1) 413 | negative_prompt_embeds = negative_prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1) 414 | 415 | pooled_prompt_embeds = pooled_prompt_embeds.repeat(1, num_images_per_prompt).view( 416 | bs_embed * num_images_per_prompt, -1 417 | ) 418 | if do_classifier_free_guidance: 419 | negative_pooled_prompt_embeds = negative_pooled_prompt_embeds.repeat(1, num_images_per_prompt).view( 420 | bs_embed * num_images_per_prompt, -1 421 | ) 422 | 423 | return prompt_embeds, negative_prompt_embeds, pooled_prompt_embeds, negative_pooled_prompt_embeds 424 | 425 | # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.prepare_extra_step_kwargs 426 | def prepare_extra_step_kwargs(self, generator, eta): 427 | # prepare extra kwargs for the scheduler step, since not all schedulers have the same signature 428 | # eta (η) is only used with the DDIMScheduler, it will be ignored for other schedulers. 429 | # eta corresponds to η in DDIM paper: https://arxiv.org/abs/2010.02502 430 | # and should be between [0, 1] 431 | 432 | accepts_eta = "eta" in set(inspect.signature(self.scheduler.step).parameters.keys()) 433 | extra_step_kwargs = {} 434 | if accepts_eta: 435 | extra_step_kwargs["eta"] = eta 436 | 437 | # check if the scheduler accepts generator 438 | accepts_generator = "generator" in set(inspect.signature(self.scheduler.step).parameters.keys()) 439 | if accepts_generator: 440 | extra_step_kwargs["generator"] = generator 441 | return extra_step_kwargs 442 | 443 | def check_inputs( 444 | self, 445 | prompt, 446 | prompt_2, 447 | height, 448 | width, 449 | callback_steps, 450 | negative_prompt=None, 451 | negative_prompt_2=None, 452 | prompt_embeds=None, 453 | negative_prompt_embeds=None, 454 | pooled_prompt_embeds=None, 455 | negative_pooled_prompt_embeds=None, 456 | ): 457 | if height % 8 != 0 or width % 8 != 0: 458 | raise ValueError(f"`height` and `width` have to be divisible by 8 but are {height} and {width}.") 459 | 460 | if (callback_steps is None) or ( 461 | callback_steps is not None and (not isinstance(callback_steps, int) or callback_steps <= 0) 462 | ): 463 | raise ValueError( 464 | f"`callback_steps` has to be a positive integer but is {callback_steps} of type" 465 | f" {type(callback_steps)}." 466 | ) 467 | 468 | if prompt is not None and prompt_embeds is not None: 469 | raise ValueError( 470 | f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to" 471 | " only forward one of the two." 472 | ) 473 | elif prompt_2 is not None and prompt_embeds is not None: 474 | raise ValueError( 475 | f"Cannot forward both `prompt_2`: {prompt_2} and `prompt_embeds`: {prompt_embeds}. Please make sure to" 476 | " only forward one of the two." 477 | ) 478 | elif prompt is None and prompt_embeds is None: 479 | raise ValueError( 480 | "Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined." 481 | ) 482 | elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)): 483 | raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}") 484 | elif prompt_2 is not None and (not isinstance(prompt_2, str) and not isinstance(prompt_2, list)): 485 | raise ValueError(f"`prompt_2` has to be of type `str` or `list` but is {type(prompt_2)}") 486 | 487 | if negative_prompt is not None and negative_prompt_embeds is not None: 488 | raise ValueError( 489 | f"Cannot forward both `negative_prompt`: {negative_prompt} and `negative_prompt_embeds`:" 490 | f" {negative_prompt_embeds}. Please make sure to only forward one of the two." 491 | ) 492 | elif negative_prompt_2 is not None and negative_prompt_embeds is not None: 493 | raise ValueError( 494 | f"Cannot forward both `negative_prompt_2`: {negative_prompt_2} and `negative_prompt_embeds`:" 495 | f" {negative_prompt_embeds}. Please make sure to only forward one of the two." 496 | ) 497 | 498 | if prompt_embeds is not None and negative_prompt_embeds is not None: 499 | if prompt_embeds.shape != negative_prompt_embeds.shape: 500 | raise ValueError( 501 | "`prompt_embeds` and `negative_prompt_embeds` must have the same shape when passed directly, but" 502 | f" got: `prompt_embeds` {prompt_embeds.shape} != `negative_prompt_embeds`" 503 | f" {negative_prompt_embeds.shape}." 504 | ) 505 | 506 | if prompt_embeds is not None and pooled_prompt_embeds is None: 507 | raise ValueError( 508 | "If `prompt_embeds` are provided, `pooled_prompt_embeds` also have to be passed. Make sure to generate `pooled_prompt_embeds` from the same text encoder that was used to generate `prompt_embeds`." 509 | ) 510 | 511 | if negative_prompt_embeds is not None and negative_pooled_prompt_embeds is None: 512 | raise ValueError( 513 | "If `negative_prompt_embeds` are provided, `negative_pooled_prompt_embeds` also have to be passed. Make sure to generate `negative_pooled_prompt_embeds` from the same text encoder that was used to generate `negative_prompt_embeds`." 514 | ) 515 | 516 | # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.prepare_latents 517 | def prepare_latents(self, batch_size, num_channels_latents, height, width, dtype, device, generator, latents=None): 518 | shape = (batch_size, num_channels_latents, height // self.vae_scale_factor, width // self.vae_scale_factor) 519 | if isinstance(generator, list) and len(generator) != batch_size: 520 | raise ValueError( 521 | f"You have passed a list of generators of length {len(generator)}, but requested an effective batch" 522 | f" size of {batch_size}. Make sure the batch size matches the length of the generators." 523 | ) 524 | 525 | if latents is None: 526 | latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype) 527 | else: 528 | latents = latents.to(device) 529 | 530 | # scale the initial noise by the standard deviation required by the scheduler 531 | latents = latents * self.scheduler.init_noise_sigma 532 | return latents 533 | 534 | def _get_add_time_ids(self, original_size, crops_coords_top_left, target_size, dtype): 535 | add_time_ids = list(original_size + crops_coords_top_left + target_size) 536 | 537 | passed_add_embed_dim = ( 538 | self.unet.config.addition_time_embed_dim * len(add_time_ids) + self.text_encoder_2.config.projection_dim 539 | ) 540 | expected_add_embed_dim = self.unet.add_embedding.linear_1.in_features 541 | 542 | if expected_add_embed_dim != passed_add_embed_dim: 543 | raise ValueError( 544 | f"Model expects an added time embedding vector of length {expected_add_embed_dim}, but a vector of {passed_add_embed_dim} was created. The model has an incorrect config. Please check `unet.config.time_embedding_type` and `text_encoder_2.config.projection_dim`." 545 | ) 546 | 547 | add_time_ids = torch.tensor([add_time_ids], dtype=dtype) 548 | return add_time_ids 549 | 550 | # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_upscale.StableDiffusionUpscalePipeline.upcast_vae 551 | def upcast_vae(self): 552 | dtype = self.vae.dtype 553 | self.vae.to(dtype=torch.float32) 554 | use_torch_2_0_or_xformers = isinstance( 555 | self.vae.decoder.mid_block.attentions[0].processor, 556 | ( 557 | AttnProcessor2_0, 558 | XFormersAttnProcessor, 559 | LoRAXFormersAttnProcessor, 560 | LoRAAttnProcessor2_0, 561 | ), 562 | ) 563 | # if xformers or torch_2_0 is used attention block does not need 564 | # to be in float32 which can save lots of memory 565 | if use_torch_2_0_or_xformers: 566 | self.vae.post_quant_conv.to(dtype) 567 | self.vae.decoder.conv_in.to(dtype) 568 | self.vae.decoder.mid_block.to(dtype) 569 | 570 | def update_loss(self, latents, i, t, prompt_embeds, cross_attention_kwargs, add_text_embeds, add_time_ids): 571 | def forward_pass(latent_model_input): 572 | latent_model_input = self.scheduler.scale_model_input(latent_model_input, t) 573 | added_cond_kwargs = {"text_embeds": add_text_embeds, "time_ids": add_time_ids} 574 | self.unet( 575 | latent_model_input, 576 | t, 577 | encoder_hidden_states=prompt_embeds, 578 | cross_attention_kwargs=cross_attention_kwargs, 579 | added_cond_kwargs=added_cond_kwargs, 580 | return_dict=False, 581 | ) 582 | self.unet.zero_grad() 583 | 584 | return self.editor.update_loss(forward_pass, latents, i) 585 | 586 | @torch.no_grad() 587 | @replace_example_docstring(EXAMPLE_DOC_STRING) 588 | def __call__( 589 | self, 590 | prompt: Union[str, List[str]] = None, 591 | prompt_2: Optional[Union[str, List[str]]] = None, 592 | height: Optional[int] = None, 593 | width: Optional[int] = None, 594 | num_inference_steps: int = 50, 595 | denoising_end: Optional[float] = None, 596 | guidance_scale: float = 5.0, 597 | negative_prompt: Optional[Union[str, List[str]]] = None, 598 | negative_prompt_2: Optional[Union[str, List[str]]] = None, 599 | num_images_per_prompt: Optional[int] = 1, 600 | eta: float = 0.0, 601 | generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None, 602 | latents: Optional[torch.FloatTensor] = None, 603 | prompt_embeds: Optional[torch.FloatTensor] = None, 604 | negative_prompt_embeds: Optional[torch.FloatTensor] = None, 605 | pooled_prompt_embeds: Optional[torch.FloatTensor] = None, 606 | negative_pooled_prompt_embeds: Optional[torch.FloatTensor] = None, 607 | output_type: Optional[str] = "pil", 608 | return_dict: bool = True, 609 | callback: Optional[Callable[[int, int, torch.FloatTensor], None]] = None, 610 | callback_steps: int = 1, 611 | cross_attention_kwargs: Optional[Dict[str, Any]] = None, 612 | guidance_rescale: float = 0.0, 613 | original_size: Optional[Tuple[int, int]] = None, 614 | crops_coords_top_left: Tuple[int, int] = (0, 0), 615 | target_size: Optional[Tuple[int, int]] = None, 616 | ): 617 | r""" 618 | Function invoked when calling the pipeline for generation. 619 | 620 | Args: 621 | prompt (`str` or `List[str]`, *optional*): 622 | The prompt or prompts to guide the image generation. If not defined, one has to pass `prompt_embeds`. 623 | instead. 624 | prompt_2 (`str` or `List[str]`, *optional*): 625 | The prompt or prompts to be sent to the `tokenizer_2` and `text_encoder_2`. If not defined, `prompt` is 626 | used in both text-encoders 627 | height (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor): 628 | The height in pixels of the generated image. 629 | width (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor): 630 | The width in pixels of the generated image. 631 | num_inference_steps (`int`, *optional*, defaults to 50): 632 | The number of denoising steps. More denoising steps usually lead to a higher quality image at the 633 | expense of slower inference. 634 | denoising_end (`float`, *optional*): 635 | When specified, determines the fraction (between 0.0 and 1.0) of the total denoising process to be 636 | completed before it is intentionally prematurely terminated. As a result, the returned sample will 637 | still retain a substantial amount of noise as determined by the discrete timesteps selected by the 638 | scheduler. The denoising_end parameter should ideally be utilized when this pipeline forms a part of a 639 | "Mixture of Denoisers" multi-pipeline setup, as elaborated in [**Refining the Image 640 | Output**](https://huggingface.co/docs/diffusers/api/pipelines/stable_diffusion/stable_diffusion_xl#refining-the-image-output) 641 | guidance_scale (`float`, *optional*, defaults to 5.0): 642 | Guidance scale as defined in [Classifier-Free Diffusion Guidance](https://arxiv.org/abs/2207.12598). 643 | `guidance_scale` is defined as `w` of equation 2. of [Imagen 644 | Paper](https://arxiv.org/pdf/2205.11487.pdf). Guidance scale is enabled by setting `guidance_scale > 645 | 1`. Higher guidance scale encourages to generate images that are closely linked to the text `prompt`, 646 | usually at the expense of lower image quality. 647 | negative_prompt (`str` or `List[str]`, *optional*): 648 | The prompt or prompts not to guide the image generation. If not defined, one has to pass 649 | `negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is 650 | less than `1`). 651 | negative_prompt_2 (`str` or `List[str]`, *optional*): 652 | The prompt or prompts not to guide the image generation to be sent to `tokenizer_2` and 653 | `text_encoder_2`. If not defined, `negative_prompt` is used in both text-encoders 654 | num_images_per_prompt (`int`, *optional*, defaults to 1): 655 | The number of images to generate per prompt. 656 | eta (`float`, *optional*, defaults to 0.0): 657 | Corresponds to parameter eta (η) in the DDIM paper: https://arxiv.org/abs/2010.02502. Only applies to 658 | [`schedulers.DDIMScheduler`], will be ignored for others. 659 | generator (`torch.Generator` or `List[torch.Generator]`, *optional*): 660 | One or a list of [torch generator(s)](https://pytorch.org/docs/stable/generated/torch.Generator.html) 661 | to make generation deterministic. 662 | latents (`torch.FloatTensor`, *optional*): 663 | Pre-generated noisy latents, sampled from a Gaussian distribution, to be used as inputs for image 664 | generation. Can be used to tweak the same generation with different prompts. If not provided, a latents 665 | tensor will ge generated by sampling using the supplied random `generator`. 666 | prompt_embeds (`torch.FloatTensor`, *optional*): 667 | Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not 668 | provided, text embeddings will be generated from `prompt` input argument. 669 | negative_prompt_embeds (`torch.FloatTensor`, *optional*): 670 | Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt 671 | weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input 672 | argument. 673 | pooled_prompt_embeds (`torch.FloatTensor`, *optional*): 674 | Pre-generated pooled text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. 675 | If not provided, pooled text embeddings will be generated from `prompt` input argument. 676 | negative_pooled_prompt_embeds (`torch.FloatTensor`, *optional*): 677 | Pre-generated negative pooled text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt 678 | weighting. If not provided, pooled negative_prompt_embeds will be generated from `negative_prompt` 679 | input argument. 680 | output_type (`str`, *optional*, defaults to `"pil"`): 681 | The output format of the generate image. Choose between 682 | [PIL](https://pillow.readthedocs.io/en/stable/): `PIL.Image.Image` or `np.array`. 683 | return_dict (`bool`, *optional*, defaults to `True`): 684 | Whether or not to return a [`~pipelines.stable_diffusion_xl.StableDiffusionXLPipelineOutput`] instead 685 | of a plain tuple. 686 | callback (`Callable`, *optional*): 687 | A function that will be called every `callback_steps` steps during inference. The function will be 688 | called with the following arguments: `callback(step: int, timestep: int, latents: torch.FloatTensor)`. 689 | callback_steps (`int`, *optional*, defaults to 1): 690 | The frequency at which the `callback` function will be called. If not specified, the callback will be 691 | called at every step. 692 | cross_attention_kwargs (`dict`, *optional*): 693 | A kwargs dictionary that if specified is passed along to the `AttentionProcessor` as defined under 694 | `self.processor` in 695 | [diffusers.models.attention_processor](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py). 696 | guidance_rescale (`float`, *optional*, defaults to 0.7): 697 | Guidance rescale factor proposed by [Common Diffusion Noise Schedules and Sample Steps are 698 | Flawed](https://arxiv.org/pdf/2305.08891.pdf) `guidance_scale` is defined as `φ` in equation 16. of 699 | [Common Diffusion Noise Schedules and Sample Steps are Flawed](https://arxiv.org/pdf/2305.08891.pdf). 700 | Guidance rescale factor should fix overexposure when using zero terminal SNR. 701 | original_size (`Tuple[int]`, *optional*, defaults to (1024, 1024)): 702 | If `original_size` is not the same as `target_size` the image will appear to be down- or upsampled. 703 | `original_size` defaults to `(width, height)` if not specified. Part of SDXL's micro-conditioning as 704 | explained in section 2.2 of 705 | [https://huggingface.co/papers/2307.01952](https://huggingface.co/papers/2307.01952). 706 | crops_coords_top_left (`Tuple[int]`, *optional*, defaults to (0, 0)): 707 | `crops_coords_top_left` can be used to generate an image that appears to be "cropped" from the position 708 | `crops_coords_top_left` downwards. Favorable, well-centered images are usually achieved by setting 709 | `crops_coords_top_left` to (0, 0). Part of SDXL's micro-conditioning as explained in section 2.2 of 710 | [https://huggingface.co/papers/2307.01952](https://huggingface.co/papers/2307.01952). 711 | target_size (`Tuple[int]`, *optional*, defaults to (1024, 1024)): 712 | For most cases, `target_size` should be set to the desired height and width of the generated image. If 713 | not specified it will default to `(width, height)`. Part of SDXL's micro-conditioning as explained in 714 | section 2.2 of [https://huggingface.co/papers/2307.01952](https://huggingface.co/papers/2307.01952). 715 | 716 | Examples: 717 | 718 | Returns: 719 | [`~pipelines.stable_diffusion_xl.StableDiffusionXLPipelineOutput`] or `tuple`: 720 | [`~pipelines.stable_diffusion_xl.StableDiffusionXLPipelineOutput`] if `return_dict` is True, otherwise a 721 | `tuple`. When returning a tuple, the first element is a list with the generated images. 722 | """ 723 | # 0. Default height and width to unet 724 | height = height or self.default_sample_size * self.vae_scale_factor 725 | width = width or self.default_sample_size * self.vae_scale_factor 726 | 727 | original_size = original_size or (height, width) 728 | target_size = target_size or (height, width) 729 | 730 | # 1. Check inputs. Raise error if not correct 731 | self.check_inputs( 732 | prompt, 733 | prompt_2, 734 | height, 735 | width, 736 | callback_steps, 737 | negative_prompt, 738 | negative_prompt_2, 739 | prompt_embeds, 740 | negative_prompt_embeds, 741 | pooled_prompt_embeds, 742 | negative_pooled_prompt_embeds, 743 | ) 744 | 745 | # 2. Define call parameters 746 | if prompt is not None and isinstance(prompt, str): 747 | batch_size = 1 748 | elif prompt is not None and isinstance(prompt, list): 749 | batch_size = len(prompt) 750 | else: 751 | batch_size = prompt_embeds.shape[0] 752 | 753 | device = self._execution_device 754 | 755 | # here `guidance_scale` is defined analog to the guidance weight `w` of equation (2) 756 | # of the Imagen paper: https://arxiv.org/pdf/2205.11487.pdf . `guidance_scale = 1` 757 | # corresponds to doing no classifier free guidance. 758 | do_classifier_free_guidance = guidance_scale > 1.0 759 | 760 | # 3. Encode input prompt 761 | text_encoder_lora_scale = ( 762 | cross_attention_kwargs.get("scale", None) if cross_attention_kwargs is not None else None 763 | ) 764 | ( 765 | prompt_embeds, 766 | negative_prompt_embeds, 767 | pooled_prompt_embeds, 768 | negative_pooled_prompt_embeds, 769 | ) = self.encode_prompt( 770 | prompt=prompt, 771 | prompt_2=prompt_2, 772 | device=device, 773 | num_images_per_prompt=num_images_per_prompt, 774 | do_classifier_free_guidance=do_classifier_free_guidance, 775 | negative_prompt=negative_prompt, 776 | negative_prompt_2=negative_prompt_2, 777 | prompt_embeds=prompt_embeds, 778 | negative_prompt_embeds=negative_prompt_embeds, 779 | pooled_prompt_embeds=pooled_prompt_embeds, 780 | negative_pooled_prompt_embeds=negative_pooled_prompt_embeds, 781 | lora_scale=text_encoder_lora_scale, 782 | ) 783 | 784 | # 4. Prepare timesteps 785 | self.scheduler.set_timesteps(num_inference_steps, device=device) 786 | 787 | timesteps = self.scheduler.timesteps 788 | 789 | # 5. Prepare latent variables 790 | num_channels_latents = self.unet.config.in_channels 791 | latents = self.prepare_latents( 792 | batch_size * num_images_per_prompt, 793 | num_channels_latents, 794 | height, 795 | width, 796 | prompt_embeds.dtype, 797 | device, 798 | generator, 799 | latents, 800 | ) 801 | 802 | # 6. Prepare extra step kwargs. TODO: Logic should ideally just be moved out of the pipeline 803 | extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta) 804 | 805 | # 7. Prepare added time ids & embeddings 806 | add_text_embeds = pooled_prompt_embeds 807 | add_time_ids = self._get_add_time_ids( 808 | original_size, crops_coords_top_left, target_size, dtype=prompt_embeds.dtype 809 | ) 810 | 811 | if do_classifier_free_guidance: 812 | prompt_embeds = torch.cat([negative_prompt_embeds, prompt_embeds], dim=0) 813 | add_text_embeds = torch.cat([negative_pooled_prompt_embeds, add_text_embeds], dim=0) 814 | add_time_ids = torch.cat([add_time_ids, add_time_ids], dim=0) 815 | 816 | prompt_embeds = prompt_embeds.to(device) 817 | add_text_embeds = add_text_embeds.to(device) 818 | add_time_ids = add_time_ids.to(device).repeat(batch_size * num_images_per_prompt, 1) 819 | 820 | # 8. Denoising loop 821 | num_warmup_steps = max(len(timesteps) - num_inference_steps * self.scheduler.order, 0) 822 | 823 | # 7.1 Apply denoising_end 824 | if denoising_end is not None and type(denoising_end) == float and denoising_end > 0 and denoising_end < 1: 825 | discrete_timestep_cutoff = int( 826 | round( 827 | self.scheduler.config.num_train_timesteps 828 | - (denoising_end * self.scheduler.config.num_train_timesteps) 829 | ) 830 | ) 831 | num_inference_steps = len(list(filter(lambda ts: ts >= discrete_timestep_cutoff, timesteps))) 832 | timesteps = timesteps[:num_inference_steps] 833 | 834 | latents = latents.half() 835 | prompt_embeds = prompt_embeds.half() 836 | with self.progress_bar(total=num_inference_steps) as progress_bar: 837 | for i, t in enumerate(timesteps): 838 | latents = self.update_loss(latents, i, t, prompt_embeds, cross_attention_kwargs, add_text_embeds, add_time_ids) 839 | 840 | # expand the latents if we are doing classifier free guidance 841 | latent_model_input = torch.cat([latents] * 2) if do_classifier_free_guidance else latents 842 | 843 | latent_model_input = self.scheduler.scale_model_input(latent_model_input, t) 844 | 845 | # predict the noise residual 846 | added_cond_kwargs = {"text_embeds": add_text_embeds, "time_ids": add_time_ids} 847 | noise_pred = self.unet( 848 | latent_model_input, 849 | t, 850 | encoder_hidden_states=prompt_embeds, 851 | cross_attention_kwargs=cross_attention_kwargs, 852 | added_cond_kwargs=added_cond_kwargs, 853 | return_dict=False, 854 | )[0] 855 | 856 | # perform guidance 857 | if do_classifier_free_guidance: 858 | noise_pred_uncond, noise_pred_text = noise_pred.chunk(2) 859 | noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond) 860 | 861 | if do_classifier_free_guidance and guidance_rescale > 0.0: 862 | # Based on 3.4. in https://arxiv.org/pdf/2305.08891.pdf 863 | noise_pred = rescale_noise_cfg(noise_pred, noise_pred_text, guidance_rescale=guidance_rescale) 864 | 865 | # compute the previous noisy sample x_t -> x_t-1 866 | latents = self.scheduler.step(noise_pred, t, latents, **extra_step_kwargs, return_dict=False)[0] 867 | 868 | # call the callback, if provided 869 | if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0): 870 | progress_bar.update() 871 | if callback is not None and i % callback_steps == 0: 872 | callback(i, t, latents) 873 | 874 | # make sure the VAE is in float32 mode, as it overflows in float16 875 | if self.vae.dtype == torch.float16 and self.vae.config.force_upcast: 876 | self.upcast_vae() 877 | latents = latents.to(next(iter(self.vae.post_quant_conv.parameters())).dtype) 878 | 879 | if not output_type == "latent": 880 | image = self.vae.decode(latents / self.vae.config.scaling_factor, return_dict=False)[0] 881 | else: 882 | image = latents 883 | return StableDiffusionXLPipelineOutput(images=image) 884 | 885 | # apply watermark if available 886 | if self.watermark is not None: 887 | image = self.watermark.apply_watermark(image) 888 | 889 | image = self.image_processor.postprocess(image, output_type=output_type) 890 | 891 | # Offload last model to CPU 892 | if hasattr(self, "final_offload_hook") and self.final_offload_hook is not None: 893 | self.final_offload_hook.offload() 894 | 895 | if not return_dict: 896 | return (image,) 897 | 898 | return StableDiffusionXLPipelineOutput(images=image) 899 | 900 | # Overrride to properly handle the loading and unloading of the additional text encoder. 901 | def load_lora_weights(self, pretrained_model_name_or_path_or_dict: Union[str, Dict[str, torch.Tensor]], **kwargs): 902 | # We could have accessed the unet config from `lora_state_dict()` too. We pass 903 | # it here explicitly to be able to tell that it's coming from an SDXL 904 | # pipeline. 905 | state_dict, network_alphas = self.lora_state_dict( 906 | pretrained_model_name_or_path_or_dict, 907 | unet_config=self.unet.config, 908 | **kwargs, 909 | ) 910 | self.load_lora_into_unet(state_dict, network_alphas=network_alphas, unet=self.unet) 911 | 912 | text_encoder_state_dict = {k: v for k, v in state_dict.items() if "text_encoder." in k} 913 | if len(text_encoder_state_dict) > 0: 914 | self.load_lora_into_text_encoder( 915 | text_encoder_state_dict, 916 | network_alphas=network_alphas, 917 | text_encoder=self.text_encoder, 918 | prefix="text_encoder", 919 | lora_scale=self.lora_scale, 920 | ) 921 | 922 | text_encoder_2_state_dict = {k: v for k, v in state_dict.items() if "text_encoder_2." in k} 923 | if len(text_encoder_2_state_dict) > 0: 924 | self.load_lora_into_text_encoder( 925 | text_encoder_2_state_dict, 926 | network_alphas=network_alphas, 927 | text_encoder=self.text_encoder_2, 928 | prefix="text_encoder_2", 929 | lora_scale=self.lora_scale, 930 | ) 931 | 932 | @classmethod 933 | def save_lora_weights( 934 | self, 935 | save_directory: Union[str, os.PathLike], 936 | unet_lora_layers: Dict[str, Union[torch.nn.Module, torch.Tensor]] = None, 937 | text_encoder_lora_layers: Dict[str, Union[torch.nn.Module, torch.Tensor]] = None, 938 | text_encoder_2_lora_layers: Dict[str, Union[torch.nn.Module, torch.Tensor]] = None, 939 | is_main_process: bool = True, 940 | weight_name: str = None, 941 | save_function: Callable = None, 942 | safe_serialization: bool = True, 943 | ): 944 | state_dict = {} 945 | 946 | def pack_weights(layers, prefix): 947 | layers_weights = layers.state_dict() if isinstance(layers, torch.nn.Module) else layers 948 | layers_state_dict = {f"{prefix}.{module_name}": param for module_name, param in layers_weights.items()} 949 | return layers_state_dict 950 | 951 | state_dict.update(pack_weights(unet_lora_layers, "unet")) 952 | 953 | if text_encoder_lora_layers and text_encoder_2_lora_layers: 954 | state_dict.update(pack_weights(text_encoder_lora_layers, "text_encoder")) 955 | state_dict.update(pack_weights(text_encoder_2_lora_layers, "text_encoder_2")) 956 | 957 | self.write_lora_layers( 958 | state_dict=state_dict, 959 | save_directory=save_directory, 960 | is_main_process=is_main_process, 961 | weight_name=weight_name, 962 | save_function=save_function, 963 | safe_serialization=safe_serialization, 964 | ) 965 | 966 | def _remove_text_encoder_monkey_patch(self): 967 | self._remove_text_encoder_monkey_patch_classmethod(self.text_encoder) 968 | self._remove_text_encoder_monkey_patch_classmethod(self.text_encoder_2) 969 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | accelerate==0.21.0 2 | diffusers==0.20.0 3 | einops==0.6.1 4 | lightning-utilities==0.9.0 5 | matplotlib==3.7.3 6 | nltk==3.8.1 7 | numpy 8 | opencv-python==4.8.1.78 9 | Pillow==9.4.0 10 | pytorch-lightning==2.0.7 11 | scikit-image==0.22.0 12 | scikit-learn==1.3.1 13 | torch==2.0.1 14 | torch-kmeans==0.2.0 15 | torchvision==0.15.2 16 | transformers==4.32.0 17 | spaces==0.24.2 18 | xformers==0.0.21 19 | -------------------------------------------------------------------------------- /run_sd.py: -------------------------------------------------------------------------------- 1 | import os 2 | import torch 3 | import torchvision.transforms.functional as F 4 | 5 | from diffusers import DDIMScheduler 6 | from pipeline_stable_diffusion_opt import StableDiffusionPipeline 7 | from pytorch_lightning import seed_everything 8 | 9 | from injection_utils import register_attention_editor_diffusers 10 | from bounded_attention import BoundedAttention 11 | import utils 12 | 13 | 14 | def load_model(device): 15 | scheduler = DDIMScheduler(beta_start=0.00085, beta_end=0.012, beta_schedule="scaled_linear", clip_sample=False, set_alpha_to_one=False) 16 | model = StableDiffusionPipeline.from_pretrained("runwayml/stable-diffusion-v1-5", scheduler=scheduler, cross_attention_kwargs={"scale": 0.5}, torch_dtype=torch.float16, use_safetensors=True).to(device) 17 | model.enable_xformers_memory_efficient_attention() 18 | model.enable_sequential_cpu_offload() 19 | return model 20 | 21 | 22 | def run( 23 | boxes, 24 | prompt, 25 | subject_token_indices, 26 | out_dir='out', 27 | seed=160, 28 | batch_size=1, 29 | filter_token_indices=None, 30 | eos_token_index=None, 31 | init_step_size=8, 32 | final_step_size=2, 33 | first_refinement_step=15, 34 | num_clusters_per_subject=3, 35 | cross_loss_scale=1.5, 36 | self_loss_scale=0.5, 37 | classifier_free_guidance_scale=7.5, 38 | num_gd_iterations=5, 39 | loss_threshold=0.2, 40 | num_guidance_steps=15, 41 | ): 42 | device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") 43 | model = load_model(device) 44 | 45 | seed_everything(seed) 46 | prompts = [prompt] * batch_size 47 | start_code = torch.randn([len(prompts), 4, 64, 64], device=device) 48 | 49 | os.makedirs(out_dir, exist_ok=True) 50 | sample_count = len(os.listdir(out_dir)) 51 | out_dir = os.path.join(out_dir, f"sample_{sample_count}") 52 | os.makedirs(out_dir) 53 | 54 | editor = BoundedAttention( 55 | boxes, 56 | prompts, 57 | subject_token_indices, 58 | list(range(12, 20)), 59 | list(range(12, 20)), 60 | cross_mask_layers=list(range(14, 20)), 61 | self_mask_layers=list(range(14, 20)), 62 | filter_token_indices=filter_token_indices, 63 | eos_token_index=eos_token_index, 64 | cross_loss_coef=cross_loss_scale, 65 | self_loss_coef=self_loss_scale, 66 | max_guidance_iter=num_guidance_steps, 67 | max_guidance_iter_per_step=num_gd_iterations, 68 | start_step_size=init_step_size, 69 | end_step_size=final_step_size, 70 | loss_stopping_value=loss_threshold, 71 | min_clustering_step=first_refinement_step, 72 | num_clusters_per_box=num_clusters_per_subject, 73 | ) 74 | 75 | register_attention_editor_diffusers(model, editor) 76 | images = model(prompts, latents=start_code, guidance_scale=classifier_free_guidance_scale) 77 | 78 | for i, image in enumerate(images): 79 | image = F.to_pil_image(image) 80 | image.save(os.path.join(out_dir, f'{seed}_{i}.png')) 81 | utils.draw_box(image, boxes).save(os.path.join(out_dir, f'{seed}_{i}_boxes.png')) 82 | 83 | print("Syntheiszed images are saved in", out_dir) 84 | 85 | 86 | def main(): 87 | boxes = [ 88 | [0.05, 0.2, 0.45, 0.8], 89 | [0.55, 0.2, 0.95, 0.8], 90 | ] 91 | 92 | prompt = "A ginger kitten and a gray puppy in a yard" 93 | subject_token_indices = [[2, 3], [6, 7]] 94 | 95 | run(boxes, prompt, subject_token_indices, init_step_size=8, final_step_size=2) 96 | 97 | 98 | if __name__ == "__main__": 99 | main() 100 | -------------------------------------------------------------------------------- /run_xl.py: -------------------------------------------------------------------------------- 1 | import os 2 | import torch 3 | 4 | from diffusers import DDIMScheduler 5 | from pipeline_stable_diffusion_xl_opt import StableDiffusionXLPipeline 6 | from pytorch_lightning import seed_everything 7 | 8 | from injection_utils import register_attention_editor_diffusers 9 | from bounded_attention import BoundedAttention 10 | import utils 11 | 12 | 13 | def load_model(device): 14 | scheduler = DDIMScheduler(beta_start=0.00085, beta_end=0.012, beta_schedule="scaled_linear", clip_sample=False, set_alpha_to_one=False) 15 | model = StableDiffusionXLPipeline.from_pretrained("stabilityai/stable-diffusion-xl-base-1.0", scheduler=scheduler, torch_dtype=torch.float16).to(device) 16 | model.enable_xformers_memory_efficient_attention() 17 | model.enable_sequential_cpu_offload() 18 | return model 19 | 20 | 21 | def run( 22 | boxes, 23 | prompt, 24 | subject_token_indices, 25 | out_dir='out', 26 | seed=160, 27 | batch_size=1, 28 | filter_token_indices=None, 29 | eos_token_index=None, 30 | init_step_size=18, 31 | final_step_size=5, 32 | first_refinement_step=15, 33 | num_clusters_per_subject=3, 34 | cross_loss_scale=1.5, 35 | self_loss_scale=0.5, 36 | classifier_free_guidance_scale=7.5, 37 | num_gd_iterations=5, 38 | loss_threshold=0.2, 39 | num_guidance_steps=15, 40 | ): 41 | device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") 42 | model = load_model(device) 43 | 44 | seed_everything(seed) 45 | prompts = [prompt] * batch_size 46 | start_code = torch.randn([len(prompts), 4, 128, 128], device=device) 47 | 48 | os.makedirs(out_dir, exist_ok=True) 49 | sample_count = len(os.listdir(out_dir)) 50 | out_dir = os.path.join(out_dir, f"sample_{sample_count}") 51 | os.makedirs(out_dir) 52 | 53 | editor = BoundedAttention( 54 | boxes, 55 | prompts, 56 | subject_token_indices, 57 | list(range(70, 82)), 58 | list(range(70, 82)), 59 | filter_token_indices=filter_token_indices, 60 | eos_token_index=eos_token_index, 61 | cross_loss_coef=cross_loss_scale, 62 | self_loss_coef=self_loss_scale, 63 | max_guidance_iter=num_guidance_steps, 64 | max_guidance_iter_per_step=num_gd_iterations, 65 | start_step_size=init_step_size, 66 | end_step_size=final_step_size, 67 | loss_stopping_value=loss_threshold, 68 | min_clustering_step=first_refinement_step, 69 | num_clusters_per_box=num_clusters_per_subject, 70 | ) 71 | 72 | register_attention_editor_diffusers(model, editor) 73 | images = model(prompts, latents=start_code, guidance_scale=classifier_free_guidance_scale).images 74 | 75 | for i, image in enumerate(images): 76 | image.save(os.path.join(out_dir, f'{seed}_{i}.png')) 77 | utils.draw_box(image, boxes).save(os.path.join(out_dir, f'{seed}_{i}_boxes.png')) 78 | 79 | print("Syntheiszed images are saved in", out_dir) 80 | 81 | 82 | def main(): 83 | boxes = [ 84 | [0, 0.5, 0.2, 0.8], 85 | [0.2, 0.2, 0.4, 0.5], 86 | [0.4, 0.5, 0.6, 0.8], 87 | [0.6, 0.2, 0.8, 0.5], 88 | [0.8, 0.5, 1, 0.8], 89 | ] 90 | 91 | prompt = "a golden retriever and a german shepherd and a boston terrier and an english bulldog and a border collie in a pool" 92 | subject_token_indices = [[2, 3], [6, 7], [10, 11], [14, 15], [18, 19]] 93 | 94 | run(boxes, prompt, subject_token_indices, init_step_size=18, final_step_size=5) 95 | 96 | 97 | if __name__ == "__main__": 98 | main() 99 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from PIL import Image, ImageDraw, ImageFont 3 | import cv2 4 | from sklearn.decomposition import PCA 5 | from torchvision import transforms 6 | import matplotlib.pyplot as plt 7 | import torch 8 | 9 | import os 10 | 11 | 12 | def display_attention_maps( 13 | attention_maps, 14 | is_cross, 15 | num_heads, 16 | tokenizer, 17 | prompts, 18 | dir_name, 19 | step, 20 | layer, 21 | resolution, 22 | is_query=False, 23 | is_key=False, 24 | points=None, 25 | image_path=None, 26 | ): 27 | attention_maps = attention_maps.reshape(-1, num_heads, attention_maps.size(-2), attention_maps.size(-1)) 28 | num_samples = len(attention_maps) // 2 29 | attention_type = 'cross' if is_cross else 'self' 30 | for i, attention_map in enumerate(attention_maps): 31 | if is_query: 32 | attention_type = f'{attention_type}_queries' 33 | elif is_key: 34 | attention_type = f'{attention_type}_keys' 35 | 36 | cond = 'uncond' if i < num_samples else 'cond' 37 | i = i % num_samples 38 | cur_dir_name = f'{dir_name}/{resolution}/{attention_type}/{layer}/{cond}/{i}' 39 | os.makedirs(cur_dir_name, exist_ok=True) 40 | 41 | if is_cross and not is_query: 42 | fig = show_cross_attention(attention_map, tokenizer, prompts[i % num_samples]) 43 | else: 44 | fig = show_self_attention(attention_map) 45 | if points is not None: 46 | point_dir_name = f'{cur_dir_name}/points' 47 | os.makedirs(point_dir_name, exist_ok=True) 48 | for j, point in enumerate(points): 49 | specific_point_dir_name = f'{point_dir_name}/{j}' 50 | os.makedirs(specific_point_dir_name, exist_ok=True) 51 | point_path = f'{specific_point_dir_name}/{step}.png' 52 | point_fig = show_individual_self_attention(attention_map, point, image_path=image_path) 53 | point_fig.save(point_path) 54 | point_fig.close() 55 | 56 | fig.save(f'{cur_dir_name}/{step}.png') 57 | fig.close() 58 | 59 | 60 | def text_under_image(image: np.ndarray, text: str, text_color: tuple[int, int, int] = (0, 0, 0)): 61 | h, w, c = image.shape 62 | offset = int(h * .2) 63 | font = cv2.FONT_HERSHEY_SIMPLEX 64 | # font = ImageFont.truetype("/usr/share/fonts/truetype/noto/NotoMono-Regular.ttf", font_size) 65 | text_size = cv2.getTextSize(text, font, 1, 2)[0] 66 | lines = text.splitlines() 67 | img = np.ones((h + offset + (text_size[1] + 2) * len(lines) - 2, w, c), dtype=np.uint8) * 255 68 | img[:h, :w] = image 69 | 70 | for i, line in enumerate(lines): 71 | text_size = cv2.getTextSize(line, font, 1, 2)[0] 72 | text_x, text_y = ((w - text_size[0]) // 2, h + offset + i * (text_size[1] + 2)) 73 | cv2.putText(img, line, (text_x, text_y), font, 1, text_color, 2) 74 | 75 | return img 76 | 77 | def view_images(images, num_rows=1, offset_ratio=0.02): 78 | if type(images) is list: 79 | num_empty = len(images) % num_rows 80 | elif images.ndim == 4: 81 | num_empty = images.shape[0] % num_rows 82 | else: 83 | images = [images] 84 | num_empty = 0 85 | 86 | empty_images = np.ones(images[0].shape, dtype=np.uint8) * 255 87 | images = [image.astype(np.uint8) for image in images] + [empty_images] * num_empty 88 | num_items = len(images) 89 | 90 | h, w, c = images[0].shape 91 | offset = int(h * offset_ratio) 92 | num_cols = num_items // num_rows 93 | image_ = np.ones((h * num_rows + offset * (num_rows - 1), 94 | w * num_cols + offset * (num_cols - 1), 3), dtype=np.uint8) * 255 95 | for i in range(num_rows): 96 | for j in range(num_cols): 97 | image_[i * (h + offset): i * (h + offset) + h:, j * (w + offset): j * (w + offset) + w] = images[ 98 | i * num_cols + j] 99 | 100 | return Image.fromarray(image_) 101 | 102 | 103 | def show_cross_attention(attention_maps, tokenizer, prompt, k_norms=None, v_norms=None): 104 | attention_maps = attention_maps.mean(dim=0) 105 | res = int(attention_maps.size(-2) ** 0.5) 106 | attention_maps = attention_maps.reshape(res, res, -1) 107 | tokens = tokenizer.encode(prompt) 108 | decoder = tokenizer.decode 109 | if k_norms is not None: 110 | k_norms = k_norms.round(decimals=1) 111 | if v_norms is not None: 112 | v_norms = v_norms.round(decimals=1) 113 | images = [] 114 | for i in range(len(tokens) + 5): 115 | image = attention_maps[:, :, i] 116 | image = 255 * image / image.max() 117 | image = image.unsqueeze(-1).expand(*image.shape, 3) 118 | image = image.detach().cpu().numpy().astype(np.uint8) 119 | image = np.array(Image.fromarray(image).resize((256, 256))) 120 | token = tokens[i] if i < len(tokens) else tokens[-1] 121 | text = decoder(int(token)) 122 | if k_norms is not None and v_norms is not None: 123 | text += f'\n{k_norms[i]}\n{v_norms[i]})' 124 | image = text_under_image(image, text) 125 | images.append(image) 126 | return view_images(np.stack(images, axis=0)) 127 | 128 | 129 | def show_queries_keys(queries, keys, colors, labels): # [h ni d] 130 | num_queries = [query.size(1) for query in queries] 131 | num_keys = [key.size(1) for key in keys] 132 | h, _, d = queries[0].shape 133 | 134 | data = torch.cat((*queries, *keys), dim=1) # h n d 135 | data = data.permute(1, 0, 2) # n h d 136 | data = data.reshape(-1, h * d).detach().cpu().numpy() 137 | pca = PCA(n_components=2) 138 | data = pca.fit_transform(data) # n 2 139 | 140 | query_indices = np.array(num_queries).cumsum() 141 | total_num_queries = query_indices[-1] 142 | queries = np.split(data[:total_num_queries], query_indices[:-1]) 143 | if len(num_keys) == 0: 144 | keys = [None, ] * len(labels) 145 | else: 146 | key_indices = np.array(num_keys).cumsum() 147 | keys = np.split(data[total_num_queries:], key_indices[:-1]) 148 | 149 | fig, ax = plt.subplots() 150 | marker_size = plt.rcParams['lines.markersize'] ** 2 151 | query_size = int(1.25 * marker_size) 152 | key_size = int(2 * marker_size) 153 | for query, key, color, label in zip(queries, keys, colors, labels): 154 | print(f'# queries of {label}', query.shape[0]) 155 | ax.scatter(query[:, 0], query[:, 1], s=query_size, color=color, marker='o', label=f'"{label}" queries') 156 | 157 | if key is None: 158 | continue 159 | 160 | print(f'# keys of {label}', key.shape[0]) 161 | keys_label = f'"{label}" key' 162 | if key.shape[0] > 1: 163 | keys_label += 's' 164 | ax.scatter(key[:, 0], key[:, 1], s=key_size, color=color, marker='x', label=keys_label) 165 | 166 | ax.set_axis_off() 167 | #ax.set_xlabel('X-axis') 168 | #ax.set_ylabel('Y-axis') 169 | #ax.set_title('Scatter Plot with Circles and Crosses') 170 | 171 | #ax.legend() 172 | return fig 173 | 174 | 175 | def show_self_attention(attention_maps): # h n m 176 | attention_maps = attention_maps.transpose(0, 1).flatten(start_dim=1).detach().cpu().numpy() 177 | pca = PCA(n_components=3) 178 | pca_img = pca.fit_transform(attention_maps) # N X 3 179 | h = w = int(pca_img.shape[0] ** 0.5) 180 | pca_img = pca_img.reshape(h, w, 3) 181 | pca_img_min = pca_img.min(axis=(0, 1)) 182 | pca_img_max = pca_img.max(axis=(0, 1)) 183 | pca_img = (pca_img - pca_img_min) / (pca_img_max - pca_img_min) 184 | pca_img = Image.fromarray((pca_img * 255).astype(np.uint8)) 185 | pca_img = transforms.Resize(256, interpolation=transforms.InterpolationMode.NEAREST)(pca_img) 186 | return pca_img 187 | 188 | 189 | def draw_box(pil_img, bboxes, colors=None, width=5): 190 | draw = ImageDraw.Draw(pil_img) 191 | #font = ImageFont.truetype('./FreeMono.ttf', 25) 192 | w, h = pil_img.size 193 | colors = ['red'] * len(bboxes) if colors is None else colors 194 | for obj_bbox, color in zip(bboxes, colors): 195 | x_0, y_0, x_1, y_1 = obj_bbox[0], obj_bbox[1], obj_bbox[2], obj_bbox[3] 196 | draw.rectangle([int(x_0 * w), int(y_0 * h), int(x_1 * w), int(y_1 * h)], outline=color, width=width) 197 | return pil_img 198 | 199 | 200 | def show_individual_self_attention(attn, point, image_path=None): 201 | resolution = int(attn.size(-1) ** 0.5) 202 | attn = attn.mean(dim=0).reshape(resolution, resolution, resolution, resolution) 203 | attn = attn[round(point[1] * resolution), round(point[0] * resolution)] 204 | attn = (attn - attn.min()) / (attn.max() - attn.min()) 205 | image = None if image_path is None else Image.open(image_path).convert('RGB') 206 | image = show_image_relevance(attn, image=image) 207 | return Image.fromarray(image) 208 | 209 | 210 | def show_image_relevance(image_relevance, image: Image.Image = None, relevnace_res=16): 211 | # create heatmap from mask on image 212 | def show_cam_on_image(img, mask): 213 | img = img.resize((relevnace_res ** 2, relevnace_res ** 2)) 214 | img = np.array(img) 215 | img = (img - img.min()) / (img.max() - img.min()) 216 | heatmap = cv2.applyColorMap(np.uint8(255 * mask), cv2.COLORMAP_JET) 217 | heatmap = np.float32(heatmap) / 255 218 | cam = heatmap + np.float32(img) 219 | cam = cam / np.max(cam) 220 | return cam 221 | 222 | image_relevance = image_relevance.reshape(1, 1, image_relevance.shape[-1], image_relevance.shape[-1]) 223 | image_relevance = image_relevance.cuda() # because float16 precision interpolation is not supported on cpu 224 | image_relevance = torch.nn.functional.interpolate(image_relevance, size=relevnace_res ** 2, mode='bilinear') 225 | image_relevance = image_relevance.cpu() # send it back to cpu 226 | image_relevance = (image_relevance - image_relevance.min()) / (image_relevance.max() - image_relevance.min()) 227 | image_relevance = image_relevance.reshape(relevnace_res ** 2, relevnace_res ** 2) 228 | vis = image_relevance if image is None else show_cam_on_image(image, image_relevance) 229 | vis = np.uint8(255 * vis) 230 | vis = cv2.cvtColor(np.array(vis), cv2.COLOR_RGB2BGR) 231 | return vis 232 | --------------------------------------------------------------------------------