文章目录
- 1 DDPM(UC Berkeley, 2020)
- 1.1 如何使用DDPM生成图片
- 1.2 如何训练网络
- 1.3 模型原理
- 2 VAE:Auto-Encoding Variational Bayes(2022,Kingma)
- 2.1 如何利用VAE进行图像增广
- 2.2 如何训练VAE网络
- 2.3 VAE原理
- 2.3.1 Auto-Encoder
- 2.3.2 VAE编码器
- 2.3.3 VAE解码器
- 3 Stable Diffusion(2022, CompVis & RunwayML)
- 3.1 Stable diffusion可以干什么?
- 3.2 Stable diffusion解决什么问题?
- 3.3 训练过程
- 4 ControlNet(2023, Stanford)
- 4.1 网络结构
- 4.2 总结
- 5 LoRA:一种低成本的大模型微调方案(2021,微软)
- 5.1 LoRA要解决什么问题?
- 5.2 实际的应用方式
- 5.3 总结
1 DDPM(UC Berkeley, 2020)
DDPM:Denoising Diffusion Probabilistic Models
-
Paper:https://arxiv.org/abs/2006.11239
-
详细公式推导:https://arxiv.org/pdf/2208.11970.pdf
-
博客:
- https://juejin.cn/post/7215640327633748027
- https://zhuanlan.zhihu.com/p/563661713
- https://hf-mirror.com/blog/annotated-diffusion
生成模型的4种类型:
Source: Lilian_Weng
扩散模型中最重要的思想根基是马尔可夫链,它的一个关键性质是平稳性。即如果一个概率随时间变化,那么再马尔可夫链的作用下,它会趋向于某种平稳分布,时间越长,分布越平稳。
扩散模型包括两个过程:前向过程(forward process)和反向过程(reverse process),其中前向过程又称为扩散过程(diffusion process),如下图所示。无论是前向过程还是反向过程都是一个参数化的马尔可夫链(Markov chain),其中反向过程可以用来生成数据,这里我们将通过变分推断来进行建模和求解。
- 前向过程(扩散过程): 逐渐将高斯噪声添加到图像中,直到得到一个纯噪声的图像;
- 反向过程:从纯噪声图像中逐渐对其进行去噪,直到得到真实的图像。
1.1 如何使用DDPM生成图片
参考代码:https://github.com/zoubohao/DenoisingDiffusionProbabilityModel-ddpm-/blob/main/Diffusion/Diffusion.py
DDPM使用U-Net作为图像生成网络,并在中间层添加了Attention模块,用于将time embedding与image embedding进行计算,其流程如下:
- Step1:随机生成一组噪声,作为初始图像xT,后续过程就是就这个图像进行去噪
- Step2:将 x t x_t xt输入到UNet中,得到输出
- Step3:根据下列公式计算
x
t
−
1
x_{t-1}
xt−1,即为(
x
t
−
U
n
e
t
(
x
t
)
x_t-Unet(x_t)
xt−Unet(xt))与一组的噪声的加权
x t − 1 = 1 α t ( x t − 1 − α t 1 − α ˉ t ϵ θ ( x t , t ) ) + σ t z \mathbf{x}_{t-1}=\frac{1}{\sqrt{\alpha_t}}\left(\mathbf{x}_t-\frac{1-\alpha_t}{\sqrt{1-\bar{\alpha}_t}} \boldsymbol{\epsilon}_\theta\left(\mathbf{x}_t, t\right)\right)+\sigma_t \mathbf{z} xt−1=αt1(xt−1−αˉt1−αtϵθ(xt,t))+σtz - Step4:t=0的时候,不加上述公式的最后一项噪声,直接输出第一项作为最后的结果。
其中:
- α t α_t αt是t时刻的系数, α t = 1 − β t αt = 1 - βt αt=1−βt,其中β取值[0.0001, 0.02]
- α ˉ t \bar\alpha_{t} αˉt是t时刻α系数累乘的结果
- z是从正太分布中采样的一个跟输出一样大小的随机噪声
- σ t = β t ∗ ( 1 − α ˉ t − 1 ) / ( α ˉ t ) ≈ β t = 1 − α t \sigma_t = \beta_t * (1 - \bar\alpha_{t-1}) / (\bar\alpha_{t}) ≈ \beta_t = 1 - \alpha_t σt=βt∗(1−αˉt−1)/(αˉt)≈βt=1−αt
- ϵ θ ( x t , t ) \epsilon_\theta(x_t, t) ϵθ(xt,t)表示t时刻的网络的输出
1.2 如何训练网络
扩散模型的过程是逐渐向图像中加入噪声的过程,从时刻0~T时刻的t的变化叫做差异时间表。DDPM使用的是线性时间表:
- 对T个时间步做均匀拆分得到 β t \beta_{t} βt(与时间T相关的线性函数),越往后 β t \beta_{t} βt越大
- 根据 β t \beta_t βt计算 α t = 1 − β t \alpha_t = 1 - \beta_t αt=1−βt
随着逐渐添加噪声,图片越来越难以区分,直到彻底变成一个二维高斯噪声
Source: The Annotated Diffusion Model, Niels Rogge et al.
训练流程如下:
- Step1: 准备输入图像x0,将其归一化到[-1, 1]
- Step2: 随机生成一个与图像尺寸的相同的随机高斯噪声ε
- Step3: 通过 x t = α ˉ t x 0 + 1 − α ˉ t ϵ x_t = \sqrt{\bar\alpha_t}x_0 + \sqrt{1 - \bar\alpha_t}\epsilon xt=αˉtx0+1−αˉtϵ,计算经过T次加噪之后的结果;
- Step4: 计算随机高斯噪声 ϵ \epsilon ϵ与输入图片 x 0 x_0 x0被T次加噪之后的结果 x t x_t xt之间的均方误差损失MSE
扩散模型的损失函数是计算两张图像的相似性,输入是加完噪声后的图像,输出是预测的噪声, 计算两者的MSE损失:
def p_losses(denoise_model, x_start, t, noise=None, loss_type="l1"):
# 1. 根据时刻t计算随机噪声分布,并对图像x_start进行加噪
x_noisy = q_sample(x_start=x_start, t=t, noise=noise)
# 2. 根据噪声图像以及时刻t,预测添加的噪声
predicted_noise = denoise_model(x_noisy, t)
# 3. 对比添加的噪声和预测的噪声的相似性
loss = F.mse_loss(noise, predicted_noise)
return loss
1.3 模型原理
公式推导参考资料(后续更新公式推导笔记):
- https://arxiv.org/pdf/2006.11239.pdf
- https://zhuanlan.zhihu.com/p/565901160
- https://zhuanlan.zhihu.com/p/563661713
- https://lilianweng.github.io/posts/2021-07-11-diffusion-models/
2 VAE:Auto-Encoding Variational Bayes(2022,Kingma)
参考资料:
- Paper:https://arxiv.org/pdf/1312.6114.pdf
- 博客:
- https://blog.51cto.com/Lolitann/5920438
- https://zhuanlan.zhihu.com/p/578619659
VAE模型是Kingma(也是Adam的作者)大神在2022年发表的文章,是一篇非常非常经典,且实现非常优雅的生成模型,同时它还为bayes概率图模型难以求解的问题提供了一种有效的思路。
Source: 卷积变分自编码器
2.1 如何利用VAE进行图像增广
VAE是一种思想,需要使用Auto-Encoder结构,包含一个Encoder和一个Decoder,在论文中作者仅仅采用了简单MLP来实现Encoder和Decoder,在实际实践中可以使用更为复杂的网络结构以提升网络的生成效果。下面阐述基于VAE思想的图片生成流程:
- Step1: 图片输入到Encoder中得到图像编码image embedding
- Step2: 将图像编码输入到不同的分支中得到均值 μ \mu μ和方差 σ \sigma σ向量;
- Step3: 从正太分布中随机产生一个向量z;
- Step4: 计算新的向量: z ’ = exp ( σ ) × z + μ z’ = \exp(\sigma)×z+\mu z’=exp(σ)×z+μ
- Step5: 将新的向量送入Decoder中产生图片
2.2 如何训练VAE网络
在AE中只需要最小化重构损失。但是在VAE中就不能只最小化重构损失了,因为是模型自己学的,如果不加约束,那它可以直接学成0,开摆了,那模型就会退化回AE。所以需要加条件限制,其实就是计算其与标准正太分布的距离,让编码器输出的参数靠近标准正太分布(涉及到极大似然)。因此整体损失函数为:
L
o
s
s
=
M
S
E
(
X
,
X
′
)
+
K
L
(
N
(
μ
,
σ
2
)
,
N
(
0
,
1
)
)
K
L
(
N
(
μ
,
σ
2
)
,
N
(
0
,
1
)
)
=
1
2
(
μ
2
+
σ
2
−
2
log
(
σ
)
−
1
)
Loss = MSE(X, X') + KL(N(\mu, \sigma^2), N(0, 1)) \\ KL(N(\mu, \sigma^2), N(0, 1)) = \frac{1}{2}(\mu^2+\sigma^2-2\log(\sigma) - 1)
Loss=MSE(X,X′)+KL(N(μ,σ2),N(0,1))KL(N(μ,σ2),N(0,1))=21(μ2+σ2−2log(σ)−1)
第一项为重构损失,第二项为编码器输出的均值与方差与标准正太分布之间的KL散度。
2.3 VAE原理
2.3.1 Auto-Encoder
自编码器的目的是利用无标签数据找到一个有效的低维度的特征提取器,输入样本x通过编码器获得低维度特征z,最后通过解码器重构。
问题在于,这种方式只记录了x和z的对应关系,并没有泛化能力。
2.3.2 VAE编码器
VAE的编码器不再是对一个样本直接生成一个码空间上的一个点,而是使一个样本对应一个分布。如何做到的呢?AE编码器是直接得到一个低维的特征表示,而VAE会生成一组均值和方差,这样编码器出来的东西就可以变成一个正太分布,再从这个分布中采样获得z编码。
VAE出来的编码是:
z
=
exp
(
σ
)
×
ϵ
+
μ
z = \exp(\sigma)×\epsilon+\mu
z=exp(σ)×ϵ+μ, 这里使用的参数重整化技巧。即在一个标准的正太分布中采样一个
ϵ
\epsilon
ϵ,再与编码器输出的均值跟方差进行运算,就可以等价于从编码器输出的正太分布中进行的采样。使用这种方式的原因在于直接从原始正太分布中采用出z的这个过程是不可导的,因此采用参数重整化来实现整个过的可导。
重参数技巧:
英文名是reparameterization trick,就是我们要从
p
(
Z
∣
X
k
)
p\left(Z \mid X_k\right)
p(Z∣Xk) 中采样一个
Z
k
Z_k
Zk 出来,尽管我们知道了
p
(
Z
∣
X
k
)
p\left(Z \mid X_k\right)
p(Z∣Xk) 是正态分布,但是均值方差都是靠模型算出来的,我们要靠这个过程反过来优化均值方差的模型,但是“采样”这个操作是不可导的,而采样的结果是可导的。我们利用
1
2
π
σ
2
exp
(
−
(
z
−
μ
)
2
2
σ
2
)
d
z
=
1
2
π
exp
[
−
1
2
(
z
−
μ
σ
)
2
]
d
(
z
−
μ
σ
)
\begin{aligned} & \frac{1}{\sqrt{2 \pi \sigma^2}} \exp \left(-\frac{(z-\mu)^2}{2 \sigma^2}\right) d z \\ = & \frac{1}{\sqrt{2 \pi}} \exp \left[-\frac{1}{2}\left(\frac{z-\mu}{\sigma}\right)^2\right] d\left(\frac{z-\mu}{\sigma}\right) \end{aligned}
=2πσ21exp(−2σ2(z−μ)2)dz2π1exp[−21(σz−μ)2]d(σz−μ)
这说明 ( z − μ ) / σ = ε (z-\mu) / \sigma=\varepsilon (z−μ)/σ=ε 是服从均值为0 、方差为 1 的标准正态分布的,要同时把 d z d z dz 考虑进去,是因为乘上 d z d z dz 才算是概率,去掉 d z d z dz 是概率密度而不是概率。这时候我们得到: 从 N ( μ , σ 2 ) 从 \mathcal{N}\left(\mu, \sigma^2\right) 从N(μ,σ2) 中采样一个 Z Z Z ,相当于从 N ( 0 , I ) \mathcal{N}(0, I) N(0,I) 中采样一个 ε \varepsilon ε ,然后让 Z = μ + ε × σ Z=\mu+\varepsilon \times \sigma Z=μ+ε×σ 。
于是,我们将从
N
(
μ
,
σ
2
)
\mathcal{N}\left(\mu, \sigma^2\right)
N(μ,σ2) 来样变成了从
N
(
0
,
I
)
\mathcal{N}(0, I)
N(0,I) 中来样,然后通过参数变换得到从
N
(
μ
,
σ
2
)
\mathcal{N}\left(\mu, \sigma^2\right)
N(μ,σ2) 中采样的结果。这样一来,“采样”这个操作就不用参与梯度下降了,改为采样的结果参与,使得整个模型可训练了。
Source: 变分自编码器(一):原来是这么一回事
2.3.3 VAE解码器
高斯混合模型:任何一个部分都可以用多个高斯分布取逼近(类似于波的分解)。
编码器的作用是把输入分布编码成一个码空间,解释器的作用就是从这个码空间中采样,去尽力还原编码器的那个分布。
3 Stable Diffusion(2022, CompVis & RunwayML)
参考资料:
-
Paper:https://arxiv.org/pdf/2112.10752.pdf
-
博客:
- https://developer.aliyun.com/article/1134030
- https://www.zhangzhenhu.com/aigc/稳定扩散模型.html
Stable diffusion是latent diffusion models(LDMs,见Paper链接)的一个开源预训练模型。LDM本身是由CompVis提出并联合Runway ML进行开发实现,后来Stability AI也参与进来并提供了一些资源,联合搞了一个预训练的LDM模型,成为Stable diffusion。
Source: Deephub IMBA
3.1 Stable diffusion可以干什么?
Stable diffusion相较于DDPM,可以增加额外的指导信息,可以生成我们想要的图像:
- 基于本文生成图像
- 基于图像生成图像
3.2 Stable diffusion解决什么问题?
DDPM的问题:
- DDMP在生成图像时,需要输入与图像尺寸一样大的随机噪声进行前向去噪。生成的图像分辨率越高,所需的计算和显存资源也就越多,耗费的成本也就越高。
- DDPM无法控制图像生成的方向;
Stable diffusion的解决方案:
- 针对生成高分辨率图像成本高的问题:Stable diffusion先引入一个编码器,对原始图像进行压缩编码,然后对编码后的向量进行扩散过程,即在潜在空间进行扩散;
- 针对无法控制图像生成方向的问题:在UNet中加入Attention机制,处理条件变量y(语义、文本等)用于指导图像的生成;
3.3 训练过程
- Step1: 训练一个自编码器,将图像数据压缩为低维表示:
Source: Deephub IMBA
- Step2: 在latent space进行正向和反向扩散过程:
Source: Deephub IMBA
正向过程:向潜在数据中添加噪声
反向过程:从潜在数据中去除噪声
-
Step3: 在反向过程中接受条件输入:如文本、图像、语义等
-
Step4: 计算loss
相较于DDMP,条件化的LDM目标函数稍微变化了一些,即预测的噪声需要上一轮的结果、时间步和条件输入(文本、图像等)
4 ControlNet(2023, Stanford)
-
Paper:https://arxiv.org/abs/2302.05543
-
Github:https://github.com/lllyasviel/ControlNet-v1-1-nightly
-
博客:
- https://www.zhangzhenhu.com/aigc/controlnet.html
- https://blog.csdn.net/xs1997/article/details/134482651
Stable diffusion支持text guidance图像生成,但是文本的描述能力是有限的,图像的很多细节是无法用文本进行清晰描述的,比如人的姿态等。ControlNet在Stable Diffusion的基础上加入了更多可控生成方式,通过一张图像(姿态图、线稿、草图等)去控制图像生产的过程,这个额外的输入作为stable diffusion的一个控制条件condition,它可以控制图像的生成结果,使其更符合我们输入的条件图像特征。ControlNet和SAM同时获得了ICCV2023最佳论文奖。
4.1 网络结构
要使图像作为额外的信息,需要先对图像进行编码,controlNet的做法是直接利用stable diffusion的UNet来作为图像编码器:
首先回顾一下 Unet 的结构,Unet 的网络结构呈现一个 U 型,按照左侧、中间底部、右侧的划分方式,可以将整个网络结构分为三个子部分:
- 降采样部分,一共包含12个网络块(blocks),把输入的 64×64 的 latent image 逐步降维到 8×8, 这个过程类似于压缩过程,所以可以称为编码器(encoder),也可以称为降采样(down sample)。
- 中间部分,U 型结构的最低层,负责处理 8×8 的数据,一般称为 Middle block。
- 上采样部分,同样包含12个网络块(blocks),把输入的 8×8 的 latent image 逐步升维到 64×64, 这个过程类似于解压过程,所以可以称为解码器(decoder),也可以称为上采样(up sample)。
注:zero convolution层指的是卷积层z初始化为0的1x1卷积,这样在训练的前期不会影响stable diffusion图像生成部分,不对原始SD的能力造成较大的破坏。
ControlNet的输出会和Unet的对应部分输出的元素Add到一起,然后再通过跳线输入给Unet的Decoder部分,所以增加了ControlNet并不会影响原来的结构。
4.2 总结
ControlNet 是一个任务相关的端到端方法,即对于每一种控制类型都要训练一个特定的 ControlNet 支持, 比如线图控制、深度图控制、姿态控制等等。这样有好处也有坏处, 单独看一个场景,拥有使用简单、训练成本低等优点。 但是,如果面对一个复杂场景(多场景),反而变得略麻烦,每一个细分场景都要训练和维护一个模型, 成本高昂,也不易用。
5 LoRA:一种低成本的大模型微调方案(2021,微软)
LoRA: Low-Rank Adaptation of Large Language Models
-
Paper:https://arxiv.org/abs/2106.09685
-
博客:
-
- https://finisky.github.io/lora/
- https://www.cnblogs.com/LittleHann/p/17318509.html#_label3
- https://finisky.github.io/lora/
5.1 LoRA要解决什么问题?
微调大模型目前的方案有:部分微调、使用adapters和prompting,这些方法都存在一些问题:
- Adapters引入额外的推理延迟 (由于增加了模型层数)
- Prefix-Tuning难于训练,且预留给prompt的序列挤占了下游任务的输入序列空间,影响模型性能
LoRA的解决思路: 不对原始模型进行微调
LoRA的思想也很简单,在原始大模型旁边增加一个旁路,做一个降维再升维的操作,来模拟所谓的intrinsic rank。训练的时候固定大模型的参数,只训练降维矩阵A与升维矩阵B。而模型的输入输出维度不变,输出时将BA与大模型的结果叠加。用随机高斯分布初始化A,用0矩阵初始化B,保证训练的开始此旁路矩阵依然是0矩阵。这种思想有点类似于残差连接,同时使用这个旁路的更新来模拟full finetuning的过程。
LoRA核心点:
- 不对原始模型进行微调:增加旁路,训练分布的偏移量来模拟对原始网络的整体finetune
- 对旁路矩形进行低秩分解以降低旁路模型训练参数;
5.2 实际的应用方式
理论上lora可以支持任何线性层,包括transformer中的4个attention矩阵和2个feed forward中的矩阵,论文只在attention上做了实验,它限制总参数量不变的情况下观察是:
- 在attention其中一个矩阵上,放一个更高秩的lora效果好?
- 在多个attention的矩阵上,分别放置低秩一点的lora效果好?
结论是:把秩分散到多个矩阵上,效果会优于集中在单个上的效果。在一般任务上很小的秩就可以和很大秩具备类似的效果,这也证明了作者一开始做出的改变量低秩的假设。
流程如下:
- Step1: 针对原始网络中Attention模块的变换矩阵W,构造一个旁路矩阵ΔW;
- Step2: 将旁路矩阵ΔW底秩分解成AB两个矩阵,A的维度是[n, r], B的维度是[r, m],AB相乘的结果维度是[n, m],这就与原理的矩阵规模一样了;
- Step3: 训练时将AB的结果与原来网络中的矩阵W相加,计算网络输出前向结果和Loss,反向传播时只更新A、B矩阵;
- Step4: 模型训练完成后,将AB的结果与对应的原网络中的参数进行相加,即可得到更新后的网络的参数;
5.3 总结
- LoRA与Adapter的区别:
- adapter是在模块的后面接上一个mlp,对模块的计算结果进行一个后处理
- lora是和模块的计算并行的去做一个mlp,和原来的模块共用一个输入
- LoRA通过低秩分解,降低网络训练测参数量;
- LoRA训练完成之后可以合并到原始网络中,不会增加额外的耗时与计算量;