Pytorch复现经典扩散模型DDPM、DDIM、PLMS及分布式训练应用
0 前言
当前,生成式人工智能(AIGC)已被越来越广泛应用在工业、动漫业、设计业等诸多场景。我们都知道现阶段主流的生成模型如生成对抗网络(GAN)、自分编码器(VAE)、流模型(Flow-based Models)和扩散模型(Diffusion Models)。而扩散模型中还分为概率扩散模型,噪声条件评分网络和去噪概率模型。去噪概率模型中较为经典的就是DDPM(Denoising Diffusion Probabilistic Models)。
本文章和GitHub仓库,如有问题请在此仓库提交issue,如果你认为我的项目有意思请给我点一颗⭐⭐⭐Star⭐⭐⭐吧。本文持续更新,以GitHub为准嗷~
代码最新更新仓库:https://github.com/chairc/Integrated-Design-Diffusion-Model
代码最新问题总结:https://github.com/chairc/Integrated-Design-Diffusion-Model/issues/9
访问不了问题总结可以点击CSDN链接:https://blog.csdn.net/qq_43226466/article/details/143199474
文章最新更新日期:2024年10月24日09:19:40

1 简单原理
原理应该挺多的,具体参考这个博客
2 项目结构设计
整体结构
1 |
|
datasets
用于存放数据集文件,自动划分标签torchvision.datasets.ImageFolder(args.dataset_path, transform=transforms)。若数据集为: dataset_path/class_1/image_1.jpg dataset_path/class_1/image_2.jpg ... dataset_path/class_2/image_1.jpg dataset_path/class_2/image_2.jpg ...
其中,dataset_path是数据集所在的根目录,class_1, class_2等是数据集中的不同类别,每个类别下包含若干张图像文件。使用ImageFolder类可以方便地加载这种文件夹结构的图像数据集,并自动为每个图像分配相应的标签。可以通过传递dataset_path参数指定数据集所在的根目录,并通过其他可选参数进行图像预处理、标签转换等操作。
model
是存放模型的文件夹,UNet模型和采样器模型均在其中。
results
是存放输出结果的文件夹,包括tensorboard日志、绘图和pt模型文件。
test
是进行单元测试的文件夹。
tools
是训练、生成等运行文件。
utils
是各种工具文件,例如学习率、数据加载、图像绘制与保存等。
weight
是存放预训练模型或训练较好的权重文件。
3 代码实现
代码仅为部分核心代码,代码完整版在github。
注:所有的代码及注释均为英文,以下代码为源代码的中文版本。
3.1 训练设计
扩散模型是一种对于显卡要求极高的模型。对于如何解决训练速度问题,我们使用了多机多卡训练方式,换言之就是分布式训练(未进行模型分布式,仅为数据分布式)。
3.1.1 分布式训练
分布式训练中,一般分为主线程和其他线程。对于我们这个应用,主线程的作用是广播整个训练中的参数、指数、资源、写入保存等操作。
1 |
|
如上图所示,在数据集初始化方面,首先DistributedSampler类将数据集读入并返回一个Sampler类,该采样器是将数据加载限制为数据集子集,换言之就是显卡平分数据集。例如有1000张图,2张GPU,那么每张GPU中的500张图片作为数据集。
1 |
|
在train.py
启动线程,训练器会自动根据当前情况判断存在的设备数量,从而进行其它线程的开启。每个进程都会有一个rank,也就是当前的设备编号。具体实现如下:
1 |
|
1 |
|
3.1.2 普通训练
均为正常训练方法,不开启多线程,这里不做过多讲解。
3.2 模型基类
因为DDPM与DDIM在方法实现上相近,DDIM相较于DDPM仅在采样过程有所改变(只是增加了跳步方案,如果跳步设置为noise_steps的个数即是DDPM),所以我们在这里定义一个扩散模型基类BaseDiffusion,方便DDPM和DDIM继承相同方法与变量。
1 |
|
3.3 DDPM类
1 |
|
3.4 DDIM类(改进DDPM采样器)
实验表明,DDPM的每次去噪加噪过程都是从初始到结束,完成N次。而这种方法尽管生成的效果非常出色,但是带来的问题是采样时间过长,训练速度慢。为了解决该问题,跳步方法sample_steps被设计出来。
1 |
|
4 生成结果
我们在以下4个数据集做了训练,采样器为DDPM
,图片尺寸均为64*64
,分别是cifar10
,NEUDET
,NRSD-MN
和Animate Face
。结果如下图所示:
cifar10
NEUDET
NRSD
Animate Face(整活生成)
同时,我们利用生成的64× 64的模型生成了160×160的NEU-DET图像(显存占用21GB)
5 项目使用流程与参数设计
该部分为GitHub项目的具体使用流程和使用参数设计,详情请移步README.md
5.1 环境检查
首先,你需要检查当前Anaconda或Miniconda中的环境是否符合本项目运行。
5.2 训练
注意
本自README的训练GPU环境如下:使用具有6GB显存的NVIDIA RTX 3060显卡、具有11GB显存的NVIDIA RTX 2080Ti显卡和具有24GB(总计48GB,分布式训练)显存的NVIDIA RTX 6000(×2)显卡对模型进行训练和测试。上述GPU均可正常训练。
5.2.1 开始你的第一个训练(以cifar10为例,模式单卡,有条件训练)
导入数据集
首先,将数据集上传至目标文件夹
datasets
中。上传后文件夹格式(例如:cifar10文件夹下存放着所有类别;class0文件夹下存储着class0这个类别的所有图片)如下方列表所示:1
2
3
4
5
6
7
8
9
10
11
12datasets
└── cifar10
├── class0
├── class1
├── class2
├── class3
├── class4
├── class5
├── class6
├── class7
├── class8
└── class9此时你的训练前准备已经完毕。
设置训练参数
打开
train.py
文件,修改if __name__ == "__main__":
中的parser
参数;设置
--conditional
参数为True
,因为是多类别训练,所以需要开启,单类别可以不开启也可以开启;设置
--run_name
参数为你想创建的文件名称,例如cifar_exp1
;设置
--dataset_path
参数为/你的/本地/或/远程服务器/文件/地址/datasets/cifar10
;设置
--result_path
参数为/你的/本地/或/远程服务器/文件/地址/results
;设置
--num_classes
参数为10
,这是你的类别总数);设置更多参数(自定义),如果报
CUDA out of memory
错误,将--batch_size
、--num_workers
调小;在自定义参数中,你可以设置不同的
--sample
例如ddpm
或ddim
,设置不同的训练网络--network
例如unet
或cspdarkunet
。当然激活函数--act
,优化器--optim
,半精度训练--fp16
,学习率方法--lr_func
等参数也都是可以自定义设置的。详细命令可参考训练参数。
等待训练过程
点击
run
运行后,项目会在results
文件夹中生成cifar_exp1
文件夹,该文件夹中会保存训练日志文件、模型训练文件、模型EMA文件、模型优化器文件、训练的所有最后一次保存的文件和评估后生成的图片。查看结果
找到
results/cifar_exp1
文件夹即可查看训练结果。
↓↓↓↓↓↓↓↓↓↓下方为多种训练方式、训练详细参数讲解↓↓↓↓↓↓↓↓↓↓ ### 5.2.2 普通训练
以
landscape
数据集为例,将数据集文件放入datasets
文件夹中,该数据集的总路径如下/your/path/datasets/landscape
,图片存放在/your/path/datasets/landscape/images
(是的你需要把图片放到文件夹下面,不然util
中的ImageFolder
会报找不到错误),数据集图片路径如下/your/path/datasets/landscape/images/*.jpg
打开
train.py
文件,找到--dataset_path
参数,将参数中的路径修改为数据集的总路径,例如/your/path/datasets/landscape
设置必要参数,例如
--sample
,--conditional
,--run_name
,--epochs
,--batch_size
,--image_size
,--result_path
等参数,若不设置参数则使用默认设置。我们有两种参数设置方法,其一是直接对train.py
文件if __name__ == "__main__":
中的parser
进行设置(我们推荐这种方式);其二是在控制台在/your/path/Defect-Diffiusion-Model/tools
路径下输入以下命令:有条件训练命令
1
python train.py --sample ddpm --conditional --run_name df --epochs 300 --batch_size 16 --image_size 64 --num_classes 10 --dataset_path /your/dataset/path --result_path /your/save/path
无条件训练命令
1
python train.py --sample ddpm --run_name df --epochs 300 --batch_size 16 --image_size 64 --dataset_path /your/dataset/path --result_path /your/save/path
等待训练即可
若因异常原因中断训练,我们可以在
train.py
文件,首先将--resume
设置为True
,其次设置异常中断的迭代编号,再写入该次训练的所在文件夹(run_name),最后运行文件即可。也可以使用如下命令进行恢复:有条件恢复训练命令
1
2# 此处为输入--start_epoch参数,使用当前编号权重
python train.py --resume --start_epoch 10 --sample ddpm --conditional --run_name df --epochs 300 --batch_size 16 --image_size 64 --num_classes 10 --dataset_path /your/dataset/path --result_path /your/save/path1
2# 此处为不输入--start_epoch参数,默认使用last权重
python train.py --resume --sample ddpm --conditional --run_name df --epochs 300 --batch_size 16 --image_size 64 --num_classes 10 --dataset_path /your/dataset/path --result_path /your/save/path无条件恢复训练命令
1
python train.py --resume --start_epoch 10 --sample ddpm --run_name df --epochs 300 --batch_size 16 --image_size 64 --dataset_path /your/dataset/path --result_path /your/save/path
1
2# 此处为不输入--start_epoch参数,默认使用last权重
python train.py --resume --sample ddpm --run_name df --epochs 300 --batch_size 16 --image_size 64 --dataset_path /your/dataset/path --result_path /your/save/path预训练模型在每次大版本Release中发布,请留意。预训练模型使用方法如下,首先将对应
network
、image_size
、act
等相同参数的模型下到本地任意文件夹下。直接调整train.py
中--pretrain
和--pretrain_path
即可。也可以使用如下命令进行预训练:使用有条件预训练模型训练命令
1
python train.py --pretrain --pretrain_path /your/pretrain/path/model.pt --sample ddpm --conditional --run_name df --epochs 300 --batch_size 16 --image_size 64 --dataset_path /your/dataset/path --result_path /your/save/path
使用无条件预训练模型训练命令
1
python train.py --pretrain --pretrain_path /your/pretrain/path/model.pt --sample ddpm --run_name df --epochs 300 --batch_size 16 --image_size 64 --dataset_path /your/dataset/path --result_path /your/save/path
5.2.3 分布式训练
基本配置与普通训练相似,值得注意的是开启分布式训练需要将
--distributed
设置为True
。为了防止随意设置分布式训练,我们为开启分布式训练设置了几个基本条件,例如args.distributed
、torch.cuda.device_count() > 1
和torch.cuda.is_available()
。设置必要的参数,例如
--main_gpu
和--world_size
。--main_gpu
通常设置为主要GPU,例如做验证、做测试或保存权重,我们仅在单卡中运行即可。而world_size
的值会与实际使用的GPU数量或分布式节点数量相对应。我们有两种参数设置方法,其一是直接对
train.py
文件if __name__ == "__main__":
中的parser
进行设置;其二是在控制台在/your/path/Defect-Diffiusion-Model/tools
路径下输入以下命令:
有条件训练命令
1
python train.py --sample ddpm --conditional --run_name df --epochs 300 --batch_size 16 --image_size 64 --num_classes 10 --dataset_path /your/dataset/path --result_path /your/save/path --distributed --main_gpu 0 --world_size 2
无条件训练命令
1
python train.py --sample ddpm --run_name df --epochs 300 --batch_size 16 --image_size 64 --dataset_path /your/dataset/path --result_path /your/save/path --distributed --main_gpu 0 --world_size 2
- 等待训练即可,中断恢复同基本训练一致。

参数讲解
参数名称 | 条件参数 | 参数使用方法 | 参数类型 | 参数解释 |
---|---|---|---|---|
--seed | 初始化种子 | int | 设置初始化种子,可复现网络生成的图片 | |
--conditional | 开启条件训练 | bool | 若开启可修改自定义配置,例如修改类别、classifier-free guidance插值权重 | |
--sample | 采样方式 | str | 设置采样器类别,当前支持ddpm,ddim | |
--network | 训练网络 | str | 设置训练网络,当前支持UNet,CSPDarkUNet | |
--run_name | 文件名称 | str | 初始化模型的文件名称,用于设置保存信息 | |
--epochs | 总迭代次数 | int | 训练总迭代次数 | |
--batch_size | 训练批次 | int | 训练批次大小 | |
--num_workers | 加载进程数量 | int | 用于数据加载的子进程数量,大量占用CPU和内存,但可以加快训练速度 | |
--image_size | 输入图像大小 | int | 输入图像大小,自适应输入输出尺寸 | |
--dataset_path | 数据集路径 | str | 有条件数据集,例如cifar10,每个类别一个文件夹,路径为主文件夹;无条件数据集,所有图放在一个文件夹,路径为图片文件夹 | |
--amp | 混合精度训练 | bool | 开启混合精度训练,有效减少显存使用,但无法保证训练精度和训练结果 | |
--optim | 优化器 | str | 优化器选择,目前支持adam和adamw | |
--act | 激活函数 | str | 激活函数选择,目前支持gelu、silu、relu、relu6和lrelu | |
--lr | 学习率 | float | 初始化学习率 | |
--lr_func | 学习率方法 | str | 设置学习率方法,当前支持linear、cosine和warmup_cosine | |
--result_path | 保存路径 | str | 保存路径 | |
--save_model_interval | 是否在训练中储存 | bool | 是否在训练中储存,根据可视化生成样本信息筛选模型,如果为False,则只保存最后一个模型 | |
--save_model_interval_epochs | 保存模型周期 | int | 保存模型间隔并每 X 周期保存一个模型 | |
--start_model_interval | 设置开始每次训练存储编号 | int | 设置开始每次训练存储的epoch编号,该设置可节约磁盘空间,若不设置默认-1,若设置则从第epoch时开始保存每次训练pt文件,需要与--save_model_interval同时开启 | |
--vis | 可视化数据集信息 | bool | 打开可视化数据集信息,根据可视化生成样本信息筛选模型 | |
--num_vis | 生成的可视化图像数量 | int | 生成的可视化图像数量。如果不填写,则默认生成图片个数为数据集类别的个数 | |
--image_format | 生成图片格式 | str | 在训练中生成图片格式,默认为png | |
--noise_schedule | 加噪方法 | str | 该方法是模型噪声添加方法 | |
--resume | 中断恢复训练 | bool | 恢复训练将设置为“True”。注意:设置异常中断的epoch编号若在--start_model_interval参数条件外,则不生效。例如开始保存模型时间为100,中断编号为50,由于我们没有保存模型,所以无法设置任意加载epoch点。每次训练我们都会保存xxx_last.pt文件,所以我们需要使用最后一次保存的模型进行中断训练 | |
--start_epoch | 中断迭代编号 | int | 设置异常中断的epoch编号,模型会自动加载当前编号的检查点 | |
--pretrain | 预训练模型训练 | bool | 设置是否启用加载预训练模型训练 | |
--pretrain_path | 预训练模型路径 | str | 预训练模型加载地址 | |
--use_gpu | 设置运行指定的GPU | int | 一般训练中设置指定的运行GPU,输入为GPU的编号 | |
--distributed | 分布式训练 | bool | 开启分布式训练 | |
--main_gpu | 分布式训练主显卡 | int | 设置分布式中主显卡 | |
--world_size | 分布式训练的节点等级 | int | 分布式训练的节点等级, world_size的值会与实际使用的GPU数量或分布式节点数量相对应 | |
--num_classes | 是 | 类别个数 | int | 类别个数,用于区分类别 |
--cfg_scale | 是 | classifier-free guidance插值权重 | int | classifier-free guidance插值权重,用户更好生成模型效果 |
5.3 生成
打开
generate.py
文件,找到--weight_path
参数,将参数中的路径修改为模型权重路径,例如/your/path/weight/model.pt
设置必要参数,例如
--conditional
,--generate_name
,--num_images
,--num_classes
,--class_name
,--image_size
,--result_path
等参数,若不设置参数则使用默认设置。我们有两种参数设置方法,其一是直接对generate.py
文件if __name__ == "__main__":
中的parser
进行设置;其二是在控制台在/your/path/Defect-Diffiusion-Model/tools
路径下输入以下命令:有条件生成命令(1.1.1版本以上)
1
python generate.py --generate_name df --num_images 8 --class_name 0 --image_size 64 --weight_path /your/path/weight/model.pt --sample ddpm
无条件生成命令(1.1.1版本以上)
1
python generate.py --generate_name df --num_images 8 --image_size 64 --weight_path /your/path/weight/model.pt --sample ddpm
有条件生成命令(1.1.1版本及以下)
1
python generate.py --conditional --generate_name df --num_images 8 --num_classes 10 --class_name 0 --image_size 64 --weight_path /your/path/weight/model.pt --sample ddpm --network unet --act gelu
无条件生成命令(1.1.1版本及以下)
1
python generate.py --generate_name df --num_images 8 --image_size 64 --weight_path /your/path/weight/model.pt --sample ddpm --network unet --act gelu
等待生成即可
参数讲解
参数名称 | 条件参数 | 参数使用方法 | 参数类型 | 参数解释 |
---|---|---|---|---|
--conditional | 开启条件生成 | bool | 若开启可修改自定义配置,例如修改类别、classifier-free guidance插值权重 | |
--generate_name | 文件名称 | str | 初始化模型的文件名称,用于设置保存信息 | |
--image_size | 输入图像大小 | int | 输入图像大小,自适应输入输出尺寸。如果输入为-1并且开启条件生成为真,则模型为每类输出一张图片 | |
--image_format | 生成图片格式 | str | 生成图片格式,jpg/png/jpeg等。推荐使用png获取更好的生产质量 | |
--num_images | 生成图片个数 | int | 单次生成图片个数 | |
--weight_path | 权重路径 | str | 模型权重路径,网络生成需要加载文件 | |
--result_path | 保存路径 | str | 保存路径 | |
--sample | 采样方式 | str | 设置采样器类别,当前支持ddpm,ddim(1.1.1版本后的模型可不用设置) | |
--network | 训练网络 | str | 设置训练网络,当前支持UNet,CSPDarkUNet(1.1.1版本后的模型可不用设置) | |
--act | 激活函数 | str | 激活函数选择,目前支持gelu、silu、relu、relu6和lrelu。如果不选择,会产生马赛克现象(1.1.1版本后的模型可不用设置) | |
--num_classes | 是 | 类别个数 | int | 类别个数,用于区分类别(1.1.1版本后的模型可不用设置) |
--class_name | 是 | 类别名称 | int | 类别序号,用于对指定类别生成。如果输入为-1,则模型为每类输出一张图片 |
--cfg_scale | 是 | classifier-free guidance插值权重 | int | classifier-free guidance插值权重,用户更好生成模型效果 |
6 当前已完成工作
- 新增cosine学习率优化(2023-07-31)
- 使用效果更优的U-Net网络模型(2023-11-09)
- 更大尺寸的生成图像(2023-11-09)
- 多卡分布式训练(2023-07-15)
- 云服务器快速部署和接口(2023-08-28)
- 增加DDIM采样方法(2023-08-03)
- 支持其它图像生成(2023-09-16)
-
低分辨率生成图像进行超分辨率增强[
超分模型效果待定](2024-02-18) - 重构model整体结构(2023-12-06)
- 编写可视化webui界面(2024-01-23)
- 增加PLMS采样方法(2024-03-12)
- 增加FID方法验证图像质量(2024-05-06)
- 增加生成图像Socket和网站服务部署(2024-11-13)
7 下一步计划
- 使用Latent方式降低显存消耗
- 增加Docker部署
- 增加PSNR和SSIM方法验证超分图像质
如有任何问题,请到Github提交issue或联系我email:chenyu1998424@gmail.com