当前位置:   article > 正文

如何训练一个简单的stable diffusion模型(附详细注释)_stablediffusion模型训练

stablediffusion模型训练

注:代码来自https://github.com/darcula1993/diffusion-models-class-CN/blob/main/unit1/01_introduction_to_diffusers_CN.ipynb 

本文是本人学习后的的尝试以及注解

一、准备工作

  1. """
  2. 这行命令使用pip工具来安装或升级多个Python包。具体来说,它执行以下操作:
  3. -qq:这是pip的安静模式选项,它会减少输出信息,只显示关键信息,使安装过程更为简洁。
  4. -U:这是pip的升级选项,它指示pip升级已经安装的包到最新版本(如果存在新版本)。
  5. 接下来,列出了要安装或升级的包:
  6. diffusers:一个Python包
  7. datasets:Hugging Face Transformers库的一部分,用于提供和管理各种自然语言处理(NLP)数据集的工具。
  8. transformers:Hugging Face Transformers库,提供了预训练的深度学习模型,用于自然语言处理和文本生成任务。
  9. accelerate:Hugging Face库的一部分,用于加速深度学习模型的训练和推理。
  10. ftfy:一个用于处理Unicode文本的Python库,用于修复和清理不规范的Unicode文本。
  11. pyarrow:正如前面所提到的,pyarrow是一个用于高效处理大规模数据集的Python库,支持列式存储和跨语言互操作性。
  12. """
  13. %pip install -qq -U diffusers datasets transformers accelerate ftfy pyarrow
  1. # 登录hugging face
  2. from huggingface_hub import notebook_login
  3. notebook_login()

显示下图则登陆成功:

  1. # 安装 Git LFS 来上传模型检查点:
  2. %%capture
  3. !sudo apt -qq install git-lfs
  4. !git config --global credential.helper store
  1. # 导入将要使用的库,并定义一些方便函数,稍后将会使用这些函数
  2. import numpy as np
  3. import torch
  4. import torch.nn.functional as F
  5. from matplotlib import pyplot as plt
  6. from PIL import Image
  7. def show_images(x):
  8. """Given a batch of images x, make a grid and convert to PIL"""
  9. '''
  10. 输入参数:x,一个批量的图像数据(通常是PyTorch张量)。
  11. 功能:将输入的图像数据从范围(-1, 1)映射到(0, 1),然后将这些图像排列成一个网格,并将网格转换为PIL图像。
  12. 返回值:返回一个PIL图像,其中包含了排列好的输入图像网格。
  13. '''
  14. x = x * 0.5 + 0.5 # Map from (-1, 1) back to (0, 1)
  15. grid = torchvision.utils.make_grid(x)
  16. grid_im = grid.detach().cpu().permute(1, 2, 0).clip(0, 1) * 255
  17. grid_im = Image.fromarray(np.array(grid_im).astype(np.uint8))
  18. return grid_im
  19. def make_grid(images, size=64):
  20. """Given a list of PIL images, stack them together into a line for easy viewing"""
  21. '''
  22. 输入参数:images,一个包含多个PIL图像的列表,以及一个可选的size参数,用于指定图像的大小。
  23. 功能:将多个PIL图像按照指定的大小堆叠在一行上,以便于查看。
  24. 返回值:返回一个新的PIL图像,其中包含了堆叠在一行上的输入图像。
  25. '''
  26. output_im = Image.new("RGB", (size * len(images), size))
  27. for i, im in enumerate(images):
  28. output_im.paste(im.resize((size, size)), (i * size, 0))
  29. return output_im
  30. # Mac users may need device = 'mps' (untested)
  31. device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

二、下载训练数据集

  1. # 下载一个来自 Hugging Face Hub 的图像集。具体来说,是个 1000 张蝴蝶图像收藏集
  2. import torchvision # 导入PyTorch的torchvision库
  3. from datasets import load_dataset # 导入数据集
  4. from torchvision import transforms # 导入PyTorch的transforms模块
  5. # 使用load_dataset函数加载名为"huggan/smithsonian_butterflies_subset"的数据集中的训练集数据
  6. dataset = load_dataset("huggan/smithsonian_butterflies_subset", split="train")
  7. # 或者从本地文件夹加载图像数据
  8. # dataset = load_dataset("imagefolder", data_dir="path/to/folder")
  9. # 指定图像大小为32x32像素
  10. image_size = 32
  11. # 如果GPU内存不足,可以降低批次大小
  12. batch_size = 64
  13. # 定义数据增强操作
  14. preprocess = transforms.Compose(
  15. [
  16. transforms.Resize((image_size, image_size)), # 调整图像大小
  17. transforms.RandomHorizontalFlip(), # 随机水平翻转(数据增强)
  18. transforms.ToTensor(), # 将图像转换为张量(数值范围从0到1)
  19. transforms.Normalize([0.5], [0.5]), # 将图像像素值归一化到(-1, 1)的范围
  20. ]
  21. )
  22. # 定义一个用于对数据进行转换的函数
  23. def transform(examples):
  24. images = [preprocess(image.convert("RGB")) for image in examples["image"]]
  25. return {"images": images}
  26. # 将数据集的转换函数设置为上述定义的transform函数
  27. dataset.set_transform(transform)
  28. # 创建一个数据加载器,用于以批次方式提供转换后的图像数据
  29. train_dataloader = torch.utils.data.DataLoader(
  30. dataset, batch_size=batch_size, shuffle=True # 使用指定的批次大小和随机打乱数据
  31. )
  1. # 我们可以从中取出一批图像数据来看一看他们是什么样子:
  2. xb = next(iter(train_dataloader))["images"].to(device)[:8]
  3. print("X shape:", xb.shape)
  4. show_images(xb).resize((8 * 64, 64), resample=Image.NEAREST)

结果如下图所示:

三、定义管理器

我们的训练计划是,取出这些输入图片然后对它们增添噪声,在这之后把带噪的图片送入模型。在推理阶段,我们将用模型的预测值来不断迭代去除这些噪点。在diffusers中,这两个步骤都是由 管理器(调度器) 来处理的。噪声管理器决定在不同的迭代周期时分别加入多少噪声。

  1. from diffusers import DDPMScheduler
  2. noise_scheduler = DDPMScheduler(num_train_timesteps=1000)
  1. # 绘图查看输入 (x) 与噪声是如何在不同迭代周期中量化和叠加的
  2. plt.plot(noise_scheduler.alphas_cumprod.cpu() ** 0.5, label=r"${\sqrt{\bar{\alpha}_t}}$")
  3. plt.plot((1 - noise_scheduler.alphas_cumprod.cpu()) ** 0.5, label=r"$\sqrt{(1 - \bar{\alpha}_t)}$")
  4. plt.legend(fontsize="x-large");

  1. # 使用noise_scheduler.add_noise功能来添加不同程度的噪声
  2. timesteps = torch.linspace(0, 999, 8).long().to(device)
  3. noise = torch.randn_like(xb)
  4. noisy_xb = noise_scheduler.add_noise(xb, noise, timesteps)
  5. print("Noisy X shape", noisy_xb.shape)
  6. show_images(noisy_xb).resize((8 * 64, 64), resample=Image.NEAREST)

四、定义、训练模型

  1. # 定义模型
  2. from diffusers import UNet2DModel
  3. # Create a model
  4. model = UNet2DModel(
  5. sample_size=image_size, # the target image resolution
  6. in_channels=3, # the number of input channels, 3 for RGB images
  7. out_channels=3, # the number of output channels
  8. layers_per_block=2, # how many ResNet layers to use per UNet block
  9. block_out_channels=(64, 128, 128, 256), # More channels -> more parameters
  10. down_block_types=(
  11. "DownBlock2D", # a regular ResNet downsampling block
  12. "DownBlock2D",
  13. "AttnDownBlock2D", # a ResNet downsampling block with spatial self-attention
  14. "AttnDownBlock2D",
  15. ),
  16. up_block_types=(
  17. "AttnUpBlock2D",
  18. "AttnUpBlock2D", # a ResNet upsampling block with spatial self-attention
  19. "UpBlock2D",
  20. "UpBlock2D", # a regular ResNet upsampling block
  21. ),
  22. )
  23. model.to(device);

开始训练模型 

  1. # 创建了一个对象,用于调度噪声的添加。参数指定了训练的总步数和噪声的变化规律
  2. noise_scheduler = DDPMScheduler(
  3. num_train_timesteps=1000, beta_schedule="squaredcos_cap_v2"
  4. )
  5. # 创建了一个AdamW优化器,用于更新模型的参数。返回模型的可训练参数,参数指定了学习率。
  6. optimizer = torch.optim.AdamW(model.parameters(), lr=4e-4)
  7. # 创建一个空列表,用于存储每个步骤的损失值。
  8. losses = []
  9. for epoch in range(30):
  10. for step, batch in enumerate(train_dataloader):
  11. # 从批次中获取干净的图像数据,并将其移动到指定的设备(例如GPU)上进行计算。
  12. clean_images = batch["images"].to(device)
  13. # 生成与干净图像相同形状的噪声张量,该噪声将被添加到图像中。
  14. noise = torch.randn(clean_images.shape).to(clean_images.device)
  15. # 获取批次的大小。
  16. bs = clean_images.shape[0]
  17. # 为每个图像随机生成一个时间步长,该时间步长将用于确定噪声的变化程度。
  18. timesteps = torch.randint(
  19. 0, noise_scheduler.num_train_timesteps, (bs,), device=clean_images.device
  20. ).long()
  21. # 根据噪声调度器中的每个噪声幅度和时间步长,将噪声添加到干净图像中,生成带有噪声的图像。
  22. noisy_images = noise_scheduler.add_noise(clean_images, noise, timesteps)
  23. # 使用模型对带有噪声的图像进行预测,得到去噪后的图像。
  24. noise_pred = model(noisy_images, timesteps, return_dict=False)[0]
  25. # 计算预测图像与真实噪声之间的均方误差损失,计算损失相对于模型参数的梯度,并将当前步骤的损失添加到列表中。
  26. loss = F.mse_loss(noise_pred, noise)
  27. loss.backward(loss)
  28. losses.append(loss.item())
  29. # 使用优化器更新模型的参数,并将梯度置零。
  30. optimizer.step()
  31. optimizer.zero_grad()
  32. # 每隔5个epoch,计算最近一个epoch的平均损失,并打印出来。
  33. if (epoch + 1) % 5 == 0:
  34. loss_last_epoch = sum(losses[-len(train_dataloader) :]) / len(train_dataloader)
  35. print(f"Epoch:{epoch+1}, loss: {loss_last_epoch}")

  1. # 绘制 loss 曲线,我们能看到模型在一开始快速的收敛,接下来以一个较慢的速度持续优化(我们用右边 log 坐标轴的视图可以看的更清楚):
  2. fig, axs = plt.subplots(1, 2, figsize=(12, 4))
  3. axs[0].plot(losses)
  4. axs[1].plot(np.log(losses))
  5. plt.show()

五、生成图像 

下面开始生成图像 

  1. # 方法 1:建立一个管道:
  2. from diffusers import DDPMPipeline
  3. image_pipe = DDPMPipeline(unet=model, scheduler=noise_scheduler)
  4. pipeline_output = image_pipe()
  5. pipeline_output.images[0]

  1. # 我们可以在本地文件夹这样保存一个管道:
  2. image_pipe.save_pretrained("my_pipeline")
  3. # 检查文件夹的内容:
  4. !ls my_pipeline/

 这里scheduler与unet子文件夹中包含了生成图像所需的全部组件。比如,在unet文件中能看到模型参数 (diffusion_pytorch_model.bin) 与描述模型结构的配置文件。

  1. # 方法 2:写一个取样循环
  2. # 从随机噪声开始,遍历管理器的迭代周期来看从最嘈杂直到最微小的噪声变化,基于模型的预测一步步减少一些噪声:
  3. # Random starting point (8 random images):
  4. sample = torch.randn(8, 3, 32, 32).to(device)
  5. for i, t in enumerate(noise_scheduler.timesteps):
  6. # Get model pred
  7. with torch.no_grad():
  8. residual = model(sample, t).sample
  9. # Update sample with step
  10. sample = noise_scheduler.step(residual, t, sample).prev_sample
  11. # noise_scheduler.step () 函数相应做了 sample(取样)时的数学运算。
  12. show_images(sample)

 六、将模型上传到Hugging Face

在上面的例子中我们把管道保存在了本地。 把模型 push 到 hub 上,我们会需要建立模型和相应文件的仓库名。 我们根据你的选择(模型 ID)来决定仓库的名字(大胆的去替换掉model_name吧;需要包含你的用户名,get_full_repo_name ()会帮你做到):

  1. from huggingface_hub import get_full_repo_name
  2. model_name = "sd-class-butterflies-32"
  3. hub_model_id = get_full_repo_name(model_name)
  4. hub_model_id
  1. # 然后,在
    声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/118294
    推荐阅读
    相关标签