diffusers 源码解析(二十七)
.\diffusers\pipelines\deepfloyd_if\__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 = {
"timesteps": [ # timesteps 模块的导入项列表
"fast27_timesteps",
"smart100_timesteps",
"smart185_timesteps",
"smart27_timesteps",
"smart50_timesteps",
"super100_timesteps",
"super27_timesteps",
"super40_timesteps",
]
}
# 尝试块,用于检查所需库的可用性
try:
# 检查 Transformers 和 PyTorch 是否都可用,如果不可用则抛出异常
if not (is_transformers_available() and is_torch_available()):
raise OptionalDependencyNotAvailable() # 抛出可选依赖不可用的异常
# 捕获可选依赖不可用的异常
except OptionalDependencyNotAvailable:
# 从 utils 模块导入虚拟对象以处理依赖不可用的情况
from ...utils import dummy_torch_and_transformers_objects # noqa F403
# 更新虚拟对象字典,填充从 dummy 对象模块获取的对象
_dummy_objects.update(get_objects_from_module(dummy_torch_and_transformers_objects))
# 如果没有异常,继续执行以下代码
else:
# 向导入结构字典添加与管道相关的项
_import_structure["pipeline_if"] = ["IFPipeline"]
_import_structure["pipeline_if_img2img"] = ["IFImg2ImgPipeline"]
_import_structure["pipeline_if_img2img_superresolution"] = ["IFImg2ImgSuperResolutionPipeline"]
_import_structure["pipeline_if_inpainting"] = ["IFInpaintingPipeline"]
_import_structure["pipeline_if_inpainting_superresolution"] = ["IFInpaintingSuperResolutionPipeline"]
_import_structure["pipeline_if_superresolution"] = ["IFSuperResolutionPipeline"]
_import_structure["pipeline_output"] = ["IFPipelineOutput"]
_import_structure["safety_checker"] = ["IFSafetyChecker"]
_import_structure["watermark"] = ["IFWatermarker"]
# 检查类型检查或慢速导入标志
if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
# 尝试块,检查所需库的可用性
try:
# 检查 Transformers 和 PyTorch 是否都可用
if not (is_transformers_available() and is_torch_available()):
raise OptionalDependencyNotAvailable() # 抛出可选依赖不可用的异常
# 捕获可选依赖不可用的异常
except OptionalDependencyNotAvailable:
# 从虚拟对象模块导入所有内容
from ...utils.dummy_torch_and_transformers_objects import *
# 如果没有异常,继续执行以下代码
else:
# 从各个管道模块导入所需的类
from .pipeline_if import IFPipeline
from .pipeline_if_img2img import IFImg2ImgPipeline
from .pipeline_if_img2img_superresolution import IFImg2ImgSuperResolutionPipeline
from .pipeline_if_inpainting import IFInpaintingPipeline
from .pipeline_if_inpainting_superresolution import IFInpaintingSuperResolutionPipeline
from .pipeline_if_superresolution import IFSuperResolutionPipeline
from .pipeline_output import IFPipelineOutput
from .safety_checker import IFSafetyChecker
# 从 timesteps 模块导入所需的时间步长对象
from .timesteps import (
fast27_timesteps,
smart27_timesteps,
smart50_timesteps,
smart100_timesteps,
smart185_timesteps,
super27_timesteps,
super40_timesteps,
super100_timesteps,
)
# 从 watermark 模块导入水印对象
from .watermark import IFWatermarker
# 如果不进行类型检查且没有慢速导入标志
else:
# 导入 sys 模块
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\deprecated\alt_diffusion\modeling_roberta_series.py
# 从 dataclasses 模块导入 dataclass 装饰器
from dataclasses import dataclass
# 从 typing 模块导入可选类型和元组类型
from typing import Optional, Tuple
# 导入 PyTorch 库
import torch
# 从 torch 模块导入神经网络相关的功能
from torch import nn
# 从 transformers 模块导入 Roberta 预训练模型、配置和模型类
from transformers import RobertaPreTrainedModel, XLMRobertaConfig, XLMRobertaModel
# 从 transformers.utils 导入模型输出基类
from transformers.utils import ModelOutput
# 定义一个数据类,用于存储转换模型的输出
@dataclass
class TransformationModelOutput(ModelOutput):
"""
文本模型输出的基类,包含最后隐藏状态的池化。
参数:
text_embeds (`torch.Tensor`,形状为 `(batch_size, output_dim)` *可选*,当模型以 `with_projection=True` 初始化时返回):
通过对 pooler_output 应用投影层获得的文本嵌入。
last_hidden_state (`torch.Tensor`,形状为 `(batch_size, sequence_length, hidden_size)`):
模型最后一层输出的隐藏状态序列。
hidden_states (`tuple(torch.Tensor)`,*可选*,当 `output_hidden_states=True` 被传递或当 `config.output_hidden_states=True` 时返回):
隐藏状态的元组(每层输出一个,若模型有嵌入层则还包括嵌入输出),形状为 `(batch_size, sequence_length, hidden_size)`。
模型在每层输出的隐藏状态,加上可选的初始嵌入输出。
attentions (`tuple(torch.Tensor)`,*可选*,当 `output_attentions=True` 被传递或当 `config.output_attentions=True` 时返回):
每层的注意力权重元组,形状为 `(batch_size, num_heads, sequence_length, sequence_length)`。
在自注意力头中计算加权平均的注意力权重。
"""
# 投影状态,默认为 None,类型为可选的 torch.Tensor
projection_state: Optional[torch.Tensor] = None
# 最后隐藏状态,类型为 torch.Tensor,默认为 None
last_hidden_state: torch.Tensor = None
# 隐藏状态,类型为可选的元组,包含多个 torch.Tensor
hidden_states: Optional[Tuple[torch.Tensor]] = None
# 注意力权重,类型为可选的元组,包含多个 torch.Tensor
attentions: Optional[Tuple[torch.Tensor]] = None
# 定义一个配置类,继承自 XLMRobertaConfig
class RobertaSeriesConfig(XLMRobertaConfig):
# 初始化方法,设置不同的配置参数
def __init__(
self,
pad_token_id=1, # 填充 token 的 ID,默认为 1
bos_token_id=0, # 句子开头 token 的 ID,默认为 0
eos_token_id=2, # 句子结尾 token 的 ID,默认为 2
project_dim=512, # 投影维度,默认为 512
pooler_fn="cls", # 池化函数,默认为 "cls"
learn_encoder=False, # 是否学习编码器,默认为 False
use_attention_mask=True, # 是否使用注意力掩码,默认为 True
**kwargs, # 其他关键字参数
):
# 调用父类的初始化方法,设置填充、开头和结尾 token 的 ID
super().__init__(pad_token_id=pad_token_id, bos_token_id=bos_token_id, eos_token_id=eos_token_id, **kwargs)
# 设置投影维度
self.project_dim = project_dim
# 设置池化函数
self.pooler_fn = pooler_fn
# 设置是否学习编码器
self.learn_encoder = learn_encoder
# 设置是否使用注意力掩码
self.use_attention_mask = use_attention_mask
# 定义一个模型类,继承自 RobertaPreTrainedModel
class RobertaSeriesModelWithTransformation(RobertaPreTrainedModel):
# 加载时忽略的意外键
_keys_to_ignore_on_load_unexpected = [r"pooler", r"logit_scale"]
# 加载时忽略的缺失键
_keys_to_ignore_on_load_missing = [r"position_ids", r"predictions.decoder.bias"]
# 基础模型前缀
base_model_prefix = "roberta"
# 配置类
config_class = RobertaSeriesConfig
# 初始化方法,接收配置参数
def __init__(self, config):
# 调用父类的初始化方法,传入配置参数
super().__init__(config)
# 初始化 XLM-RoBERTa 模型,使用配置参数
self.roberta = XLMRobertaModel(config)
# 定义线性变换层,输入维度为隐藏层大小,输出维度为项目维度
self.transformation = nn.Linear(config.hidden_size, config.project_dim)
# 获取配置中的 has_pre_transformation 属性,默认值为 False
self.has_pre_transformation = getattr(config, "has_pre_transformation", False)
# 如果启用预变换层
if self.has_pre_transformation:
# 定义另一个线性变换层用于预变换
self.transformation_pre = nn.Linear(config.hidden_size, config.project_dim)
# 定义层归一化,应用于隐藏层输出
self.pre_LN = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
# 调用后续初始化方法
self.post_init()
# 前向传播方法,接收多种输入参数
def forward(
self,
input_ids: Optional[torch.Tensor] = None, # 输入 ID
attention_mask: Optional[torch.Tensor] = None, # 注意力掩码
token_type_ids: Optional[torch.Tensor] = None, # 令牌类型 ID
position_ids: Optional[torch.Tensor] = None, # 位置 ID
head_mask: Optional[torch.Tensor] = None, # 头掩码
inputs_embeds: Optional[torch.Tensor] = None, # 输入嵌入
encoder_hidden_states: Optional[torch.Tensor] = None, # 编码器的隐藏状态
encoder_attention_mask: Optional[torch.Tensor] = None, # 编码器的注意力掩码
output_attentions: Optional[bool] = None, # 是否输出注意力
return_dict: Optional[bool] = None, # 是否返回字典格式
output_hidden_states: Optional[bool] = None, # 是否输出隐藏状态
):
r""" """
# 如果 return_dict 为 None,则使用配置中的默认值
return_dict = return_dict if return_dict is not None else self.config.use_return_dict
# 调用基础模型进行前向传播,传入各种输入参数
outputs = self.base_model(
input_ids=input_ids,
attention_mask=attention_mask,
token_type_ids=token_type_ids,
position_ids=position_ids,
head_mask=head_mask,
inputs_embeds=inputs_embeds,
encoder_hidden_states=encoder_hidden_states,
encoder_attention_mask=encoder_attention_mask,
output_attentions=output_attentions,
# 根据是否有预变换层决定是否输出隐藏状态
output_hidden_states=True if self.has_pre_transformation else output_hidden_states,
return_dict=return_dict,
)
# 如果启用了预变换层
if self.has_pre_transformation:
# 获取倒数第二个隐藏状态作为序列输出
sequence_output2 = outputs["hidden_states"][-2]
# 对序列输出进行层归一化
sequence_output2 = self.pre_LN(sequence_output2)
# 应用预变换层得到投影状态
projection_state2 = self.transformation_pre(sequence_output2)
# 返回变换模型输出,包含投影状态和其他输出
return TransformationModelOutput(
projection_state=projection_state2,
last_hidden_state=outputs.last_hidden_state,
hidden_states=outputs.hidden_states,
attentions=outputs.attentions,
)
else:
# 如果没有预变换层,直接对最后的隐藏状态应用变换层
projection_state = self.transformation(outputs.last_hidden_state)
# 返回变换模型输出,包含投影状态和其他输出
return TransformationModelOutput(
projection_state=projection_state,
last_hidden_state=outputs.last_hidden_state,
hidden_states=outputs.hidden_states,
attentions=outputs.attentions,
)
.\diffusers\pipelines\deprecated\alt_diffusion\pipeline_alt_diffusion.py
# 版权声明,表示该文件的所有权归 HuggingFace 团队所有
# 该文件在 Apache 许可证 2.0 下授权使用
# 许可证的详细信息可以在以下链接获取
# http://www.apache.org/licenses/LICENSE-2.0
# 根据许可证,软件按“原样”提供,不提供任何形式的保证
# 详细的许可证条款可以在下面查看
import inspect # 导入 inspect 模块,用于检查对象的类型和属性
from typing import Any, Callable, Dict, List, Optional, Union # 导入类型注解以增强可读性和静态检查
import torch # 导入 PyTorch 库,进行深度学习相关操作
from packaging import version # 导入 version 模块,处理版本信息
from transformers import CLIPImageProcessor, CLIPVisionModelWithProjection, XLMRobertaTokenizer # 导入变换器模型和处理器
# 从当前模块的父级导入配置和处理器工具
from ....configuration_utils import FrozenDict
from ....image_processor import PipelineImageInput, VaeImageProcessor
# 从加载器模块导入多种混合类
from ....loaders import (
FromSingleFileMixin, # 单文件加载混合
IPAdapterMixin, # IP 适配器混合
StableDiffusionLoraLoaderMixin, # 稳定扩散 Lora 加载混合
TextualInversionLoaderMixin, # 文本反转加载混合
)
# 从模型模块导入不同的模型类
from ....models import AutoencoderKL, ImageProjection, UNet2DConditionModel
from ....models.lora import adjust_lora_scale_text_encoder # 导入调整 Lora 比例的函数
from ....schedulers import KarrasDiffusionSchedulers # 导入 Karras 扩散调度器
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.safety_checker import StableDiffusionSafetyChecker # 导入安全检查器
from .modeling_roberta_series import RobertaSeriesModelWithTransformation # 导入 Roberta 系列模型
from .pipeline_output import AltDiffusionPipelineOutput # 导入备用扩散管道输出
logger = logging.get_logger(__name__) # 初始化日志记录器,用于记录模块相关信息
EXAMPLE_DOC_STRING = """ # 定义示例文档字符串,用于展示如何使用管道
Examples: # 示例标题
```py # 示例代码开始
>>> import torch # 导入 PyTorch
>>> from diffusers import AltDiffusionPipeline # 导入备用扩散管道
>>> pipe = AltDiffusionPipeline.from_pretrained("BAAI/AltDiffusion-m9", torch_dtype=torch.float16) # 从预训练模型加载管道
>>> pipe = pipe.to("cuda") # 将管道移至 CUDA 设备
>>> # "dark elf princess, highly detailed, d & d, fantasy, highly detailed, digital painting, trending on artstation, concept art, sharp focus, illustration, art by artgerm and greg rutkowski and fuji choko and viktoria gavrilenko and hoang lap"
>>> prompt = "黑暗精灵公主,非常详细,幻想,非常详细,数字绘画,概念艺术,敏锐的焦点,插图" # 定义提示内容
>>> image = pipe(prompt).images[0] # 使用提示生成图像并获取第一张
```py # 示例代码结束
"""
# 从扩散管道中复制的函数,调整噪声配置
def rescale_noise_cfg(noise_cfg, noise_pred_text, guidance_rescale=0.0): # 定义重标定噪声配置的函数
"""
根据指导重标定噪声配置。基于研究[Common Diffusion Noise Schedules and
Sample Steps are Flawed](https://arxiv.org/pdf/2305.08891.pdf)。见第 3.4 节
"""
std_text = noise_pred_text.std(dim=list(range(1, noise_pred_text.ndim)), keepdim=True) # 计算噪声预测文本的标准差
# 计算噪声配置的标准差,沿指定维度进行
std_cfg = noise_cfg.std(dim=list(range(1, noise_cfg.ndim)), keepdim=True)
# 对指导结果进行重新缩放,以修正过度曝光的问题
noise_pred_rescaled = noise_cfg * (std_text / std_cfg)
# 将重新缩放的噪声与原始指导结果混合,通过引导缩放因子避免“平淡”图像
noise_cfg = guidance_rescale * noise_pred_rescaled + (1 - guidance_rescale) * noise_cfg
# 返回调整后的噪声配置
return noise_cfg
# 从 diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion 复制的代码
def retrieve_timesteps(
# 调度器对象,用于获取时间步
scheduler,
# 生成样本时使用的扩散步骤数量,可选参数
num_inference_steps: Optional[int] = None,
# 时间步应移动到的设备,字符串或 torch.device 类型
device: Optional[Union[str, torch.device]] = None,
# 自定义时间步的列表,可选参数
timesteps: Optional[List[int]] = None,
# 自定义 sigma 的列表,可选参数
sigmas: Optional[List[float]] = None,
# 其他关键字参数
**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]`: 一个元组,其中第一个元素是调度器的时间步调度,第二个元素是推理步骤的数量。
"""
# 检查是否同时传入了时间步和 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."
)
# 调用调度器的 set_timesteps 方法,传入自定义时间步和设备
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())
# 如果不支持自定义 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)
# 否则,设置推理步骤和设备,传入额外的参数
else:
scheduler.set_timesteps(num_inference_steps, device=device, **kwargs)
# 获取调度器的时间步列表
timesteps = scheduler.timesteps
# 返回时间步和推理步骤的数量
return timesteps, num_inference_steps
# 定义一个名为 AltDiffusionPipeline 的类,继承多个父类以实现文本到图像生成的功能
class AltDiffusionPipeline(
# 继承自 DiffusionPipeline,提供基础的扩散管道功能
DiffusionPipeline,
# 继承自 StableDiffusionMixin,提供稳定扩散相关的功能
StableDiffusionMixin,
# 继承自 TextualInversionLoaderMixin,提供文本反转加载功能
TextualInversionLoaderMixin,
# 继承自 StableDiffusionLoraLoaderMixin,提供 LoRA 权重的加载和保存功能
StableDiffusionLoraLoaderMixin,
# 继承自 IPAdapterMixin,提供 IP 适配器的加载功能
IPAdapterMixin,
# 继承自 FromSingleFileMixin,提供从单个文件加载的功能
FromSingleFileMixin,
):
# 文档字符串,描述该类的用途和功能
r"""
Pipeline for text-to-image generation using Alt Diffusion.
This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods
implemented for all pipelines (downloading, saving, running on a particular device, etc.).
The pipeline also inherits the following loading methods:
- [`~loaders.TextualInversionLoaderMixin.load_textual_inversion`] for loading textual inversion embeddings
- [`~loaders.StableDiffusionLoraLoaderMixin.load_lora_weights`] for loading LoRA weights
- [`~loaders.StableDiffusionLoraLoaderMixin.save_lora_weights`] for saving LoRA weights
- [`~loaders.FromSingleFileMixin.from_single_file`] for loading `.ckpt` files
- [`~loaders.IPAdapterMixin.load_ip_adapter`] for loading IP Adapters
Args:
vae ([`AutoencoderKL`]):
Variational Auto-Encoder (VAE) model to encode and decode images to and from latent representations.
text_encoder ([`~transformers.RobertaSeriesModelWithTransformation`]):
Frozen text-encoder ([clip-vit-large-patch14](https://huggingface.co/openai/clip-vit-large-patch14)).
tokenizer ([`~transformers.XLMRobertaTokenizer`]):
A `XLMRobertaTokenizer` to tokenize text.
unet ([`UNet2DConditionModel`]):
A `UNet2DConditionModel` to denoise the encoded image latents.
scheduler ([`SchedulerMixin`]):
A scheduler to be used in combination with `unet` to denoise the encoded image latents. Can be one of
[`DDIMScheduler`], [`LMSDiscreteScheduler`], or [`PNDMScheduler`].
safety_checker ([`StableDiffusionSafetyChecker`]):
Classification module that estimates whether generated images could be considered offensive or harmful.
Please refer to the [model card](https://huggingface.co/runwayml/stable-diffusion-v1-5) for more details
about a model's potential harms.
feature_extractor ([`~transformers.CLIPImageProcessor`]):
A `CLIPImageProcessor` to extract features from generated images; used as inputs to the `safety_checker`.
"""
# 定义模型在 CPU 上卸载的顺序,确保特定组件在内存中的顺序
model_cpu_offload_seq = "text_encoder->image_encoder->unet->vae"
# 定义可选组件的列表,这些组件可能在使用时被添加
_optional_components = ["safety_checker", "feature_extractor", "image_encoder"]
# 定义在 CPU 卸载时排除的组件,这些组件不会被卸载
_exclude_from_cpu_offload = ["safety_checker"]
# 定义需要回调的张量输入列表,这些输入将在特定操作中被处理
_callback_tensor_inputs = ["latents", "prompt_embeds", "negative_prompt_embeds"]
# 初始化类的构造函数,接收多个参数以配置模型组件
def __init__(
self,
vae: AutoencoderKL, # 变分自编码器实例
text_encoder: RobertaSeriesModelWithTransformation, # 文本编码器实例
tokenizer: XLMRobertaTokenizer, # 令牌化工具
unet: UNet2DConditionModel, # UNet模型实例
scheduler: KarrasDiffusionSchedulers, # 调度器实例
safety_checker: StableDiffusionSafetyChecker, # 安全检查器实例
feature_extractor: CLIPImageProcessor, # 特征提取器实例
image_encoder: CLIPVisionModelWithProjection = None, # 可选的图像编码器实例
requires_safety_checker: bool = True, # 是否需要安全检查器的布尔值
def _encode_prompt(
self,
prompt, # 输入的提示文本
device, # 设备类型(如CPU或GPU)
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, # 可选的LoRA缩放因子
**kwargs, # 额外的关键字参数
):
# 生成弃用警告信息,提示用户使用新方法
deprecation_message = "`_encode_prompt()` is deprecated and it will be removed in a future version. Use `encode_prompt()` instead. Also, be aware that the output format changed from a concatenated tensor to a tuple."
# 调用弃用处理函数,记录弃用警告
deprecate("_encode_prompt()", "1.0.0", deprecation_message, standard_warn=False)
# 调用encode_prompt方法处理提示文本,并返回嵌入的元组
prompt_embeds_tuple = self.encode_prompt(
prompt=prompt, # 输入的提示文本
device=device, # 设备类型
num_images_per_prompt=num_images_per_prompt, # 图像数量
do_classifier_free_guidance=do_classifier_free_guidance, # 引导选项
negative_prompt=negative_prompt, # 负面提示文本
prompt_embeds=prompt_embeds, # 提示嵌入
negative_prompt_embeds=negative_prompt_embeds, # 负面嵌入
lora_scale=lora_scale, # LoRA缩放
**kwargs, # 额外参数
)
# 将返回的嵌入元组拼接为一个张量,兼容旧版本
prompt_embeds = torch.cat([prompt_embeds_tuple[1], prompt_embeds_tuple[0]])
# 返回最终的提示嵌入张量
return prompt_embeds
# 定义新的提示编码方法,接收多个参数以处理输入提示
def encode_prompt(
self,
prompt, # 输入的提示文本
device, # 设备类型(如CPU或GPU)
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, # 可选的LoRA缩放因子
clip_skip: Optional[int] = None, # 可选的剪裁跳过参数
# 定义一个方法,用于编码图像,参数包括图像、设备、每个提示的图像数量和可选的隐藏状态输出
def encode_image(self, image, device, num_images_per_prompt, output_hidden_states=None):
# 获取图像编码器参数的 dtype
dtype = next(self.image_encoder.parameters()).dtype
# 检查传入的图像是否为张量类型,如果不是,则使用特征提取器处理图像
if not isinstance(image, torch.Tensor):
# 将图像转换为 PyTorch 张量,并提取像素值
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 run_safety_checker(self, image, device, dtype):
# 检查安全检查器是否为 None
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:
# 如果不是张量,则将其转换为 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
# 定义一个方法,用于解码潜在变量
def decode_latents(self, latents):
# 定义弃用警告消息
deprecation_message = "The decode_latents method is deprecated and will be removed in 1.0.0. Please use VaeImageProcessor.postprocess(...) instead"
# 发出弃用警告
deprecate("decode_latents", "1.0.0", deprecation_message, standard_warn=False)
# 对潜在变量进行缩放
latents = 1 / self.vae.config.scaling_factor * latents
# 使用 VAE 解码潜在变量,返回解码后的图像
image = self.vae.decode(latents, return_dict=False)[0]
# 对图像进行归一化和裁剪,以确保值在 [0, 1] 范围内
image = (image / 2 + 0.5).clamp(0, 1)
# 将图像移动到 CPU,并调整维度顺序,转换为 numpy 数组,保持为 float32 类型
image = image.cpu().permute(0, 2, 3, 1).float().numpy()
# 返回解码后的图像
return image
# 定义一个方法,用于准备额外的调度器步骤参数
def prepare_extra_step_kwargs(self, generator, eta):
# 为调度器步骤准备额外的参数,因为不同调度器的参数签名可能不同
# eta (η) 仅在 DDIMScheduler 中使用,其他调度器将忽略它
# eta 对应于 DDIM 论文中的 η: https://arxiv.org/abs/2010.02502
# 应在 [0, 1] 之间
# 检查调度器的 step 方法是否接受 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
# 检查调度器的 step 方法是否接受 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
# 定义一个方法,用于检查输入参数的有效性
def check_inputs(
self,
prompt, # 用户输入的提示文本
height, # 输出图像的高度
width, # 输出图像的宽度
callback_steps, # 用于回调的步骤数量
negative_prompt=None, # 可选的负面提示文本
prompt_embeds=None, # 可选的提示嵌入向量
negative_prompt_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 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."
)
# 检查提示嵌入和负提示嵌入形状是否匹配
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}."
)
# 准备潜在变量,根据输入参数设置形状
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."
)
# 如果潜在变量未提供,生成随机潜在变量
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, embedding_dim=512, dtype=torch.float32):
"""
查看 https://github.com/google-research/vdm/blob/dc27b98a554f65cdc654b800da5aa1846545d41b/model_vdm.py#L298
Args:
timesteps (`torch.Tensor`):
在这些时间步生成嵌入向量
embedding_dim (`int`, *optional*, defaults to 512):
生成嵌入的维度
dtype:
生成嵌入的数据类型
Returns:
`torch.Tensor`: 形状为 `(len(timesteps), embedding_dim)` 的嵌入向量
"""
# 确保输入的 w 是一维的
assert len(w.shape) == 1
# 将 w 的值扩大 1000 倍
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 转换为目标数据类型并进行广播
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
# 引导比例的属性,返回存储的引导比例值
@property
def guidance_scale(self):
return self._guidance_scale
# 引导重缩放的属性,返回存储的重缩放值
@property
def guidance_rescale(self):
return self._guidance_rescale
# clip_skip 属性,返回存储的跳过值
@property
def clip_skip(self):
return self._clip_skip
# 判断是否执行无分类器引导的属性,基于引导比例和条件配置
@property
def do_classifier_free_guidance(self):
return self._guidance_scale > 1 and self.unet.config.time_cond_proj_dim is None
# 跨注意力参数的属性,返回存储的参数
@property
def cross_attention_kwargs(self):
return self._cross_attention_kwargs
# 时间步数的属性,返回存储的时间步数
@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,
# 生成图像的高度
height: Optional[int] = None,
# 生成图像的宽度
width: Optional[int] = None,
# 推理步骤的数量,默认为50
num_inference_steps: int = 50,
# 可选的时间步列表
timesteps: List[int] = None,
# 可选的标准差列表
sigmas: List[float] = None,
# 指导缩放系数,默认为7.5
guidance_scale: float = 7.5,
# 可选的负面提示,可以是字符串或字符串列表
negative_prompt: Optional[Union[str, List[str]]] = None,
# 每个提示生成的图像数量,默认为1
num_images_per_prompt: Optional[int] = 1,
# 额外的超参数,默认为0.0
eta: float = 0.0,
# 可选的随机数生成器或生成器列表
generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
# 可选的潜在变量张量
latents: Optional[torch.Tensor] = None,
# 可选的提示嵌入张量
prompt_embeds: Optional[torch.Tensor] = None,
# 可选的负面提示嵌入张量
negative_prompt_embeds: Optional[torch.Tensor] = None,
# 可选的适配器输入图像
ip_adapter_image: Optional[PipelineImageInput] = None,
# 输出类型,默认为“pil”格式
output_type: Optional[str] = "pil",
# 是否返回字典格式的结果,默认为True
return_dict: bool = True,
# 可选的跨注意力参数字典
cross_attention_kwargs: Optional[Dict[str, Any]] = None,
# 指导再缩放的系数,默认为0.0
guidance_rescale: float = 0.0,
# 可选的跳过的剪辑步数
clip_skip: Optional[int] = 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\deprecated\alt_diffusion\pipeline_alt_diffusion_img2img.py
# 版权所有 2024 HuggingFace 团队。保留所有权利。
#
# 根据 Apache 许可证第 2.0 版(“许可证”)进行授权;
# 除非遵守许可证,否则您不得使用此文件。
# 您可以在以下网址获得许可证的副本:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# 除非适用法律要求或书面协议另有约定,依据许可证分发的软件
# 在“按原样”基础上提供,没有任何形式的保证或条件,
# 无论是明示或暗示的。有关许可证的特定权限和
# 限制,请参见许可证。
import inspect # 导入inspect模块,用于获取对象的实时信息
from typing import Any, Callable, Dict, List, Optional, Union # 导入类型注解,用于类型提示
import numpy as np # 导入numpy库,通常用于数值计算
import PIL.Image # 导入PIL库的Image模块,用于图像处理
import torch # 导入PyTorch库,用于深度学习
from packaging import version # 导入version模块,用于处理版本字符串
from transformers import CLIPImageProcessor, CLIPVisionModelWithProjection, XLMRobertaTokenizer # 导入transformers库中的图像处理和模型类
from ....configuration_utils import FrozenDict # 从配置工具导入FrozenDict,用于不可变字典
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模块导入调整Lora规模的函数
from ....schedulers import KarrasDiffusionSchedulers # 从调度器模块导入Karras扩散调度器
from ....utils import ( # 从工具模块导入多个工具函数和常量
PIL_INTERPOLATION,
USE_PEFT_BACKEND,
deprecate,
logging,
replace_example_docstring,
scale_lora_layers,
unscale_lora_layers,
)
from ....utils.torch_utils import randn_tensor # 从torch工具模块导入生成随机张量的函数
from ...pipeline_utils import DiffusionPipeline, StableDiffusionMixin # 从管道工具模块导入扩散管道和稳定扩散混合类
from ...stable_diffusion.safety_checker import StableDiffusionSafetyChecker # 从稳定扩散模块导入安全检查器
from .modeling_roberta_series import RobertaSeriesModelWithTransformation # 从Roberta系列建模模块导入模型
from .pipeline_output import AltDiffusionPipelineOutput # 从管道输出模块导入输出类
logger = logging.get_logger(__name__) # 创建当前模块的日志记录器,禁用pylint对名称的警告
EXAMPLE_DOC_STRING = """ # 定义一个示例文档字符串,展示用法示例
Examples:
```py
>>> import requests # 导入requests库,用于发送HTTP请求
>>> import torch # 导入PyTorch库,用于深度学习
>>> from PIL import Image # 从PIL库导入Image模块,用于图像处理
>>> from io import BytesIO # 从io模块导入BytesIO,用于字节流操作
>>> from diffusers import AltDiffusionImg2ImgPipeline # 从diffusers库导入图像到图像的扩散管道类
>>> device = "cuda" # 设置设备为CUDA(GPU)
>>> model_id_or_path = "BAAI/AltDiffusion-m9" # 指定模型的ID或路径
>>> pipe = AltDiffusionImg2ImgPipeline.from_pretrained(model_id_or_path, torch_dtype=torch.float16) # 从预训练模型加载管道,并设置数据类型为float16
>>> pipe = pipe.to(device) # 将管道移动到指定设备
>>> url = "https://raw.githubusercontent.com/CompVis/stable-diffusion/main/assets/stable-samples/img2img/sketch-mountains-input.jpg" # 指定输入图像的URL
>>> response = requests.get(url) # 发送HTTP GET请求以获取输入图像
>>> init_image = Image.open(BytesIO(response.content)).convert("RGB") # 将响应内容转为图像并转换为RGB模式
>>> init_image = init_image.resize((768, 512)) # 调整图像大小为768x512
>>> # "A fantasy landscape, trending on artstation" # 提示字符串,用于生成图像
>>> prompt = "幻想风景, artstation" # 设置生成图像的描述性提示
>>> images = pipe(prompt=prompt, image=init_image, strength=0.75, guidance_scale=7.5).images # 调用管道生成图像
>>> images[0].save("幻想风景.png") # 保存生成的图像
```py # 结束示例文档字符串
"""
# 从 diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_img2img 复制
def retrieve_latents(
# 定义函数接收编码器输出(张量),可选的生成器,和采样模式字符串
encoder_output: torch.Tensor, generator: Optional[torch.Generator] = None, sample_mode: str = "sample"
):
# 检查编码器输出是否具有 'latent_dist' 属性,并且采样模式为 'sample'
if hasattr(encoder_output, "latent_dist") and sample_mode == "sample":
# 从 'latent_dist' 中进行采样并返回结果
return encoder_output.latent_dist.sample(generator)
# 检查编码器输出是否具有 'latent_dist' 属性,并且采样模式为 'argmax'
elif hasattr(encoder_output, "latent_dist") and sample_mode == "argmax":
# 返回 'latent_dist' 的众数
return encoder_output.latent_dist.mode()
# 检查编码器输出是否具有 'latents' 属性
elif hasattr(encoder_output, "latents"):
# 直接返回 'latents'
return encoder_output.latents
else:
# 如果没有有效的属性,抛出属性错误
raise AttributeError("Could not access latents of provided encoder_output")
# 从 diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_img2img 复制
def preprocess(image):
# 定义弃用消息,指明该方法将在 diffusers 1.0.0 中被移除
deprecation_message = "The preprocess method is deprecated and will be removed in diffusers 1.0.0. Please use VaeImageProcessor.preprocess(...) instead"
# 发出弃用警告
deprecate("preprocess", "1.0.0", deprecation_message, standard_warn=False)
# 检查图像是否为张量类型
if isinstance(image, torch.Tensor):
# 如果是张量,直接返回
return image
# 检查图像是否为 PIL 图像
elif isinstance(image, PIL.Image.Image):
# 如果是单个 PIL 图像,转为列表
image = [image]
# 检查列表中的第一个元素是否为 PIL 图像
if isinstance(image[0], PIL.Image.Image):
# 获取图像的宽和高
w, h = image[0].size
# 将宽和高调整为8的整数倍
w, h = (x - x % 8 for x in (w, h)) # resize to integer multiple of 8
# 调整每个图像的大小并转换为 NumPy 数组
image = [np.array(i.resize((w, h), resample=PIL_INTERPOLATION["lanczos"]))[None, :] for i in image]
# 将所有图像沿第0维连接
image = np.concatenate(image, axis=0)
# 归一化图像数组
image = np.array(image).astype(np.float32) / 255.0
# 调整数组维度顺序
image = image.transpose(0, 3, 1, 2)
# 将像素值缩放到 [-1, 1]
image = 2.0 * image - 1.0
# 将 NumPy 数组转换为张量
image = torch.from_numpy(image)
# 检查列表中的第一个元素是否为张量
elif isinstance(image[0], torch.Tensor):
# 沿着第0维连接所有张量
image = torch.cat(image, dim=0)
# 返回处理后的图像
return image
# 从 diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion 复制
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,
**kwargs,
):
"""
调用调度器的 `set_timesteps` 方法,并在调用后从调度器检索时间步。处理自定义时间步。任何 kwargs 将被传递给 `scheduler.set_timesteps`。
# 参数说明部分
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`.
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.
"""
# 检查是否同时传入时间步和 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:
# 检查调度器是否支持自定义时间步
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( # 抛出错误提示当前调度器不支持自定义 sigma
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
# 定义一个名为 AltDiffusionImg2ImgPipeline 的类,继承自多个父类
class AltDiffusionImg2ImgPipeline(
# 继承自 DiffusionPipeline
DiffusionPipeline,
# 继承自 StableDiffusionMixin
StableDiffusionMixin,
# 继承自 TextualInversionLoaderMixin
TextualInversionLoaderMixin,
# 继承自 IPAdapterMixin
IPAdapterMixin,
# 继承自 StableDiffusionLoraLoaderMixin
StableDiffusionLoraLoaderMixin,
# 继承自 FromSingleFileMixin
FromSingleFileMixin,
):
# 文档字符串,描述该类的作用和参数
r"""
Pipeline for text-guided image-to-image generation using Alt Diffusion.
# 描述该模型的功能:基于文本指导的图像到图像生成
This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods
implemented for all pipelines (downloading, saving, running on a particular device, etc.).
# 说明该管道继承了一些加载方法
The pipeline also inherits the following loading methods:
- [`~loaders.TextualInversionLoaderMixin.load_textual_inversion`] for loading textual inversion embeddings
- [`~loaders.StableDiffusionLoraLoaderMixin.load_lora_weights`] for loading LoRA weights
- [`~loaders.StableDiffusionLoraLoaderMixin.save_lora_weights`] for saving LoRA weights
- [`~loaders.FromSingleFileMixin.from_single_file`] for loading `.ckpt` files
- [`~loaders.IPAdapterMixin.load_ip_adapter`] for loading IP Adapters
# 定义构造函数的参数
Args:
vae ([`AutoencoderKL`]):
# Variational Auto-Encoder (VAE) 模型,用于图像的编码和解码
Variational Auto-Encoder (VAE) model to encode and decode images to and from latent representations.
text_encoder ([`~transformers.RobertaSeriesModelWithTransformation`]):
# 冻结的文本编码器,用于处理输入文本
Frozen text-encoder ([clip-vit-large-patch14](https://huggingface.co/openai/clip-vit-large-patch14)).
tokenizer ([`~transformers.XLMRobertaTokenizer`]):
# 用于对文本进行分词的工具
A `XLMRobertaTokenizer` to tokenize text.
unet ([`UNet2DConditionModel`]):
# 用于去噪图像潜在表示的 UNet 模型
A `UNet2DConditionModel` to denoise the encoded image latents.
scheduler ([`SchedulerMixin`]):
# 与 UNet 结合使用的调度器,用于去噪
A scheduler to be used in combination with `unet` to denoise the encoded image latents. Can be one of
[`DDIMScheduler`], [`LMSDiscreteScheduler`], or [`PNDMScheduler`].
safety_checker ([`StableDiffusionSafetyChecker`]):
# 评估生成图像是否可能被视为冒犯或有害的分类模块
Classification module that estimates whether generated images could be considered offensive or harmful.
Please refer to the [model card](https://huggingface.co/runwayml/stable-diffusion-v1-5) for more details
about a model's potential harms.
feature_extractor ([`~transformers.CLIPImageProcessor`]):
# 用于从生成图像中提取特征的处理器
A `CLIPImageProcessor` to extract features from generated images; used as inputs to the `safety_checker`.
"""
# 定义模型在 CPU 上的卸载顺序
model_cpu_offload_seq = "text_encoder->image_encoder->unet->vae"
# 可选组件列表
_optional_components = ["safety_checker", "feature_extractor", "image_encoder"]
# 从 CPU 卸载中排除的组件
_exclude_from_cpu_offload = ["safety_checker"]
# 用于回调的张量输入列表
_callback_tensor_inputs = ["latents", "prompt_embeds", "negative_prompt_embeds"]
# 初始化方法,用于创建类的实例
def __init__(
# VAE(变分自编码器)实例,用于数据编码
self,
vae: AutoencoderKL,
# 文本编码器,用于处理文本输入
text_encoder: RobertaSeriesModelWithTransformation,
# 分词器,用于将文本转换为标记
tokenizer: XLMRobertaTokenizer,
# UNet 模型,用于图像生成
unet: UNet2DConditionModel,
# 调度器,用于控制生成过程中的时间步
scheduler: KarrasDiffusionSchedulers,
# 安全检查器,用于过滤不安全的内容
safety_checker: StableDiffusionSafetyChecker,
# 特征提取器,用于图像处理
feature_extractor: CLIPImageProcessor,
# 可选的图像编码器,用于处理图像输入
image_encoder: CLIPVisionModelWithProjection = None,
# 指示是否需要安全检查器的布尔值,默认为 True
requires_safety_checker: bool = True,
# 编码提示的方法
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,
# 其他可选参数
**kwargs,
):
# 警告信息,说明该方法已弃用并将在未来版本中删除,建议使用新的方法
deprecation_message = "`_encode_prompt()` is deprecated and it will be removed in a future version. Use `encode_prompt()` instead. Also, be aware that the output format changed from a concatenated tensor to a tuple."
# 调用弃用函数的警告方法,记录弃用信息
deprecate("_encode_prompt()", "1.0.0", deprecation_message, standard_warn=False)
# 调用新的编码方法,获取提示嵌入元组
prompt_embeds_tuple = self.encode_prompt(
# 提示文本
prompt=prompt,
# 计算设备
device=device,
# 每个提示生成的图像数量
num_images_per_prompt=num_images_per_prompt,
# 是否执行分类器自由引导
do_classifier_free_guidance=do_classifier_free_guidance,
# 可选的负面提示文本
negative_prompt=negative_prompt,
# 可选的提示嵌入
prompt_embeds=prompt_embeds,
# 可选的负面提示嵌入
negative_prompt_embeds=negative_prompt_embeds,
# 可选的 LoRA 比例
lora_scale=lora_scale,
# 传递其他参数
**kwargs,
)
# 将嵌入元组的两个部分连接在一起,便于后续处理
prompt_embeds = torch.cat([prompt_embeds_tuple[1], prompt_embeds_tuple[0]])
# 返回连接后的提示嵌入
return prompt_embeds
# 编码提示的方法,用于处理输入的提示文本
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 比例
lora_scale: Optional[float] = None,
# 可选的跳过剪辑层的参数
clip_skip: Optional[int] = None,
# 定义编码图像的方法,接受图像、设备、每个提示的图像数量和隐藏状态输出参数
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 run_safety_checker(self, image, device, dtype):
# 如果安全检查器不存在,设置无NSFW概念标志为None
if self.safety_checker is None:
has_nsfw_concept = None
else:
# 检查图像是否为张量,若是则进行后处理
if torch.is_tensor(image):
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 decode_latents(self, latents):
# 定义弃用提示信息
deprecation_message = "The decode_latents method is deprecated and will be removed in 1.0.0. Please use VaeImageProcessor.postprocess(...) instead"
# 发出弃用警告
deprecate("decode_latents", "1.0.0", deprecation_message, standard_warn=False)
# 对潜在变量进行缩放
latents = 1 / self.vae.config.scaling_factor * latents
# 解码潜在变量以生成图像
image = self.vae.decode(latents, return_dict=False)[0]
# 将图像进行归一化处理并限制范围
image = (image / 2 + 0.5).clamp(0, 1)
# 将图像转换为float32格式,方便与bfloat16兼容且不造成显著开销
image = image.cpu().permute(0, 2, 3, 1).float().numpy()
# 返回处理后的图像
return image
# 准备调度器步骤所需的额外参数,因不同调度器的参数签名可能不同
def prepare_extra_step_kwargs(self, generator, eta):
# eta (η) 仅用于 DDIMScheduler,其他调度器将忽略它
# eta 对应于 DDIM 论文中的 η: https://arxiv.org/abs/2010.02502
# eta 的值应在 [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
# 检查输入参数的有效性
def check_inputs(
self,
prompt,
strength,
callback_steps,
negative_prompt=None,
prompt_embeds=None,
negative_prompt_embeds=None,
callback_on_step_end_tensor_inputs=None,
):
# 检查 strength 参数是否在有效范围内 (0 到 1)
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 中的每个键是否在 self._callback_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 的类型是否为 str 或 list
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)}")
# 检查是否同时提供了 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 是否都不为 None
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}."
)
def get_timesteps(self, num_inference_steps, strength, device):
# 根据初始化时间步计算原始时间步
init_timestep = min(int(num_inference_steps * strength), num_inference_steps)
# 计算时间步的起始位置
t_start = max(num_inference_steps - init_timestep, 0)
# 从调度器中获取相应的时间步
timesteps = self.scheduler.timesteps[t_start * self.scheduler.order :]
# 返回计算的时间步和剩余推理步骤
return timesteps, num_inference_steps - t_start
# 准备潜在向量(latents)以便于后续处理
def prepare_latents(self, image, timestep, batch_size, num_images_per_prompt, dtype, device, generator=None):
# 检查输入的 image 是否为指定类型之一:torch.Tensor、PIL.Image.Image 或列表
if not isinstance(image, (torch.Tensor, PIL.Image.Image, list)):
raise ValueError(
# 抛出异常,告知 image 的类型不符合要求
f"`image` has to be of type `torch.Tensor`, `PIL.Image.Image` or list but is {type(image)}"
)
# 将 image 转移到指定的设备并转换为指定的数据类型
image = image.to(device=device, dtype=dtype)
# 计算有效的 batch_size,即每个提示的图像数量
batch_size = batch_size * num_images_per_prompt
# 如果 image 的通道数为 4,直接将其赋值为初始化潜在向量
if image.shape[1] == 4:
init_latents = image
else:
# 如果 generator 是列表且其长度与 batch_size 不匹配,抛出异常
if isinstance(generator, list) and len(generator) != batch_size:
raise ValueError(
# 抛出异常,告知 generator 的长度与请求的 batch_size 不匹配
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."
)
# 如果 generator 是列表,遍历每个生成器以获取潜在向量
elif isinstance(generator, list):
init_latents = [
# 对每个图像进行编码并检索对应的潜在向量
retrieve_latents(self.vae.encode(image[i : i + 1]), generator=generator[i])
for i in range(batch_size)
]
# 将所有的潜在向量合并成一个张量
init_latents = torch.cat(init_latents, dim=0)
else:
# 如果 generator 不是列表,直接对整个图像进行编码并检索潜在向量
init_latents = retrieve_latents(self.vae.encode(image), generator=generator)
# 将潜在向量乘以配置中的缩放因子
init_latents = self.vae.config.scaling_factor * init_latents
# 检查请求的 batch_size 是否大于初始化潜在向量的数量并且能够整除
if batch_size > init_latents.shape[0] and batch_size % init_latents.shape[0] == 0:
# 扩展 init_latents 以满足 batch_size
deprecation_message = (
# 构造弃用警告消息
f"You have passed {batch_size} text prompts (`prompt`), but only {init_latents.shape[0]} initial"
" images (`image`). Initial images are now duplicating to match the number of text prompts. Note"
" that this behavior is deprecated and will be removed in a version 1.0.0. Please make sure to update"
" your script to pass as many initial images as text prompts to suppress this warning."
)
# 记录弃用警告
deprecate("len(prompt) != len(image)", "1.0.0", deprecation_message, standard_warn=False)
# 计算每个提示需要的额外图像数量
additional_image_per_prompt = batch_size // init_latents.shape[0]
# 复制初始化潜在向量以满足 batch_size
init_latents = torch.cat([init_latents] * additional_image_per_prompt, dim=0)
# 如果 batch_size 大于初始化潜在向量的数量但不能整除,抛出异常
elif batch_size > init_latents.shape[0] and batch_size % init_latents.shape[0] != 0:
raise ValueError(
# 抛出异常,告知不能将潜在向量重复以满足 batch_size
f"Cannot duplicate `image` of batch size {init_latents.shape[0]} to {batch_size} text prompts."
)
else:
# 如果 batch_size 合法,则将 init_latents 扩展为单一张量
init_latents = torch.cat([init_latents], dim=0)
# 获取初始化潜在向量的形状
shape = init_latents.shape
# 生成与潜在向量形状相同的随机噪声
noise = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
# 将噪声添加到潜在向量中
init_latents = self.scheduler.add_noise(init_latents, noise, timestep)
# 将处理后的潜在向量赋值给 latents
latents = init_latents
# 返回最终的潜在向量
return latents
# 定义获取指导尺度嵌入的函数,接收输入参数 w、嵌入维度和数据类型
def get_guidance_scale_embedding(self, w, embedding_dim=512, dtype=torch.float32):
"""
查看关于嵌入生成的更多信息,链接到相关 GitHub 页面
参数:
timesteps (`torch.Tensor`):
在这些时间步生成嵌入向量
embedding_dim (`int`, *可选*, 默认值为 512):
生成的嵌入的维度
dtype:
生成嵌入的数据类型
返回:
`torch.Tensor`: 形状为 `(len(timesteps), embedding_dim)` 的嵌入向量
"""
# 确保输入的 w 是一维的
assert len(w.shape) == 1
# 将 w 放大 1000 倍
w = w * 1000.0
# 计算嵌入维度的一半
half_dim = embedding_dim // 2
# 计算 log(10000) 除以 (half_dim - 1) 的值,用于后续计算
emb = torch.log(torch.tensor(10000.0)) / (half_dim - 1)
# 生成从 0 到 half_dim 的指数衰减的嵌入
emb = torch.exp(torch.arange(half_dim, dtype=dtype) * -emb)
# 将 w 转换为指定数据类型,并生成与 emb 组合的嵌入
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
# 定义属性,返回内部的指导尺度
@property
def guidance_scale(self):
return self._guidance_scale
# 定义属性,返回内部的剪辑跳过值
@property
def clip_skip(self):
return self._clip_skip
# 这里 `guidance_scale` 类似于公式 (2) 中的指导权重 `w`
# 在 Imagen 论文中: https://arxiv.org/pdf/2205.11487.pdf . `guidance_scale = 1`
# 对应于不进行分类器自由指导。
@property
def do_classifier_free_guidance(self):
# 检查指导尺度是否大于 1 且时间条件投影维度是否为空
return self._guidance_scale > 1 and self.unet.config.time_cond_proj_dim is None
# 定义属性,返回交叉注意力的关键字参数
@property
def cross_attention_kwargs(self):
return self._cross_attention_kwargs
# 定义属性,返回时间步的数量
@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,
# 输入的图像,类型为 PipelineImageInput
image: PipelineImageInput = None,
# 控制生成强度的参数,默认值为 0.8
strength: float = 0.8,
# 推理步骤的数量,默认为 50
num_inference_steps: Optional[int] = 50,
# 可选的时间步列表
timesteps: List[int] = None,
# 可选的 sigma 值列表
sigmas: List[float] = None,
# 引导缩放因子,默认为 7.5
guidance_scale: Optional[float] = 7.5,
# 可选的负提示文本,可以是字符串或字符串列表
negative_prompt: Optional[Union[str, List[str]]] = None,
# 每个提示生成的图像数量,默认为 1
num_images_per_prompt: Optional[int] = 1,
# 可选的 eta 值,默认为 0.0
eta: Optional[float] = 0.0,
# 随机数生成器,可以是单个或列表形式
generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
# 可选的提示嵌入,类型为 torch.Tensor
prompt_embeds: Optional[torch.Tensor] = None,
# 可选的负提示嵌入,类型为 torch.Tensor
negative_prompt_embeds: Optional[torch.Tensor] = None,
# 可选的图像输入,用于 IP 适配器
ip_adapter_image: Optional[PipelineImageInput] = None,
# 输出类型,默认为 "pil"
output_type: Optional[str] = "pil",
# 是否返回字典,默认为 True
return_dict: bool = True,
# 可选的交叉注意力参数字典
cross_attention_kwargs: Optional[Dict[str, Any]] = None,
# 可选的跳过剪辑参数
clip_skip: int = 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\deprecated\alt_diffusion\pipeline_output.py
# 从 dataclasses 模块导入 dataclass 装饰器
from dataclasses import dataclass
# 导入 List、Optional 和 Union 类型
from typing import List, Optional, Union
# 导入 numpy 库并简写为 np
import numpy as np
# 导入 PIL.Image 模块
import PIL.Image
# 从上级模块导入 BaseOutput 类
from ....utils import (
BaseOutput,
)
@dataclass
# 注释说明:从 diffusers.pipelines.stable_diffusion.pipeline_output 导入的类,Stable 替换为 Alt
class AltDiffusionPipelineOutput(BaseOutput):
"""
Alt Diffusion 管道的输出类。
参数:
images (`List[PIL.Image.Image]` 或 `np.ndarray`)
包含去噪的 PIL 图像的列表,长度为 `batch_size` 或形状为 `(batch_size, height, width,
num_channels)` 的 NumPy 数组。
nsfw_content_detected (`List[bool]`)
列表,指示对应生成的图像是否包含 "不安全内容" (nsfw),
如果无法进行安全检查,则为 `None`。
"""
# 定义 images 属性,类型为列表或 NumPy 数组
images: Union[List[PIL.Image.Image], np.ndarray]
# 定义 nsfw_content_detected 属性,类型为可选列表
nsfw_content_detected: Optional[List[bool]]
.\diffusers\pipelines\deprecated\alt_diffusion\__init__.py
# 导入类型检查相关的常量
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:
# 导入虚拟对象的模块
from ....utils import dummy_torch_and_transformers_objects
# 更新虚拟对象字典
_dummy_objects.update(get_objects_from_module(dummy_torch_and_transformers_objects))
else:
# 添加可用的模型导入结构
_import_structure["modeling_roberta_series"] = ["RobertaSeriesModelWithTransformation"]
_import_structure["pipeline_alt_diffusion"] = ["AltDiffusionPipeline"]
_import_structure["pipeline_alt_diffusion_img2img"] = ["AltDiffusionImg2ImgPipeline"]
_import_structure["pipeline_output"] = ["AltDiffusionPipelineOutput"]
# 检查类型或慢导入标志
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 .modeling_roberta_series import RobertaSeriesModelWithTransformation
from .pipeline_alt_diffusion import AltDiffusionPipeline
from .pipeline_alt_diffusion_img2img import AltDiffusionImg2ImgPipeline
from .pipeline_output import AltDiffusionPipelineOutput
else:
# 导入 sys 模块
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\deprecated\audio_diffusion\mel.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.
# 导入 NumPy 库,供后续使用
import numpy as np # noqa: E402
# 从配置工具导入混合类和注册装饰器
from ....configuration_utils import ConfigMixin, register_to_config
# 从调度工具导入调度混合类
from ....schedulers.scheduling_utils import SchedulerMixin
# 尝试导入 librosa 库
try:
import librosa # noqa: E402
# 如果导入成功,设置标志为真
_librosa_can_be_imported = True
# 初始化导入错误信息为空
_import_error = ""
# 捕获导入错误并设置相关标志和错误信息
except Exception as e:
_librosa_can_be_imported = False
# 设置错误信息,指示如何解决导入问题
_import_error = (
f"Cannot import librosa because {e}. Make sure to correctly install librosa to be able to install it."
)
# 导入 PIL 库中的 Image 模块
from PIL import Image # noqa: E402
# 定义 Mel 类,继承配置和调度混合类
class Mel(ConfigMixin, SchedulerMixin):
"""
参数说明:
x_res (`int`):
频谱图的 x 方向分辨率(时间)。
y_res (`int`):
频谱图的 y 方向分辨率(频率区间)。
sample_rate (`int`):
音频的采样率。
n_fft (`int`):
快速傅里叶变换的数量。
hop_length (`int`):
每次移动的长度(当 `y_res` < 256 时,推荐更大的值)。
top_db (`int`):
最大分贝值。
n_iter (`int`):
Griffin-Lim Mel 反转的迭代次数。
"""
# 指定配置文件名
config_name = "mel_config.json"
# 初始化方法,注册到配置中
@register_to_config
def __init__(
self,
x_res: int = 256, # x 方向分辨率,默认值为 256
y_res: int = 256, # y 方向分辨率,默认值为 256
sample_rate: int = 22050, # 默认采样率为 22050
n_fft: int = 2048, # 默认 FFT 数量为 2048
hop_length: int = 512, # 默认移动长度为 512
top_db: int = 80, # 默认最大分贝值为 80
n_iter: int = 32, # 默认迭代次数为 32
):
# 设置 hop_length 属性
self.hop_length = hop_length
# 设置采样率属性
self.sr = sample_rate
# 设置 FFT 数量属性
self.n_fft = n_fft
# 设置最大分贝值属性
self.top_db = top_db
# 设置迭代次数属性
self.n_iter = n_iter
# 调用方法设置频谱图分辨率
self.set_resolution(x_res, y_res)
# 初始化音频属性为 None
self.audio = None
# 检查 librosa 是否成功导入,若未导入则引发错误
if not _librosa_can_be_imported:
raise ValueError(_import_error)
# 设置频谱图分辨率的方法
def set_resolution(self, x_res: int, y_res: int):
"""设置分辨率。
参数:
x_res (`int`):
频谱图的 x 方向分辨率(时间)。
y_res (`int`):
频谱图的 y 方向分辨率(频率区间)。
"""
# 设置 x 方向分辨率
self.x_res = x_res
# 设置 y 方向分辨率
self.y_res = y_res
# 设置梅尔频率数量
self.n_mels = self.y_res
# 计算切片大小
self.slice_size = self.x_res * self.hop_length - 1
# 加载音频文件或原始音频数据
def load_audio(self, audio_file: str = None, raw_audio: np.ndarray = None):
"""Load audio.
Args:
audio_file (`str`):
An audio file that must be on disk due to [Librosa](https://librosa.org/) limitation.
raw_audio (`np.ndarray`):
The raw audio file as a NumPy array.
"""
# 如果提供了音频文件名,则使用 Librosa 加载音频
if audio_file is not None:
self.audio, _ = librosa.load(audio_file, mono=True, sr=self.sr)
# 否则,使用提供的原始音频数据
else:
self.audio = raw_audio
# 如果音频长度不足,使用静音进行填充
if len(self.audio) < self.x_res * self.hop_length:
self.audio = np.concatenate([self.audio, np.zeros((self.x_res * self.hop_length - len(self.audio),))])
# 获取音频切片的数量
def get_number_of_slices(self) -> int:
"""Get number of slices in audio.
Returns:
`int`:
Number of spectograms audio can be sliced into.
"""
# 返回音频长度除以每个切片的大小,得到切片数量
return len(self.audio) // self.slice_size
# 获取指定音频切片
def get_audio_slice(self, slice: int = 0) -> np.ndarray:
"""Get slice of audio.
Args:
slice (`int`):
Slice number of audio (out of `get_number_of_slices()`).
Returns:
`np.ndarray`:
The audio slice as a NumPy array.
"""
# 返回指定切片的音频数据
return self.audio[self.slice_size * slice : self.slice_size * (slice + 1)]
# 获取音频的采样率
def get_sample_rate(self) -> int:
"""Get sample rate.
Returns:
`int`:
Sample rate of audio.
"""
# 返回音频的采样率
return self.sr
# 将音频切片转换为声谱图
def audio_slice_to_image(self, slice: int) -> Image.Image:
"""Convert slice of audio to spectrogram.
Args:
slice (`int`):
Slice number of audio to convert (out of `get_number_of_slices()`).
Returns:
`PIL Image`:
A grayscale image of `x_res x y_res`.
"""
# 计算音频切片的梅尔声谱图
S = librosa.feature.melspectrogram(
y=self.get_audio_slice(slice), sr=self.sr, n_fft=self.n_fft, hop_length=self.hop_length, n_mels=self.n_mels
)
# 将声谱图转换为对数刻度
log_S = librosa.power_to_db(S, ref=np.max, top_db=self.top_db)
# 将对数声谱图归一化并转换为8位无符号整数
bytedata = (((log_S + self.top_db) * 255 / self.top_db).clip(0, 255) + 0.5).astype(np.uint8)
# 从数组创建图像
image = Image.fromarray(bytedata)
# 返回生成的图像
return image
# 定义一个将光谱图转换为音频的函数
def image_to_audio(self, image: Image.Image) -> np.ndarray:
"""将光谱图转换为音频。
参数:
image (`PIL Image`):
一个灰度图像,尺寸为 `x_res x y_res`。
返回:
audio (`np.ndarray`):
以 NumPy 数组形式返回的音频。
"""
# 将图像数据转换为字节并从字节缓冲区创建一个 NumPy 数组,数据类型为无符号 8 位整型
bytedata = np.frombuffer(image.tobytes(), dtype="uint8").reshape((image.height, image.width))
# 将字节数据转换为浮点数,并进行归一化处理,计算对数幅度谱
log_S = bytedata.astype("float") * self.top_db / 255 - self.top_db
# 将对数幅度谱转换为功率谱
S = librosa.db_to_power(log_S)
# 使用逆梅尔频谱将功率谱转换为音频信号
audio = librosa.feature.inverse.mel_to_audio(
S, sr=self.sr, n_fft=self.n_fft, hop_length=self.hop_length, n_iter=self.n_iter
)
# 返回生成的音频数组
return audio
.\diffusers\pipelines\deprecated\audio_diffusion\pipeline_audio_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.
# 导入数学库中的反余弦函数和正弦函数
from math import acos, sin
# 导入类型提示相关的类
from typing import List, Tuple, Union
# 导入 NumPy 库
import numpy as np
# 导入 PyTorch 库
import torch
# 导入图像处理库
from PIL import Image
# 从模型模块导入自编码器和 UNet 模型
from ....models import AutoencoderKL, UNet2DConditionModel
# 从调度器模块导入调度器类
from ....schedulers import DDIMScheduler, DDPMScheduler
# 从工具模块导入随机张量生成函数
from ....utils.torch_utils import randn_tensor
# 从管道工具模块导入音频输出和基本输出类
from ...pipeline_utils import AudioPipelineOutput, BaseOutput, DiffusionPipeline, ImagePipelineOutput
# 从音频处理模块导入 Mel 类
from .mel import Mel
# 定义音频扩散管道类,继承自扩散管道基类
class AudioDiffusionPipeline(DiffusionPipeline):
"""
音频扩散管道。
此模型继承自 [`DiffusionPipeline`]。请查看超类文档,以获取所有管道实现的通用方法(下载、保存、在特定设备上运行等)。
参数:
vqae ([`AutoencoderKL`]):
用于编码和解码图像到潜在表示的变分自编码器(VAE)模型。
unet ([`UNet2DConditionModel`]):
用于对编码图像潜在值去噪的 `UNet2DConditionModel`。
mel ([`Mel`]):
将音频转换为声谱图。
scheduler ([`DDIMScheduler`] 或 [`DDPMScheduler`]):
与 `unet` 一起使用的调度器,用于对编码图像潜在值去噪。可以是 [`DDIMScheduler`] 或 [`DDPMScheduler`] 中的任意一种。
"""
# 定义可选组件的名称列表
_optional_components = ["vqvae"]
# 初始化方法,定义所需的参数
def __init__(
self,
vqvae: AutoencoderKL, # 变分自编码器模型
unet: UNet2DConditionModel, # UNet 模型
mel: Mel, # Mel 转换器
scheduler: Union[DDIMScheduler, DDPMScheduler], # 调度器
):
super().__init__() # 调用父类的初始化方法
# 注册模型和组件
self.register_modules(unet=unet, scheduler=scheduler, mel=mel, vqvae=vqvae)
# 获取默认步骤数的方法
def get_default_steps(self) -> int:
"""返回推荐的推理默认步骤数。
返回:
`int`:
步骤数。
"""
# 根据调度器类型返回对应的步骤数
return 50 if isinstance(self.scheduler, DDIMScheduler) else 1000
# 装饰器,指示以下方法不需要计算梯度
@torch.no_grad()
# 定义可调用对象,支持多种参数配置
def __call__(
self, # 该方法可以被直接调用
batch_size: int = 1, # 批次大小,默认为 1
audio_file: str = None, # 音频文件的路径,默认为 None
raw_audio: np.ndarray = None, # 原始音频数据,默认为 None
slice: int = 0, # 切片起始位置,默认为 0
start_step: int = 0, # 起始步数,默认为 0
steps: int = None, # 总步数,默认为 None
generator: torch.Generator = None, # 随机数生成器,默认为 None
mask_start_secs: float = 0, # 掩码开始时间(秒),默认为 0
mask_end_secs: float = 0, # 掩码结束时间(秒),默认为 0
step_generator: torch.Generator = None, # 步数生成器,默认为 None
eta: float = 0, # 噪声控制参数,默认为 0
noise: torch.Tensor = None, # 噪声张量,默认为 None
encoding: torch.Tensor = None, # 编码张量,默认为 None
return_dict=True, # 是否返回字典格式的结果,默认为 True
) -> Union[ # 返回值的类型说明
Union[AudioPipelineOutput, ImagePipelineOutput], # 可返回音频或图像管道输出
Tuple[List[Image.Image], Tuple[int, List[np.ndarray]]], # 也可返回图像列表和元组
@torch.no_grad() # 关闭梯度计算以节省内存
def encode(self, images: List[Image.Image], steps: int = 50) -> np.ndarray: # 编码方法
"""
反向去噪过程以恢复生成图像的噪声图像。
参数:
images (`List[PIL Image]`): # 输入图像列表
要编码的图像列表。
steps (`int`): # 编码步数
执行的编码步数(默认为 `50`)。
返回:
`np.ndarray`: # 返回的噪声张量
形状为 `(batch_size, 1, height, width)` 的噪声张量。
"""
# 仅适用于 DDIM,因为此方法是确定性的
assert isinstance(self.scheduler, DDIMScheduler) # 确保调度器是 DDIM 类型
self.scheduler.set_timesteps(steps) # 设置调度器的时间步
sample = np.array( # 将输入图像转换为 NumPy 数组
[np.frombuffer(image.tobytes(), dtype="uint8").reshape((1, image.height, image.width)) for image in images] # 读取图像数据并重塑为适当形状
)
sample = (sample / 255) * 2 - 1 # 归一化样本到 [-1, 1] 范围
sample = torch.Tensor(sample).to(self.device) # 转换为 PyTorch 张量并移动到设备上
for t in self.progress_bar(torch.flip(self.scheduler.timesteps, (0,))): # 迭代时间步的反向顺序
prev_timestep = t - self.scheduler.config.num_train_timesteps // self.scheduler.num_inference_steps # 计算前一个时间步
alpha_prod_t = self.scheduler.alphas_cumprod[t] # 获取当前时间步的累积 alpha 值
alpha_prod_t_prev = ( # 获取前一个时间步的累积 alpha 值
self.scheduler.alphas_cumprod[prev_timestep] # 如果存在,则使用前一个时间步
if prev_timestep >= 0
else self.scheduler.final_alpha_cumprod # 否则使用最终的累积 alpha 值
)
beta_prod_t = 1 - alpha_prod_t # 计算 beta 值
model_output = self.unet(sample, t)["sample"] # 通过 UNet 模型获取输出样本
pred_sample_direction = (1 - alpha_prod_t_prev) ** (0.5) * model_output # 预测样本方向
sample = (sample - pred_sample_direction) * alpha_prod_t_prev ** (-0.5) # 更新样本
sample = sample * alpha_prod_t ** (0.5) + beta_prod_t ** (0.5) * model_output # 应用当前时间步的更新
return sample # 返回处理后的样本
@staticmethod # 标记为静态方法
# 定义球面线性插值函数,接受两个张量和一个插值因子
def slerp(x0: torch.Tensor, x1: torch.Tensor, alpha: float) -> torch.Tensor:
"""Spherical Linear intERPolation.
Args:
x0 (`torch.Tensor`):
第一个用于插值的张量。
x1 (`torch.Tensor`):
第二个用于插值的张量。
alpha (`float`):
插值因子,范围在 0 到 1 之间
Returns:
`torch.Tensor`:
插值后的张量。
"""
# 计算两个张量之间的夹角 theta,使用余弦定理
theta = acos(torch.dot(torch.flatten(x0), torch.flatten(x1)) / torch.norm(x0) / torch.norm(x1))
# 根据球面线性插值公式计算并返回插值结果
return sin((1 - alpha) * theta) * x0 / sin(theta) + sin(alpha * theta) * x1 / sin(theta)
.\diffusers\pipelines\deprecated\audio_diffusion\__init__.py
# 从 typing 模块导入 TYPE_CHECKING,用于类型检查时的条件导入
from typing import TYPE_CHECKING
# 从上层模块导入 DIFFUSERS_SLOW_IMPORT 和 _LazyModule
from ....utils import DIFFUSERS_SLOW_IMPORT, _LazyModule
# 定义模块的导入结构,指定可导入的子模块及其内容
_import_structure = {
"mel": ["Mel"],
"pipeline_audio_diffusion": ["AudioDiffusionPipeline"],
}
# 根据条件选择性导入模块
if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
# 从当前包中导入 Mel 类
from .mel import Mel
# 从当前包中导入 AudioDiffusionPipeline 类
from .pipeline_audio_diffusion import AudioDiffusionPipeline
else:
# 导入 sys 模块
import sys
# 将当前模块替换为 _LazyModule 的实例,实现延迟加载
sys.modules[__name__] = _LazyModule(
__name__,
globals()["__file__"],
_import_structure,
module_spec=__spec__,
)
.\diffusers\pipelines\deprecated\latent_diffusion_uncond\pipeline_latent_diffusion_uncond.py
# 版权所有 2024 HuggingFace 团队。保留所有权利。
#
# 根据 Apache 许可证第 2.0 版(“许可证”)授权;
# 除非符合许可证,否则您不得使用此文件。
# 您可以在以下网址获得许可证副本:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# 除非适用法律要求或书面同意,软件在“按原样”基础上分发,
# 不提供任何形式的担保或条件,明示或暗示。
# 请参阅许可证以了解有关权限和
# 限制的具体信息。
# 导入检查模块,用于获取对象的源代码或签名信息
import inspect
# 从 typing 模块导入类型注解
from typing import List, Optional, Tuple, Union
# 导入 PyTorch 库
import torch
# 从相对路径导入 VQModel 和 UNet2DModel 模型
from ....models import UNet2DModel, VQModel
# 从相对路径导入 DDIMScheduler 调度器
from ....schedulers import DDIMScheduler
# 从工具模块导入随机张量生成函数
from ....utils.torch_utils import randn_tensor
# 从管道工具模块导入扩散管道和图像输出类
from ...pipeline_utils import DiffusionPipeline, ImagePipelineOutput
# 定义一个用于无条件图像生成的潜在扩散管道类
class LDMPipeline(DiffusionPipeline):
r"""
使用潜在扩散进行无条件图像生成的管道。
该模型继承自 [`DiffusionPipeline`]。请查看超类文档以获取所有管道的通用方法(下载、保存、在特定设备上运行等)。
参数:
vqvae ([`VQModel`]):
用于编码和解码图像到潜在表示的向量量化(VQ)模型。
unet ([`UNet2DModel`]):
用于对编码的图像潜在数据进行去噪的 `UNet2DModel`。
scheduler ([`SchedulerMixin`]):
[`DDIMScheduler`] 与 `unet` 一起使用以去噪编码的图像潜在数据。
"""
# 初始化方法,用于创建 LDMPipeline 实例
def __init__(self, vqvae: VQModel, unet: UNet2DModel, scheduler: DDIMScheduler):
# 调用父类的初始化方法
super().__init__()
# 注册模型模块
self.register_modules(vqvae=vqvae, unet=unet, scheduler=scheduler)
# 禁用梯度计算,优化内存使用
@torch.no_grad()
def __call__(
# 默认批次大小为 1
self,
batch_size: int = 1,
# 可选的生成器,支持单个或多个生成器
generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
# 默认的 eta 参数设置为 0.0
eta: float = 0.0,
# 默认推理步骤数量为 50
num_inference_steps: int = 50,
# 输出类型默认为 "pil"
output_type: Optional[str] = "pil",
# 返回字典标志,默认为 True
return_dict: bool = True,
# 接受额外参数
**kwargs,
.\diffusers\pipelines\deprecated\latent_diffusion_uncond\__init__.py
# 导入类型检查的相关功能
from typing import TYPE_CHECKING
# 从相对路径导入工具模块中的常量
from ....utils import DIFFUSERS_SLOW_IMPORT, _LazyModule
# 定义模块导入结构,指定包含的子模块
_import_structure = {"pipeline_latent_diffusion_uncond": ["LDMPipeline"]}
# 如果处于类型检查阶段或慢导入标志为真,则导入指定的子模块
if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
from .pipeline_latent_diffusion_uncond import LDMPipeline
else:
# 否则,导入 sys 模块
import sys
# 使用懒加载模块将当前模块替换为 _LazyModule 实例
sys.modules[__name__] = _LazyModule(
__name__,
globals()["__file__"],
_import_structure,
module_spec=__spec__,
)
.\diffusers\pipelines\deprecated\pndm\pipeline_pndm.py
# 版权声明,声明此代码的版权所有者及使用许可信息
# Copyright 2024 The HuggingFace Team. All rights reserved.
#
# 使用 Apache 许可证,版本 2.0 的许可声明
# 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.
# 导入类型提示
from typing import List, Optional, Tuple, Union
# 导入 PyTorch 库
import torch
# 从模型模块导入 UNet2DModel 类
from ....models import UNet2DModel
# 从调度器模块导入 PNDMScheduler 类
from ....schedulers import PNDMScheduler
# 从工具模块导入随机张量生成函数
from ....utils.torch_utils import randn_tensor
# 从管道工具模块导入 DiffusionPipeline 和 ImagePipelineOutput
from ...pipeline_utils import DiffusionPipeline, ImagePipelineOutput
# 定义无条件图像生成的管道类
class PNDMPipeline(DiffusionPipeline):
r"""
无条件图像生成的管道。
该模型继承自 [`DiffusionPipeline`]。请查看超类文档以获取所有管道的通用方法
(下载、保存、在特定设备上运行等) 的实现。
参数:
unet ([`UNet2DModel`]):
用于去噪编码图像潜在值的 `UNet2DModel`。
scheduler ([`PNDMScheduler`]):
用于与 `unet` 结合使用以去噪编码图像的 `PNDMScheduler`。
"""
# 定义 UNet2DModel 实例变量
unet: UNet2DModel
# 定义 PNDMScheduler 实例变量
scheduler: PNDMScheduler
# 初始化方法,接收 UNet2DModel 和 PNDMScheduler 实例
def __init__(self, unet: UNet2DModel, scheduler: PNDMScheduler):
# 调用父类的初始化方法
super().__init__()
# 从配置中创建 PNDMScheduler 实例
scheduler = PNDMScheduler.from_config(scheduler.config)
# 注册 unet 和 scheduler 模块
self.register_modules(unet=unet, scheduler=scheduler)
# 定义无梯度上下文中的可调用方法
@torch.no_grad()
def __call__(
# 批处理大小,默认为 1
self,
batch_size: int = 1,
# 推理步骤数,默认为 50
num_inference_steps: int = 50,
# 可选的随机数生成器,支持单个或多个生成器
generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
# 输出类型,默认为 "pil"
output_type: Optional[str] = "pil",
# 是否返回字典形式的结果,默认为 True
return_dict: bool = True,
# 其他可选参数
**kwargs,
# 定义生成管道调用的函数,返回生成结果
) -> Union[ImagePipelineOutput, Tuple]:
# 文档字符串,描述生成过程的函数调用及参数说明
r"""
生成管道的调用函数。
参数:
batch_size (`int`, `optional`, defaults to 1):
生成图像的数量。
num_inference_steps (`int`, `optional`, defaults to 50):
去噪步骤的数量。更多的去噪步骤通常会导致更高质量的图像,但推理速度较慢。
generator (`torch.Generator`, `optional`):
用于生成确定性结果的 [`torch.Generator`](https://pytorch.org/docs/stable/generated/torch.Generator.html)。
output_type (`str`, `optional`, defaults to `"pil"`):
生成图像的输出格式。可以选择 `PIL.Image` 或 `np.array`。
return_dict (`bool`, *optional*, defaults to `True`):
是否返回 [`ImagePipelineOutput`] 而不是普通元组。
示例:
```py
>>> from diffusers import PNDMPipeline
>>> # 加载模型和调度器
>>> pndm = PNDMPipeline.from_pretrained("google/ddpm-cifar10-32")
>>> # 在推理中运行管道(采样随机噪声并去噪)
>>> image = pndm().images[0]
>>> # 保存图像
>>> image.save("pndm_generated_image.png")
```py
返回:
[`~pipelines.ImagePipelineOutput`] 或 `tuple`:
如果 `return_dict` 为 `True`,则返回 [`~pipelines.ImagePipelineOutput`],否则返回一个元组,
其中第一个元素是生成的图像列表。
"""
# 采样方法的更多信息可以参考官方论文的算法2: https://arxiv.org/pdf/2202.09778.pdf
# 生成高斯噪声以开始循环
image = randn_tensor(
# 创建形状为 (batch_size, 通道数, 样本大小, 样本大小) 的随机张量
(batch_size, self.unet.config.in_channels, self.unet.config.sample_size, self.unet.config.sample_size),
generator=generator, # 使用指定的生成器
device=self.device, # 设置设备为指定的设备
)
# 设置调度器的时间步长
self.scheduler.set_timesteps(num_inference_steps)
# 遍历进度条中的每个时间步
for t in self.progress_bar(self.scheduler.timesteps):
# 通过 UNet 模型进行推理,获取模型输出
model_output = self.unet(image, t).sample
# 根据调度器步骤更新图像,获取前一帧样本
image = self.scheduler.step(model_output, t, image).prev_sample
# 将图像缩放到 [0, 1] 的范围
image = (image / 2 + 0.5).clamp(0, 1)
# 将图像从张量转为 numpy 数组,并调整维度顺序
image = image.cpu().permute(0, 2, 3, 1).numpy()
# 如果输出类型为 "pil",则将 numpy 数组转换为 PIL 图像
if output_type == "pil":
image = self.numpy_to_pil(image)
# 如果不返回字典,返回元组格式
if not return_dict:
return (image,)
# 返回包含生成图像的 ImagePipelineOutput 对象
return ImagePipelineOutput(images=image)
.\diffusers\pipelines\deprecated\pndm\__init__.py
# 从 typing 模块导入 TYPE_CHECKING,主要用于类型检查
from typing import TYPE_CHECKING
# 从上级目录的 utils 模块导入 DIFFUSERS_SLOW_IMPORT 和 _LazyModule
from ....utils import DIFFUSERS_SLOW_IMPORT, _LazyModule
# 定义模块导入结构,指定需要导入的子模块和类
_import_structure = {"pipeline_pndm": ["PNDMPipeline"]}
# 检查是否处于类型检查阶段或需要慢速导入
if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
# 从子模块 pipeline_pndm 导入 PNDMPipeline 类
from .pipeline_pndm import PNDMPipeline
else:
# 如果不是类型检查阶段,则导入 sys 模块
import sys
# 将当前模块替换为 _LazyModule 对象,以实现懒加载功能
sys.modules[__name__] = _LazyModule(
__name__, # 当前模块名称
globals()["__file__"], # 当前模块文件路径
_import_structure, # 导入结构
module_spec=__spec__, # 模块的规格
)
.\diffusers\pipelines\deprecated\repaint\pipeline_repaint.py
# 版权声明,表明版权所有者和使用许可证
# Copyright 2024 ETH Zurich Computer Vision Lab and The HuggingFace Team. All rights reserved.
#
# 根据 Apache 许可证第 2.0 版("许可证")进行许可;
# 除非遵守许可证,否则您不得使用此文件。
# 您可以在以下网址获得许可证的副本:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# 除非适用的法律要求或书面协议另有约定,软件按“原样”分发,
# 不提供任何明示或暗示的担保或条件。
# 请参见许可证以获取有关权限和限制的具体信息。
# 导入所需的类型
from typing import List, Optional, Tuple, Union
# 导入 numpy 库用于数组操作
import numpy as np
# 导入 PIL 库用于图像处理
import PIL.Image
# 导入 PyTorch 库用于深度学习模型
import torch
# 从本地模块导入 UNet2DModel 类
from ....models import UNet2DModel
# 从本地模块导入 RePaintScheduler 类
from ....schedulers import RePaintScheduler
# 从本地模块导入工具函数
from ....utils import PIL_INTERPOLATION, deprecate, logging
# 从本地模块导入随机张量生成函数
from ....utils.torch_utils import randn_tensor
# 从本地模块导入 DiffusionPipeline 和 ImagePipelineOutput 类
from ...pipeline_utils import DiffusionPipeline, ImagePipelineOutput
# 创建一个日志记录器,用于记录信息
logger = logging.get_logger(__name__) # pylint: disable=invalid-name
# 预处理图像的函数,支持多种输入格式
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_img2img.preprocess
def _preprocess_image(image: Union[List, PIL.Image.Image, torch.Tensor]):
# 定义弃用信息,提示用户将来版本会删除此方法
deprecation_message = "The preprocess method is deprecated and will be removed in diffusers 1.0.0. Please use VaeImageProcessor.preprocess(...) instead"
# 调用弃用函数,发出警告
deprecate("preprocess", "1.0.0", deprecation_message, standard_warn=False)
# 如果输入是 PyTorch 张量,直接返回
if isinstance(image, torch.Tensor):
return image
# 如果输入是 PIL 图像,转换为单元素列表
elif isinstance(image, PIL.Image.Image):
image = [image]
# 如果列表中的第一个元素是 PIL 图像
if isinstance(image[0], PIL.Image.Image):
# 获取图像的宽度和高度
w, h = image[0].size
# 调整宽度和高度,使其为 8 的整数倍
w, h = (x - x % 8 for x in (w, h)) # resize to integer multiple of 8
# 调整图像大小并转换为 NumPy 数组,添加新维度
image = [np.array(i.resize((w, h), resample=PIL_INTERPOLATION["lanczos"]))[None, :] for i in image]
# 将所有图像沿第 0 维连接成一个数组
image = np.concatenate(image, axis=0)
# 转换为浮点型数组并归一化到 [0, 1]
image = np.array(image).astype(np.float32) / 255.0
# 调整数组维度顺序为 (批次, 通道, 高度, 宽度)
image = image.transpose(0, 3, 1, 2)
# 将像素值从 [0, 1] 映射到 [-1, 1]
image = 2.0 * image - 1.0
# 将 NumPy 数组转换为 PyTorch 张量
image = torch.from_numpy(image)
# 如果列表中的第一个元素是 PyTorch 张量
elif isinstance(image[0], torch.Tensor):
# 将张量沿第 0 维连接
image = torch.cat(image, dim=0)
# 返回处理后的图像
return image
# 预处理掩码的函数,支持多种输入格式
def _preprocess_mask(mask: Union[List, PIL.Image.Image, torch.Tensor]):
# 如果输入是 PyTorch 张量,直接返回
if isinstance(mask, torch.Tensor):
return mask
# 如果输入是 PIL 图像,转换为单元素列表
elif isinstance(mask, PIL.Image.Image):
mask = [mask]
# 如果列表中的第一个元素是 PIL 图像
if isinstance(mask[0], PIL.Image.Image):
# 获取掩码的宽度和高度
w, h = mask[0].size
# 调整宽度和高度,使其为 32 的整数倍
w, h = (x - x % 32 for x in (w, h)) # resize to integer multiple of 32
# 将掩码调整大小并转换为灰度图像的 NumPy 数组,添加新维度
mask = [np.array(m.convert("L").resize((w, h), resample=PIL_INTERPOLATION["nearest"]))[None, :] for m in mask]
# 将所有掩码沿第 0 维连接成一个数组
mask = np.concatenate(mask, axis=0)
# 转换为浮点型数组并归一化到 [0, 1]
mask = mask.astype(np.float32) / 255.0
# 将值小于 0.5 的部分置为 0
mask[mask < 0.5] = 0
# 将值大于等于 0.5 的部分置为 1
mask[mask >= 0.5] = 1
# 将 NumPy 数组转换为 PyTorch 张量
mask = torch.from_numpy(mask)
# 如果列表中的第一个元素是 PyTorch 张量
elif isinstance(mask[0], torch.Tensor):
# 将张量沿第 0 维连接
mask = torch.cat(mask, dim=0)
# 返回处理后的掩码
return mask
# 定义 RePaintPipeline 类,继承自 DiffusionPipeline
class RePaintPipeline(DiffusionPipeline):
# 文档字符串,描述图像修复的管道使用 RePaint 模型
r"""
Pipeline for image inpainting using RePaint.
This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods
implemented for all pipelines (downloading, saving, running on a particular device, etc.).
Parameters:
unet ([`UNet2DModel`]):
A `UNet2DModel` to denoise the encoded image latents.
scheduler ([`RePaintScheduler`]):
A `RePaintScheduler` to be used in combination with `unet` to denoise the encoded image.
"""
# 定义 UNet2DModel 类型的属性,用于图像去噪
unet: UNet2DModel
# 定义 RePaintScheduler 类型的属性,用于图像去噪的调度
scheduler: RePaintScheduler
# 设置模型 CPU 卸载序列为 "unet"
model_cpu_offload_seq = "unet"
# 初始化方法,接收 unet 和 scheduler 参数
def __init__(self, unet, scheduler):
# 调用父类的初始化方法
super().__init__()
# 注册 unet 和 scheduler 模块
self.register_modules(unet=unet, scheduler=scheduler)
# 禁用梯度计算的上下文装饰器
@torch.no_grad()
def __call__(
# 接收图像和掩码图像,类型可以是 Torch 张量或 PIL 图像
image: Union[torch.Tensor, PIL.Image.Image],
mask_image: Union[torch.Tensor, PIL.Image.Image],
# 设置推理步骤的数量,默认为 250
num_inference_steps: int = 250,
# 设置 eta 参数,默认为 0.0
eta: float = 0.0,
# 设置跳跃长度,默认为 10
jump_length: int = 10,
# 设置跳跃样本数量,默认为 10
jump_n_sample: int = 10,
# 生成器参数,可以是一个生成器或生成器列表,默认为 None
generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
# 输出类型参数,默认为 "pil"
output_type: Optional[str] = "pil",
# 是否返回字典形式的结果,默认为 True
return_dict: bool = True,