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
9likes22kdownloads
clip_guided_stable_diffusion.py347 linesDownload Raw Back to v0.8.1
1import inspect2from typing import List, Optional, Union3 4import torch5from torch import nn6from torch.nn import functional as F7 8from diffusers import (9    AutoencoderKL,10    DDIMScheduler,11    DiffusionPipeline,12    LMSDiscreteScheduler,13    PNDMScheduler,14    UNet2DConditionModel,15)16from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion import StableDiffusionPipelineOutput17from torchvision import transforms18from transformers import CLIPFeatureExtractor, CLIPModel, CLIPTextModel, CLIPTokenizer19 20 21class MakeCutouts(nn.Module):22    def __init__(self, cut_size, cut_power=1.0):23        super().__init__()24 25        self.cut_size = cut_size26        self.cut_power = cut_power27 28    def forward(self, pixel_values, num_cutouts):29        sideY, sideX = pixel_values.shape[2:4]30        max_size = min(sideX, sideY)31        min_size = min(sideX, sideY, self.cut_size)32        cutouts = []33        for _ in range(num_cutouts):34            size = int(torch.rand([]) ** self.cut_power * (max_size - min_size) + min_size)35            offsetx = torch.randint(0, sideX - size + 1, ())36            offsety = torch.randint(0, sideY - size + 1, ())37            cutout = pixel_values[:, :, offsety : offsety + size, offsetx : offsetx + size]38            cutouts.append(F.adaptive_avg_pool2d(cutout, self.cut_size))39        return torch.cat(cutouts)40 41 42def spherical_dist_loss(x, y):43    x = F.normalize(x, dim=-1)44    y = F.normalize(y, dim=-1)45    return (x - y).norm(dim=-1).div(2).arcsin().pow(2).mul(2)46 47 48def set_requires_grad(model, value):49    for param in model.parameters():50        param.requires_grad = value51 52 53class CLIPGuidedStableDiffusion(DiffusionPipeline):54    """CLIP guided stable diffusion based on the amazing repo by @crowsonkb and @Jack00055    - https://github.com/Jack000/glid-3-xl56    - https://github.dev/crowsonkb/k-diffusion57    """58 59    def __init__(60        self,61        vae: AutoencoderKL,62        text_encoder: CLIPTextModel,63        clip_model: CLIPModel,64        tokenizer: CLIPTokenizer,65        unet: UNet2DConditionModel,66        scheduler: Union[PNDMScheduler, LMSDiscreteScheduler, DDIMScheduler],67        feature_extractor: CLIPFeatureExtractor,68    ):69        super().__init__()70        self.register_modules(71            vae=vae,72            text_encoder=text_encoder,73            clip_model=clip_model,74            tokenizer=tokenizer,75            unet=unet,76            scheduler=scheduler,77            feature_extractor=feature_extractor,78        )79 80        self.normalize = transforms.Normalize(mean=feature_extractor.image_mean, std=feature_extractor.image_std)81        self.make_cutouts = MakeCutouts(feature_extractor.size)82 83        set_requires_grad(self.text_encoder, False)84        set_requires_grad(self.clip_model, False)85 86    def enable_attention_slicing(self, slice_size: Optional[Union[str, int]] = "auto"):87        if slice_size == "auto":88            # half the attention head size is usually a good trade-off between89            # speed and memory90            slice_size = self.unet.config.attention_head_dim // 291        self.unet.set_attention_slice(slice_size)92 93    def disable_attention_slicing(self):94        self.enable_attention_slicing(None)95 96    def freeze_vae(self):97        set_requires_grad(self.vae, False)98 99    def unfreeze_vae(self):100        set_requires_grad(self.vae, True)101 102    def freeze_unet(self):103        set_requires_grad(self.unet, False)104 105    def unfreeze_unet(self):106        set_requires_grad(self.unet, True)107 108    @torch.enable_grad()109    def cond_fn(110        self,111        latents,112        timestep,113        index,114        text_embeddings,115        noise_pred_original,116        text_embeddings_clip,117        clip_guidance_scale,118        num_cutouts,119        use_cutouts=True,120    ):121        latents = latents.detach().requires_grad_()122 123        if isinstance(self.scheduler, LMSDiscreteScheduler):124            sigma = self.scheduler.sigmas[index]125            # the model input needs to be scaled to match the continuous ODE formulation in K-LMS126            latent_model_input = latents / ((sigma**2 + 1) ** 0.5)127        else:128            latent_model_input = latents129 130        # predict the noise residual131        noise_pred = self.unet(latent_model_input, timestep, encoder_hidden_states=text_embeddings).sample132 133        if isinstance(self.scheduler, (PNDMScheduler, DDIMScheduler)):134            alpha_prod_t = self.scheduler.alphas_cumprod[timestep]135            beta_prod_t = 1 - alpha_prod_t136            # compute predicted original sample from predicted noise also called137            # "predicted x_0" of formula (12) from https://arxiv.org/pdf/2010.02502.pdf138            pred_original_sample = (latents - beta_prod_t ** (0.5) * noise_pred) / alpha_prod_t ** (0.5)139 140            fac = torch.sqrt(beta_prod_t)141            sample = pred_original_sample * (fac) + latents * (1 - fac)142        elif isinstance(self.scheduler, LMSDiscreteScheduler):143            sigma = self.scheduler.sigmas[index]144            sample = latents - sigma * noise_pred145        else:146            raise ValueError(f"scheduler type {type(self.scheduler)} not supported")147 148        sample = 1 / 0.18215 * sample149        image = self.vae.decode(sample).sample150        image = (image / 2 + 0.5).clamp(0, 1)151 152        if use_cutouts:153            image = self.make_cutouts(image, num_cutouts)154        else:155            image = transforms.Resize(self.feature_extractor.size)(image)156        image = self.normalize(image).to(latents.dtype)157 158        image_embeddings_clip = self.clip_model.get_image_features(image)159        image_embeddings_clip = image_embeddings_clip / image_embeddings_clip.norm(p=2, dim=-1, keepdim=True)160 161        if use_cutouts:162            dists = spherical_dist_loss(image_embeddings_clip, text_embeddings_clip)163            dists = dists.view([num_cutouts, sample.shape[0], -1])164            loss = dists.sum(2).mean(0).sum() * clip_guidance_scale165        else:166            loss = spherical_dist_loss(image_embeddings_clip, text_embeddings_clip).mean() * clip_guidance_scale167 168        grads = -torch.autograd.grad(loss, latents)[0]169 170        if isinstance(self.scheduler, LMSDiscreteScheduler):171            latents = latents.detach() + grads * (sigma**2)172            noise_pred = noise_pred_original173        else:174            noise_pred = noise_pred_original - torch.sqrt(beta_prod_t) * grads175        return noise_pred, latents176 177    @torch.no_grad()178    def __call__(179        self,180        prompt: Union[str, List[str]],181        height: Optional[int] = 512,182        width: Optional[int] = 512,183        num_inference_steps: Optional[int] = 50,184        guidance_scale: Optional[float] = 7.5,185        num_images_per_prompt: Optional[int] = 1,186        eta: float = 0.0,187        clip_guidance_scale: Optional[float] = 100,188        clip_prompt: Optional[Union[str, List[str]]] = None,189        num_cutouts: Optional[int] = 4,190        use_cutouts: Optional[bool] = True,191        generator: Optional[torch.Generator] = None,192        latents: Optional[torch.FloatTensor] = None,193        output_type: Optional[str] = "pil",194        return_dict: bool = True,195    ):196        if isinstance(prompt, str):197            batch_size = 1198        elif isinstance(prompt, list):199            batch_size = len(prompt)200        else:201            raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")202 203        if height % 8 != 0 or width % 8 != 0:204            raise ValueError(f"`height` and `width` have to be divisible by 8 but are {height} and {width}.")205 206        # get prompt text embeddings207        text_input = self.tokenizer(208            prompt,209            padding="max_length",210            max_length=self.tokenizer.model_max_length,211            truncation=True,212            return_tensors="pt",213        )214        text_embeddings = self.text_encoder(text_input.input_ids.to(self.device))[0]215        # duplicate text embeddings for each generation per prompt216        text_embeddings = text_embeddings.repeat_interleave(num_images_per_prompt, dim=0)217 218        if clip_guidance_scale > 0:219            if clip_prompt is not None:220                clip_text_input = self.tokenizer(221                    clip_prompt,222                    padding="max_length",223                    max_length=self.tokenizer.model_max_length,224                    truncation=True,225                    return_tensors="pt",226                ).input_ids.to(self.device)227            else:228                clip_text_input = text_input.input_ids.to(self.device)229            text_embeddings_clip = self.clip_model.get_text_features(clip_text_input)230            text_embeddings_clip = text_embeddings_clip / text_embeddings_clip.norm(p=2, dim=-1, keepdim=True)231            # duplicate text embeddings clip for each generation per prompt232            text_embeddings_clip = text_embeddings_clip.repeat_interleave(num_images_per_prompt, dim=0)233 234        # here `guidance_scale` is defined analog to the guidance weight `w` of equation (2)235        # of the Imagen paper: https://arxiv.org/pdf/2205.11487.pdf . `guidance_scale = 1`236        # corresponds to doing no classifier free guidance.237        do_classifier_free_guidance = guidance_scale > 1.0238        # get unconditional embeddings for classifier free guidance239        if do_classifier_free_guidance:240            max_length = text_input.input_ids.shape[-1]241            uncond_input = self.tokenizer([""], padding="max_length", max_length=max_length, return_tensors="pt")242            uncond_embeddings = self.text_encoder(uncond_input.input_ids.to(self.device))[0]243            # duplicate unconditional embeddings for each generation per prompt244            uncond_embeddings = uncond_embeddings.repeat_interleave(num_images_per_prompt, dim=0)245 246            # For classifier free guidance, we need to do two forward passes.247            # Here we concatenate the unconditional and text embeddings into a single batch248            # to avoid doing two forward passes249            text_embeddings = torch.cat([uncond_embeddings, text_embeddings])250 251        # get the initial random noise unless the user supplied it252 253        # Unlike in other pipelines, latents need to be generated in the target device254        # for 1-to-1 results reproducibility with the CompVis implementation.255        # However this currently doesn't work in `mps`.256        latents_shape = (batch_size * num_images_per_prompt, self.unet.in_channels, height // 8, width // 8)257        latents_dtype = text_embeddings.dtype258        if latents is None:259            if self.device.type == "mps":260                # randn does not work reproducibly on mps261                latents = torch.randn(latents_shape, generator=generator, device="cpu", dtype=latents_dtype).to(262                    self.device263                )264            else:265                latents = torch.randn(latents_shape, generator=generator, device=self.device, dtype=latents_dtype)266        else:267            if latents.shape != latents_shape:268                raise ValueError(f"Unexpected latents shape, got {latents.shape}, expected {latents_shape}")269            latents = latents.to(self.device)270 271        # set timesteps272        accepts_offset = "offset" in set(inspect.signature(self.scheduler.set_timesteps).parameters.keys())273        extra_set_kwargs = {}274        if accepts_offset:275            extra_set_kwargs["offset"] = 1276 277        self.scheduler.set_timesteps(num_inference_steps, **extra_set_kwargs)278 279        # Some schedulers like PNDM have timesteps as arrays280        # It's more optimized to move all timesteps to correct device beforehand281        timesteps_tensor = self.scheduler.timesteps.to(self.device)282 283        # scale the initial noise by the standard deviation required by the scheduler284        latents = latents * self.scheduler.init_noise_sigma285 286        # prepare extra kwargs for the scheduler step, since not all schedulers have the same signature287        # eta (η) is only used with the DDIMScheduler, it will be ignored for other schedulers.288        # eta corresponds to η in DDIM paper: https://arxiv.org/abs/2010.02502289        # and should be between [0, 1]290        accepts_eta = "eta" in set(inspect.signature(self.scheduler.step).parameters.keys())291        extra_step_kwargs = {}292        if accepts_eta:293            extra_step_kwargs["eta"] = eta294 295        # check if the scheduler accepts generator296        accepts_generator = "generator" in set(inspect.signature(self.scheduler.step).parameters.keys())297        if accepts_generator:298            extra_step_kwargs["generator"] = generator299 300        for i, t in enumerate(self.progress_bar(timesteps_tensor)):301            # expand the latents if we are doing classifier free guidance302            latent_model_input = torch.cat([latents] * 2) if do_classifier_free_guidance else latents303            latent_model_input = self.scheduler.scale_model_input(latent_model_input, t)304 305            # predict the noise residual306            noise_pred = self.unet(latent_model_input, t, encoder_hidden_states=text_embeddings).sample307 308            # perform classifier free guidance309            if do_classifier_free_guidance:310                noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)311                noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond)312 313            # perform clip guidance314            if clip_guidance_scale > 0:315                text_embeddings_for_guidance = (316                    text_embeddings.chunk(2)[1] if do_classifier_free_guidance else text_embeddings317                )318                noise_pred, latents = self.cond_fn(319                    latents,320                    t,321                    i,322                    text_embeddings_for_guidance,323                    noise_pred,324                    text_embeddings_clip,325                    clip_guidance_scale,326                    num_cutouts,327                    use_cutouts,328                )329 330            # compute the previous noisy sample x_t -> x_t-1331            latents = self.scheduler.step(noise_pred, t, latents, **extra_step_kwargs).prev_sample332 333        # scale and decode the image latents with vae334        latents = 1 / 0.18215 * latents335        image = self.vae.decode(latents).sample336 337        image = (image / 2 + 0.5).clamp(0, 1)338        image = image.cpu().permute(0, 2, 3, 1).numpy()339 340        if output_type == "pil":341            image = self.numpy_to_pil(image)342 343        if not return_dict:344            return (image, None)345 346        return StableDiffusionPipelineOutput(images=image, nsfw_content_detected=None)347