🤗 Diffusers 简介
在本笔记本中,您将训练第一个扩散模型来 **生成可爱的蝴蝶 🦋 的图像。** 在此过程中,您将了解 🤗 Diffusers 库的核心组件,这将为我们将在课程后面学习的更高级应用奠定良好的基础。
让我们开始吧!
您将学到什么
在本笔记本中,您将
- 了解强大的自定义扩散模型管道的实际应用(以及如何制作自己的版本)
- 通过以下步骤创建您自己的迷你管道:
- 回顾扩散模型背后的核心思想
- 从 Hub 加载用于训练的数据
- 探索如何使用调度器将噪声添加到此数据
- 创建和训练 UNet 模型
- 将这些部分组合成一个可工作的管道
- 编辑并运行一个脚本来初始化更长的训练运行,这将处理
- 通过 🤗 Accelerate 实现多 GPU 训练
- 实验日志记录以跟踪关键统计数据
- 将最终模型上传到 Hugging Face Hub
❓如果您有任何问题,请在 Hugging Face Discord 服务器上的 #diffusion-models-class
频道中发布。如果您还没有注册,可以在此处注册:https://huggingface.co/join/discord
先决条件
在深入研究笔记本之前,您应该
- 📖 阅读第一单元的材料
- 🤗 在 Hugging Face Hub 上创建一个帐户。如果您还没有,可以在此处注册:https://huggingface.co/join
步骤 1:设置
运行以下单元格以安装 diffusers 库以及其他一些要求
%pip install -qq -U diffusers datasets transformers accelerate ftfy pyarrow==9.0.0
接下来,前往 https://huggingface.co/settings/tokens 并创建一个具有写入权限的访问令牌(如果您还没有的话)。
您可以使用命令行(huggingface-cli login
)或运行以下单元格来使用此令牌登录。
>>> from huggingface_hub import notebook_login
>>> notebook_login()
Login successful Your token has been saved to /root/.huggingface/token
然后,您需要安装 Git-LFS 来上传模型检查点。
%%capture
!sudo apt -qq install git-lfs
!git config --global credential.helper store
最后,让我们导入我们将要使用的库并定义一些我们将在笔记本后面使用的便捷函数。
import numpy as np
import torch
import torch.nn.functional as F
from matplotlib import pyplot as plt
from PIL import Image
def show_images(x):
"""Given a batch of images x, make a grid and convert to PIL"""
x = x * 0.5 + 0.5 # Map from (-1, 1) back to (0, 1)
grid = torchvision.utils.make_grid(x)
grid_im = grid.detach().cpu().permute(1, 2, 0).clip(0, 1) * 255
grid_im = Image.fromarray(np.array(grid_im).astype(np.uint8))
return grid_im
def make_grid(images, size=64):
"""Given a list of PIL images, stack them together into a line for easy viewing"""
output_im = Image.new("RGB", (size * len(images), size))
for i, im in enumerate(images):
output_im.paste(im.resize((size, size)), (i * size, 0))
return output_im
# Mac users may need device = 'mps' (untested)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
好的,我们准备就绪!
Dreambooth:对未来事物的简单了解
如果您在过去几个月里关注过 AI 相关的社交媒体,您一定听说过 Stable Diffusion。它是一个强大的文本条件化潜在扩散模型(别担心,我们很快就会了解这些含义)。但它有一个缺陷:除非我们足够有名,否则它不知道我们或我是什么样子,因为我们的图像遍布互联网。
Dreambooth 让我们能够创建自己的模型变体,其中包含有关特定面孔、物体或风格的额外知识。Corridor Crew 制作了一个很棒的视频,使用这种技术来讲述具有连贯角色的故事,这是一个很好的例子,说明了这种技术能够做什么。
>>> from IPython.display import YouTubeVideo
>>> YouTubeVideo("W4Mcuh38wyM")
以下是一个使用 模型 的例子,该模型针对一种名为“土豆先生”的流行儿童玩具的 5 张照片进行了训练。
首先,我们加载管道。这将从 Hub 下载模型权重等。由于这将为一个简单的演示下载数 GB 的数据,您可以跳过此单元格,只需欣赏示例输出即可!
from diffusers import StableDiffusionPipeline
# Check out https://huggingface.co/sd-dreambooth-library for loads of models from the community
model_id = "sd-dreambooth-library/mr-potato-head"
# Load the pipeline
pipe = StableDiffusionPipeline.from_pretrained(model_id, torch_dtype=torch.float16).to(device)
管道加载完成后,我们可以使用以下代码生成图像。
>>> prompt = "an abstract oil painting of sks mr potato head by picasso"
>>> image = pipe(prompt, num_inference_steps=50, guidance_scale=7.5).images[0]
>>> image
练习:自己尝试使用不同的提示。sks
令牌代表此情况下的新概念的唯一标识符 - 如果您省略该令牌会发生什么?您还可以尝试更改采样步骤的数量(您能降低到多低?)和 guidance_scale
,它决定了模型将尝试在多大程度上匹配提示。
在那个神奇的管道中有很多事情发生!在本课程结束时,您将了解其工作原理。现在,让我们看看如何从头开始训练扩散模型。
MVP (最小可行管道)
🤗 Diffusers 的核心 API 分为三个主要部分:
- **管道**:旨在以用户友好的方式快速从流行的训练扩散模型生成样本的高级类。
- **模型**:用于训练新的扩散模型的流行架构,例如 UNet。
- **调度器**:在推理过程中从噪声生成图像的各种技术,以及为训练生成噪声图像。
管道非常适合最终用户,但如果你来参加本课程,我们假设你想要知道幕后发生了什么!因此,在本笔记本的剩余部分,我们将构建自己的管道,能够生成小型蝴蝶图片。以下是最终结果:
>>> from diffusers import DDPMPipeline
>>> # Load the butterfly pipeline
>>> butterfly_pipeline = DDPMPipeline.from_pretrained("johnowhitaker/ddpm-butterflies-32px").to(device)
>>> # Create 8 images
>>> images = butterfly_pipeline(batch_size=8).images
>>> # View the result
>>> make_grid(images)
可能不像 DreamBooth 示例那么令人印象深刻,但我们是从零开始训练的,训练数据只有用于训练 Stable Diffusion 的数据的 0.0001%。说到训练,回想一下本单元的介绍,训练扩散模型看起来像这样:
- 从训练数据中加载一些图像。
- 添加噪声,以不同量。
- 将输入的噪声版本馈送到模型中。
- 评估模型在对这些输入进行降噪方面的表现。
- 使用此信息更新模型权重,并重复此过程。
我们将在接下来的几节中逐一探讨这些步骤,直到我们有一个完整的训练循环,然后我们将探讨如何从训练好的模型中采样,以及如何将所有内容打包到管道中,以方便共享。让我们从数据开始……
步骤 2:下载训练数据集
在本示例中,我们将使用来自 Hugging Face Hub 的图像数据集。具体来说,这个包含 1000 张蝴蝶图片的集合。这是一个非常小的数据集,因此我们还包含了一些较大的选项的注释代码。如果你想使用自己的图像集,也可以使用注释掉的代码示例从文件夹中加载图片。
import torchvision
from datasets import load_dataset
from torchvision import transforms
dataset = load_dataset("huggan/smithsonian_butterflies_subset", split="train")
# Or load images from a local folder
# dataset = load_dataset("imagefolder", data_dir="path/to/folder")
# We'll train on 32-pixel square images, but you can try larger sizes too
image_size = 32
# You can lower your batch size if you're running out of GPU memory
batch_size = 64
# Define data augmentations
preprocess = transforms.Compose(
[
transforms.Resize((image_size, image_size)), # Resize
transforms.RandomHorizontalFlip(), # Randomly flip (data augmentation)
transforms.ToTensor(), # Convert to tensor (0, 1)
transforms.Normalize([0.5], [0.5]), # Map to (-1, 1)
]
)
def transform(examples):
images = [preprocess(image.convert("RGB")) for image in examples["image"]]
return {"images": images}
dataset.set_transform(transform)
# Create a dataloader from the dataset to serve up the transformed images in batches
train_dataloader = torch.utils.data.DataLoader(dataset, batch_size=batch_size, shuffle=True)
我们可以获取一批图像并查看其中一些,如下所示:
>>> xb = next(iter(train_dataloader))["images"].to(device)[:8]
>>> print("X shape:", xb.shape)
>>> show_images(xb).resize((8 * 64, 64), resample=Image.NEAREST)
X shape: torch.Size([8, 3, 32, 32])
我们坚持使用 32 像素图像的小数据集,以使本笔记本中的训练时间可控。
步骤 3:定义调度器
我们训练的计划是取这些输入图像并向它们添加噪声,然后将噪声图像馈送到模型。在推理过程中,我们将使用模型预测来迭代地消除噪声。在 diffusers
中,这两个过程都由调度器处理。
噪声调度决定了在不同时间步长添加多少噪声。以下是我们如何使用“DDPM”训练和采样的默认设置创建调度器的方法(基于论文 “去噪扩散概率模型”):
from diffusers import DDPMScheduler
noise_scheduler = DDPMScheduler(num_train_timesteps=1000)
DDPM 论文描述了一个损坏过程,它为每个“时间步长”添加少量噪声。给定某个时间步长的 $x_{t-1}$,我们可以使用以下公式获得下一个(稍微带更多噪声的)版本 $x_t$:
$q(\mathbf{x}t \vert \mathbf{x}{t-1}) = \mathcal{N}(\mathbf{x}t; \sqrt{1 - \beta_t} \mathbf{x}{t-1}, \betat\mathbf{I}) \quad q(\mathbf{x}{1:T} \vert \mathbf{x}0) = \prod^T{t=1} q(\mathbf{x}t \vert \mathbf{x}{t-1})$
也就是说,我们取 $x{t-1}$,将其按 $\sqrt{1 - \beta_t}$ 缩放,并添加按 $\beta_t$ 缩放的噪声。这个 $\beta$ 是根据某个调度为每个 t 定义的,并决定每个时间步长添加多少噪声。现在,我们不一定要执行 500 次操作才能获得 $x{500}$,因此我们有另一个公式来获取给定 $x_0$ 的任何 t 的 $x_t$:
$\begin{aligned} q(\mathbf{x}t \vert \mathbf{x}_0) &= \mathcal{N}(\mathbf{x}_t; \sqrt{\bar{\alpha}_t} \mathbf{x}_0, {(1 - \bar{\alpha}_t)} \mathbf{I}) \end{aligned}$ 其中 $\bar{\alpha}_t = \prod{i=1}^T \alpha_i$ 且 $\alpha_i = 1-\beta_i$
数学符号总是看起来很吓人!幸运的是,调度器为我们处理了所有这些。我们可以绘制 $\sqrt{\bar{\alpha}_t}$(标记为 sqrt_alpha_prod
)和 $\sqrt{(1 - \bar{\alpha}_t)}$(标记为 sqrt_one_minus_alpha_prod
)来查看输入 (x) 和噪声是如何在不同时间步长进行缩放和混合的:
>>> plt.plot(noise_scheduler.alphas_cumprod.cpu() ** 0.5, label=r"${\sqrt{\bar{\alpha}_t}}$")
>>> plt.plot((1 - noise_scheduler.alphas_cumprod.cpu()) ** 0.5, label=r"$\sqrt{(1 - \bar{\alpha}_t)}$")
>>> plt.legend(fontsize="x-large")
**练习:**你可以通过交换此处注释掉的选项之一来探索此图如何随着 beta_start、beta_end 和 beta_schedule 的不同设置而改变:
# One with too little noise added:
# noise_scheduler = DDPMScheduler(num_train_timesteps=1000, beta_start=0.001, beta_end=0.004)
# The 'cosine' schedule, which may be better for small image sizes:
# noise_scheduler = DDPMScheduler(num_train_timesteps=1000, beta_schedule='squaredcos_cap_v2')
无论你选择了哪个调度器,我们现在都可以使用它来使用 noise_scheduler.add_noise
函数以不同量添加噪声,如下所示:
>>> timesteps = torch.linspace(0, 999, 8).long().to(device)
>>> noise = torch.randn_like(xb)
>>> noisy_xb = noise_scheduler.add_noise(xb, noise, timesteps)
>>> print("Noisy X shape", noisy_xb.shape)
>>> show_images(noisy_xb).resize((8 * 64, 64), resample=Image.NEAREST)
Noisy X shape torch.Size([8, 3, 32, 32])
同样,探索在此处使用不同噪声调度和参数的效果。 此视频很好地解释了上面的一些数学,并对这些概念进行了很好的介绍。
步骤 4:定义模型
现在我们来到核心部分:模型本身。
大多数扩散模型使用作为 U-net 的某种变体的架构,我们将在此处使用它。
简而言之:
- 模型让输入图像通过几个 ResNet 层块,每个块将图像大小缩小 2 倍。
- 然后通过相同数量的块,再次放大图像。
- 存在跳跃连接,将降采样路径上的特征链接到上采样路径中的对应层。
此模型的一个关键特征是它预测与输入大小相同的图像,这正是我们在这里需要的。
Diffusers 为我们提供了一个方便的 UNet2DModel
类,它在 PyTorch 中创建了所需的架构。
让我们为我们想要的图像大小创建一个 U-net。请注意,down_block_types
对应于降采样块(上图中的绿色),而 up_block_types
是上采样块(上图中的红色)。
from diffusers import UNet2DModel
# Create a model
model = UNet2DModel(
sample_size=image_size, # the target image resolution
in_channels=3, # the number of input channels, 3 for RGB images
out_channels=3, # the number of output channels
layers_per_block=2, # how many ResNet layers to use per UNet block
block_out_channels=(64, 128, 128, 256), # More channels -> more parameters
down_block_types=(
"DownBlock2D", # a regular ResNet downsampling block
"DownBlock2D",
"AttnDownBlock2D", # a ResNet downsampling block with spatial self-attention
"AttnDownBlock2D",
),
up_block_types=(
"AttnUpBlock2D",
"AttnUpBlock2D", # a ResNet upsampling block with spatial self-attention
"UpBlock2D",
"UpBlock2D", # a regular ResNet upsampling block
),
)
model.to(device)
当处理更高分辨率的输入时,你可能希望使用更多降采样和上采样块,并将注意力层仅保留在最低分辨率(底部)层以减少内存使用。我们将在后面讨论如何进行实验以找到最适合你的用例的设置。
我们可以检查一下,通过一批数据和一些随机时间步长,是否会产生与输入数据形状相同的输出。
with torch.no_grad():
model_prediction = model(noisy_xb, timesteps).sample
model_prediction.shape
在下一节中,我们将看到如何训练此模型。
步骤 5:创建训练循环
现在开始训练!以下是 PyTorch 中典型的优化循环,我们逐批次遍历数据,并在每一步使用优化器更新模型的参数——在本例中是 AdamW 优化器,学习率为 0.0004。
对于每批数据,我们:
- 采样一些随机时间步长。
- 相应地对数据添加噪声。
- 将噪声数据通过模型。
- 使用均方误差作为我们的损失函数,将模型预测与目标(在本例中是噪声)进行比较。
- 通过
loss.backward()
和optimizer.step()
更新模型参数。
在此过程中,我们还会记录随时间的损失,以便稍后绘制。
注意:这段代码运行需要近 10 分钟——如果你赶时间,可以跳过这两个单元格并使用预训练模型。或者,你可以探索如何通过上面模型定义中减少每个层的通道数来加快速度。
官方 diffusers 训练示例 以更高分辨率对该数据集训练了一个更大的模型,它是关于更不简化的训练循环的样子很好的参考。
>>> # Set the noise scheduler
>>> noise_scheduler = DDPMScheduler(num_train_timesteps=1000, beta_schedule="squaredcos_cap_v2")
>>> # Training loop
>>> optimizer = torch.optim.AdamW(model.parameters(), lr=4e-4)
>>> losses = []
>>> for epoch in range(30):
... for step, batch in enumerate(train_dataloader):
... clean_images = batch["images"].to(device)
... # Sample noise to add to the images
... noise = torch.randn(clean_images.shape).to(clean_images.device)
... bs = clean_images.shape[0]
... # Sample a random timestep for each image
... timesteps = torch.randint(0, noise_scheduler.num_train_timesteps, (bs,), device=clean_images.device).long()
... # Add noise to the clean images according to the noise magnitude at each timestep
... noisy_images = noise_scheduler.add_noise(clean_images, noise, timesteps)
... # Get the model prediction
... noise_pred = model(noisy_images, timesteps, return_dict=False)[0]
... # Calculate the loss
... loss = F.mse_loss(noise_pred, noise)
... loss.backward(loss)
... losses.append(loss.item())
... # Update the model parameters with the optimizer
... optimizer.step()
... optimizer.zero_grad()
... if (epoch + 1) % 5 == 0:
... loss_last_epoch = sum(losses[-len(train_dataloader) :]) / len(train_dataloader)
... print(f"Epoch:{epoch+1}, loss: {loss_last_epoch}")
Epoch:5, loss: 0.16273280512541533 Epoch:10, loss: 0.11161588924005628 Epoch:15, loss: 0.10206522420048714 Epoch:20, loss: 0.08302505919709802 Epoch:25, loss: 0.07805309211835265 Epoch:30, loss: 0.07474562455900013
绘制损失,我们可以看到模型最初迅速改进,然后继续以更慢的速度变好(如果我们使用对数刻度,如右侧所示,则这更加明显)。
>>> fig, axs = plt.subplots(1, 2, figsize=(12, 4))
>>> axs[0].plot(losses)
>>> axs[1].plot(np.log(losses))
>>> plt.show()
作为运行上面训练代码的替代方案,你可以使用管道中的模型,如下所示:
# Uncomment to instead load the model I trained earlier:
# model = butterfly_pipeline.unet
步骤 6:生成图像
我们如何使用这个模型生成图像?
选项 1:创建管道:
from diffusers import DDPMPipeline
image_pipe = DDPMPipeline(unet=model, scheduler=noise_scheduler)
>>> pipeline_output = image_pipe()
>>> pipeline_output.images[0]
我们可以将管道保存到本地文件夹中,如下所示
image_pipe.save_pretrained("my_pipeline")
检查文件夹内容
>>> !ls my_pipeline/
model_index.json scheduler unet
scheduler
和 unet
子文件夹包含重新创建这些组件所需的所有内容。例如,在 unet
文件夹中,你会找到模型权重 (diffusion_pytorch_model.bin
) 以及一个指定 UNet 架构的配置文件。
>>> !ls my_pipeline/unet/
config.json diffusion_pytorch_model.bin
这些文件共同包含了重新创建管道所需的一切。你可以手动将它们上传到 Hub 与其他人共享管道,或者查看下一节中通过 API 执行此操作的代码。
选项 2:编写采样循环
如果你检查管道的 forward 方法,你就可以看到运行 image_pipe()
时发生了什么
# ??image_pipe.forward
我们从随机噪声开始,并从最嘈杂到最不嘈杂的调度器时间步长运行,每一步根据模型预测移除少量噪声
>>> # Random starting point (8 random images):
>>> sample = torch.randn(8, 3, 32, 32).to(device)
>>> for i, t in enumerate(noise_scheduler.timesteps):
... # Get model pred
... with torch.no_grad():
... residual = model(sample, t).sample
... # Update sample with step
... sample = noise_scheduler.step(residual, t, sample).prev_sample
>>> show_images(sample)
noise_scheduler.step()
函数执行了更新 sample
所需的数学运算。有许多采样方法 - 在下一个单元中,我们将看到如何更换不同的采样器来使用现有模型加快图像生成速度,并更多地讨论从扩散模型采样的理论。
步骤 7:将你的模型推送到 Hub
在上面的示例中,我们将管道保存到本地文件夹。为了将我们的模型推送到 Hub,我们需要模型存储库来推送我们的文件。我们将从我们想要赋予模型的模型 ID 中确定存储库名称(随意用你自己的选择替换 model_name
;它只需要包含你的用户名,这就是函数 get_full_repo_name()
做的)
from huggingface_hub import get_full_repo_name
model_name = "sd-class-butterflies-32"
hub_model_id = get_full_repo_name(model_name)
hub_model_id
接下来,在 🤗 Hub 上创建一个模型存储库并推送我们的模型
from huggingface_hub import HfApi, create_repo
create_repo(hub_model_id)
api = HfApi()
api.upload_folder(folder_path="my_pipeline/scheduler", path_in_repo="", repo_id=hub_model_id)
api.upload_folder(folder_path="my_pipeline/unet", path_in_repo="", repo_id=hub_model_id)
api.upload_file(
path_or_fileobj="my_pipeline/model_index.json",
path_in_repo="model_index.json",
repo_id=hub_model_id,
)
最后要做的是创建一个漂亮的模型卡,以便我们的蝴蝶生成器可以在 Hub 上轻松找到(随意扩展和编辑描述!)
from huggingface_hub import ModelCard
content = f"""
---
license: mit
tags:
- pytorch
- diffusers
- unconditional-image-generation
- diffusion-models-class
---
# Model Card for Unit 1 of the [Diffusion Models Class 🧨](https://github.com/huggingface/diffusion-models-class)
This model is a diffusion model for unconditional image generation of cute 🦋.
## Usage
```python
from diffusers import DDPMPipeline
pipeline = DDPMPipeline.from_pretrained('{hub_model_id}')
image = pipeline().images[0]
image
"""
card = ModelCard(content) card.push_to_hub(hub_model_id)
Now that the model is on the Hub, you can download it from anywhere by using the `from_pretrained()` method of the `DDPMPipeline` as follows"
```python
>>> from diffusers import DDPMPipeline
>>> image_pipe = DDPMPipeline.from_pretrained(hub_model_id)
>>> pipeline_output = image_pipe()
>>> pipeline_output.images[0]
太好了,它起作用了!
使用 🤗 Accelerate 扩展
此笔记本旨在用于学习目的,因此我尝试使代码尽可能简洁和干净。因此,我们省略了一些你可能在尝试对更多数据进行更大模型训练时想要的功能,例如多 GPU 支持、进度日志和示例图像、用于支持更大批次大小的梯度检查点、模型的自动上传等等。幸运的是,这些功能中的大多数都在示例训练脚本 这里 中可用。
你可以像这样下载文件
!wget https://github.com/huggingface/diffusers/raw/main/examples/unconditional_image_generation/train_unconditional.py
打开该文件,你将看到模型的定义位置以及可用的设置。我使用以下命令运行了脚本
# Let's give our new model a name for the Hub
model_name = "sd-class-butterflies-64"
hub_model_id = get_full_repo_name(model_name)
hub_model_id
!accelerate launch train_unconditional.py \
--dataset_name="huggan/smithsonian_butterflies_subset" \
--resolution=64 \
--output_dir={model_name} \
--train_batch_size=32 \
--num_epochs=50 \
--gradient_accumulation_steps=1 \
--learning_rate=1e-4 \
--lr_warmup_steps=500 \
--mixed_precision="no"
像以前一样,让我们将模型推送到 Hub 并创建一个漂亮的模型卡(随意根据你的需要进行编辑!)
create_repo(hub_model_id)
api = HfApi()
api.upload_folder(folder_path=f"{model_name}/scheduler", path_in_repo="", repo_id=hub_model_id)
api.upload_folder(folder_path=f"{model_name}/unet", path_in_repo="", repo_id=hub_model_id)
api.upload_file(
path_or_fileobj=f"{model_name}/model_index.json",
path_in_repo="model_index.json",
repo_id=hub_model_id,
)
content = f"""
---
license: mit
tags:
- pytorch
- diffusers
- unconditional-image-generation
- diffusion-models-class
---
# Model Card for Unit 1 of the [Diffusion Models Class 🧨](https://github.com/huggingface/diffusion-models-class)
This model is a diffusion model for unconditional image generation of cute 🦋.
## Usage
```python
from diffusers import DDPMPipeline
pipeline = DDPMPipeline.from_pretrained('{hub_model_id}')
image = pipeline().images[0]
image
"""
card = ModelCard(content) card.push_to_hub(hub_model_id)
About 45 minutes later, this is the result:
```python
>>> pipeline = DDPMPipeline.from_pretrained(hub_model_id).to(device)
>>> images = pipeline(batch_size=8).images
>>> make_grid(images)
练习:看看你是否可以在尽可能短的时间内找到能获得良好结果的训练/模型设置,并将你的发现与社区分享。深入研究脚本以查看你是否可以理解代码,并对任何看起来令人困惑的内容寻求澄清。
进一步探索的途径
希望这能让你体验到 🤗 Diffusers 库的功能!以下是一些可能的下一步
- 尝试在新的数据集上训练一个无条件扩散模型 - 如果你 自己创建一个,将会获得额外的加分。你可以在 Hub 上的 HugGan 组织 中找到一些用于此任务的优秀图像数据集。只要确保如果你不想等待很长时间来训练模型,就对它们进行降采样!
- 尝试使用 DreamBooth 创建你自己的定制 Stable Diffusion 管道,可以使用 此 Space 或 此笔记本
- 修改训练脚本以探索不同的 UNet 超参数(层数、通道数等)、不同的噪声调度等。
- 查看 从头开始的扩散模型 笔记本,了解我们在此单元中涵盖的核心思想的不同观点
祝你好运,敬请期待第二单元!