【图书推荐】《PyTorch深度学习与计算机视觉实践》-CSDN博客
《PyTorch深度学习与计算机视觉实践(人工智能技术丛书)》(王晓华)【摘要 书评 试读】- 京东图书
PyTorch深度学习算法与应用_夏天又到了的博客-CSDN博客
Diffusion Model是一类生成模型,它和VAE(Variational Autoencoder,变分自动编码器),GAN (Generative Adversarial Network,生成对抗网络)等生成网络不同的是,Diffusion Model在前向阶段对图像逐步施加噪声,直至图像被破坏变成完全的高斯噪声,然后在逆向阶段学习从高斯噪声还原为原始图像的过程。
深度学习去噪的Diffusion Model主要用于图像去噪,它是一种通过添加噪声并逐步去噪来提高图像质量的模型。该模型是通过扩散过程来实现的,包括前向扩散和反向扩散两个阶段。
在前向扩散阶段,模型将逐步向原始图像添加噪声,直到得到纯噪声的图像。这个过程可以看作对原始图像的“破坏”。
在反向扩散阶段,模型会从纯噪声的图像开始,逐步去除噪声,最终得到一个相对原始图像质量较好的版本。这个过程可以看作“重建”原始图像。
12.1.1 Diffusion Model的传播流程
Diffusion Model是一个对输入数据进行动态处理的过程。具体来说, 前向阶段在原始图像x0 上逐步增加噪声,每一步得到的图像xt 只和上一步的结果xt-1 相关,直至第T 步的图像xT 变为纯高斯噪声,如图12-1所示。
图12-1 加噪过程(x0 为原始图像)
逆向阶段则是不断去除噪声的过程,首先给定高斯噪声xT ,通过逐步去噪,直至将原图像x0 给恢复出来,如图12-2所示。
图12-2 去噪过程(xT 为全噪音图像)
模型训练完成后,只要给定高斯随机噪声,就可以生成一幅从未见过的图像。这里x0 为原始图像,xT 为全噪音图像,读者一定要牢记,后面会有公式讲解。
所谓的加噪声,就是基于目标图片计算一个(多维)高斯分布(每个像素点都有一个高斯分布,且均值就是这个像素点的值,方差是预先定义的),然后从这个多维分布中抽样一个数据出来,这个数据就是加噪之后的结果。显然,如果方差非常小,那么每个抽样得到的像素点的值和原本的像素点的值非常接近,也就是加了一个非常小的噪声。如果方差比较大,那么抽样结果就会和原本的结果差距较大。
同理,去噪声基于噪声的图片计算一个条件分布,希望从这个分布中抽样得到的是相比于噪声图片更加接近真实图片的稍微干净的图片。假设这样的条件分布是存在的,并且也是个高斯分布,那么只需要知道均值和方差就可以了。加噪声和去噪声如图12-3所示。
图12-3 加噪声和去噪声
但问题是这个均值和方差是无法直接计算的,所以用神经网络去学习近似这样一个高斯分布。高斯噪声是一种随机信号,也称为正态分布噪声。它的数学模型是基于高斯分布的概率密度函数,因此也被称为高斯分布噪声。
在实际应用中,高斯噪声通常是由于测量设备、传感器或电子元件等因素引起的随机误差所产生的。高斯噪声具有以下特点:
- 平均值为0:即高斯噪声随机变量的期望值为0。
- 对称性:高斯噪声在平均值处呈现对称分布。
- 方差决定波动的幅度:高斯噪声的波动幅度与方差成正比,方差越大,则波动幅度越大。
- 由于高斯噪声的统计特性十分稳定,因此在许多领域,如图像处理、信号处理、控制系统等得到了广泛使用。
总结起来Diffusion Model的处理过程是给一幅图片逐步加噪声直到变成纯粹的噪声,然后对噪声进行去噪,得到真实的图片。所谓的扩散模型就是让神经网络学习这个去除噪声的方法。
12.1.2 直接运行的DDPM的模型训练实战
本书配套资源中提供了简单的可运行DDPM框架,如图12-4所示。
图12-4 DDPM模型文件存档
其中的train.py文件是DDPM的训练文件,代码如下:
import torch
import get_dataset
import cv2
from tqdm import tqdm
import ddpmbatch_size = 48
dataloader = torch.utils.data.DataLoader(get_dataset.SamplerDataset(), batch_size=batch_size)#Unet作为生成模型从噪音生成图像
import unet
device = "cuda" if torch.cuda.is_available() else "cpu"
model = unet.Unet(dim=28,dim_mults=[1,2,4]).to(device)
optimizer = torch.optim.AdamW(model.parameters(), lr = 2e-4)epochs = 3
timesteps = 200
save_path = "./saver/ddpm_saver.pth"
model.load_state_dict(torch.load(save_path),strict=False)
for epoch in range(epochs):pbar = tqdm(dataloader, total=len(dataloader))#使用DataLoader进行迭代,获取每个批次的数据样本和标签for batch_sample, batch_label in pbar:optimizer.zero_grad()batch_size = batch_sample.size()[0]batch = batch_sample.to(device)optimizer.zero_grad()t = torch.randint(0, timesteps, (batch_size,), device=device).long()loss = ddpm.p_losses(model, batch, t, loss_type="huber")loss.backward()optimizer.step()pbar.set_description(f"epoch:{epoch + 1}, train_loss:{loss.item():.5f}")torch.save(model.state_dict(), save_path)
从训练数据中可以看到,Unet作为生成模型,直接从噪音数据中生成图像。Unet模型在第1章中已经介绍了,这里的整体架构在原有模型的基础上增加了注意力模块,使用了经典的ResNet架构,因而可以获得更好的生成效果。
ddpm.p_losses函数是DDPM中使用的损失函数,它通过计算生成的预测图像与噪音图像的L1距离(也就是曼哈顿距离Manhattan Distance)完成损失函数的计算。读者可以直接使用train.py开启训练过程,如图12-5所示。
图12-5 训练过程
根据不同的硬件设备情况,训练时间也会略有不同。这里设置了epoch的次数为20,读者可以等待训练完成,也可以在等待模型训练的同时阅读下一小节了解运行背后的原理。
而对于使用模型进行推理,读者可以直接使用相同目录下的predicate.py文件,只需要直接加载训练好的模型文件即可。代码如下:
import torch
import ddpm
import cv2
import unet#导入的依旧是一个Unet模型
device = "cuda" if torch.cuda.is_available() else "cpu"
model = unet.Unet(dim=28,dim_mults=[1,2,4]).to(device)#加载Unet模型存档
save_path = "./saver/ddpm_saver.pth"
model.load_state_dict(torch.load(save_path))#sample 25 images
bs = 25
#使用sample函数生成25个MNIST手写体图像
samples = ddpm.sample(model, image_size=28, batch_size=bs, channels=1)imgs = []
for i in range(bs):img = (samples[-1][i].reshape(28, 28, 1))imgs.append(img)#以矩阵的形式加载和展示图像
import numpy as np
blank_image = np.zeros((28*5, 28*5, 1)) + 1e-5
for i in range(5):for j in range(5):blank_image[i*28:(i+1)*28, j*28:(j+1)*28] = imgs[i*5+j]
cv2.imshow('Images', blank_image)
cv2.waitKey(0)
需要注意的是,模型加载的参数是针对Unet模型的参数。实际上也可以看到,Diffusion Model就是通过一个Unet模型对一组噪音进行“去噪”处理,从噪音中“生成”一个特定的图像。生成的结果如图12-6所示。
图12-6 生成的结果
尽管生成的结果略显模糊,但仍可明确地辨认出,模型确实基于所接收的训练数据成功地生成了一组随机手写体。这个成果无疑在一定程度上证明了模型训练的可行性。
读者可以自行尝试。