diffusers 源码解析(三十五)
.\diffusers\pipelines\kolors\__init__.py
# 从 typing 模块导入 TYPE_CHECKING,以支持类型检查
from typing import TYPE_CHECKING
# 从上层模块导入一系列工具函数和常量
from ...utils import (
DIFFUSERS_SLOW_IMPORT, # 表示是否需要慢速导入
OptionalDependencyNotAvailable, # 当可选依赖不可用时引发的异常
_LazyModule, # 延迟加载模块的工具
get_objects_from_module, # 从模块中获取对象的工具函数
is_sentencepiece_available, # 检查 SentencePiece 是否可用
is_torch_available, # 检查 PyTorch 是否可用
is_transformers_available, # 检查 Transformers 是否可用
)
# 初始化一个空字典,用于存放虚拟对象
_dummy_objects = {}
# 初始化一个空字典,用于存放导入结构
_import_structure = {}
# 尝试检查可选依赖是否可用
try:
# 如果 Transformers 和 PyTorch 不可用,但 SentencePiece 可用,抛出异常
if not (is_transformers_available() and is_torch_available()) and is_sentencepiece_available():
raise OptionalDependencyNotAvailable()
# 捕获可选依赖不可用的异常
except OptionalDependencyNotAvailable:
# 从工具模块导入虚拟对象,避免依赖错误
from ...utils import dummy_torch_and_transformers_and_sentencepiece_objects # noqa F403
# 更新虚拟对象字典,获取 dummy 对象
_dummy_objects.update(get_objects_from_module(dummy_torch_and_transformers_and_sentencepiece_objects))
else:
# 如果依赖可用,更新导入结构,包含相应的管道和模型
_import_structure["pipeline_kolors"] = ["KolorsPipeline"]
_import_structure["pipeline_kolors_img2img"] = ["KolorsImg2ImgPipeline"]
_import_structure["text_encoder"] = ["ChatGLMModel"]
_import_structure["tokenizer"] = ["ChatGLMTokenizer"]
# 如果在类型检查中或需要慢速导入
if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
try:
# 再次检查可选依赖是否可用
if not (is_transformers_available() and is_torch_available()) and is_sentencepiece_available():
raise OptionalDependencyNotAvailable()
# 捕获可选依赖不可用的异常
except OptionalDependencyNotAvailable:
# 从工具模块导入所有虚拟对象
from ...utils.dummy_torch_and_transformers_and_sentencepiece_objects import *
else:
# 导入实际的管道和模型
from .pipeline_kolors import KolorsPipeline
from .pipeline_kolors_img2img import KolorsImg2ImgPipeline
from .text_encoder import ChatGLMModel
from .tokenizer import ChatGLMTokenizer
else:
# 如果不在类型检查中,执行懒加载模块的操作
import sys
# 使用懒加载模块初始化当前模块
sys.modules[__name__] = _LazyModule(
__name__, # 当前模块名
globals()["__file__"], # 当前文件名
_import_structure, # 导入结构
module_spec=__spec__, # 模块规格
)
# 将虚拟对象添加到当前模块
for name, value in _dummy_objects.items():
setattr(sys.modules[__name__], name, value)
.\diffusers\pipelines\latent_consistency_models\pipeline_latent_consistency_img2img.py
# 版权所有 2024 斯坦福大学团队和 HuggingFace 团队。保留所有权利。
#
# 根据 Apache 许可证第 2.0 版(“许可证”)授权;
# 除非遵守许可证,否则您不得使用此文件。
# 您可以在以下地址获取许可证的副本:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# 除非适用法律或书面协议另有约定,软件
# 在许可证下分发是按“原样”基础提供的,
# 不提供任何形式的保证或条件,无论是明示还是暗示。
# 有关特定权限和
# 许可证下的限制,请参见许可证。
# 免责声明:此代码受以下项目强烈影响 https://github.com/pesser/pytorch_diffusion
# 和 https://github.com/hojonathanho/diffusion
import inspect # 导入用于检查对象及其属性的模块
from typing import Any, Callable, Dict, List, Optional, Union # 导入类型注解
import PIL.Image # 导入用于图像处理的PIL库
import torch # 导入PyTorch库
from transformers import CLIPImageProcessor, CLIPTextModel, CLIPTokenizer, CLIPVisionModelWithProjection # 导入transformers库中的模型和处理器
from ...image_processor import PipelineImageInput, VaeImageProcessor # 导入自定义图像处理模块
from ...loaders import FromSingleFileMixin, IPAdapterMixin, StableDiffusionLoraLoaderMixin, TextualInversionLoaderMixin # 导入自定义加载器
from ...models import AutoencoderKL, ImageProjection, UNet2DConditionModel # 导入模型类
from ...models.lora import adjust_lora_scale_text_encoder # 导入调整Lora文本编码器的函数
from ...schedulers import LCMScheduler # 导入调度器
from ...utils import ( # 导入工具函数
USE_PEFT_BACKEND, # 指示是否使用PEFT后端
deprecate, # 标记弃用的函数
logging, # 导入日志记录模块
replace_example_docstring, # 替换示例文档字符串的工具
scale_lora_layers, # 缩放Lora层的函数
unscale_lora_layers, # 反缩放Lora层的函数
)
from ...utils.torch_utils import randn_tensor # 导入用于生成随机张量的工具
from ..pipeline_utils import DiffusionPipeline, StableDiffusionMixin # 导入扩散管道相关类
from ..stable_diffusion import StableDiffusionPipelineOutput, StableDiffusionSafetyChecker # 导入稳定扩散管道输出和安全检查器
logger = logging.get_logger(__name__) # 创建当前模块的日志记录器
# 从 diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_img2img.retrieve_latents 复制的函数
def retrieve_latents( # 定义函数以检索潜在张量
encoder_output: torch.Tensor, # 输入:编码器输出的张量
generator: Optional[torch.Generator] = None, # 可选:随机数生成器
sample_mode: str = "sample" # 可选:采样模式,默认为"sample"
):
if hasattr(encoder_output, "latent_dist") and sample_mode == "sample": # 检查是否有潜在分布且模式为采样
return encoder_output.latent_dist.sample(generator) # 从潜在分布中采样并返回
elif hasattr(encoder_output, "latent_dist") and sample_mode == "argmax": # 检查是否有潜在分布且模式为最大值
return encoder_output.latent_dist.mode() # 返回潜在分布的众数
elif hasattr(encoder_output, "latents"): # 检查是否有直接的潜在值
return encoder_output.latents # 返回潜在值
else: # 如果没有匹配的属性
raise AttributeError("Could not access latents of provided encoder_output") # 抛出属性错误
# 从 diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.retrieve_timesteps 复制的函数
def retrieve_timesteps( # 定义函数以检索时间步
scheduler, # 输入:调度器
num_inference_steps: Optional[int] = None, # 可选:推理步骤数
device: Optional[Union[str, torch.device]] = None, # 可选:设备类型
timesteps: Optional[List[int]] = None, # 可选:时间步列表
sigmas: Optional[List[float]] = None, # 可选:sigma值列表
**kwargs, # 其他可选参数
):
""" # 文档字符串:调用调度器的`set_timesteps`方法并从调度器检索时间步
# 该函数用于设置自定义的时间步,任何额外的关键字参数将传递给 `scheduler.set_timesteps`。
# 函数参数说明:
Args:
scheduler (`SchedulerMixin`): # 用于获取时间步的调度器
The scheduler to get timesteps from.
num_inference_steps (`int`): # 生成样本时使用的扩散步骤数,使用时 `timesteps` 必须为 `None`
The number of diffusion steps used when generating samples with a pre-trained model. If used, `timesteps`
must be `None`.
device (`str` or `torch.device`, *optional*): # 指定时间步要移动到的设备,如果为 `None` 则不移动
The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
timesteps (`List[int]`, *optional*): # 自定义时间步,如果提供则覆盖调度器的时间步间隔策略
Custom timesteps used to override the timestep spacing strategy of the scheduler. If `timesteps` is passed,
`num_inference_steps` and `sigmas` must be `None`.
sigmas (`List[float]`, *optional*): # 自定义 sigma 值,如果提供则覆盖调度器的时间步间隔策略
Custom sigmas used to override the timestep spacing strategy of the scheduler. If `sigmas` is passed,
`num_inference_steps` and `timesteps` must be `None`.
# 返回一个包含时间步调度和推理步骤数量的元组
Returns:
`Tuple[torch.Tensor, int]`: A tuple where the first element is the timestep schedule from the scheduler and the
second element is the number of inference steps.
"""
# 检查是否同时提供了 `timesteps` 和 `sigmas`
if timesteps is not None and sigmas is not None:
# 如果同时提供,抛出错误
raise ValueError("Only one of `timesteps` or `sigmas` can be passed. Please choose one to set custom values")
# 检查是否提供了自定义的时间步
if timesteps is not None:
# 检查当前调度器是否支持自定义时间步
accepts_timesteps = "timesteps" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
if not accepts_timesteps:
# 如果不支持,自定义错误信息
raise ValueError(
f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
f" timestep schedules. Please check whether you are using the correct scheduler."
)
# 设置自定义时间步
scheduler.set_timesteps(timesteps=timesteps, device=device, **kwargs)
# 从调度器获取时间步
timesteps = scheduler.timesteps
# 计算推理步骤的数量
num_inference_steps = len(timesteps)
# 检查是否提供了自定义的 sigma 值
elif sigmas is not None:
# 检查当前调度器是否支持自定义 sigma 值
accept_sigmas = "sigmas" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
if not accept_sigmas:
# 如果不支持,自定义错误信息
raise ValueError(
f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
f" sigmas schedules. Please check whether you are using the correct scheduler."
)
# 设置自定义 sigma 值
scheduler.set_timesteps(sigmas=sigmas, device=device, **kwargs)
# 从调度器获取时间步
timesteps = scheduler.timesteps
# 计算推理步骤的数量
num_inference_steps = len(timesteps)
# 如果没有提供自定义的时间步或 sigma 值,使用推理步骤数量设置时间步
else:
scheduler.set_timesteps(num_inference_steps, device=device, **kwargs)
# 从调度器获取时间步
timesteps = scheduler.timesteps
# 返回时间步和推理步骤数量的元组
return timesteps, num_inference_steps
# 示例文档字符串,包含用法示例
EXAMPLE_DOC_STRING = """
Examples:
```py
>>> from diffusers import AutoPipelineForImage2Image # 从 diffusers 模块导入图像到图像的自动管道
>>> import torch # 导入 PyTorch 库
>>> import PIL # 导入 Python Imaging Library (PIL)
>>> pipe = AutoPipelineForImage2Image.from_pretrained("SimianLuo/LCM_Dreamshaper_v7") # 从预训练模型加载管道
>>> # 为了节省 GPU 内存,可以使用 torch.float16,但可能会影响图像质量。
>>> pipe.to(torch_device="cuda", torch_dtype=torch.float32) # 将管道移动到 CUDA 设备,并设置数据类型为 float32
>>> prompt = "High altitude snowy mountains" # 设置生成图像的提示词
>>> image = PIL.Image.open("./snowy_mountains.png") # 打开一张输入图像
>>> # 可设置为 1~50 步。LCM 支持快速推断,即使步数 <= 4。推荐:1~8 步。
>>> num_inference_steps = 4 # 设置推断步骤数为 4
>>> images = pipe( # 调用管道生成图像
... prompt=prompt, image=image, num_inference_steps=num_inference_steps, guidance_scale=8.0 # 传入提示词、图像、推断步骤数和引导比例
... ).images # 获取生成的图像列表
>>> images[0].save("image.png") # 保存生成的第一张图像为 'image.png'
```py
"""
# 图像到图像生成的潜在一致性模型管道类
class LatentConsistencyModelImg2ImgPipeline(
DiffusionPipeline, # 继承自扩散管道类
StableDiffusionMixin, # 继承自稳定扩散混合类
TextualInversionLoaderMixin, # 继承自文本反演加载混合类
IPAdapterMixin, # 继承自 IP 适配器混合类
StableDiffusionLoraLoaderMixin, # 继承自稳定扩散 LoRA 加载混合类
FromSingleFileMixin, # 继承自单文件加载混合类
):
r"""
使用潜在一致性模型进行图像到图像生成的管道。
此模型继承自 [`DiffusionPipeline`]。查看超类文档以获取所有管道实现的通用方法
(下载、保存、在特定设备上运行等)。
该管道还继承以下加载方法:
- [`~loaders.TextualInversionLoaderMixin.load_textual_inversion`] 用于加载文本反演嵌入
- [`~loaders.StableDiffusionLoraLoaderMixin.load_lora_weights`] 用于加载 LoRA 权重
- [`~loaders.StableDiffusionLoraLoaderMixin.save_lora_weights`] 用于保存 LoRA 权重
- [`~loaders.FromSingleFileMixin.from_single_file`] 用于加载 `.ckpt` 文件
- [`~loaders.IPAdapterMixin.load_ip_adapter`] 用于加载 IP 适配器
# 文档字符串,描述各个参数的作用
Args:
vae ([`AutoencoderKL`]):
# 变分自编码器(VAE)模型,用于将图像编码为潜在表示并解码。
text_encoder ([`~transformers.CLIPTextModel`]):
# 冻结的文本编码器,使用 [clip-vit-large-patch14](https://huggingface.co/openai/clip-vit-large-patch14) 模型。
tokenizer ([`~transformers.CLIPTokenizer`]):
# 一个 `CLIPTokenizer` 用于对文本进行标记化。
unet ([`UNet2DConditionModel`]):
# 一个 `UNet2DConditionModel` 用于对编码的图像潜在表示进行去噪。
scheduler ([`SchedulerMixin`]):
# 调度器,用于与 `unet` 一起去噪编码的图像潜在表示。当前仅支持 [`LCMScheduler`]。
safety_checker ([`StableDiffusionSafetyChecker`]):
# 分类模块,用于评估生成的图像是否可能被视为冒犯或有害。
# 详情请参考 [模型卡片](https://huggingface.co/runwayml/stable-diffusion-v1-5) 以了解模型的潜在危害。
feature_extractor ([`~transformers.CLIPImageProcessor`]):
# 一个 `CLIPImageProcessor` 用于提取生成图像的特征;这些特征用于输入到 `safety_checker`。
requires_safety_checker (`bool`, *optional*, defaults to `True`):
# 指示管道是否需要安全检查器组件。
# 定义模型在 CPU 上的卸载顺序
model_cpu_offload_seq = "text_encoder->unet->vae"
# 定义可选组件列表
_optional_components = ["safety_checker", "feature_extractor", "image_encoder"]
# 定义不从 CPU 卸载的组件列表
_exclude_from_cpu_offload = ["safety_checker"]
# 定义回调张量输入列表
_callback_tensor_inputs = ["latents", "denoised", "prompt_embeds", "w_embedding"]
# 构造函数的定义
def __init__(
# VAE 模型的参数
vae: AutoencoderKL,
# 文本编码器的参数
text_encoder: CLIPTextModel,
# 标记器的参数
tokenizer: CLIPTokenizer,
# UNet 模型的参数
unet: UNet2DConditionModel,
# 调度器的参数
scheduler: LCMScheduler,
# 安全检查器的参数
safety_checker: StableDiffusionSafetyChecker,
# 特征提取器的参数
feature_extractor: CLIPImageProcessor,
# 可选的图像编码器参数,默认为 None
image_encoder: Optional[CLIPVisionModelWithProjection] = None,
# 指示是否需要安全检查器的布尔参数,默认为 True
requires_safety_checker: bool = True,
# 初始化父类
):
super().__init__()
# 注册各个模块,包括 VAE、文本编码器、分词器等
self.register_modules(
vae=vae,
text_encoder=text_encoder,
tokenizer=tokenizer,
unet=unet,
scheduler=scheduler,
safety_checker=safety_checker,
feature_extractor=feature_extractor,
image_encoder=image_encoder,
)
# 检查安全检查器是否为 None,并判断是否需要安全检查器
if safety_checker is None and requires_safety_checker:
# 记录警告信息,提醒用户禁用安全检查器的后果
logger.warning(
f"You have disabled the safety checker for {self.__class__} by passing `safety_checker=None`. Ensure"
" that you abide to the conditions of the Stable Diffusion license and do not expose unfiltered"
" results in services or applications open to the public. Both the diffusers team and Hugging Face"
" strongly recommend to keep the safety filter enabled in all public facing circumstances, disabling"
" it only for use-cases that involve analyzing network behavior or auditing its results. For more"
" information, please have a look at https://github.com/huggingface/diffusers/pull/254 ."
)
# 计算 VAE 的缩放因子
self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1)
# 创建图像处理器实例,使用 VAE 缩放因子
self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor)
# 从 StableDiffusionPipeline 复制的编码提示方法
def encode_prompt(
self,
prompt,
device,
num_images_per_prompt,
do_classifier_free_guidance,
negative_prompt=None,
prompt_embeds: Optional[torch.Tensor] = None,
negative_prompt_embeds: Optional[torch.Tensor] = None,
lora_scale: Optional[float] = None,
clip_skip: Optional[int] = None,
# 从 StableDiffusionPipeline 复制的编码图像方法
# 定义一个编码图像的函数,接受图像、设备、每个提示的图像数量及可选的隐藏状态输出
def encode_image(self, image, device, num_images_per_prompt, output_hidden_states=None):
# 获取图像编码器参数的数据类型
dtype = next(self.image_encoder.parameters()).dtype
# 检查输入是否为张量,如果不是则使用特征提取器将其转换为张量
if not isinstance(image, torch.Tensor):
image = self.feature_extractor(image, return_tensors="pt").pixel_values
# 将图像移动到指定设备并转换为相应的数据类型
image = image.to(device=device, dtype=dtype)
# 如果要求输出隐藏状态
if output_hidden_states:
# 编码图像并获取倒数第二层的隐藏状态
image_enc_hidden_states = self.image_encoder(image, output_hidden_states=True).hidden_states[-2]
# 根据每个提示的图像数量重复隐藏状态
image_enc_hidden_states = image_enc_hidden_states.repeat_interleave(num_images_per_prompt, dim=0)
# 对于无条件图像,编码一个零张量并获取其隐藏状态
uncond_image_enc_hidden_states = self.image_encoder(
torch.zeros_like(image), output_hidden_states=True
).hidden_states[-2]
# 根据每个提示的图像数量重复无条件隐藏状态
uncond_image_enc_hidden_states = uncond_image_enc_hidden_states.repeat_interleave(
num_images_per_prompt, dim=0
)
# 返回编码的图像隐藏状态和无条件图像隐藏状态
return image_enc_hidden_states, uncond_image_enc_hidden_states
else:
# 编码图像并获取其嵌入
image_embeds = self.image_encoder(image).image_embeds
# 根据每个提示的图像数量重复图像嵌入
image_embeds = image_embeds.repeat_interleave(num_images_per_prompt, dim=0)
# 创建与图像嵌入相同形状的零张量作为无条件嵌入
uncond_image_embeds = torch.zeros_like(image_embeds)
# 返回编码的图像嵌入和无条件图像嵌入
return image_embeds, uncond_image_embeds
# 从 StableDiffusionPipeline 的 prepare_ip_adapter_image_embeds 函数复制而来
def prepare_ip_adapter_image_embeds(
# 定义函数接受的参数:适配器图像、图像嵌入、设备、每个提示的图像数量、是否进行无分类器自由引导
self, ip_adapter_image, ip_adapter_image_embeds, device, num_images_per_prompt, do_classifier_free_guidance
):
# 初始化图像嵌入列表
image_embeds = []
# 如果启用了分类器自由引导,则初始化负图像嵌入列表
if do_classifier_free_guidance:
negative_image_embeds = []
# 如果输入适配器图像嵌入为空
if ip_adapter_image_embeds is None:
# 如果输入适配器图像不是列表,则将其转换为列表
if not isinstance(ip_adapter_image, list):
ip_adapter_image = [ip_adapter_image]
# 检查输入适配器图像数量是否与 IP 适配器数量一致
if len(ip_adapter_image) != len(self.unet.encoder_hid_proj.image_projection_layers):
raise ValueError(
# 抛出错误信息,说明图像数量与适配器数量不匹配
f"`ip_adapter_image` must have same length as the number of IP Adapters. Got {len(ip_adapter_image)} images and {len(self.unet.encoder_hid_proj.image_projection_layers)} IP Adapters."
)
# 遍历每个输入适配器图像和对应的图像投影层
for single_ip_adapter_image, image_proj_layer in zip(
ip_adapter_image, self.unet.encoder_hid_proj.image_projection_layers
):
# 检查输出是否为隐藏状态
output_hidden_state = not isinstance(image_proj_layer, ImageProjection)
# 编码单个适配器图像,获取图像嵌入和负图像嵌入
single_image_embeds, single_negative_image_embeds = self.encode_image(
single_ip_adapter_image, device, 1, output_hidden_state
)
# 将单个图像嵌入添加到列表中
image_embeds.append(single_image_embeds[None, :])
# 如果启用了分类器自由引导,则添加负图像嵌入
if do_classifier_free_guidance:
negative_image_embeds.append(single_negative_image_embeds[None, :])
else:
# 遍历已存在的输入适配器图像嵌入
for single_image_embeds in ip_adapter_image_embeds:
# 如果启用了分类器自由引导,则分离负图像嵌入和图像嵌入
if do_classifier_free_guidance:
single_negative_image_embeds, single_image_embeds = single_image_embeds.chunk(2)
negative_image_embeds.append(single_negative_image_embeds)
# 将图像嵌入添加到列表中
image_embeds.append(single_image_embeds)
# 初始化输入适配器图像嵌入的列表
ip_adapter_image_embeds = []
# 遍历图像嵌入及其索引
for i, single_image_embeds in enumerate(image_embeds):
# 将单个图像嵌入复制指定次数
single_image_embeds = torch.cat([single_image_embeds] * num_images_per_prompt, dim=0)
# 如果启用了分类器自由引导,处理负图像嵌入
if do_classifier_free_guidance:
single_negative_image_embeds = torch.cat([negative_image_embeds[i]] * num_images_per_prompt, dim=0)
single_image_embeds = torch.cat([single_negative_image_embeds, single_image_embeds], dim=0)
# 将图像嵌入移动到指定设备
single_image_embeds = single_image_embeds.to(device=device)
# 将处理后的图像嵌入添加到列表中
ip_adapter_image_embeds.append(single_image_embeds)
# 返回输入适配器图像嵌入列表
return ip_adapter_image_embeds
# 从 diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.run_safety_checker 复制的代码
# 定义安全检查器的运行方法,输入为图像、设备和数据类型
def run_safety_checker(self, image, device, dtype):
# 检查安全检查器是否存在
if self.safety_checker is None:
# 如果不存在,设置不安全内容的概念为 None
has_nsfw_concept = None
else:
# 检查输入的图像是否为张量
if torch.is_tensor(image):
# 将图像进行后处理,转换为 PIL 格式
feature_extractor_input = self.image_processor.postprocess(image, output_type="pil")
else:
# 如果不是张量,将 NumPy 数组转换为 PIL 格式
feature_extractor_input = self.image_processor.numpy_to_pil(image)
# 使用特征提取器处理输入,返回张量格式并移动到指定设备
safety_checker_input = self.feature_extractor(feature_extractor_input, return_tensors="pt").to(device)
# 调用安全检查器,获取处理后的图像和不安全内容概念
image, has_nsfw_concept = self.safety_checker(
images=image, clip_input=safety_checker_input.pixel_values.to(dtype)
)
# 返回处理后的图像和不安全内容概念
return image, has_nsfw_concept
# 从 StableDiffusionImg2ImgPipeline 类中复制的 prepare_latents 方法
# 从 LatentConsistencyModelPipeline 类中复制的 get_guidance_scale_embedding 方法
def get_guidance_scale_embedding(
self, w: torch.Tensor, embedding_dim: int = 512, dtype: torch.dtype = torch.float32
) -> torch.Tensor:
"""
详细信息见 GitHub 上的 VDM 代码链接
参数:
w (`torch.Tensor`):
生成具有指定引导比例的嵌入向量,以丰富时间步嵌入。
embedding_dim (`int`, *可选*, 默认值为 512):
生成的嵌入维度。
dtype (`torch.dtype`, *可选*, 默认值为 `torch.float32`):
生成嵌入的数值类型。
返回:
`torch.Tensor`: 形状为 `(len(w), embedding_dim)` 的嵌入向量。
"""
# 确保输入张量是一维的
assert len(w.shape) == 1
# 将输入乘以 1000.0 进行缩放
w = w * 1000.0
# 计算半个嵌入维度
half_dim = embedding_dim // 2
# 计算嵌入的底数
emb = torch.log(torch.tensor(10000.0)) / (half_dim - 1)
# 计算嵌入的指数形式
emb = torch.exp(torch.arange(half_dim, dtype=dtype) * -emb)
# 将缩放后的 w 转换为指定 dtype,并生成嵌入
emb = w.to(dtype)[:, None] * emb[None, :]
# 将正弦和余弦嵌入合并
emb = torch.cat([torch.sin(emb), torch.cos(emb)], dim=1)
# 如果嵌入维度为奇数,进行零填充
if embedding_dim % 2 == 1: # zero pad
emb = torch.nn.functional.pad(emb, (0, 1))
# 确保嵌入形状正确
assert emb.shape == (w.shape[0], embedding_dim)
# 返回最终嵌入
return emb
# 从 StableDiffusionPipeline 类中复制的 prepare_extra_step_kwargs 方法
# 准备调度器步骤的额外参数,因为并非所有调度器都有相同的参数签名
def prepare_extra_step_kwargs(self, generator, eta):
# eta (η) 仅用于 DDIMScheduler,其他调度器将忽略此参数
# eta 对应于 DDIM 论文中的 η:https://arxiv.org/abs/2010.02502
# 并且应在 [0, 1] 之间
# 检查调度器的步骤是否接受 eta 参数
accepts_eta = "eta" in set(inspect.signature(self.scheduler.step).parameters.keys())
# 初始化额外步骤参数字典
extra_step_kwargs = {}
# 如果接受 eta,则将其添加到字典中
if accepts_eta:
extra_step_kwargs["eta"] = eta
# 检查调度器的步骤是否接受 generator 参数
accepts_generator = "generator" in set(inspect.signature(self.scheduler.step).parameters.keys())
# 如果接受 generator,则将其添加到字典中
if accepts_generator:
extra_step_kwargs["generator"] = generator
# 返回包含额外参数的字典
return extra_step_kwargs
# 从 diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_img2img.StableDiffusionImg2ImgPipeline.get_timesteps 复制的函数
def get_timesteps(self, num_inference_steps, strength, device):
# 使用 init_timestep 获取原始时间步
init_timestep = min(int(num_inference_steps * strength), num_inference_steps)
# 计算开始时间步,确保不小于 0
t_start = max(num_inference_steps - init_timestep, 0)
# 根据开始时间步获取时间步列表
timesteps = self.scheduler.timesteps[t_start * self.scheduler.order :]
# 如果调度器有设置开始索引的方法,则调用它
if hasattr(self.scheduler, "set_begin_index"):
self.scheduler.set_begin_index(t_start * self.scheduler.order)
# 返回时间步列表和剩余的推理步骤数
return timesteps, num_inference_steps - t_start
def check_inputs(
self,
prompt: Union[str, List[str]],
strength: float,
callback_steps: int,
# 可选的提示嵌入参数,默认为 None
prompt_embeds: Optional[torch.Tensor] = None,
# 可选的图像适配器输入,默认为 None
ip_adapter_image=None,
# 可选的图像适配器嵌入,默认为 None
ip_adapter_image_embeds=None,
# 可选的回调结束时的张量输入,默认为 None
callback_on_step_end_tensor_inputs=None,
):
# 检查 strength 的值是否在有效范围 [0.0, 1.0] 内
if strength < 0 or strength > 1:
# 如果不在范围内,抛出值错误
raise ValueError(f"The value of strength should in [0.0, 1.0] but is {strength}")
# 检查 callback_steps 是否是正整数
if callback_steps is not None and (not isinstance(callback_steps, int) or callback_steps <= 0):
# 如果不是正整数,抛出值错误
raise ValueError(
f"`callback_steps` has to be a positive integer but is {callback_steps} of type"
f" {type(callback_steps)}."
)
# 检查 callback_on_step_end_tensor_inputs 中的键是否在自定义的回调输入中
if callback_on_step_end_tensor_inputs is not None and not all(
k in self._callback_tensor_inputs for k in callback_on_step_end_tensor_inputs
):
# 如果有不在列表中的键,抛出值错误
raise ValueError(
f"`callback_on_step_end_tensor_inputs` has to be in {self._callback_tensor_inputs}, but found {[k for k in callback_on_step_end_tensor_inputs if k not in self._callback_tensor_inputs]}"
)
# 检查 prompt 和 prompt_embeds 是否同时存在
if prompt is not None and prompt_embeds is not None:
# 如果同时存在,抛出值错误
raise ValueError(
f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
" only forward one of the two."
)
# 检查 prompt 和 prompt_embeds 是否同时为 None
elif prompt is None and prompt_embeds is None:
# 如果都是 None,抛出值错误
raise ValueError(
"Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined."
)
# 检查 prompt 的类型是否正确
elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):
# 如果类型不正确,抛出值错误
raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")
# 检查 ip_adapter_image 和 ip_adapter_image_embeds 是否同时存在
if ip_adapter_image is not None and ip_adapter_image_embeds is not None:
# 如果同时存在,抛出值错误
raise ValueError(
"Provide either `ip_adapter_image` or `ip_adapter_image_embeds`. Cannot leave both `ip_adapter_image` and `ip_adapter_image_embeds` defined."
)
# 检查 ip_adapter_image_embeds 的类型是否为列表
if ip_adapter_image_embeds is not None:
if not isinstance(ip_adapter_image_embeds, list):
# 如果类型不正确,抛出值错误
raise ValueError(
f"`ip_adapter_image_embeds` has to be of type `list` but is {type(ip_adapter_image_embeds)}"
)
# 检查列表中第一个元素的维度是否为 3D 或 4D
elif ip_adapter_image_embeds[0].ndim not in [3, 4]:
# 如果维度不正确,抛出值错误
raise ValueError(
f"`ip_adapter_image_embeds` has to be a list of 3D or 4D tensors but is {ip_adapter_image_embeds[0].ndim}D"
)
# 属性方法,返回指导比例
@property
def guidance_scale(self):
return self._guidance_scale
# 属性方法,返回交叉注意力的参数
@property
def cross_attention_kwargs(self):
return self._cross_attention_kwargs
# 属性方法,返回剪切跳过的标志
@property
def clip_skip(self):
return self._clip_skip
# 属性方法,返回是否执行无分类器的自由引导
@property
def do_classifier_free_guidance(self):
return False
# 属性方法,返回时间步数
@property
def num_timesteps(self):
return self._num_timesteps
# 装饰器,禁用梯度计算
@torch.no_grad()
# 替换示例文档字符串的装饰器
@replace_example_docstring(EXAMPLE_DOC_STRING)
# 定义可调用对象的方法,允许该对象像函数一样被调用
def __call__(
# 提示文本,可以是字符串或字符串列表,默认为 None
self,
prompt: Union[str, List[str]] = None,
# 输入图像,类型为 PipelineImageInput,默认为 None
image: PipelineImageInput = None,
# 推理步骤的数量,默认为 4
num_inference_steps: int = 4,
# 强度参数,影响图像生成的效果,默认为 0.8
strength: float = 0.8,
# 原始推理步骤的数量,默认为 None
original_inference_steps: int = None,
# 时间步的列表,默认为 None
timesteps: List[int] = None,
# 指导比例,控制生成的多样性,默认为 8.5
guidance_scale: float = 8.5,
# 每个提示生成的图像数量,默认为 1
num_images_per_prompt: Optional[int] = 1,
# 随机数生成器,可以是单个或列表,默认为 None
generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
# 潜在变量,类型为 torch.Tensor,默认为 None
latents: Optional[torch.Tensor] = None,
# 提示嵌入,类型为 torch.Tensor,默认为 None
prompt_embeds: Optional[torch.Tensor] = None,
# 图像适配器输入,类型为 PipelineImageInput,默认为 None
ip_adapter_image: Optional[PipelineImageInput] = None,
# 图像适配器的嵌入列表,类型为 List[torch.Tensor],默认为 None
ip_adapter_image_embeds: Optional[List[torch.Tensor]] = None,
# 输出类型,默认为 "pil",表示 PIL 图像格式
output_type: Optional[str] = "pil",
# 是否返回字典格式的结果,默认为 True
return_dict: bool = True,
# 跨注意力的关键字参数,默认为 None
cross_attention_kwargs: Optional[Dict[str, Any]] = None,
# 跳过的 CLIP 步数,默认为 None
clip_skip: Optional[int] = None,
# 步骤结束时的回调函数,默认为 None
callback_on_step_end: Optional[Callable[[int, int, Dict], None]] = None,
# 步骤结束时的张量输入回调,默认为 ["latents"]
callback_on_step_end_tensor_inputs: List[str] = ["latents"],
# 其他关键字参数
**kwargs,
.\diffusers\pipelines\latent_consistency_models\pipeline_latent_consistency_text2img.py
# 版权声明,注明版权归斯坦福大学团队和HuggingFace团队所有
#
# 根据Apache许可证第2.0版(“许可证”)许可;
# 除非遵守许可证,否则您不能使用此文件。
# 您可以在以下网址获取许可证副本:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# 除非适用法律要求或书面同意,软件以“原样”基础分发,
# 不提供任何明示或暗示的担保或条件。
# 请参阅许可证以获取有关权限和限制的具体语言。
# 声明:此代码受到以下项目的强烈影响:https://github.com/pesser/pytorch_diffusion
# 和 https://github.com/hojonathanho/diffusion
import inspect # 导入inspect模块以获取对象的获取信息
from typing import Any, Callable, Dict, List, Optional, Union # 导入类型提示以支持类型注解
import torch # 导入PyTorch库以进行深度学习操作
from transformers import CLIPImageProcessor, CLIPTextModel, CLIPTokenizer, CLIPVisionModelWithProjection # 从transformers库导入相关的CLIP模型
from ...image_processor import PipelineImageInput, VaeImageProcessor # 导入自定义图像处理器
from ...loaders import FromSingleFileMixin, IPAdapterMixin, StableDiffusionLoraLoaderMixin, TextualInversionLoaderMixin # 导入不同的加载器混合类
from ...models import AutoencoderKL, ImageProjection, UNet2DConditionModel # 导入各种模型类
from ...models.lora import adjust_lora_scale_text_encoder # 导入用于调整LoRA缩放的函数
from ...schedulers import LCMScheduler # 导入调度器类
from ...utils import ( # 从utils模块导入多个工具函数和常量
USE_PEFT_BACKEND,
deprecate,
logging,
replace_example_docstring,
scale_lora_layers,
unscale_lora_layers,
)
from ...utils.torch_utils import randn_tensor # 从torch_utils模块导入随机张量生成函数
from ..pipeline_utils import DiffusionPipeline, StableDiffusionMixin # 导入扩散管道和混合类
from ..stable_diffusion import StableDiffusionPipelineOutput, StableDiffusionSafetyChecker # 导入稳定扩散输出和安全检查器类
logger = logging.get_logger(__name__) # 获取当前模块的日志记录器
EXAMPLE_DOC_STRING = """ # 定义示例文档字符串,提供使用示例
Examples:
```py
>>> from diffusers import DiffusionPipeline # 从diffusers模块导入DiffusionPipeline类
>>> import torch # 导入torch库
>>> pipe = DiffusionPipeline.from_pretrained("SimianLuo/LCM_Dreamshaper_v7") # 从预训练模型创建扩散管道实例
>>> # 为了节省GPU内存,可以使用torch.float16,但可能会影响图像质量。
>>> pipe.to(torch_device="cuda", torch_dtype=torch.float32) # 将管道转移到指定设备并设置数据类型
>>> prompt = "Self-portrait oil painting, a beautiful cyborg with golden hair, 8k" # 定义生成图像的提示
>>> # 可以设置为1~50步。LCM支持快速推理,即使步数<=4。建议:1~8步。
>>> num_inference_steps = 4 # 设置推理步骤数
>>> images = pipe(prompt=prompt, num_inference_steps=num_inference_steps, guidance_scale=8.0).images # 生成图像
>>> images[0].save("image.png") # 保存生成的第一张图像
```py
"""
# 从diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion复制的retrieve_timesteps函数
def retrieve_timesteps( # 定义函数以检索时间步
scheduler, # 调度器对象,负责控制时间步
num_inference_steps: Optional[int] = None, # 可选的推理步骤数
device: Optional[Union[str, torch.device]] = None, # 可选的设备参数,可以是字符串或torch设备对象
timesteps: Optional[List[int]] = None, # 可选的时间步列表
sigmas: Optional[List[float]] = None, # 可选的sigma值列表
**kwargs, # 额外的关键字参数
):
"""
# 调用调度器的 `set_timesteps` 方法并在调用后从调度器中获取时间步。处理自定义时间步。
# 任何 kwargs 将被传递给 `scheduler.set_timesteps`。
# 参数说明:
# scheduler (`SchedulerMixin`): 从中获取时间步的调度器。
# num_inference_steps (`int`): 用于生成样本的扩散步骤数。如果使用,则 `timesteps` 必须为 `None`。
# device (`str` 或 `torch.device`, *可选*):
# 时间步应移动到的设备。如果为 `None`,则不移动时间步。
# timesteps (`List[int]`, *可选*):
# 自定义时间步,用于覆盖调度器的时间步间隔策略。如果传递 `timesteps`,则 `num_inference_steps` 和 `sigmas` 必须为 `None`。
# sigmas (`List[float]`, *可选*):
# 自定义 sigma,用于覆盖调度器的时间步间隔策略。如果传递 `sigmas`,则 `num_inference_steps` 和 `timesteps` 必须为 `None`。
# 返回:
# `Tuple[torch.Tensor, int]`: 一个元组,其中第一个元素是来自调度器的时间步计划,第二个元素是推理步骤的数量。
"""
# 检查是否同时传入 `timesteps` 和 `sigmas`
if timesteps is not None and sigmas is not None:
# 如果同时传入,抛出错误,提示只能选择一个自定义值
raise ValueError("Only one of `timesteps` or `sigmas` can be passed. Please choose one to set custom values")
# 检查是否传入了 `timesteps`
if timesteps is not None:
# 检查调度器的 `set_timesteps` 方法是否接受 `timesteps` 参数
accepts_timesteps = "timesteps" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
# 如果不接受,抛出错误,提示当前调度器不支持自定义时间步
if not accepts_timesteps:
raise ValueError(
f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
f" timestep schedules. Please check whether you are using the correct scheduler."
)
# 调用调度器的 `set_timesteps` 方法设置时间步
scheduler.set_timesteps(timesteps=timesteps, device=device, **kwargs)
# 从调度器获取时间步
timesteps = scheduler.timesteps
# 计算时间步的数量
num_inference_steps = len(timesteps)
# 检查是否传入了 `sigmas`
elif sigmas is not None:
# 检查调度器的 `set_timesteps` 方法是否接受 `sigmas` 参数
accept_sigmas = "sigmas" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
# 如果不接受,抛出错误,提示当前调度器不支持自定义 sigma
if not accept_sigmas:
raise ValueError(
f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
f" sigmas schedules. Please check whether you are using the correct scheduler."
)
# 调用调度器的 `set_timesteps` 方法设置 sigma
scheduler.set_timesteps(sigmas=sigmas, device=device, **kwargs)
# 从调度器获取时间步
timesteps = scheduler.timesteps
# 计算时间步的数量
num_inference_steps = len(timesteps)
# 如果都没有传入,则使用推理步骤数调用 `set_timesteps`
else:
scheduler.set_timesteps(num_inference_steps, device=device, **kwargs)
# 从调度器获取时间步
timesteps = scheduler.timesteps
# 返回时间步和推理步骤的数量
return timesteps, num_inference_steps
# 定义一个潜在一致性模型的管道类,继承多个混入类
class LatentConsistencyModelPipeline(
# 从扩散管道类继承
DiffusionPipeline,
# 从稳定扩散混入类继承
StableDiffusionMixin,
# 从文本反转加载混入类继承
TextualInversionLoaderMixin,
# 从 IP 适配器混入类继承
IPAdapterMixin,
# 从稳定扩散 LoRA 加载混入类继承
StableDiffusionLoraLoaderMixin,
# 从单文件加载混入类继承
FromSingleFileMixin,
):
r"""
使用潜在一致性模型进行文本到图像生成的管道。
该模型继承自 [`DiffusionPipeline`]。有关所有管道实现的通用方法的文档,请查看超类文档
(下载、保存、在特定设备上运行等)。
该管道还继承以下加载方法:
- [`~loaders.TextualInversionLoaderMixin.load_textual_inversion`] 用于加载文本反转嵌入
- [`~loaders.StableDiffusionLoraLoaderMixin.load_lora_weights`] 用于加载 LoRA 权重
- [`~loaders.StableDiffusionLoraLoaderMixin.save_lora_weights`] 用于保存 LoRA 权重
- [`~loaders.FromSingleFileMixin.from_single_file`] 用于加载 `.ckpt` 文件
- [`~loaders.IPAdapterMixin.load_ip_adapter`] 用于加载 IP 适配器
参数:
vae ([`AutoencoderKL`]):
变分自编码器 (VAE) 模型,用于将图像编码和解码为潜在表示。
text_encoder ([`~transformers.CLIPTextModel`]):
冻结的文本编码器 ([clip-vit-large-patch14](https://huggingface.co/openai/clip-vit-large-patch14))。
tokenizer ([`~transformers.CLIPTokenizer`]):
一个 `CLIPTokenizer` 用于对文本进行标记化。
unet ([`UNet2DConditionModel`]):
一个 `UNet2DConditionModel` 用于去噪编码的图像潜在表示。
scheduler ([`SchedulerMixin`]):
与 `unet` 一起使用的调度器,用于去噪编码的图像潜在表示。当前仅支持 [`LCMScheduler`]。
safety_checker ([`StableDiffusionSafetyChecker`]):
分类模块,估计生成的图像是否可能被认为是冒犯性或有害的。
有关模型潜在危害的更多详细信息,请参阅 [model card](https://huggingface.co/runwayml/stable-diffusion-v1-5)。
feature_extractor ([`~transformers.CLIPImageProcessor`]):
一个 `CLIPImageProcessor` 用于从生成的图像中提取特征;作为输入用于 `safety_checker`。
requires_safety_checker (`bool`, *可选*, 默认为 `True`):
管道是否需要安全检查器组件。
"""
# 定义模型的 CPU 卸载顺序
model_cpu_offload_seq = "text_encoder->unet->vae"
# 定义可选组件
_optional_components = ["safety_checker", "feature_extractor", "image_encoder"]
# 定义不参与 CPU 卸载的组件
_exclude_from_cpu_offload = ["safety_checker"]
# 定义回调的张量输入
_callback_tensor_inputs = ["latents", "denoised", "prompt_embeds", "w_embedding"]
# 初始化方法,用于创建类的实例,接收多个参数以初始化模型组件
def __init__(
# VAE(变分自编码器)模型,用于图像生成
self,
vae: AutoencoderKL,
# 文本编码器,用于将文本转换为向量表示
text_encoder: CLIPTextModel,
# 分词器,用于将文本拆分为词汇单元
tokenizer: CLIPTokenizer,
# UNet模型,用于生成条件图像
unet: UNet2DConditionModel,
# 调度器,控制模型训练和生成过程中的学习率等参数
scheduler: LCMScheduler,
# 安全检查器,确保生成内容的安全性
safety_checker: StableDiffusionSafetyChecker,
# 特征提取器,处理和提取图像特征
feature_extractor: CLIPImageProcessor,
# 可选的图像编码器,用于对图像进行编码
image_encoder: Optional[CLIPVisionModelWithProjection] = None,
# 是否需要安全检查器的标志,默认为 True
requires_safety_checker: bool = True,
):
# 调用父类的初始化方法
super().__init__()
# 检查是否禁用安全检查器且要求使用安全检查器
if safety_checker is None and requires_safety_checker:
# 记录警告信息,提醒用户遵循Stable Diffusion许可证的条件
logger.warning(
f"You have disabled the safety checker for {self.__class__} by passing `safety_checker=None`. Ensure"
" that you abide to the conditions of the Stable Diffusion license and do not expose unfiltered"
" results in services or applications open to the public. Both the diffusers team and Hugging Face"
" strongly recommend to keep the safety filter enabled in all public facing circumstances, disabling"
" it only for use-cases that involve analyzing network behavior or auditing its results. For more"
" information, please have a look at https://github.com/huggingface/diffusers/pull/254 ."
)
# 检查是否提供了安全检查器但未提供特征提取器
if safety_checker is not None and feature_extractor is None:
# 抛出异常,要求用户提供特征提取器
raise ValueError(
"Make sure to define a feature extractor when loading {self.__class__} if you want to use the safety"
" checker. If you do not want to use the safety checker, you can pass `'safety_checker=None'` instead."
)
# 注册模型组件,便于管理和使用
self.register_modules(
vae=vae,
text_encoder=text_encoder,
tokenizer=tokenizer,
unet=unet,
scheduler=scheduler,
safety_checker=safety_checker,
feature_extractor=feature_extractor,
image_encoder=image_encoder,
)
# 计算 VAE 的缩放因子,通常用于图像生成的调整
self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1)
# 初始化图像处理器,使用上面计算的缩放因子
self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor)
# 将是否需要安全检查器的配置注册到类的配置中
self.register_to_config(requires_safety_checker=requires_safety_checker)
# 从StableDiffusionPipeline复制的方法,用于编码文本提示
def encode_prompt(
# 输入的文本提示
self,
prompt,
# 设备类型(如CPU或GPU)
device,
# 每个提示生成的图像数量
num_images_per_prompt,
# 是否执行分类器自由引导
do_classifier_free_guidance,
# 可选的负面提示
negative_prompt=None,
# 可选的提示嵌入,预先计算的文本嵌入
prompt_embeds: Optional[torch.Tensor] = None,
# 可选的负面提示嵌入,预先计算的负面文本嵌入
negative_prompt_embeds: Optional[torch.Tensor] = None,
# 可选的LoRA缩放因子,用于调节模型的生成效果
lora_scale: Optional[float] = None,
# 可选的跳过CLIP模型的某些层
clip_skip: Optional[int] = None,
# 从StableDiffusionPipeline复制的方法,用于编码图像
# 定义一个方法,用于编码图像并返回相应的嵌入
def encode_image(self, image, device, num_images_per_prompt, output_hidden_states=None):
# 获取图像编码器参数的数据类型
dtype = next(self.image_encoder.parameters()).dtype
# 如果输入的图像不是张量,则使用特征提取器将其转换为张量
if not isinstance(image, torch.Tensor):
image = self.feature_extractor(image, return_tensors="pt").pixel_values
# 将图像转移到指定设备,并设置数据类型
image = image.to(device=device, dtype=dtype)
# 如果需要输出隐藏状态,则进行隐藏状态的编码
if output_hidden_states:
# 编码图像并获取倒数第二层的隐藏状态
image_enc_hidden_states = self.image_encoder(image, output_hidden_states=True).hidden_states[-2]
# 根据每个提示重复隐藏状态以匹配数量
image_enc_hidden_states = image_enc_hidden_states.repeat_interleave(num_images_per_prompt, dim=0)
# 对于无条件输入,编码全零图像并获取隐藏状态
uncond_image_enc_hidden_states = self.image_encoder(
torch.zeros_like(image), output_hidden_states=True
).hidden_states[-2]
# 同样重复无条件隐藏状态以匹配数量
uncond_image_enc_hidden_states = uncond_image_enc_hidden_states.repeat_interleave(
num_images_per_prompt, dim=0
)
# 返回编码的图像和无条件图像的隐藏状态
return image_enc_hidden_states, uncond_image_enc_hidden_states
else:
# 如果不需要输出隐藏状态,则编码图像以获取图像嵌入
image_embeds = self.image_encoder(image).image_embeds
# 根据每个提示重复图像嵌入以匹配数量
image_embeds = image_embeds.repeat_interleave(num_images_per_prompt, dim=0)
# 创建一个与图像嵌入形状相同的全零张量作为无条件嵌入
uncond_image_embeds = torch.zeros_like(image_embeds)
# 返回编码的图像嵌入和无条件图像嵌入
return image_embeds, uncond_image_embeds
# 从稳定扩散管道中复制的方法,用于准备图像嵌入
def prepare_ip_adapter_image_embeds(
self, ip_adapter_image, ip_adapter_image_embeds, device, num_images_per_prompt, do_classifier_free_guidance
# 定义一个处理图像嵌入的部分
):
# 初始化图像嵌入列表
image_embeds = []
# 如果启用分类器自由引导
if do_classifier_free_guidance:
# 初始化负图像嵌入列表
negative_image_embeds = []
# 如果没有提供图像嵌入
if ip_adapter_image_embeds is None:
# 确保输入的图像是列表格式
if not isinstance(ip_adapter_image, list):
ip_adapter_image = [ip_adapter_image]
# 检查输入图像数量是否与 IP 适配器数量匹配
if len(ip_adapter_image) != len(self.unet.encoder_hid_proj.image_projection_layers):
raise ValueError(
# 抛出错误提示信息
f"`ip_adapter_image` must have same length as the number of IP Adapters. Got {len(ip_adapter_image)} images and {len(self.unet.encoder_hid_proj.image_projection_layers)} IP Adapters."
)
# 遍历每个图像和对应的投影层
for single_ip_adapter_image, image_proj_layer in zip(
ip_adapter_image, self.unet.encoder_hid_proj.image_projection_layers
):
# 判断输出是否为隐藏状态
output_hidden_state = not isinstance(image_proj_layer, ImageProjection)
# 编码单个图像,获取其嵌入
single_image_embeds, single_negative_image_embeds = self.encode_image(
single_ip_adapter_image, device, 1, output_hidden_state
)
# 将单个图像嵌入添加到列表中
image_embeds.append(single_image_embeds[None, :])
# 如果启用分类器自由引导,添加负图像嵌入
if do_classifier_free_guidance:
negative_image_embeds.append(single_negative_image_embeds[None, :])
else:
# 遍历已提供的图像嵌入
for single_image_embeds in ip_adapter_image_embeds:
# 如果启用分类器自由引导,分离负图像嵌入
if do_classifier_free_guidance:
single_negative_image_embeds, single_image_embeds = single_image_embeds.chunk(2)
negative_image_embeds.append(single_negative_image_embeds)
# 将图像嵌入添加到列表中
image_embeds.append(single_image_embeds)
# 初始化适配器图像嵌入列表
ip_adapter_image_embeds = []
# 遍历图像嵌入及其索引
for i, single_image_embeds in enumerate(image_embeds):
# 将每个图像嵌入复制指定次数
single_image_embeds = torch.cat([single_image_embeds] * num_images_per_prompt, dim=0)
# 如果启用分类器自由引导,复制负图像嵌入
if do_classifier_free_guidance:
single_negative_image_embeds = torch.cat([negative_image_embeds[i]] * num_images_per_prompt, dim=0)
# 连接负图像嵌入和正图像嵌入
single_image_embeds = torch.cat([single_negative_image_embeds, single_image_embeds], dim=0)
# 将图像嵌入移动到指定设备
single_image_embeds = single_image_embeds.to(device=device)
# 添加到适配器图像嵌入列表
ip_adapter_image_embeds.append(single_image_embeds)
# 返回适配器图像嵌入列表
return ip_adapter_image_embeds
# 从 diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.run_safety_checker 中复制
# 定义运行安全检查器的方法,输入为图像、设备和数据类型
def run_safety_checker(self, image, device, dtype):
# 检查安全检查器是否存在
if self.safety_checker is None:
# 如果不存在,设置无 NSFW 概念为 None
has_nsfw_concept = None
else:
# 检查输入图像是否为 PyTorch 张量
if torch.is_tensor(image):
# 将张量图像后处理为 PIL 格式
feature_extractor_input = self.image_processor.postprocess(image, output_type="pil")
else:
# 将 NumPy 数组图像转换为 PIL 格式
feature_extractor_input = self.image_processor.numpy_to_pil(image)
# 提取特征并将其转移到指定设备上
safety_checker_input = self.feature_extractor(feature_extractor_input, return_tensors="pt").to(device)
# 使用安全检查器处理图像,并获取是否存在 NSFW 概念
image, has_nsfw_concept = self.safety_checker(
images=image, clip_input=safety_checker_input.pixel_values.to(dtype)
)
# 返回处理后的图像和 NSFW 概念标志
return image, has_nsfw_concept
# 从稳定扩散管道准备潜在数据的方法
def prepare_latents(self, batch_size, num_channels_latents, height, width, dtype, device, generator, latents=None):
# 定义潜在数据的形状
shape = (
batch_size,
num_channels_latents,
int(height) // self.vae_scale_factor,
int(width) // self.vae_scale_factor,
)
# 检查生成器列表的长度是否与批量大小匹配
if isinstance(generator, list) and len(generator) != batch_size:
raise ValueError(
f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"
f" size of {batch_size}. Make sure the batch size matches the length of the generators."
)
# 如果潜在数据为 None,生成新的随机张量
if latents is None:
latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
else:
# 如果提供了潜在数据,则将其转移到指定设备上
latents = latents.to(device)
# 根据调度器要求的标准差缩放初始噪声
latents = latents * self.scheduler.init_noise_sigma
# 返回处理后的潜在数据
return latents
# 定义获取引导尺度嵌入的方法,输入为张量、嵌入维度和数据类型
def get_guidance_scale_embedding(
self, w: torch.Tensor, embedding_dim: int = 512, dtype: torch.dtype = torch.float32
) -> torch.Tensor:
"""
参考链接:https://github.com/google-research/vdm/blob/dc27b98a554f65cdc654b800da5aa1846545d41b/model_vdm.py#L298
参数:
w (`torch.Tensor`):
生成具有指定引导尺度的嵌入向量,以便后续丰富时间步嵌入。
embedding_dim (`int`, *可选*, 默认值为 512):
生成的嵌入的维度。
dtype (`torch.dtype`, *可选*, 默认值为 `torch.float32`):
生成的嵌入的数据类型。
返回:
`torch.Tensor`: 形状为 `(len(w), embedding_dim)` 的嵌入向量。
"""
# 确保输入的张量 w 是一维的
assert len(w.shape) == 1
# 将 w 的值放大 1000.0,以调整引导尺度
w = w * 1000.0
# 计算半维度
half_dim = embedding_dim // 2
# 计算嵌入的基础对数缩放因子
emb = torch.log(torch.tensor(10000.0)) / (half_dim - 1)
# 生成指数衰减的嵌入值
emb = torch.exp(torch.arange(half_dim, dtype=dtype) * -emb)
# 将输入的 w 转换为目标 dtype,并进行维度扩展,生成最终的嵌入
emb = w.to(dtype)[:, None] * emb[None, :]
# 对嵌入应用正弦和余弦变换
emb = torch.cat([torch.sin(emb), torch.cos(emb)], dim=1)
# 如果嵌入维度为奇数,则在最后一维进行零填充
if embedding_dim % 2 == 1: # zero pad
emb = torch.nn.functional.pad(emb, (0, 1))
# 确保输出的形状为 (w.shape[0], embedding_dim)
assert emb.shape == (w.shape[0], embedding_dim)
# 返回生成的嵌入
return emb
# 从 diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.prepare_extra_step_kwargs 复制的代码
def prepare_extra_step_kwargs(self, generator, eta):
# 为调度器步骤准备额外的关键字参数,因为并非所有调度器都有相同的签名
# eta (η) 仅在 DDIMScheduler 中使用,对于其他调度器将被忽略。
# eta 对应于 DDIM 论文中的 η:https://arxiv.org/abs/2010.02502
# 并且应在 [0, 1] 范围内
# 检查调度器的步骤方法是否接受 eta 参数
accepts_eta = "eta" in set(inspect.signature(self.scheduler.step).parameters.keys())
extra_step_kwargs = {}
# 如果接受 eta,则将其添加到额外参数中
if accepts_eta:
extra_step_kwargs["eta"] = eta
# 检查调度器的步骤方法是否接受 generator 参数
accepts_generator = "generator" in set(inspect.signature(self.scheduler.step).parameters.keys())
# 如果接受 generator,则将其添加到额外参数中
if accepts_generator:
extra_step_kwargs["generator"] = generator
# 返回包含额外参数的字典
return extra_step_kwargs
# 当前的 StableDiffusionPipeline.check_inputs,已移除负提示部分
def check_inputs(
self,
prompt: Union[str, List[str]], # 提示文本,可以是单个字符串或字符串列表
height: int, # 图像的高度
width: int, # 图像的宽度
callback_steps: int, # 回调步骤数
prompt_embeds: Optional[torch.Tensor] = None, # 可选的提示嵌入
ip_adapter_image=None, # 可选的图像适配器
ip_adapter_image_embeds=None, # 可选的图像适配器嵌入
callback_on_step_end_tensor_inputs=None, # 可选的回调输入张量
):
# 检查高度和宽度是否能被8整除,如果不能则引发错误
if height % 8 != 0 or width % 8 != 0:
raise ValueError(f"`height` and `width` have to be divisible by 8 but are {height} and {width}.")
# 检查回调步骤是否为正整数,如果不是则引发错误
if callback_steps is not None and (not isinstance(callback_steps, int) or callback_steps <= 0):
raise ValueError(
f"`callback_steps` has to be a positive integer but is {callback_steps} of type"
f" {type(callback_steps)}."
)
# 检查回调结束时的张量输入是否在允许的输入列表中
if callback_on_step_end_tensor_inputs is not None and not all(
k in self._callback_tensor_inputs for k in callback_on_step_end_tensor_inputs
):
raise ValueError(
f"`callback_on_step_end_tensor_inputs` has to be in {self._callback_tensor_inputs}, but found {[k for k in callback_on_step_end_tensor_inputs if k not in self._callback_tensor_inputs]}"
)
# 检查是否同时提供了提示和提示嵌入,如果是则引发错误
if prompt is not None and prompt_embeds is not None:
raise ValueError(
f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
" only forward one of the two."
)
# 检查提示和提示嵌入是否都未定义,如果是则引发错误
elif prompt is None and prompt_embeds is None:
raise ValueError(
"Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined."
)
# 检查提示的类型是否为字符串或列表,如果不是则引发错误
elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):
raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")
# 检查适配器图像和嵌入是否同时定义,如果是则引发错误
if ip_adapter_image is not None and ip_adapter_image_embeds is not None:
raise ValueError(
"Provide either `ip_adapter_image` or `ip_adapter_image_embeds`. Cannot leave both `ip_adapter_image` and `ip_adapter_image_embeds` defined."
)
# 检查适配器图像嵌入的类型和维度是否符合要求
if ip_adapter_image_embeds is not None:
if not isinstance(ip_adapter_image_embeds, list):
raise ValueError(
f"`ip_adapter_image_embeds` has to be of type `list` but is {type(ip_adapter_image_embeds)}"
)
elif ip_adapter_image_embeds[0].ndim not in [3, 4]:
raise ValueError(
f"`ip_adapter_image_embeds` has to be a list of 3D or 4D tensors but is {ip_adapter_image_embeds[0].ndim}D"
)
# 返回指导尺度属性
@property
def guidance_scale(self):
return self._guidance_scale
# 返回交叉注意力关键字参数属性
@property
def cross_attention_kwargs(self):
return self._cross_attention_kwargs
# 返回跳过剪辑属性
@property
def clip_skip(self):
return self._clip_skip
# 返回是否执行无分类器引导的属性
@property
def do_classifier_free_guidance(self):
return False
# 返回时间步数属性
@property
def num_timesteps(self):
return self._num_timesteps
# 在不计算梯度的情况下执行后续装饰器
@torch.no_grad()
@replace_example_docstring(EXAMPLE_DOC_STRING)
# 定义可调用对象的方法,接受多个参数
def __call__(
# 输入提示,可以是单个字符串或字符串列表
self,
prompt: Union[str, List[str]] = None,
# 图像高度,默认为 None
height: Optional[int] = None,
# 图像宽度,默认为 None
width: Optional[int] = None,
# 推理步骤的数量,默认为 4
num_inference_steps: int = 4,
# 原始推理步骤的数量,默认为 None
original_inference_steps: int = None,
# 指定时间步,默认为 None
timesteps: List[int] = None,
# 引导尺度,默认为 8.5
guidance_scale: float = 8.5,
# 每个提示生成的图像数量,默认为 1
num_images_per_prompt: Optional[int] = 1,
# 随机数生成器,可以是单个或多个生成器,默认为 None
generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
# 预先生成的潜在表示,默认为 None
latents: Optional[torch.Tensor] = None,
# 预先计算的提示嵌入,默认为 None
prompt_embeds: Optional[torch.Tensor] = None,
# 图像输入的适配器,默认为 None
ip_adapter_image: Optional[PipelineImageInput] = None,
# 适配器图像嵌入,默认为 None
ip_adapter_image_embeds: Optional[List[torch.Tensor]] = None,
# 输出类型,默认为 "pil"
output_type: Optional[str] = "pil",
# 是否返回字典格式的结果,默认为 True
return_dict: bool = True,
# 交叉注意力的关键字参数,默认为 None
cross_attention_kwargs: Optional[Dict[str, Any]] = None,
# 跳过的剪辑步骤,默认为 None
clip_skip: Optional[int] = None,
# 步骤结束时的回调函数,默认为 None
callback_on_step_end: Optional[Callable[[int, int, Dict], None]] = None,
# 步骤结束时的张量输入回调,默认为 ["latents"]
callback_on_step_end_tensor_inputs: List[str] = ["latents"],
# 额外的关键字参数,默认为空
**kwargs,
.\diffusers\pipelines\latent_consistency_models\__init__.py
# 从 typing 模块导入 TYPE_CHECKING,以便在类型检查时使用
from typing import TYPE_CHECKING
# 从相对路径下的 utils 模块导入所需的工具和常量
from ...utils import (
DIFFUSERS_SLOW_IMPORT, # 导入用于判断慢导入的常量
OptionalDependencyNotAvailable, # 导入用于处理缺少可选依赖的异常
_LazyModule, # 导入懒加载模块的工具
get_objects_from_module, # 导入从模块中获取对象的函数
is_torch_available, # 导入判断 PyTorch 是否可用的函数
is_transformers_available, # 导入判断 Transformers 是否可用的函数
)
# 初始化一个空字典,用于存储虚拟对象
_dummy_objects = {}
# 初始化一个空字典,用于定义导入结构
_import_structure = {}
# 尝试检查 Transformers 和 PyTorch 的可用性
try:
if not (is_transformers_available() and is_torch_available()): # 如果两者都不可用
raise OptionalDependencyNotAvailable() # 抛出异常,表示缺少可选依赖
except OptionalDependencyNotAvailable: # 捕获可选依赖缺失的异常
from ...utils import dummy_torch_and_transformers_objects # noqa F403 # 导入虚拟对象模块
# 更新虚拟对象字典,填充缺失的对象
_dummy_objects.update(get_objects_from_module(dummy_torch_and_transformers_objects))
else:
# 如果可用,更新导入结构,添加模型管道
_import_structure["pipeline_latent_consistency_img2img"] = ["LatentConsistencyModelImg2ImgPipeline"]
_import_structure["pipeline_latent_consistency_text2img"] = ["LatentConsistencyModelPipeline"]
# 检查是否处于类型检查状态或慢导入模式
if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
try:
if not (is_transformers_available() and is_torch_available()): # 如果两者都不可用
raise OptionalDependencyNotAvailable() # 抛出异常,表示缺少可选依赖
except OptionalDependencyNotAvailable: # 捕获可选依赖缺失的异常
from ...utils.dummy_torch_and_transformers_objects import * # 导入虚拟对象
else:
# 如果可用,导入相应的模型管道
from .pipeline_latent_consistency_img2img import LatentConsistencyModelImg2ImgPipeline
from .pipeline_latent_consistency_text2img import LatentConsistencyModelPipeline
else: # 如果不是类型检查或慢导入模式
import sys # 导入系统模块
# 用懒加载模块替代当前模块
sys.modules[__name__] = _LazyModule(
__name__, # 模块名
globals()["__file__"], # 当前文件的路径
_import_structure, # 导入结构
module_spec=__spec__, # 模块的规范
)
# 将虚拟对象添加到当前模块
for name, value in _dummy_objects.items():
setattr(sys.modules[__name__], name, value) # 设置模块属性
.\diffusers\pipelines\latent_diffusion\pipeline_latent_diffusion.py
# 版权声明,标明版权信息
# Copyright 2024 The HuggingFace Team. All rights reserved.
#
# 授权信息,声明文件的使用条件
# Licensed under the Apache License, Version 2.0 (the "License");
# 说明用户使用该文件必须遵循的许可证
# you may not use this file except in compliance with the License.
# 用户可在此处获取许可证的副本
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# 免责声明,声明软件以"原样"方式分发,不提供任何担保
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# 导入 inspect 模块,用于获取对象的活跃信息
import inspect
# 从 typing 模块导入类型注解
from typing import List, Optional, Tuple, Union
# 导入 PyTorch 库
import torch
# 导入 PyTorch 的神经网络模块
import torch.nn as nn
# 导入 PyTorch 的检查点工具
import torch.utils.checkpoint
# 从 transformers 库导入配置、预训练模型和预训练分词器
from transformers import PretrainedConfig, PreTrainedModel, PreTrainedTokenizer
# 从 transformers 导入激活函数映射
from transformers.activations import ACT2FN
# 从 transformers 导入基础模型输出格式
from transformers.modeling_outputs import BaseModelOutput
# 从 transformers 导入日志工具
from transformers.utils import logging
# 导入特定模型类
from ...models import AutoencoderKL, UNet2DConditionModel, UNet2DModel, VQModel
# 导入调度器类
from ...schedulers import DDIMScheduler, LMSDiscreteScheduler, PNDMScheduler
# 导入随机张量工具
from ...utils.torch_utils import randn_tensor
# 导入扩散管道和图像输出类
from ..pipeline_utils import DiffusionPipeline, ImagePipelineOutput
# 定义用于文本到图像生成的管道类,继承自 DiffusionPipeline
class LDMTextToImagePipeline(DiffusionPipeline):
r"""
使用潜在扩散进行文本到图像生成的管道。
该模型继承自 [`DiffusionPipeline`]。查看超类文档以获取所有管道实现的通用方法
(下载、保存、在特定设备上运行等)。
参数:
vqvae ([`VQModel`]):
向量量化(VQ)模型,用于将图像编码和解码为潜在表示。
bert ([`LDMBertModel`]):
基于 [`~transformers.BERT`] 的文本编码模型。
tokenizer ([`~transformers.BertTokenizer`]):
用于对文本进行分词的 `BertTokenizer`。
unet ([`UNet2DConditionModel`]):
用于去噪编码图像潜在表示的 `UNet2DConditionModel`。
scheduler ([`SchedulerMixin`]):
用于与 `unet` 结合使用的调度器,用于去噪编码图像潜在表示。可以是
[`DDIMScheduler`], [`LMSDiscreteScheduler`] 或 [`PNDMScheduler`]。
"""
# 定义模型的 CPU 卸载顺序
model_cpu_offload_seq = "bert->unet->vqvae"
# 初始化方法,接受多个参数并注册模块
def __init__(
self,
vqvae: Union[VQModel, AutoencoderKL],
bert: PreTrainedModel,
tokenizer: PreTrainedTokenizer,
unet: Union[UNet2DModel, UNet2DConditionModel],
scheduler: Union[DDIMScheduler, PNDMScheduler, LMSDiscreteScheduler],
):
# 调用父类构造函数
super().__init__()
# 注册所需模块
self.register_modules(vqvae=vqvae, bert=bert, tokenizer=tokenizer, unet=unet, scheduler=scheduler)
# 计算 VAE 的缩放因子
self.vae_scale_factor = 2 ** (len(self.vqvae.config.block_out_channels) - 1)
# 禁用梯度计算,优化内存使用
@torch.no_grad()
# 定义一个可调用的方法,允许通过实例直接调用
def __call__(
# 输入提示,可以是字符串或字符串列表
self,
prompt: Union[str, List[str]],
# 可选参数,指定生成图像的高度
height: Optional[int] = None,
# 可选参数,指定生成图像的宽度
width: Optional[int] = None,
# 可选参数,指定推理步骤的数量,默认为50
num_inference_steps: Optional[int] = 50,
# 可选参数,指导比例,默认为1.0
guidance_scale: Optional[float] = 1.0,
# 可选参数,噪声的η值,默认为0.0
eta: Optional[float] = 0.0,
# 可选参数,指定随机数生成器,可以是单个或多个生成器
generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
# 可选参数,指定潜在张量,默认情况下为None
latents: Optional[torch.Tensor] = None,
# 可选参数,输出类型,默认为“pil”格式
output_type: Optional[str] = "pil",
# 可选参数,指示是否返回字典形式的结果,默认为True
return_dict: bool = True,
# 额外参数,允许传入其他关键字参数
**kwargs,
################################################################################
# Code for the text transformer model
################################################################################
""" PyTorch LDMBERT model.""" # 定义该代码文件的文档字符串,表示使用 PyTorch 实现 LDMBERT 模型
logger = logging.get_logger(__name__) # 获取当前模块的日志记录器
LDMBERT_PRETRAINED_MODEL_ARCHIVE_LIST = [ # 定义一个列表,包含预训练模型的名称
"ldm-bert", # LDMBERT 模型的名称
# See all LDMBert models at https://huggingface.co/models?filter=ldmbert # 该行注释提供了 LDMBERT 模型的更多信息链接
]
LDMBERT_PRETRAINED_CONFIG_ARCHIVE_MAP = { # 定义一个字典,映射模型名称到其配置文件的 URL
"ldm-bert": "https://huggingface.co/valhalla/ldm-bert/blob/main/config.json", # LDMBERT 配置文件的 URL
}
""" LDMBERT model configuration""" # 文档字符串,描述 LDMBERT 模型的配置类
class LDMBertConfig(PretrainedConfig): # 定义 LDMBertConfig 类,继承自 PretrainedConfig
model_type = "ldmbert" # 设置模型类型为 "ldmbert"
keys_to_ignore_at_inference = ["past_key_values"] # 定义在推理时需要忽略的键
attribute_map = {"num_attention_heads": "encoder_attention_heads", "hidden_size": "d_model"} # 映射属性名称
def __init__( # 初始化方法
self,
vocab_size=30522, # 词汇表大小
max_position_embeddings=77, # 最大位置嵌入数
encoder_layers=32, # 编码器层数
encoder_ffn_dim=5120, # 编码器前馈网络维度
encoder_attention_heads=8, # 编码器注意力头数
head_dim=64, # 每个注意力头的维度
encoder_layerdrop=0.0, # 编码器层的丢弃率
activation_function="gelu", # 激活函数类型
d_model=1280, # 模型的维度
dropout=0.1, # 丢弃率
attention_dropout=0.0, # 注意力丢弃率
activation_dropout=0.0, # 激活丢弃率
init_std=0.02, # 初始化标准差
classifier_dropout=0.0, # 分类器丢弃率
scale_embedding=False, # 是否缩放嵌入
use_cache=True, # 是否使用缓存
pad_token_id=0, # 填充标记的 ID
**kwargs, # 其他可选关键字参数
):
self.vocab_size = vocab_size # 设置词汇表大小
self.max_position_embeddings = max_position_embeddings # 设置最大位置嵌入数
self.d_model = d_model # 设置模型的维度
self.encoder_ffn_dim = encoder_ffn_dim # 设置编码器前馈网络维度
self.encoder_layers = encoder_layers # 设置编码器层数
self.encoder_attention_heads = encoder_attention_heads # 设置编码器注意力头数
self.head_dim = head_dim # 设置每个注意力头的维度
self.dropout = dropout # 设置丢弃率
self.attention_dropout = attention_dropout # 设置注意力丢弃率
self.activation_dropout = activation_dropout # 设置激活丢弃率
self.activation_function = activation_function # 设置激活函数类型
self.init_std = init_std # 设置初始化标准差
self.encoder_layerdrop = encoder_layerdrop # 设置编码器层的丢弃率
self.classifier_dropout = classifier_dropout # 设置分类器丢弃率
self.use_cache = use_cache # 设置是否使用缓存
self.num_hidden_layers = encoder_layers # 设置隐藏层数
self.scale_embedding = scale_embedding # 设置是否缩放嵌入,若为 True,则缩放因子为 sqrt(d_model)
super().__init__(pad_token_id=pad_token_id, **kwargs) # 调用父类构造函数,传递填充标记 ID 和其他参数
def _expand_mask(mask: torch.Tensor, dtype: torch.dtype, tgt_len: Optional[int] = None): # 定义一个函数用于扩展注意力掩码
"""
Expands attention_mask from `[bsz, seq_len]` to `[bsz, 1, tgt_seq_len, src_seq_len]`. # 函数说明,扩展注意力掩码形状
"""
bsz, src_len = mask.size() # 获取批次大小和源序列长度
tgt_len = tgt_len if tgt_len is not None else src_len # 如果目标长度未提供,则使用源序列长度
expanded_mask = mask[:, None, None, :].expand(bsz, 1, tgt_len, src_len).to(dtype) # 扩展掩码的维度并转换类型
inverted_mask = 1.0 - expanded_mask # 反转掩码
return inverted_mask.masked_fill(inverted_mask.to(torch.bool), torch.finfo(dtype).min) # 将反转掩码中的 True 部分填充为最小值
# Copied from transformers.models.bart.modeling_bart.BartAttention with Bart->LDMBert # 从 BartAttention 复制的类,并将 Bart 替换为 LDMBert
class LDMBertAttention(nn.Module): # 定义 LDMBertAttention 类,继承自 nn.Module
"""Multi-headed attention from 'Attention Is All You Need' paper""" # 文档字符串,描述这是来自论文 "Attention Is All You Need" 的多头注意力机制
# 初始化方法,定义多头注意力层的参数
def __init__(
self,
embed_dim: int, # 嵌入向量的维度
num_heads: int, # 注意力头的数量
head_dim: int, # 每个注意力头的维度
dropout: float = 0.0, # Dropout 的比率,默认值为 0.0
is_decoder: bool = False, # 是否为解码器,默认值为 False
bias: bool = False, # 是否使用偏置,默认值为 False
):
# 调用父类的初始化方法
super().__init__()
# 保存嵌入维度
self.embed_dim = embed_dim
# 保存注意力头的数量
self.num_heads = num_heads
# 保存 Dropout 比率
self.dropout = dropout
# 保存每个头的维度
self.head_dim = head_dim
# 计算内部维度,即每个头维度与头数量的乘积
self.inner_dim = head_dim * num_heads
# 计算缩放因子,用于缩放注意力分数
self.scaling = self.head_dim**-0.5
# 保存是否为解码器的标志
self.is_decoder = is_decoder
# 创建键投影层,映射嵌入维度到内部维度
self.k_proj = nn.Linear(embed_dim, self.inner_dim, bias=bias)
# 创建值投影层,映射嵌入维度到内部维度
self.v_proj = nn.Linear(embed_dim, self.inner_dim, bias=bias)
# 创建查询投影层,映射嵌入维度到内部维度
self.q_proj = nn.Linear(embed_dim, self.inner_dim, bias=bias)
# 创建输出投影层,映射内部维度回到嵌入维度
self.out_proj = nn.Linear(self.inner_dim, embed_dim)
# 形状调整方法,将输入张量调整为适合多头注意力的形状
def _shape(self, tensor: torch.Tensor, seq_len: int, bsz: int):
# 调整张量形状,并进行维度转置
return tensor.view(bsz, seq_len, self.num_heads, self.head_dim).transpose(1, 2).contiguous()
# 前向传播方法,定义多头注意力层的计算过程
def forward(
self,
hidden_states: torch.Tensor, # 输入的隐藏状态张量
key_value_states: Optional[torch.Tensor] = None, # 可选的键值状态
past_key_value: Optional[Tuple[torch.Tensor]] = None, # 可选的过去键值状态
attention_mask: Optional[torch.Tensor] = None, # 可选的注意力掩码
layer_head_mask: Optional[torch.Tensor] = None, # 可选的层头掩码
output_attentions: bool = False, # 是否输出注意力权重的标志
# 定义一个名为 LDMBertEncoderLayer 的神经网络模块,继承自 nn.Module
class LDMBertEncoderLayer(nn.Module):
# 初始化方法,接收一个配置对象 config
def __init__(self, config: LDMBertConfig):
# 调用父类的初始化方法
super().__init__()
# 获取模型嵌入维度
self.embed_dim = config.d_model
# 创建自注意力层,使用配置中的参数
self.self_attn = LDMBertAttention(
embed_dim=self.embed_dim,
num_heads=config.encoder_attention_heads,
head_dim=config.head_dim,
dropout=config.attention_dropout,
)
# 创建自注意力层的归一化层
self.self_attn_layer_norm = nn.LayerNorm(self.embed_dim)
# 获取 dropout 比率
self.dropout = config.dropout
# 根据配置选择激活函数
self.activation_fn = ACT2FN[config.activation_function]
# 获取激活函数的 dropout 比率
self.activation_dropout = config.activation_dropout
# 创建第一层全连接层,输入维度为嵌入维度,输出维度为前馈网络维度
self.fc1 = nn.Linear(self.embed_dim, config.encoder_ffn_dim)
# 创建第二层全连接层,输入维度为前馈网络维度,输出维度为嵌入维度
self.fc2 = nn.Linear(config.encoder_ffn_dim, self.embed_dim)
# 创建最终的归一化层
self.final_layer_norm = nn.LayerNorm(self.embed_dim)
# 前向传播方法,定义输入和输出
def forward(
self,
hidden_states: torch.Tensor,
attention_mask: torch.Tensor,
layer_head_mask: torch.Tensor,
output_attentions: Optional[bool] = False,
# 定义返回类型为元组,包含一个张量和一个可选张量
) -> Tuple[torch.Tensor, Optional[torch.Tensor]]:
"""
参数:
hidden_states (`torch.Tensor`): 输入张量,形状为 `(seq_len, batch, embed_dim)`
attention_mask (`torch.Tensor`): 注意力掩码,大小为
`(batch, 1, tgt_len, src_len)`,填充元素由非常大的负值表示。
layer_head_mask (`torch.Tensor`): 给定层中注意力头的掩码,大小为
`(encoder_attention_heads,)`。
output_attentions (`bool`, *可选*):
是否返回所有注意力层的注意力张量。更多详细信息请参见返回张量中的 `attentions`。
"""
# 保存输入的残差连接
residual = hidden_states
# 对输入的隐藏状态进行层归一化
hidden_states = self.self_attn_layer_norm(hidden_states)
# 计算注意力并获取权重和其他输出
hidden_states, attn_weights, _ = self.self_attn(
hidden_states=hidden_states,
attention_mask=attention_mask,
layer_head_mask=layer_head_mask,
output_attentions=output_attentions,
)
# 应用 dropout 正则化
hidden_states = nn.functional.dropout(hidden_states, p=self.dropout, training=self.training)
# 将残差添加到隐藏状态中
hidden_states = residual + hidden_states
# 保存当前隐藏状态的残差连接
residual = hidden_states
# 对隐藏状态进行最终层归一化
hidden_states = self.final_layer_norm(hidden_states)
# 应用全连接层和激活函数
hidden_states = self.activation_fn(self.fc1(hidden_states))
# 应用激活函数的 dropout 正则化
hidden_states = nn.functional.dropout(hidden_states, p=self.activation_dropout, training=self.training)
# 通过第二个全连接层
hidden_states = self.fc2(hidden_states)
# 再次应用 dropout 正则化
hidden_states = nn.functional.dropout(hidden_states, p=self.dropout, training=self.training)
# 将残差添加到隐藏状态中
hidden_states = residual + hidden_states
# 检查隐藏状态是否为 float16 类型,并处理无穷大或 NaN 值
if hidden_states.dtype == torch.float16 and (
torch.isinf(hidden_states).any() or torch.isnan(hidden_states).any()
):
# 计算限制值
clamp_value = torch.finfo(hidden_states.dtype).max - 1000
# 将隐藏状态限制在有效范围内
hidden_states = torch.clamp(hidden_states, min=-clamp_value, max=clamp_value)
# 将最终的隐藏状态放入输出元组中
outputs = (hidden_states,)
# 如果需要,添加注意力权重到输出中
if output_attentions:
outputs += (attn_weights,)
# 返回输出元组
return outputs
# 复制自 transformers.models.bart.modeling_bart.BartPretrainedModel,进行 Bart->LDMBert 的修改
class LDMBertPreTrainedModel(PreTrainedModel):
# 配置类为 LDMBertConfig
config_class = LDMBertConfig
# 基础模型前缀
base_model_prefix = "model"
# 支持梯度检查点
_supports_gradient_checkpointing = True
# 加载时忽略的键
_keys_to_ignore_on_load_unexpected = [r"encoder\.version", r"decoder\.version"]
# 初始化权重
def _init_weights(self, module):
# 获取初始化标准差
std = self.config.init_std
# 如果模块是线性层
if isinstance(module, nn.Linear):
# 用正态分布初始化权重
module.weight.data.normal_(mean=0.0, std=std)
# 如果有偏置,则初始化为零
if module.bias is not None:
module.bias.data.zero_()
# 如果模块是嵌入层
elif isinstance(module, nn.Embedding):
# 用正态分布初始化权重
module.weight.data.normal_(mean=0.0, std=std)
# 如果有填充索引,则将其权重设为零
if module.padding_idx is not None:
module.weight.data[module.padding_idx].zero_()
# 设置梯度检查点
def _set_gradient_checkpointing(self, module, value=False):
# 如果模块是 LDMBertEncoder,则设置其梯度检查点属性
if isinstance(module, (LDMBertEncoder,)):
module.gradient_checkpointing = value
# 属性,返回虚拟输入
@property
def dummy_inputs(self):
# 获取填充标记
pad_token = self.config.pad_token_id
# 创建输入 ID 张量
input_ids = torch.tensor([[0, 6, 10, 4, 2], [0, 8, 12, 2, pad_token]], device=self.device)
# 创建虚拟输入字典
dummy_inputs = {
"attention_mask": input_ids.ne(pad_token),
"input_ids": input_ids,
}
# 返回虚拟输入
return dummy_inputs
class LDMBertEncoder(LDMBertPreTrainedModel):
"""
包含 *config.encoder_layers* 个自注意力层的 Transformer 编码器。每层是一个
[`LDMBertEncoderLayer`]。
参数:
config: LDMBertConfig
embed_tokens (nn.Embedding): 输出嵌入
"""
# 初始化方法
def __init__(self, config: LDMBertConfig):
# 调用父类初始化
super().__init__(config)
# 设置丢弃率
self.dropout = config.dropout
# 获取嵌入维度
embed_dim = config.d_model
# 获取填充索引
self.padding_idx = config.pad_token_id
# 获取最大源位置
self.max_source_positions = config.max_position_embeddings
# 初始化嵌入层
self.embed_tokens = nn.Embedding(config.vocab_size, embed_dim)
# 初始化位置嵌入层
self.embed_positions = nn.Embedding(config.max_position_embeddings, embed_dim)
# 创建编码器层的模块列表
self.layers = nn.ModuleList([LDMBertEncoderLayer(config) for _ in range(config.encoder_layers)])
# 初始化层归一化
self.layer_norm = nn.LayerNorm(embed_dim)
# 默认不使用梯度检查点
self.gradient_checkpointing = False
# 初始化权重并应用最终处理
self.post_init()
# 获取输入嵌入
def get_input_embeddings(self):
return self.embed_tokens
# 设置输入嵌入
def set_input_embeddings(self, value):
self.embed_tokens = value
# 前向传播方法
def forward(
self,
input_ids: torch.LongTensor = None,
attention_mask: Optional[torch.Tensor] = None,
position_ids: Optional[torch.LongTensor] = None,
head_mask: Optional[torch.Tensor] = None,
inputs_embeds: Optional[torch.Tensor] = None,
output_attentions: Optional[bool] = None,
output_hidden_states: Optional[bool] = None,
return_dict: Optional[bool] = None,
):
pass # 此处代码未完待续
class LDMBertModel(LDMBertPreTrainedModel):
# 不进行模块拆分的模块列表
_no_split_modules = []
# 初始化方法,接收配置参数并设置模型相关属性
def __init__(self, config: LDMBertConfig):
# 调用父类的初始化方法
super().__init__(config)
# 创建 LDMBertEncoder 模型实例
self.model = LDMBertEncoder(config)
# 定义一个线性层,将隐藏层大小映射到词汇表大小
self.to_logits = nn.Linear(config.hidden_size, config.vocab_size)
# 前向传播方法,定义模型的输入和输出
def forward(
self,
input_ids=None, # 输入的 ID 列表
attention_mask=None, # 注意力掩码,指示哪些位置需要注意
position_ids=None, # 位置 ID,表示输入中每个 token 的位置
head_mask=None, # 头掩码,用于指定哪些注意力头被禁用
inputs_embeds=None, # 输入的嵌入向量,替代 input_ids
output_attentions=None, # 是否输出注意力权重
output_hidden_states=None, # 是否输出隐藏状态
return_dict=None, # 是否返回字典格式的输出
):
# 将输入传入模型,获取输出
outputs = self.model(
input_ids,
attention_mask=attention_mask, # 将注意力掩码传入模型
position_ids=position_ids, # 将位置 ID 传入模型
head_mask=head_mask, # 将头掩码传入模型
inputs_embeds=inputs_embeds, # 将输入嵌入向量传入模型
output_attentions=output_attentions, # 是否输出注意力权重
output_hidden_states=output_hidden_states, # 是否输出隐藏状态
return_dict=return_dict, # 是否返回字典格式的输出
)
# 返回模型的输出结果
return outputs
.\diffusers\pipelines\latent_diffusion\pipeline_latent_diffusion_superresolution.py
# 导入 inspect 模块以便于获取有关对象的信息
import inspect
# 从 typing 模块导入 List, Optional, Tuple 和 Union 类型
from typing import List, Optional, Tuple, Union
# 导入 numpy 库,通常用于数组和矩阵操作
import numpy as np
# 导入 PIL.Image 库,用于图像处理
import PIL.Image
# 导入 PyTorch 库,用于深度学习
import torch
# 导入 PyTorch 的检查点工具,用于内存优化
import torch.utils.checkpoint
# 从上层模块导入 UNet2DModel 和 VQModel 模型
from ...models import UNet2DModel, VQModel
# 从上层模块导入不同的调度器
from ...schedulers import (
DDIMScheduler,
DPMSolverMultistepScheduler,
EulerAncestralDiscreteScheduler,
EulerDiscreteScheduler,
LMSDiscreteScheduler,
PNDMScheduler,
)
# 从上层模块导入 PIL_INTERPOLATION 用于图像插值
from ...utils import PIL_INTERPOLATION
# 从上层模块导入 randn_tensor 用于生成随机张量
from ...utils.torch_utils import randn_tensor
# 从上层模块导入 DiffusionPipeline 和 ImagePipelineOutput
from ..pipeline_utils import DiffusionPipeline, ImagePipelineOutput
# 定义一个图像预处理函数
def preprocess(image):
# 获取图像的宽度和高度
w, h = image.size
# 将宽度和高度调整为32的整数倍
w, h = (x - x % 32 for x in (w, h)) # resize to integer multiple of 32
# 使用兰索斯插值法调整图像大小
image = image.resize((w, h), resample=PIL_INTERPOLATION["lanczos"])
# 将图像转换为 numpy 数组并归一化到 [0, 1] 范围
image = np.array(image).astype(np.float32) / 255.0
# 增加一个维度并调整数组的轴顺序
image = image[None].transpose(0, 3, 1, 2)
# 将 numpy 数组转换为 PyTorch 张量
image = torch.from_numpy(image)
# 将图像值范围从 [0, 1] 转换到 [-1, 1]
return 2.0 * image - 1.0
# 定义一个用于图像超分辨率的潜在扩散管道类
class LDMSuperResolutionPipeline(DiffusionPipeline):
r"""
使用潜在扩散进行图像超分辨率的管道。
该模型继承自 [`DiffusionPipeline`]。有关所有管道实现的通用方法的文档(下载、保存、在特定设备上运行等),请查看超类文档。
参数:
vqvae ([`VQModel`]):
用于将图像编码和解码为潜在表示的矢量量化(VQ)模型。
unet ([`UNet2DModel`]):
用于去噪编码图像的 `UNet2DModel`。
scheduler ([`SchedulerMixin`]):
用于与 `unet` 结合使用的调度器,以去噪编码图像的潜在表示。可以是以下之一:
[`DDIMScheduler`], [`LMSDiscreteScheduler`], [`EulerDiscreteScheduler`],
[`EulerAncestralDiscreteScheduler`], [`DPMSolverMultistepScheduler`] 或 [`PNDMScheduler`]。
"""
# 初始化方法,设置 VQ 模型、UNet 模型和调度器
def __init__(
self,
vqvae: VQModel,
unet: UNet2DModel,
scheduler: Union[
DDIMScheduler,
PNDMScheduler,
LMSDiscreteScheduler,
EulerDiscreteScheduler,
EulerAncestralDiscreteScheduler,
DPMSolverMultistepScheduler,
],
):
# 调用父类的初始化方法
super().__init__()
# 注册 VQ 模型、UNet 模型和调度器
self.register_modules(vqvae=vqvae, unet=unet, scheduler=scheduler)
# 在不计算梯度的情况下调用方法
@torch.no_grad()
def __call__(
self,
# 输入图像,可以是 PyTorch 张量或 PIL 图像
image: Union[torch.Tensor, PIL.Image.Image] = None,
# 批处理大小的可选参数,默认为 1
batch_size: Optional[int] = 1,
# 可选的推理步骤数,默认为 100
num_inference_steps: Optional[int] = 100,
# 可选的 eta 值,默认为 0.0
eta: Optional[float] = 0.0,
# 可选的随机生成器,可以是单个生成器或生成器列表
generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
# 输出类型的可选参数,默认为 "pil"
output_type: Optional[str] = "pil",
# 是否返回字典形式的结果,默认为 True
return_dict: bool = True,
.\diffusers\pipelines\latent_diffusion\__init__.py
# 从 typing 模块导入 TYPE_CHECKING,用于类型检查
from typing import TYPE_CHECKING
# 从相对路径导入一些工具函数和常量
from ...utils import (
DIFFUSERS_SLOW_IMPORT, # 导入用于指示慢导入的常量
OptionalDependencyNotAvailable, # 导入用于处理缺少可选依赖的异常
_LazyModule, # 导入延迟加载模块的类
get_objects_from_module, # 导入从模块获取对象的函数
is_torch_available, # 导入检查 PyTorch 是否可用的函数
is_transformers_available, # 导入检查 Transformers 是否可用的函数
)
# 初始化一个空字典,用于存放虚拟对象
_dummy_objects = {}
# 初始化一个空字典,用于存放模块的导入结构
_import_structure = {}
# 尝试执行以下代码块
try:
# 检查 Transformers 和 PyTorch 是否可用,如果任意一个不可用,则抛出异常
if not (is_transformers_available() and is_torch_available()):
raise OptionalDependencyNotAvailable()
# 捕获缺少可选依赖的异常
except OptionalDependencyNotAvailable:
# 从工具模块导入虚拟的 Torch 和 Transformers 对象,忽略某些检查
from ...utils import dummy_torch_and_transformers_objects # noqa F403
# 更新虚拟对象字典,将导入的对象添加进去
_dummy_objects.update(get_objects_from_module(dummy_torch_and_transformers_objects))
else:
# 如果依赖都可用,更新导入结构字典,记录需要导入的类
_import_structure["pipeline_latent_diffusion"] = ["LDMBertModel", "LDMTextToImagePipeline"]
_import_structure["pipeline_latent_diffusion_superresolution"] = ["LDMSuperResolutionPipeline"]
# 如果在类型检查状态或需要慢导入的状态下执行
if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
# 尝试执行以下代码块
try:
# 检查 Transformers 和 PyTorch 是否可用
if not (is_transformers_available() and is_torch_available()):
raise OptionalDependencyNotAvailable()
# 捕获缺少可选依赖的异常
except OptionalDependencyNotAvailable:
# 从工具模块导入虚拟的 Torch 和 Transformers 对象
from ...utils.dummy_torch_and_transformers_objects import *
else:
# 如果依赖可用,导入相关的类
from .pipeline_latent_diffusion import LDMBertModel, LDMTextToImagePipeline
from .pipeline_latent_diffusion_superresolution import LDMSuperResolutionPipeline
# 否则(不是在类型检查状态下)
else:
# 导入 sys 模块
import sys
# 将当前模块的 sys.modules 条目替换为延迟加载的模块
sys.modules[__name__] = _LazyModule(
__name__, # 当前模块名
globals()["__file__"], # 当前模块文件路径
_import_structure, # 模块的导入结构
module_spec=__spec__, # 模块的规范
)
# 遍历虚拟对象字典,将其中的对象设置为当前模块的属性
for name, value in _dummy_objects.items():
setattr(sys.modules[__name__], name, value)
.\diffusers\pipelines\latte\pipeline_latte.py
# 版权所有 2024 Latte 团队和 HuggingFace 团队。
# 所有权利保留。
#
# 根据 Apache 许可证第 2.0 版(“许可证”)授权;
# 除非遵循许可证,否则您不得使用此文件。
# 您可以在以下地址获取许可证副本:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# 除非适用法律或书面协议另有规定,按照许可证分发的软件
# 是按“原样”基础分发,不附带任何明示或暗示的担保或条件。
# 有关许可证的具体权限和限制,请参阅许可证。
import html # 导入 html 模块,用于处理 HTML 实体和字符串
import inspect # 导入 inspect 模块,用于获取对象的信息
import re # 导入 re 模块,用于处理正则表达式
import urllib.parse as ul # 导入 urllib.parse 模块,简化 URL 解析和构建
from dataclasses import dataclass # 从 dataclasses 模块导入 dataclass 装饰器,用于简化数据类的定义
from typing import Callable, Dict, List, Optional, Tuple, Union # 导入类型提示,用于类型注解
import torch # 导入 PyTorch 库,用于张量操作和深度学习
from transformers import T5EncoderModel, T5Tokenizer # 从 transformers 导入 T5 模型和分词器
from ...callbacks import MultiPipelineCallbacks, PipelineCallback # 从本地模块导入多管道回调和单管道回调
from ...models import AutoencoderKL, LatteTransformer3DModel # 从本地模块导入自动编码器和 Latte 3D 模型
from ...pipelines.pipeline_utils import DiffusionPipeline # 从本地模块导入扩散管道
from ...schedulers import KarrasDiffusionSchedulers # 从本地模块导入 Karras 扩散调度器
from ...utils import ( # 从本地模块导入多个工具函数和常量
BACKENDS_MAPPING, # 后端映射
BaseOutput, # 基础输出类
is_bs4_available, # 检查 BeautifulSoup 是否可用的函数
is_ftfy_available, # 检查 ftfy 是否可用的函数
logging, # 日志记录模块
replace_example_docstring, # 替换示例文档字符串的函数
)
from ...utils.torch_utils import is_compiled_module, randn_tensor # 从工具模块导入 PyTorch 相关的实用函数
from ...video_processor import VideoProcessor # 从本地模块导入视频处理器
logger = logging.get_logger(__name__) # 获取当前模块的日志记录器,命名为模块名,禁用 pylint 的无效名称警告
if is_bs4_available(): # 如果 BeautifulSoup 可用
from bs4 import BeautifulSoup # 导入 BeautifulSoup 库
if is_ftfy_available(): # 如果 ftfy 可用
import ftfy # 导入 ftfy 库
EXAMPLE_DOC_STRING = """ # 定义示例文档字符串,用于展示如何使用某个功能
Examples: # 示例标题
```py # Python 代码块开始
>>> import torch # 导入 PyTorch 库
>>> from diffusers import LattePipeline # 从 diffusers 导入 LattePipeline
>>> from diffusers.utils import export_to_gif # 从 diffusers 导入导出 GIF 的工具函数
>>> # 您也可以将检查点 ID 替换为 "maxin-cn/Latte-1"。
>>> pipe = LattePipeline.from_pretrained("maxin-cn/Latte-1", torch_dtype=torch.float16).to("cuda") # 从预训练模型加载管道并转移到 GPU
>>> # 启用内存优化。
>>> pipe.enable_model_cpu_offload() # 启用模型的 CPU 卸载以优化内存使用
>>> prompt = "A small cactus with a happy face in the Sahara desert." # 定义提示语
>>> videos = pipe(prompt).frames[0] # 使用管道生成视频并提取第一帧
>>> export_to_gif(videos, "latte.gif") # 将生成的视频导出为 GIF 文件
```py # Python 代码块结束
"""
# 从 diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.retrieve_timesteps 复制的函数
def retrieve_timesteps( # 定义一个名为 retrieve_timesteps 的函数
scheduler, # 调度器对象,用于控制时间步长
num_inference_steps: Optional[int] = None, # 推理步骤的可选数量
device: Optional[Union[str, torch.device]] = None, # 设备类型的可选参数,可以是字符串或 torch.device 对象
timesteps: Optional[List[int]] = None, # 自定义时间步长的可选列表
sigmas: Optional[List[float]] = None, # 自定义 sigma 值的可选列表
**kwargs, # 允许其他关键字参数
):
"""
调用调度器的 `set_timesteps` 方法并在调用后从调度器中检索时间步长。处理自定义时间步长。
任何 kwargs 将被提供给 `scheduler.set_timesteps`。
``` # 函数文档字符串的开始
```py # 函数文档字符串的结束
``` # 代码块的结束
# 参数说明
Args:
scheduler (`SchedulerMixin`): # 接受一个调度器,用于获取时间步
The scheduler to get timesteps from.
num_inference_steps (`int`): # 生成样本时使用的扩散步骤数量
The number of diffusion steps used when generating samples with a pre-trained model. If used, `timesteps`
must be `None`. # 如果使用此参数,`timesteps` 必须为 None
device (`str` or `torch.device`, *optional*): # 指定时间步要移动到的设备
The device to which the timesteps should be moved to. If `None`, the timesteps are not moved. # 如果为 None,时间步不会移动
timesteps (`List[int]`, *optional*): # 自定义时间步,覆盖调度器的时间步间隔策略
Custom timesteps used to override the timestep spacing strategy of the scheduler. If `timesteps` is passed,
`num_inference_steps` and `sigmas` must be `None`. # 如果传递了此参数,`num_inference_steps` 和 `sigmas` 必须为 None
sigmas (`List[float]`, *optional*): # 自定义 sigma,覆盖调度器的时间步间隔策略
Custom sigmas used to override the timestep spacing strategy of the scheduler. If `sigmas` is passed,
`num_inference_steps` and `timesteps` must be `None`. # 如果传递了此参数,`num_inference_steps` 和 `timesteps` 必须为 None
Returns:
`Tuple[torch.Tensor, int]`: A tuple where the first element is the timestep schedule from the scheduler and the
second element is the number of inference steps. # 返回一个元组,第一个元素是调度器的时间步安排,第二个元素是推断步骤的数量
"""
# 检查是否同时传递了 time步和 sigma
if timesteps is not None and sigmas is not None:
# 抛出值错误,提示只能传递一个自定义值
raise ValueError("Only one of `timesteps` or `sigmas` can be passed. Please choose one to set custom values")
# 如果传递了时间步
if timesteps is not None:
# 检查调度器的 set_timesteps 方法是否接受时间步参数
accepts_timesteps = "timesteps" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
# 如果不接受,则抛出值错误
if not accepts_timesteps:
raise ValueError(
f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
f" timestep schedules. Please check whether you are using the correct scheduler."
)
# 设置时间步
scheduler.set_timesteps(timesteps=timesteps, device=device, **kwargs)
# 从调度器中获取设置后的时间步
timesteps = scheduler.timesteps
# 计算推断步骤数量
num_inference_steps = len(timesteps)
# 如果传递了 sigma
elif sigmas is not None:
# 检查调度器的 set_timesteps 方法是否接受 sigma 参数
accept_sigmas = "sigmas" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
# 如果不接受,则抛出值错误
if not accept_sigmas:
raise ValueError(
f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
f" sigmas schedules. Please check whether you are using the correct scheduler."
)
# 设置 sigma
scheduler.set_timesteps(sigmas=sigmas, device=device, **kwargs)
# 从调度器中获取设置后的时间步
timesteps = scheduler.timesteps
# 计算推断步骤数量
num_inference_steps = len(timesteps)
# 如果没有传递时间步和 sigma
else:
# 使用推断步骤数量设置时间步
scheduler.set_timesteps(num_inference_steps, device=device, **kwargs)
# 从调度器中获取设置后的时间步
timesteps = scheduler.timesteps
# 返回时间步和推断步骤数量
return timesteps, num_inference_steps
# 定义一个数据类,用于保存拉铁管道的输出帧
@dataclass
class LattePipelineOutput(BaseOutput):
# 输出帧以张量形式存储
frames: torch.Tensor
# 定义一个拉铁管道类,用于文本到视频生成
class LattePipeline(DiffusionPipeline):
r"""
使用拉铁生成文本到视频的管道。
该模型继承自 [`DiffusionPipeline`]。查看超类文档以获取所有管道实现的通用方法
(例如下载或保存,运行在特定设备等)。
参数:
vae ([`AutoencoderKL`]):
用于将视频编码和解码为潜在表示的变分自编码器(VAE)模型。
text_encoder ([`T5EncoderModel`]):
冻结的文本编码器。拉铁使用
[T5](https://huggingface.co/docs/transformers/model_doc/t5#transformers.T5EncoderModel),特别是
[t5-v1_1-xxl](https://huggingface.co/PixArt-alpha/PixArt-alpha/tree/main/t5-v1_1-xxl) 变体。
tokenizer (`T5Tokenizer`):
类
[T5Tokenizer](https://huggingface.co/docs/transformers/model_doc/t5#transformers.T5Tokenizer) 的分词器。
transformer ([`LatteTransformer3DModel`]):
一个文本条件的 `LatteTransformer3DModel`,用于去噪编码的视频潜在表示。
scheduler ([`SchedulerMixin`]):
与 `transformer` 结合使用的调度器,用于去噪编码的视频潜在表示。
"""
# 定义一个正则表达式,用于匹配坏的标点符号
bad_punct_regex = re.compile(r"[#®•©™&@·º½¾¿¡§~\)\(\]\[\}\{\|\\/\\*]{1,}")
# 可选组件列表,包括分词器和文本编码器
_optional_components = ["tokenizer", "text_encoder"]
# 定义模型在 CPU 上的卸载顺序
model_cpu_offload_seq = "text_encoder->transformer->vae"
# 定义需要作为回调的张量输入
_callback_tensor_inputs = [
"latents",
"prompt_embeds",
"negative_prompt_embeds",
]
# 初始化方法,接受各个组件作为参数
def __init__(
self,
tokenizer: T5Tokenizer,
text_encoder: T5EncoderModel,
vae: AutoencoderKL,
transformer: LatteTransformer3DModel,
scheduler: KarrasDiffusionSchedulers,
):
# 调用父类初始化方法
super().__init__()
# 注册各个模块,包括分词器、文本编码器、VAE、变换器和调度器
self.register_modules(
tokenizer=tokenizer, text_encoder=text_encoder, vae=vae, transformer=transformer, scheduler=scheduler
)
# 计算 VAE 的缩放因子
self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1)
# 创建视频处理器实例,传入缩放因子
self.video_processor = VideoProcessor(vae_scale_factor=self.vae_scale_factor)
# 从指定链接修改而来,掩蔽文本嵌入
def mask_text_embeddings(self, emb, mask):
# 如果嵌入的第一个维度为1
if emb.shape[0] == 1:
# 计算保留的索引
keep_index = mask.sum().item()
# 返回被掩蔽的嵌入和保留的索引
return emb[:, :, :keep_index, :], keep_index # 1, 120, 4096 -> 1 7 4096
else:
# 应用掩蔽,生成被掩蔽的特征
masked_feature = emb * mask[:, None, :, None] # 1 120 4096
# 返回被掩蔽的特征和原始嵌入的形状
return masked_feature, emb.shape[2]
# 从 diffusers.pipelines.deepfloyd_if.pipeline_if.encode_prompt 修改而来
# 定义一个编码提示的函数
def encode_prompt(
self, # 当前实例的引用
prompt: Union[str, List[str]], # 输入的提示,支持字符串或字符串列表
do_classifier_free_guidance: bool = True, # 是否使用无分类器引导
negative_prompt: str = "", # 负提示,用于引导模型避免生成特定内容
num_images_per_prompt: int = 1, # 每个提示生成的图像数量
device: Optional[torch.device] = None, # 指定设备(如 GPU),默认为 None
prompt_embeds: Optional[torch.FloatTensor] = None, # 提示的嵌入表示,默认为 None
negative_prompt_embeds: Optional[torch.FloatTensor] = None, # 负提示的嵌入表示,默认为 None
clean_caption: bool = False, # 是否清理标题,默认为 False
mask_feature: bool = True, # 是否使用特征掩码,默认为 True
dtype=None, # 数据类型,默认为 None
# 从 diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.prepare_extra_step_kwargs 复制
def prepare_extra_step_kwargs(self, generator, eta): # 定义一个准备额外步骤参数的函数
# 为调度器步骤准备额外的参数,因为并非所有调度器具有相同的参数签名
# eta (η) 仅在 DDIMScheduler 中使用,其他调度器将被忽略。
# eta 对应于 DDIM 论文中的 η: https://arxiv.org/abs/2010.02502
# 并应在 [0, 1] 范围内
# 检查调度器步骤是否接受 eta 参数
accepts_eta = "eta" in set(inspect.signature(self.scheduler.step).parameters.keys())
extra_step_kwargs = {} # 初始化额外步骤参数字典
if accepts_eta: # 如果接受 eta
extra_step_kwargs["eta"] = eta # 将 eta 添加到额外参数中
# 检查调度器是否接受 generator 参数
accepts_generator = "generator" in set(inspect.signature(self.scheduler.step).parameters.keys())
if accepts_generator: # 如果接受 generator
extra_step_kwargs["generator"] = generator # 将 generator 添加到额外参数中
return extra_step_kwargs # 返回准备好的额外参数
# 定义一个检查输入的函数
def check_inputs(
self, # 当前实例的引用
prompt, # 输入的提示
height, # 图像高度
width, # 图像宽度
negative_prompt, # 负提示
callback_on_step_end_tensor_inputs, # 步骤结束时的回调
prompt_embeds=None, # 提示嵌入,默认为 None
negative_prompt_embeds=None, # 负提示嵌入,默认为 None
): # 定义函数的结束括号
# 检查高度和宽度是否为8的倍数
if height % 8 != 0 or width % 8 != 0:
# 如果不是,抛出值错误,提示高度和宽度必须是8的倍数
raise ValueError(f"`height` and `width` have to be divisible by 8 but are {height} and {width}.")
# 检查回调的输入是否为None且是否包含在预定义的回调输入中
if callback_on_step_end_tensor_inputs is not None and not all(
k in self._callback_tensor_inputs for k in callback_on_step_end_tensor_inputs
):
# 如果不在,抛出值错误,提示回调输入不合法
raise ValueError(
f"`callback_on_step_end_tensor_inputs` has to be in {self._callback_tensor_inputs}, but found {[k for k in callback_on_step_end_tensor_inputs if k not in self._callback_tensor_inputs]}"
)
# 检查是否同时提供了prompt和prompt_embeds
if prompt is not None and prompt_embeds is not None:
# 如果是,抛出值错误,提示只能提供其中一个
raise ValueError(
f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
" only forward one of the two."
)
# 检查prompt和prompt_embeds是否都为None
elif prompt is None and prompt_embeds is None:
# 如果是,抛出值错误,提示必须提供其中一个
raise ValueError(
"Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined."
)
# 检查prompt的类型是否合法
elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):
# 如果不合法,抛出值错误,提示类型错误
raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")
# 检查是否同时提供了prompt和negative_prompt_embeds
if prompt is not None and negative_prompt_embeds is not None:
# 如果是,抛出值错误,提示只能提供其中一个
raise ValueError(
f"Cannot forward both `prompt`: {prompt} and `negative_prompt_embeds`:"
f" {negative_prompt_embeds}. Please make sure to only forward one of the two."
)
# 检查是否同时提供了negative_prompt和negative_prompt_embeds
if negative_prompt is not None and negative_prompt_embeds is not None:
# 如果是,抛出值错误,提示只能提供其中一个
raise ValueError(
f"Cannot forward both `negative_prompt`: {negative_prompt} and `negative_prompt_embeds`:"
f" {negative_prompt_embeds}. Please make sure to only forward one of the two."
)
# 检查prompt_embeds和negative_prompt_embeds是否同时提供且形状一致
if prompt_embeds is not None and negative_prompt_embeds is not None:
if prompt_embeds.shape != negative_prompt_embeds.shape:
# 如果形状不一致,抛出值错误,提示形状必须相同
raise ValueError(
"`prompt_embeds` and `negative_prompt_embeds` must have the same shape when passed directly, but"
f" got: `prompt_embeds` {prompt_embeds.shape} != `negative_prompt_embeds`"
f" {negative_prompt_embeds.shape}."
)
# 从 diffusers.pipelines.deepfloyd_if.pipeline_if.IFPipeline._text_preprocessing 复制的部分
# 文本预处理方法,接受文本和一个指示是否清理标题的标志
def _text_preprocessing(self, text, clean_caption=False):
# 如果需要清理标题且 BeautifulSoup4 不可用,记录警告并禁用清理标志
if clean_caption and not is_bs4_available():
logger.warning(BACKENDS_MAPPING["bs4"][-1].format("Setting `clean_caption=True`"))
logger.warning("Setting `clean_caption` to False...")
clean_caption = False
# 如果需要清理标题且 ftfy 不可用,记录警告并禁用清理标志
if clean_caption and not is_ftfy_available():
logger.warning(BACKENDS_MAPPING["ftfy"][-1].format("Setting `clean_caption=True`"))
logger.warning("Setting `clean_caption` to False...")
clean_caption = False
# 如果文本不是元组或列表,则将其转为列表
if not isinstance(text, (tuple, list)):
text = [text]
# 定义处理单个文本的内部函数
def process(text: str):
# 如果需要清理标题,则调用清理方法两次
if clean_caption:
text = self._clean_caption(text)
text = self._clean_caption(text)
else:
# 否则将文本转换为小写并去除首尾空格
text = text.lower().strip()
return text
# 对列表中的每个文本进行处理并返回结果
return [process(t) for t in text]
# 从 diffusers.pipelines.deepfloyd_if.pipeline_if.IFPipeline._clean_caption 复制
# 从 diffusers.pipelines.text_to_video_synthesis.pipeline_text_to_video_synth.TextToVideoSDPipeline.prepare_latents 复制
def prepare_latents(
# 定义方法参数,指定批量大小、通道数、帧数、高度、宽度、数据类型、设备、生成器和潜在变量
self, batch_size, num_channels_latents, num_frames, height, width, dtype, device, generator, latents=None
):
# 计算潜在变量的形状
shape = (
batch_size,
num_channels_latents,
num_frames,
height // self.vae_scale_factor,
width // self.vae_scale_factor,
)
# 检查生成器列表长度是否与批量大小匹配,若不匹配则抛出错误
if isinstance(generator, list) and len(generator) != batch_size:
raise ValueError(
f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"
f" size of {batch_size}. Make sure the batch size matches the length of the generators."
)
# 如果潜在变量为 None,则生成随机张量
if latents is None:
latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
else:
# 否则将已有潜在变量转换到指定设备
latents = latents.to(device)
# 按调度器所需的标准差缩放初始噪声
latents = latents * self.scheduler.init_noise_sigma
# 返回处理后的潜在变量
return latents
# 定义属性,返回指导比例的值
@property
def guidance_scale(self):
return self._guidance_scale
# 这里定义的 `guidance_scale` 类似于 Imagen 论文中公式(2)中的指导权重 `w`
# `guidance_scale = 1` 表示不进行无分类器引导
@property
def do_classifier_free_guidance(self):
return self._guidance_scale > 1
# 定义属性,返回时间步数的值
@property
def num_timesteps(self):
return self._num_timesteps
# 定义属性,返回中断标志的值
@property
def interrupt(self):
return self._interrupt
# 在不计算梯度的情况下执行以下方法
@torch.no_grad()
# 替换示例文档字符串
@replace_example_docstring(EXAMPLE_DOC_STRING)
# 定义可调用对象的 __call__ 方法,接受多个参数
def __call__(
# 输入提示,可以是字符串或字符串列表,默认为 None
self,
prompt: Union[str, List[str]] = None,
# 负向提示,默认为空字符串
negative_prompt: str = "",
# 推理步骤数量,默认为 50
num_inference_steps: int = 50,
# 时间步列表,默认为 None
timesteps: Optional[List[int]] = None,
# 引导尺度,默认为 7.5
guidance_scale: float = 7.5,
# 每个提示生成的图像数量,默认为 1
num_images_per_prompt: int = 1,
# 视频长度,默认为 16
video_length: int = 16,
# 图像高度,默认为 512
height: int = 512,
# 图像宽度,默认为 512
width: int = 512,
# ETA 值,默认为 0.0
eta: float = 0.0,
# 随机生成器,默认为 None
generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
# 潜在空间张量,默认为 None
latents: Optional[torch.FloatTensor] = None,
# 提示嵌入张量,默认为 None
prompt_embeds: Optional[torch.FloatTensor] = None,
# 负向提示嵌入张量,默认为 None
negative_prompt_embeds: Optional[torch.FloatTensor] = None,
# 输出类型,默认为 "pil"
output_type: str = "pil",
# 是否返回字典,默认为 True
return_dict: bool = True,
# 步骤结束时的回调函数,默认为 None
callback_on_step_end: Optional[
Union[Callable[[int, int, Dict], None], PipelineCallback, MultiPipelineCallbacks]
] = None,
# 步骤结束时的张量输入回调,默认为 ["latents"]
callback_on_step_end_tensor_inputs: List[str] = ["latents"],
# 是否清理提示,默认为 True
clean_caption: bool = True,
# 是否启用特征掩码,默认为 True
mask_feature: bool = True,
# 是否启用时间注意力机制,默认为 True
enable_temporal_attentions: bool = True,
# 解码块大小,默认为 None
decode_chunk_size: Optional[int] = None,
# 与 diffusers.pipelines.stable_video_diffusion.pipeline_stable_video_diffusion.decode_latents 相似
def decode_latents(self, latents: torch.Tensor, video_length: int, decode_chunk_size: int = 14):
# 将张量维度调整为 [batch, channels, frames, height, width] 格式
latents = latents.permute(0, 2, 1, 3, 4).flatten(0, 1)
# 使用缩放因子调整潜在空间张量
latents = 1 / self.vae.config.scaling_factor * latents
# 获取 VAE 的前向函数,判断其是否编译过
forward_vae_fn = self.vae._orig_mod.forward if is_compiled_module(self.vae) else self.vae.forward
# 检查前向函数是否接受帧数参数
accepts_num_frames = "num_frames" in set(inspect.signature(forward_vae_fn).parameters.keys())
# 每次解码 decode_chunk_size 帧以避免内存溢出
frames = []
for i in range(0, latents.shape[0], decode_chunk_size):
# 当前块中的帧数量
num_frames_in = latents[i : i + decode_chunk_size].shape[0]
decode_kwargs = {}
if accepts_num_frames:
# 如果需要,传递当前帧数量
decode_kwargs["num_frames"] = num_frames_in
# 解码当前块的潜在张量,获取样本帧
frame = self.vae.decode(latents[i : i + decode_chunk_size], **decode_kwargs).sample
frames.append(frame)
# 将所有帧沿第 0 维拼接
frames = torch.cat(frames, dim=0)
# 将帧维度调整回 [batch, channels, frames, height, width] 格式
frames = frames.reshape(-1, video_length, *frames.shape[1:]).permute(0, 2, 1, 3, 4)
# 将帧转换为 float32 类型,以保持兼容性
frames = frames.float()
# 返回处理后的帧
return frames