当前位置:   article > 正文

【diffusers】(一) diffusers库介绍 & 框架代码解析

diffusers

1.简介

说到现在最常用的stable diffusion代码,那肯定莫过于stable-diffusion-webui了,它的快捷安装、可视化界面、extension模块等等功能都拓展了使用人群。

虽然在大多数情况下webui都有很好的适用性,但是在某些特殊需求或者应用场景下,我们需要对模型部分结构进行修改(比如把condition模块从文字换成图像,甚至是点云、图表、图结构等数据形式),这时修改模型的同时也需要修改前端可视化代码,时间成本上会较高(主要是我也不会Gradio)。

那可不可以在源码上进行修改呢?我去看了一遍Compvis的1.x版本代码和Stability AI的2.x版本代码,整体的布局和层次都稍微有点混乱(主要是底层部分,以及可替换模块的结构统一性),没有训练验证代码,最关键的是注释太!少!了!

因此,在这个【diffusers】专栏的几篇文章里,我主要给大家介绍一下这个好用的diffusers库,后续会包括整体的框架结构、其中关键的模块、训练及验证的pipe、以及进行自定义修改。


2.diffusers

diffusers是Hugging Face推出的一个diffusion库,它提供了简单方便的diffusion推理训练pipe,同时拥有一个模型和数据社区,代码可以像torchhub一样直接从指定的仓库去调用别人上传的数据集和pretrain checkpoint。除此之外,安装方便,代码结构清晰,注释齐全,二次开发会十分有效率。


3.安装

傻瓜式安装,使用pip或者conda即可。

  1. # pip
  2. pip install --upgrade diffusers[torch]
  3. # conda
  4. conda install -c conda-forge diffusers

4.使用

diffusers使用pipeline类封装各个模块,以及安排其中的调用顺序和输出。

我们来举个例子,以下代码调用StableDiffusionPipeline,from_pretrained是指从特定仓库加载别人预训练好的模型。其中model_id可以是本地的路径,如果本地没找到对应的文件,则会自动去Hugging Face的Community中去自动下载。

  1. from diffusers import StableDiffusionPipeline
  2. model_id = "runwayml/stable-diffusion-v1-5"
  3. stable_diffusion_txt2img = StableDiffusionPipeline.from_pretrained(model_id)

需要注意的是,下载需要科学上网。如果科学上网之后还是下载有问题,可以在model_id前面加上https://huggingface.co/,然后进入对应网站,找到HTTPS或SSH直接用git clone去下载。比如在上述代码里,网址就是https://huggingface.co/runwayml/stable-diffusion-v1-5,然后点击下图里的clone repository用git下载。下载好后记得把文件夹放在对应的路径上。

如果我们不想用别人的模板,或者我想在模板基础上进行修改,应该怎么办呢?

这里我们可以先定义其中的模块,包括Unet、VAE、CLIP、scheduler等,进行编辑和修改后再输入到pipe中去。

  1. from diffusers import StableDiffusionPipeline, DDPMScheduler
  2. model_id = "runwayml/stable-diffusion-v1-5"
  3. scheduler = DDPMScheduler.from_pretrained(model_id)
  4. stable_diffusion_txt2img = StableDiffusionPipeline.from_pretrained(model_id, scheduler=scheduler)

ok,不同的inpainting、txt2img、img2img的pipeline,以及里面不同种类的Unet、VAE、scheduler的替换和自定义我们会在以后单独介绍,现在我们先来看看代码怎么进行推理。

  1. from diffusers import StableDiffusionPipeline
  2. model_id = "runwayml/stable-diffusion-v1-5"
  3. stable_diffusion_txt2img = StableDiffusionPipeline.from_pretrained(model_id)
  4. prompt = "A boy wearing white suspenders and playing basketball"
  5. image = stable_diffusion_txt2img (prompt).images[0]
  6. image.save('generated_image.png')

简单来说,我们只需要把文字prompt输入进去,然后就会按照StableDiffusionPipeline中设置好的流程来进行推理。


5.推理

我们看下StableDiffusionPipeline源码里的具体推理流程,便于更好理解SD,也便于后续进行修改和自定义。

  1. # 0. 定义unet中的高宽,这里要考虑经过vae后的缩小系数
  2. height = height or self.unet.config.sample_size * self.vae_scale_factor
  3. width = width or self.unet.config.sample_size * self.vae_scale_factor
  4. # 1. 检查输入是否合规
  5. self.check_inputs(
  6. prompt, height, width, callback_steps, negative_prompt, prompt_embeds, negative_prompt_embeds
  7. )
  8. # 2. 定义调用的batch,device以及classifier-free的监督系数
  9. if prompt is not None and isinstance(prompt, str):
  10. batch_size = 1
  11. elif prompt is not None and isinstance(prompt, list):
  12. batch_size = len(prompt)
  13. else:
  14. batch_size = prompt_embeds.shape[0]
  15. device = self._execution_device
  16. do_classifier_free_guidance = guidance_scale > 1.0
  17. # 3. 将输入的文字prompt进行encoding
  18. text_encoder_lora_scale = (
  19. cross_attention_kwargs.get("scale", None) if cross_attention_kwargs is not None else None
  20. )
  21. prompt_embeds = self._encode_prompt(
  22. prompt,
  23. device,
  24. num_images_per_prompt,
  25. do_classifier_free_guidance,
  26. negative_prompt,
  27. prompt_embeds=prompt_embeds,
  28. negative_prompt_embeds=negative_prompt_embeds,
  29. lora_scale=text_encoder_lora_scale,
  30. )
  31. # 4. 准备scheduler中的时间步数
  32. self.scheduler.set_timesteps(num_inference_steps, device=device)
  33. timesteps = self.scheduler.timesteps
  34. # 5. 生成对应尺寸的初始噪声图latent
  35. num_channels_latents = self.unet.config.in_channels
  36. latents = self.prepare_latents(
  37. batch_size * num_images_per_prompt,
  38. num_channels_latents,
  39. height,
  40. width,
  41. prompt_embeds.dtype,
  42. device,
  43. generator,
  44. latents,
  45. )
  46. # 6. 额外的去噪参数
  47. extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta)
  48. # 7. 循环多个步长进行去噪
  49. num_warmup_steps = len(timesteps) - num_inference_steps * self.scheduler.order
  50. with self.progress_bar(total=num_inference_steps) as progress_bar:
  51. for i, t in enumerate(timesteps):
  52. latent_model_input = torch.cat([latents] * 2) if do_classifier_free_guidance else latents
  53. latent_model_input = self.scheduler.scale_model_input(latent_model_input, t)
  54. noise_pred = self.unet(
  55. latent_model_input,
  56. t,
  57. encoder_hidden_states=prompt_embeds,
  58. cross_attention_kwargs=cross_attention_kwargs,
  59. return_dict=False,
  60. )[0]
  61. if do_classifier_free_guidance:
  62. noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
  63. noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond)
  64. if do_classifier_free_guidance and guidance_rescale > 0.0:
  65. noise_pred = rescale_noise_cfg(noise_pred, noise_pred_text, guidance_rescale=guidance_rescale)
  66. latents = self.scheduler.step(noise_pred, t, latents, **extra_step_kwargs, return_dict=False)[0]
  67. if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
  68. progress_bar.update()
  69. if callback is not None and i % callback_steps == 0:
  70. callback(i, t, latents)
  71. if not output_type == "latent":
  72. image = self.vae.decode(latents / self.vae.config.scaling_factor, return_dict=False)[0]
  73. image, has_nsfw_concept = self.run_safety_checker(image, device, prompt_embeds.dtype)
  74. else:
  75. image = latents
  76. has_nsfw_concept = None
  77. if has_nsfw_concept is None:
  78. do_denormalize = [True] * image.shape[0]
  79. else:
  80. do_denormalize = [not has_nsfw for has_nsfw in has_nsfw_concept]
  81. image = self.image_processor.postprocess(image, output_type=output_type, do_denormalize=do_denormalize)
  82. if hasattr(self, "final_offload_hook") and self.final_offload_hook is not None:
  83. self.final_offload_hook.offload()
  84. if not return_dict:
  85. return (image, has_nsfw_concept)
  86. return StableDiffusionPipelineOutput(images=image, nsfw_content_detected=has_nsfw_concept)

其实也能看出来是怎样一个大概的流程,如果我们后续需要改输入模块,那么直接在pipeline的对应部分重写就可以,十分方便。

后续文章会介绍每个模块的原理特点以及应用场景、如何进行训练和finetune、自定义。


业务合作/学习交流+v:lizhiTechnology

 如果想要了解更多diffusers相关知识,可以参考我的专栏和其他相关文章:

diffusers_Lcm_Tech的博客-CSDN博客

【diffusers】(一) diffusers库介绍 & 框架代码解析-CSDN博客

【diffusers】(二) scheduler介绍 & 代码解析_ddpmscheduler-CSDN博客

【diffusers】(三) pipeline原理及自定义训练推理-CSDN博客

如果想要了解更多深度学习相关知识,可以参考我的其他文章:

深度学习_Lcm_Tech的博客-CSDN博客

【优化器】(一) SGD原理 & pytorch代码解析_sgd优化器-CSDN博客

【损失函数】(一) L1Loss原理 & pytorch代码解析_l1 loss-CSDN博客

【图像生成】(一) DNN 原理 & pytorch代码实例_pytorch dnn代码-CSDN博客

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/233134
推荐阅读
相关标签
  

闽ICP备14008679号