CoolFace
Datasetpublic

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.

sourceHugging Faceupdated 1mo agoView on Hugging Face
9likes21kdownloads
clip_guided_stable_diffusion.py325 linesDownload Raw Back to v0.5.0
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