diffusers-源码解析-三十五-

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
posted @ 2024-10-22 12:33  绝不原创的飞龙  阅读(14)  评论(0编辑  收藏  举报