diffusers/community-pipelines-mirror
Community Pipeline Examples For more information about community pipelines, please have a look at this issue. Community pipeline examples consist pipelines that have been added by the community. Please have a look at the following tables to get an overview of all community examples. Click on the Code Example to get a copy-and-paste ready code example that you can try out. If a community pipeline doesn't work as expected, please open an issue and ping the author on it. Please… See the full description on the dataset page: https://huggingface.co/datasets/diffusers/community-pipelines-mirror.
921k
1import inspect2from typing import List, Optional, Union3 4import torch5from torch import nn6from torch.nn import functional as F7 8from diffusers import AutoencoderKL, DiffusionPipeline, LMSDiscreteScheduler, PNDMScheduler, UNet2DConditionModel9from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion import StableDiffusionPipelineOutput10from torchvision import transforms11from transformers import CLIPFeatureExtractor, CLIPModel, CLIPTextModel, CLIPTokenizer12 13 14class MakeCutouts(nn.Module):15 def __init__(self, cut_size, cut_power=1.0):16 super().__init__()17 18 self.cut_size = cut_size19 self.cut_power = cut_power20 21 def forward(self, pixel_values, num_cutouts):22 sideY, sideX = pixel_values.shape[2:4]23 max_size = min(sideX, sideY)24 min_size = min(sideX, sideY, self.cut_size)25 cutouts = []26 for _ in range(num_cutouts):27 size = int(torch.rand([]) ** self.cut_power * (max_size - min_size) + min_size)28 offsetx = torch.randint(0, sideX - size + 1, ())29 offsety = torch.randint(0, sideY - size + 1, ())30 cutout = pixel_values[:, :, offsety : offsety + size, offsetx : offsetx + size]31 cutouts.append(F.adaptive_avg_pool2d(cutout, self.cut_size))32 return torch.cat(cutouts)33 34 35def spherical_dist_loss(x, y):36 x = F.normalize(x, dim=-1)37 y = F.normalize(y, dim=-1)38 return (x - y).norm(dim=-1).div(2).arcsin().pow(2).mul(2)39 40 41def set_requires_grad(model, value):42 for param in model.parameters():43 param.requires_grad = value44 45 46class CLIPGuidedStableDiffusion(DiffusionPipeline):47 """CLIP guided stable diffusion based on the amazing repo by @crowsonkb and @Jack00048 - https://github.com/Jack000/glid-3-xl49 - https://github.dev/crowsonkb/k-diffusion50 """51 52 def __init__(53 self,54 vae: AutoencoderKL,55 text_encoder: CLIPTextModel,56 clip_model: CLIPModel,57 tokenizer: CLIPTokenizer,58 unet: UNet2DConditionModel,59 scheduler: Union[PNDMScheduler, LMSDiscreteScheduler],60 feature_extractor: CLIPFeatureExtractor,61 ):62 super().__init__()63 self.register_modules(64 vae=vae,65 text_encoder=text_encoder,66 clip_model=clip_model,67 tokenizer=tokenizer,68 unet=unet,69 scheduler=scheduler,70 feature_extractor=feature_extractor,71 )72 73 self.normalize = transforms.Normalize(mean=feature_extractor.image_mean, std=feature_extractor.image_std)74 self.make_cutouts = MakeCutouts(feature_extractor.size)75 76 set_requires_grad(self.text_encoder, False)77 set_requires_grad(self.clip_model, False)78 79 def enable_attention_slicing(self, slice_size: Optional[Union[str, int]] = "auto"):80 if slice_size == "auto":81 # half the attention head size is usually a good trade-off between82 # speed and memory83 slice_size = self.unet.config.attention_head_dim // 284 self.unet.set_attention_slice(slice_size)85 86 def disable_attention_slicing(self):87 self.enable_attention_slicing(None)88 89 def freeze_vae(self):90 set_requires_grad(self.vae, False)91 92 def unfreeze_vae(self):93 set_requires_grad(self.vae, True)94 95 def freeze_unet(self):96 set_requires_grad(self.unet, False)97 98 def unfreeze_unet(self):99 set_requires_grad(self.unet, True)100 101 @torch.enable_grad()102 def cond_fn(103 self,104 latents,105 timestep,106 index,107 text_embeddings,108 noise_pred_original,109 text_embeddings_clip,110 clip_guidance_scale,111 num_cutouts,112 use_cutouts=True,113 ):114 latents = latents.detach().requires_grad_()115 116 if isinstance(self.scheduler, LMSDiscreteScheduler):117 sigma = self.scheduler.sigmas[index]118 # the model input needs to be scaled to match the continuous ODE formulation in K-LMS119 latent_model_input = latents / ((sigma**2 + 1) ** 0.5)120 else:121 latent_model_input = latents122 123 # predict the noise residual124 noise_pred = self.unet(latent_model_input, timestep, encoder_hidden_states=text_embeddings).sample125 126 if isinstance(self.scheduler, PNDMScheduler):127 alpha_prod_t = self.scheduler.alphas_cumprod[timestep]128 beta_prod_t = 1 - alpha_prod_t129 # compute predicted original sample from predicted noise also called130 # "predicted x_0" of formula (12) from https://arxiv.org/pdf/2010.02502.pdf131 pred_original_sample = (latents - beta_prod_t ** (0.5) * noise_pred) / alpha_prod_t ** (0.5)132 133 fac = torch.sqrt(beta_prod_t)134 sample = pred_original_sample * (fac) + latents * (1 - fac)135 elif isinstance(self.scheduler, LMSDiscreteScheduler):136 sigma = self.scheduler.sigmas[index]137 sample = latents - sigma * noise_pred138 else:139 raise ValueError(f"scheduler type {type(self.scheduler)} not supported")140 141 sample = 1 / 0.18215 * sample142 image = self.vae.decode(sample).sample143 image = (image / 2 + 0.5).clamp(0, 1)144 145 if use_cutouts:146 image = self.make_cutouts(image, num_cutouts)147 else:148 image = transforms.Resize(self.feature_extractor.size)(image)149 image = self.normalize(image).to(latents.dtype)150 151 image_embeddings_clip = self.clip_model.get_image_features(image)152 image_embeddings_clip = image_embeddings_clip / image_embeddings_clip.norm(p=2, dim=-1, keepdim=True)153 154 if use_cutouts:155 dists = spherical_dist_loss(image_embeddings_clip, text_embeddings_clip)156 dists = dists.view([num_cutouts, sample.shape[0], -1])157 loss = dists.sum(2).mean(0).sum() * clip_guidance_scale158 else:159 loss = spherical_dist_loss(image_embeddings_clip, text_embeddings_clip).mean() * clip_guidance_scale160 161 grads = -torch.autograd.grad(loss, latents)[0]162 163 if isinstance(self.scheduler, LMSDiscreteScheduler):164 latents = latents.detach() + grads * (sigma**2)165 noise_pred = noise_pred_original166 else:167 noise_pred = noise_pred_original - torch.sqrt(beta_prod_t) * grads168 return noise_pred, latents169 170 @torch.no_grad()171 def __call__(172 self,173 prompt: Union[str, List[str]],174 height: Optional[int] = 512,175 width: Optional[int] = 512,176 num_inference_steps: Optional[int] = 50,177 guidance_scale: Optional[float] = 7.5,178 num_images_per_prompt: Optional[int] = 1,179 clip_guidance_scale: Optional[float] = 100,180 clip_prompt: Optional[Union[str, List[str]]] = None,181 num_cutouts: Optional[int] = 4,182 use_cutouts: Optional[bool] = True,183 generator: Optional[torch.Generator] = None,184 latents: Optional[torch.FloatTensor] = None,185 output_type: Optional[str] = "pil",186 return_dict: bool = True,187 ):188 if isinstance(prompt, str):189 batch_size = 1190 elif isinstance(prompt, list):191 batch_size = len(prompt)192 else:193 raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")194 195 if height % 8 != 0 or width % 8 != 0:196 raise ValueError(f"`height` and `width` have to be divisible by 8 but are {height} and {width}.")197 198 # get prompt text embeddings199 text_input = self.tokenizer(200 prompt,201 padding="max_length",202 max_length=self.tokenizer.model_max_length,203 truncation=True,204 return_tensors="pt",205 )206 text_embeddings = self.text_encoder(text_input.input_ids.to(self.device))[0]207 # duplicate text embeddings for each generation per prompt208 text_embeddings = text_embeddings.repeat_interleave(num_images_per_prompt, dim=0)209 210 if clip_guidance_scale > 0:211 if clip_prompt is not None:212 clip_text_input = self.tokenizer(213 clip_prompt,214 padding="max_length",215 max_length=self.tokenizer.model_max_length,216 truncation=True,217 return_tensors="pt",218 ).input_ids.to(self.device)219 else:220 clip_text_input = text_input.input_ids.to(self.device)221 text_embeddings_clip = self.clip_model.get_text_features(clip_text_input)222 text_embeddings_clip = text_embeddings_clip / text_embeddings_clip.norm(p=2, dim=-1, keepdim=True)223 # duplicate text embeddings clip for each generation per prompt224 text_embeddings_clip = text_embeddings_clip.repeat_interleave(num_images_per_prompt, dim=0)225 226 # here `guidance_scale` is defined analog to the guidance weight `w` of equation (2)227 # of the Imagen paper: https://arxiv.org/pdf/2205.11487.pdf . `guidance_scale = 1`228 # corresponds to doing no classifier free guidance.229 do_classifier_free_guidance = guidance_scale > 1.0230 # get unconditional embeddings for classifier free guidance231 if do_classifier_free_guidance:232 max_length = text_input.input_ids.shape[-1]233 uncond_input = self.tokenizer([""], padding="max_length", max_length=max_length, return_tensors="pt")234 uncond_embeddings = self.text_encoder(uncond_input.input_ids.to(self.device))[0]235 # duplicate unconditional embeddings for each generation per prompt236 uncond_embeddings = uncond_embeddings.repeat_interleave(num_images_per_prompt, dim=0)237 238 # For classifier free guidance, we need to do two forward passes.239 # Here we concatenate the unconditional and text embeddings into a single batch240 # to avoid doing two forward passes241 text_embeddings = torch.cat([uncond_embeddings, text_embeddings])242 243 # get the initial random noise unless the user supplied it244 245 # Unlike in other pipelines, latents need to be generated in the target device246 # for 1-to-1 results reproducibility with the CompVis implementation.247 # However this currently doesn't work in `mps`.248 latents_shape = (batch_size * num_images_per_prompt, self.unet.in_channels, height // 8, width // 8)249 latents_dtype = text_embeddings.dtype250 if latents is None:251 if self.device.type == "mps":252 # randn does not exist on mps253 latents = torch.randn(latents_shape, generator=generator, device="cpu", dtype=latents_dtype).to(254 self.device255 )256 else:257 latents = torch.randn(latents_shape, generator=generator, device=self.device, dtype=latents_dtype)258 else:259 if latents.shape != latents_shape:260 raise ValueError(f"Unexpected latents shape, got {latents.shape}, expected {latents_shape}")261 latents = latents.to(self.device)262 263 # set timesteps264 accepts_offset = "offset" in set(inspect.signature(self.scheduler.set_timesteps).parameters.keys())265 extra_set_kwargs = {}266 if accepts_offset:267 extra_set_kwargs["offset"] = 1268 269 self.scheduler.set_timesteps(num_inference_steps, **extra_set_kwargs)270 271 # Some schedulers like PNDM have timesteps as arrays272 # It's more optimized to move all timesteps to correct device beforehand273 timesteps_tensor = self.scheduler.timesteps.to(self.device)274 275 # scale the initial noise by the standard deviation required by the scheduler276 latents = latents * self.scheduler.init_noise_sigma277 278 for i, t in enumerate(self.progress_bar(timesteps_tensor)):279 # expand the latents if we are doing classifier free guidance280 latent_model_input = torch.cat([latents] * 2) if do_classifier_free_guidance else latents281 latent_model_input = self.scheduler.scale_model_input(latent_model_input, t)282 283 # predict the noise residual284 noise_pred = self.unet(latent_model_input, t, encoder_hidden_states=text_embeddings).sample285 286 # perform classifier free guidance287 if do_classifier_free_guidance:288 noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)289 noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond)290 291 # perform clip guidance292 if clip_guidance_scale > 0:293 text_embeddings_for_guidance = (294 text_embeddings.chunk(2)[1] if do_classifier_free_guidance else text_embeddings295 )296 noise_pred, latents = self.cond_fn(297 latents,298 t,299 i,300 text_embeddings_for_guidance,301 noise_pred,302 text_embeddings_clip,303 clip_guidance_scale,304 num_cutouts,305 use_cutouts,306 )307 308 # compute the previous noisy sample x_t -> x_t-1309 latents = self.scheduler.step(noise_pred, t, latents).prev_sample310 311 # scale and decode the image latents with vae312 latents = 1 / 0.18215 * latents313 image = self.vae.decode(latents).sample314 315 image = (image / 2 + 0.5).clamp(0, 1)316 image = image.cpu().permute(0, 2, 3, 1).numpy()317 318 if output_type == "pil":319 image = self.numpy_to_pil(image)320 321 if not return_dict:322 return (image, None)323 324 return StableDiffusionPipelineOutput(images=image, nsfw_content_detected=None)325 