扩散生成基础原理(二)——DDPM概率去噪扩散模型
系列文章:
扩散生成基础原理(一)——发展历程与前置知识
简介
加州大学伯克利分校20年在NIPS发表的文章,可以说直接奠定了图像生成技术的使用,去年很火的Sora,还有科研应用最广泛的Stable Diffusion均以扩散模型为核心,可以说是研究图像生成必看的一篇文章。
受非平衡热力学考虑启发,作者提出了一种潜在变量模型,使用扩散概率模型呈现高质量的图像合成结果。模型自然地接受了一种渐进式有损解压缩方案,可以解释为自回归解码的泛化。在无条件CIFAR10数据集上,我们获得了 9.46 的 Inception 分数和 3.17 分的最新 FID 分数。
DDPM模型
参考链接DDPM解读。
DDPM分为两个过程:
forward加噪过程,从右向左,不断加入噪声,将原始图像转化为噪声图像,该部分依赖数学原理;
reverse去噪过程,从左向右,利用神经网络学习规律,根据提示词将噪声退化成图像。
前向加噪
前向扩散过程中,向数据分布中逐步添加高斯噪声,加噪过程持续TTT次,产生一系列带噪声图片(x0,x1,...xT)(x_0,x_1,...x_T)(x0,x1,...xT),随着TTT的不断增大,当T→∞T\to \inftyT→∞时原有的图像就被加噪成完全的噪声图像,xT−1x_{T-1}xT−1到xTx_TxT的加噪过程,噪声的方差来自固定βT\beta_TβT,均值来自固定值βT\beta_TβT和当前图像xT−1x_{T-1}xT−1,该步骤的概率公式为:
q(xt∣xt−1):=N(xt;1−βtxt−1,βtI)\quad q\left(\mathbf{x}_{t} \mid \mathbf{x}_{t-1}\right):=\mathcal{N}\left(\mathbf{x}_{t} ; \sqrt{1-\beta_{t}} \mathbf{x}_{t-1}, \beta_{t} \mathbf{I}\right)q(xt∣xt−1):=N(xt;1−βtxt−1,βtI)
首先解释什么要用概率来表示这一过程,如果往后看我们会发现前向过程是有确定性公式可以描述的,如xt=αtxt−1+1−αtϵtx_t=\sqrt{\alpha_t}x_{t-1}+\sqrt{1-\alpha_{t}}\epsilon_{t}xt=αtxt−1+1−αtϵt,但仍然要使用概率公式定义前向过程,原因有两个:
- 虽然有确定性公式,但噪声ϵt\epsilon_{t}ϵt是不确定的,所以给定xt−1x_{t-1}xt−1的情境下xtx_txt并非唯一确定,而是服从指定方差和均值的分布,所以使用概率公式q(xt∣xt−1)\quad q\left(\mathbf{x}_{t} \mid \mathbf{x}_{t-1}\right)q(xt∣xt−1)首先是对这种不确定性的描述。
- 状态的转换是非确定马尔科夫链过程。每次迭代过程只根据上一步状态加上噪声即可实现,噪声的随机性也导致了无法实现确定性转换,只能由当前状态确定一个可能的分布。
该公式说明,再由xt−1x_{t-1}xt−1到xtx_txt的转换过程q(xt∣xt−1)q(x_t|x_{t-1})q(xt∣xt−1)满足分布N(xt;1−βtxt−1,βtI)\mathcal{N}\left(\mathbf{x}_{t} ; \sqrt{1-\beta_{t}} \mathbf{x}_{t-1}, \beta_{t} \mathbf{I}\right)N(xt;1−βtxt−1,βtI),该分布指以1−βtxt−1\sqrt{1-\beta_{t}} \mathbf{x}_{t-1}1−βtxt−1为均值,βtI\beta_{t} \mathbf{I}βtI为方差的分布。I\mathbf{I}I是单位矩阵,用于将方差放大到相应维度上。
定义αt=1−βt,ϵt−1∼N(0,1)\alpha_t=1-\beta_t,\epsilon_{t-1}\sim N(0,1)αt=1−βt,ϵt−1∼N(0,1),则上述公式可被修改为:xt=αtxt−1+1−αtϵt−1x_t=\sqrt{\alpha_t}x_{t-1}+\sqrt{1-\alpha_{t}}\epsilon_{t-1}xt=αtxt−1+1−αtϵt−1
迭代替换后可得xtx_txt与x0x_0x0的关系如下:
xt=αtˉx0+1−αtˉϵ0∼N(xt;αˉtx0,(1−αˉt)I)x_t=\sqrt{\bar{\alpha_t}}x_{0}+\sqrt{1-\bar{\alpha_{t}}}\epsilon_{0}\sim \mathcal{N}\left(\boldsymbol{x}_{t} ; \sqrt{\bar{\alpha}_{t}} \boldsymbol{x}_{0},\left(1-\bar{\alpha}_{t}\right) \mathbf{I}\right)xt=αtˉx0+1−αtˉϵ0∼N(xt;αˉtx0,(1−αˉt)I)
迭代替换的过程为:
xt=αtxt−1+1−αtϵt−1x_t=\sqrt{\alpha_t}x_{t-1}+\sqrt{1-\alpha_{t}}\epsilon_{t-1}xt=αtxt−1+1−αtϵt−1
xt−1=αt−1xt−2+1−αt−1ϵt−2x_{t-1}=\sqrt{\alpha_{t-1}}x_{t-2}+\sqrt{1-\alpha_{t-1}}\epsilon_{t-2}xt−1=αt−1xt−2+1−αt−1ϵt−2带入上式可得
xt=αt(αt−1xt−2+1−αt−2ϵt−2)+1−αtϵt−1x_t=\sqrt{\alpha_t}(\sqrt{\alpha_{t-1}}x_{t-2}+\sqrt{1-\alpha_{t-2}}\epsilon_{t-2})+\sqrt{1-\alpha_{t}}\epsilon_{t-1}xt=αt(αt−1xt−2+1−αt−2ϵt−2)+1−αtϵt−1
xt=αtαt−1xt−2+αt−αtαt−1ϵt−2+1−αtϵt−1x_t=\sqrt{\alpha_t\alpha_{t-1}}x_{t-2}+\sqrt{\alpha_t-\alpha_t\alpha_{t-1}}\epsilon_{t-2}+\sqrt{1-\alpha_{t}}\epsilon_{t-1}xt=αtαt−1xt−2+αt−αtαt−1ϵt−2+1−αtϵt−1
因为两个独立正态分布的和仍然符合正态分布,组成的新分布均值=均值1+均值2,方差=方差1+方差2,也就是将αt−αtαt−1ϵt−2\sqrt{\alpha_t-\alpha_t\alpha_{t-1}}\epsilon_{t-2}αt−αtαt−1ϵt−2视为从N(0,(αt−αtαt−1)I)N(0,(\alpha_t-\alpha_t\alpha_{t-1})I)N(0,(αt−αtαt−1)I)正态分布中的采样,1−αtϵt−1\sqrt{1-\alpha_{t}}\epsilon_{t-1}1−αtϵt−1视为从N(0,(1−αt)IN(0,(1-\alpha_{t})IN(0,(1−αt)I的采样,整合后噪声可视为从N(0,(αt−αtαt−1+1−αt)I)N(0,(\alpha_t-\alpha_t\alpha_{t-1}+1-\alpha_t)I)N(0,(αt−αtαt−1+1−αt)I)的采样
该公式可进一步整合为:
xt=αtαt−1xt−2+αt−αtαt−12+1−αt2ϵt−2x_t=\sqrt{\alpha_t\alpha_{t-1}}x_{t-2}+\sqrt{\sqrt{\alpha_t-\alpha_t\alpha_{t-1}}^2+\sqrt{1-\alpha_{t}}^2}\epsilon_{t-2}xt=αtαt−1xt−2+αt−αtαt−12+1−αt2ϵt−2
xt=αtαt−1xt−2+1−αtαt−1ϵt−2x_t=\sqrt{\alpha_t\alpha_{t-1}}x_{t-2}+\sqrt{1-\alpha_t\alpha_{t-1}}\epsilon_{t-2}xt=αtαt−1xt−2+1−αtαt−1ϵt−2
依次类推就能找到规律,直到x0x_0x0,均值系数为α\alphaα的积,即∏i=1taix0\sqrt{ {\textstyle \prod_{i=1}^{t}} a_i}x_0∏i=1taix0,方差系数则是1−α1-\alpha1−α的积,即1−∏i=1taiϵ0\sqrt{1- {\textstyle \prod_{i=1}^{t}} a_i}\epsilon_01−∏i=1taiϵ0
(这里我其实有个疑问?
噪声epsilonepsilonepsilon应该是每步独立的,为什么可以整合到一起,虽说整体分布可以叠加为新的正态分布,但已经添加的噪声与该过程无关吧。
不过有文章证明了,说用了重参数方法,就暂且接受吧。)
Understanding Diffusion Models: A Unified Perspective从统一视角了解扩散模型
其中αtˉ=∏i=1tai\bar{\alpha_t}= {\textstyle \prod_{i=1}^{t}} a_iαtˉ=∏i=1tai,也就是说xtx_txt是在x0x_0x0条件下,符合均值αtˉx0\bar{\alpha_t}x_0αtˉx0方差1−αtˉ1-\bar{\alpha_{t}}1−αtˉ的分布,只要有了β\betaβ和x0x_{0}x0就能确定噪声,计算出任意时刻的xtx_txt,该噪声为固定的,而非学习的。因此只要有了x0x_0x0和固定好的序列β0,...,βT\beta_0,...,\beta_Tβ0,...,βT,就可以推出任意一步的加噪数据x1,...,xTx_1,...,x_Tx1,...,xT,所以该过程为马尔科夫链过程,即单向依赖不可跳步的。
上述公式表示整体扩散过程的联合分布为q(x1:T∣x0)=q(x1,x2,...,xT∣x0)=∏t=1Tq(xt∣xt−1)=∏t=1TN(xt;αtˉx0,1−αtˉI)q\left(\mathbf{x}_{1: T} \mid \mathbf{x}_{0}\right)=q(x_1,x_2,...,x_T|x_0)=\prod_{t=1}^{T} q\left(\mathbf{x}_{t} \mid \mathbf{x}_{t-1}\right)=\prod_{t=1}^{T} N(x_t;\sqrt{\bar{\alpha_t}}x_0,1-\bar{\alpha_{t}}\mathbf{I})q(x1:T∣x0)=q(x1,x2,...,xT∣x0)=t=1∏Tq(xt∣xt−1)=t=1∏TN(xt;αtˉx0,1−αtˉI)
反向去噪
直接套用正向公式x0=xt−1−αtˉϵ0αtˉx_0=\frac{x_t-\sqrt{1-\bar{\alpha_t}}\epsilon_0}{\sqrt{\bar{\alpha_t}}}x0=αtˉxt−1−αtˉϵ0是解不出来的,因为最后获得的只有xtx_txt图像自身,扩散过程中的均值和方差都是未知的,反向去噪需要使用神经网络学习出该均值和方差。
即x0=xt−1−αtˉϵ(xt,t)αtˉx_0=\frac{x_t-\sqrt{1-\bar{\alpha_t}}\epsilon(x_t,t)}{\sqrt{\bar{\alpha_t}}}x0=αtˉxt−1−αtˉϵ(xt,t),DDPM旨在训练一个噪声估计器epsilon(xt,t)=ϵepsilon(x_t,t)=\epsilonepsilon(xt,t)=ϵ用于估计可能添加的噪声,现有图像-可能添加的噪声=生成的图像。
该过程的损失函数可能设置为噪声的差值或重建图像的差值,但该过程的训练效果很差,这么操作所有的中间过程都没用了,而且已知的q(xt∣x0)q(x_t|x_0)q(xt∣x0),这几乎是一个正态分布,如果每次只逆转一次前向加噪过程来预测噪声就容易得多。
到此问题转为构建单步反向传播的概率函数q(xt−1∣xt)q(x_{t-1}|x_t)q(xt−1∣xt),因为q(xt∣xt−1)q(x_t|x_{t-1})q(xt∣xt−1)已知,所以可以想到用贝叶斯公式:
q(xt−1∣xt)=q(xt∣xt−1)q(xt−1)q(xt)q(x_{t-1}|x_t)=\frac{q(x_t|x_{t-1})q(x_{t-1})}{q(x_t)}q(xt−1∣xt)=q(xt)q(xt∣xt−1)q(xt−1)
但q(xt−1)q(x_{t-1})q(xt−1)和q(xt)q(x_t)q(xt)都是未知的,借助x0x_0x0将其重新表达,将其视为xxx加噪t−1t-1t−1次和ttt次后的概率,上式可修改为:
q(xt−1∣xt)=q(xt∣xt−1)q(xt−1∣x0)q(xt∣x0)q(x_{t-1}|x_t)=\frac{q(x_t|x_{t-1})q(x_{t-1}|x_0)}{q(x_t|x_0)}q(xt−1∣xt)=q(xt∣x0)q(xt∣xt−1)q(xt−1∣x0)
若都引入x0x_0x0作为条件,则有q(xt−1∣xt,x0)=q(xt∣xt−1,x0)q(xt−1∣x0)q(xt∣x0)=N(xt−1;μ~(xt,x0),β~tI)q\left(x_{t-1} \mid x_{t}, x_{0}\right)=\frac{q(x_t|x_{t-1},x_0)q(x_{t-1}|x_0)}{q(x_t|x_0)}=\mathcal{N}\left(x_{t-1} ; \tilde{\mu}\left(x_{t}, x_{0}\right), \tilde{\beta}_{t} \mathbf{I}\right)q(xt−1∣xt,x0)=q(xt∣x0)q(xt∣xt−1,x0)q(xt−1∣x0)=N(xt−1;μ~(xt,x0),β~tI)
根据前面已经推出的分布q(xt∣x0)=N(xt;αˉtx0,(1−αˉt)I)q(x_t|x_0)=\mathcal{N}\left(\boldsymbol{x}_{t} ; \sqrt{\bar{\alpha}_{t}} \boldsymbol{x}_{0},\left(1-\bar{\alpha}_{t}\right) \mathbf{I}\right)q(xt∣x0)=N(xt;αˉtx0,(1−αˉt)I)
则有
q(xt∣xt−1,x0)=q(xt∣xt−1)=N(xt;αtxt−1(1−αt)I)q\left(x_{t} \mid x_{t-1}, x_{0}\right)=q\left(\mathbf{x}_{t} \mid \mathbf{x}_{t-1}\right)=\mathcal{N}\left(\mathbf{x}_{t} ; \sqrt{\alpha_t} \mathbf{x}_{t-1} (1-\alpha_t) \mathbf{I}\right)q(xt∣xt−1,x0)=q(xt∣xt−1)=N(xt;αtxt−1(1−αt)I)
q(xt−1∣x0)=N(xt−1;αˉt−1x0,(1−αˉt−1)I)q(x_{t-1}|x_0)=\mathcal{N}\left(\boldsymbol{x}_{t-1} ; \sqrt{\bar{\alpha}_{t-1}} \boldsymbol{x}_{0},\left(1-\bar{\alpha}_{t-1}\right) \mathbf{I}\right)q(xt−1∣x0)=N(xt−1;αˉt−1x0,(1−αˉt−1)I)
q(xt∣xt−1,x0)q(xt−1∣x0)q(xt∣x0)=N(xt;αtxt−1,(1−αt)I)N(xt−1;αˉt−1x0,(1−αˉt−1)I)N(xt;αˉtx0,(1−αˉt)I)\frac{q(x_t|x_{t-1},x_0)q(x_{t-1}|x_0)}{q(x_t|x_0)}=\frac{\mathcal{N}\left(\mathbf{x}_{t} ; \sqrt{\alpha_t} \mathbf{x}_{t-1}, (1-\alpha_t) \mathbf{I}\right)\mathcal{N}\left(\boldsymbol{x}_{t-1} ; \sqrt{\bar{\alpha}_{t-1}} \boldsymbol{x}_{0},\left(1-\bar{\alpha}_{t-1}\right) \mathbf{I}\right)}{\mathcal{N}\left(\boldsymbol{x}_{t} ; \sqrt{\bar{\alpha}_{t}} \boldsymbol{x}_{0},\left(1-\bar{\alpha}_{t}\right) \mathbf{I}\right)}q(xt∣x0)q(xt∣xt−1,x0)q(xt−1∣x0)=N(xt;αˉtx0,(1−αˉt)I)N(xt;αtxt−1,(1−αt)I)N(xt−1;αˉt−1x0,(1−αˉt−1)I)
由高斯分布公式,p(x)=12πσe−12(x−μσ)2p(x)=\frac{1}{\sqrt{2\pi \sigma}}e^{-\frac{1}{2}(\frac{x-\mu}{\sigma})^2}p(x)=2πσ1e−21(σx−μ)2,μ=αtˉx0\mu=\sqrt{\bar{\alpha_t}}x_0μ=αtˉx0对应分布中间项,σ=1−αtˉ\sigma=\sqrt{1-\bar{\alpha_t}}σ=1−αtˉ对应最后一项的开根号
故不管公共系数12πσ\frac{1}{\sqrt{2\pi \sigma}}2πσ1,这些作除法都消掉后只保留一项,上式的三个分布的指数可分别转化为−(xt−αtxt−1)22(1−αt)-\frac{\left(\boldsymbol{x}_{t}-\sqrt{\alpha_{t}} \boldsymbol{x}_{t-1}\right)^{2}}{2\left(1-\alpha_{t}\right)}−2(1−αt)(xt−αtxt−1)2、(xt−1−αˉt−1x0)22(1−αˉt−1)\frac{\left(\boldsymbol{x}_{t-1}-\sqrt{\bar{\alpha}_{t-1}} \boldsymbol{x}_{0}\right)^{2}}{2\left(1-\bar{\alpha}_{t-1}\right)}2(1−αˉt−1)(xt−1−αˉt−1x0)2、(xt−αˉtx0)22(1−αˉt)\frac{\left(\boldsymbol{x}_{t}-\sqrt{\bar{\alpha}_{t}} \boldsymbol{x}_{0}\right)^{2}}{2\left(1-\bar{\alpha}_{t}\right)}2(1−αˉt)(xt−αˉtx0)2
指数上的除法运算可以整合为指数的减法,消掉公共系数也不关心底数e,上式可转化为
=exp{−[(xt−αtxt−1)22(1−αt)+(xt−1−αˉt−1x0)22(1−αˉt−1)−(xt−αˉtx0)22(1−αˉt)]}=exp\left \{ -\left[\frac{\left(\boldsymbol{x}_{t}-\sqrt{\alpha_{t}} \boldsymbol{x}_{t-1}\right)^{2}}{2\left(1-\alpha_{t}\right)}+\frac{\left(\boldsymbol{x}_{t-1}-\sqrt{\bar{\alpha}_{t-1}} \boldsymbol{x}_{0}\right)^{2}}{2\left(1-\bar{\alpha}_{t-1}\right)}-\frac{\left(\boldsymbol{x}_{t}-\sqrt{\bar{\alpha}_{t}} \boldsymbol{x}_{0}\right)^{2}}{2\left(1-\bar{\alpha}_{t}\right)}\right] \right \} =exp{−[2(1−αt)(xt−αtxt−1)2+2(1−αˉt−1)(xt−1−αˉt−1x0)2−2(1−αˉt)(xt−αˉtx0)2]}
后续推导在Understanding Diffusion Models: A Unified Perspective从统一视角了解扩散模型中写的很详细,步骤如下:
获得最后均值和方差的分布作详细解释,承接公式:
q(xt∣xt−1,x0)q(xt−1∣x0)q(xt∣x0)=N(xt;αtxt−1,(1−αt)I),N(xt−1;αˉt−1x0,(1−αˉt−1)I)N(xt;αˉtx0,(1−αˉt)I)\frac{q(x_t|x_{t-1},x_0)q(x_{t-1}|x_0)}{q(x_t|x_0)}=\frac{\mathcal{N}\left(\mathbf{x}_{t} ; \sqrt{\alpha_t} \mathbf{x}_{t-1} ,(1-\alpha_t) \mathbf{I}\right),\mathcal{N}\left(\boldsymbol{x}_{t-1} ; \sqrt{\bar{\alpha}_{t-1}} \boldsymbol{x}_{0},\left(1-\bar{\alpha}_{t-1}\right) \mathbf{I}\right)}{\mathcal{N}\left(\boldsymbol{x}_{t} ; \sqrt{\bar{\alpha}_{t}} \boldsymbol{x}_{0},\left(1-\bar{\alpha}_{t}\right) \mathbf{I}\right)}q(xt∣x0)q(xt∣xt−1,x0)q(xt−1∣x0)=N(xt;αˉtx0,(1−αˉt)I)N(xt;αtxt−1,(1−αt)I),N(xt−1;αˉt−1x0,(1−αˉt−1)I)
先解释推出的公式如何与分布对应:
exp表示以e为底数,后续公式为指数。
因为正态分布公式p(x)=12πσe−12(x−μσ)2p(x)=\frac{1}{\sqrt{2\pi \sigma}}e^{-\frac{1}{2}(\frac{x-\mu}{\sigma})^2}p(x)=2πσ1e−21(σx−μ)2,如果后续公式能凑成该形式即可找到对应均值μ\muμ和方差σ\sigmaσ,最后一步已经中凑出了−12-\frac{1}{2}−21,平方形式需要借助常数项C(xt,x0)=−(xt−αtˉx0)21−αˉtC(x_t,x_0)=-\frac{(x_t-\sqrt{\bar{\alpha_t}}x_0)^2}{1-\bar{\alpha}_t}C(xt,x0)=−1−αˉt(xt−αtˉx0)2凑出,期望的常数项应该是[αt(1−αˉt−1)xt+αˉt−1(1−αt)x0]2(1−αˉt)2\frac{[\sqrt{\alpha_t}(1-\bar{\alpha}_{t-1})x_t+\sqrt{\bar{\alpha}_{t-1}}(1-\alpha_t)x_0]^2}{(1-\bar{\alpha}_t)^2}(1−αˉt)2[αt(1−αˉt−1)xt+αˉt−1(1−αt)x0]2
常数项参与后续运算的过程论文的证明中省略了,其实肯定是对的上的,但是笔者自己作了补充验证:
首先常数项展开为−xt2−2αtˉx0+αˉtx021−αˉt-\frac{x_t^2-2\sqrt{\bar{\alpha_t}}x_0+\bar{\alpha}_tx_0^2}{1-\bar{\alpha}_t}−1−αˉtxt2−2αtˉx0+αˉtx02
在提取系数中操作为:−xt2−2αtˉx0+αˉtx021−αˉt⋅(1−αt)(1−αˉt−1)1−αtˉ=−(xt2−2αtˉx0+αˉtx02)(1−αˉt−1−αt+αtαˉt−1)(1−αˉt)2-\frac{x_t^2-2\sqrt{\bar{\alpha_t}}x_0+\bar{\alpha}_tx_0^2}{1-\bar{\alpha}_t}\cdot\frac{(1-\alpha_t)(1-\bar{\alpha}_{t-1})}{1-\bar{\alpha_t}}=-\frac{(x_t^2-2\sqrt{\bar{\alpha_t}}x_0+\bar{\alpha}_tx_0^2)(1-\bar{\alpha}_{t-1}-\alpha_t+\alpha_t\bar{\alpha}_{t-1})}{(1-\bar{\alpha}_t)^2}−1−αˉtxt2−2αtˉx0+αˉtx02⋅1−αtˉ(1−αt)(1−αˉt−1)=−(1−αˉt)2(xt2−2αtˉx0+αˉtx02)(1−αˉt−1−αt+αtαˉt−1)
分母一致,但是光看xt2x_t^2xt2的系数计算结果也不一致啊,可能中间哪化错了,暂且搁置吧。
最后详细说明系数部分:
系数计算结果12πσ\frac{1}{\sqrt{2\pi \sigma}}2πσ1为12π(1−αt)12π(1−αˉt−1)12παˉt=2παˉt2π(1−αt)2π(1−αˉt−1)=1−αˉt(1−αt)(1−αˉt−1)\frac{\frac{1}{\sqrt{2\pi (1-\alpha_t)}}\frac{1}{\sqrt{2\pi (1-\bar{\alpha}_{t-1})}}}{\frac{1}{\sqrt{2\pi \bar{\alpha}_t}}}=\frac{\sqrt{2\pi \bar{\alpha}_t}}{\sqrt{2\pi (1-\alpha_t)}\sqrt{2\pi (1-\bar{\alpha}_{t-1})}}=\frac{\sqrt{1-\bar{\alpha}_t}}{\sqrt{(1-\alpha_t)}\sqrt{(1-\bar{\alpha}_{t-1})}}2παˉt12π(1−αt)12π(1−αˉt−1)1=2π(1−αt)2π(1−αˉt−1)2παˉt=(1−αt)(1−αˉt−1)1−αˉt
正好与前面推出的方差一致,对应σ\sigmaσ
推导后可获得反向去噪的概率均值为μ(xt,x0)=αt(1−αˉt−1)xt+αˉt−1(1−αt)x01−αˉt\mu(x_t,x_0)=\frac{\sqrt{\alpha_t}(1-\bar{\alpha}_{t-1})x_t+\sqrt{\bar{\alpha}_{t-1}}(1-\alpha_t)x_0}{1-\bar{\alpha}_t}μ(xt,x0)=1−αˉtαt(1−αˉt−1)xt+αˉt−1(1−αt)x0,此时将由xt=αtˉx0+1−αtˉϵ0x_t=\sqrt{\bar{\alpha_t}}x_{0}+\sqrt{1-\bar{\alpha_{t}}}\epsilon_{0}xt=αtˉx0+1−αtˉϵ0逆推得到的x0=xt−1−αtˉϵ0αtˉx_0=\frac{x_t-\sqrt{1-\bar{\alpha_t}}\epsilon_0}{\sqrt{\bar{\alpha_t}}}x0=αtˉxt−1−αtˉϵ0带入上式,可得如下结果:
补充第二步计算的解释,有αtˉ=∏i=1tai\bar{\alpha_t}= {\textstyle \prod_{i=1}^{t}} a_iαtˉ=∏i=1tai,则有αtˉ=at×αˉt−1\bar{\alpha_t}=a_t\times \bar{\alpha}_{t-1}αtˉ=at×αˉt−1,故针对单独一项分子分母上下同时除αˉt−1\sqrt{\bar{\alpha}_{t-1}}αˉt−1,分子消掉,分母αtˉ\sqrt{\bar{\alpha_t}}αtˉ变为αt\sqrt{\alpha_t}αt
到此我们获得了反向去噪的概率分布q(xt−1∣xt=N(xt−1;αt(1−αˉt−1)xt+αˉt−1(1−αt)x01−αˉt,(1−αt)(1−αˉt−1)1−αˉtI)q(x_{t-1}|x_t=\mathcal{N}({x}_{t-1};\frac{\sqrt{\alpha_{t}}(1-\bar{\alpha}_{t-1}){x}_ {t}+\sqrt{\bar{\alpha}_{t-1}}(1-\alpha_{t}){x}_{0}}{1-\bar{\alpha}_{t}}, \frac{(1-\alpha_{t})(1-\bar{\alpha}_{t-1})}{1-\bar{\alpha}_{t}}\mathbf{I})q(xt−1∣xt=N(xt−1;1−αˉtαt(1−αˉt−1)xt+αˉt−1(1−αt)x0,1−αˉt(1−αt)(1−αˉt−1)I),以及均值的确定性表示1αtxt−1−αt1−αˉtαtϵ0\frac{1}{\sqrt{\alpha_{t}}} \boldsymbol{x}_{t}-\frac{1-\alpha_{t}}{\sqrt{1-\bar{\alpha}_{t}} \sqrt{\alpha_{t}}} \boldsymbol{\epsilon}_{0}αt1xt−1−αˉtαt1−αtϵ0,并且通过推导发现去噪过程的均值受超参数α\alphaα和随机噪声ϵ\epsilonϵ影响,方差只由超参数决定,形式为βt(1−αˉt−1)1−αtˉ\frac{\beta_t(1-\bar{\alpha}_{t-1})}{1-\bar{\alpha_t}}1−αtˉβt(1−αˉt−1)。
训练噪声预测器
有了正向加噪和反向去噪的概率分布,按理说我们就可以根据样本生成新图像了,但是公式中只有一个未知解,噪声ϵ\epsilonϵ。该如何预测噪声,采用何种损失衡量,是DDPM需要解决的另一个关键问题。
这块的数学原理更复杂,看着就头大,而且又不是我们使用的重点,所以仅贴几个图意思意思。
生成图像
假设有一正态分布的采样X∼N(μ,σ2)X\sim N(\mu,\sigma^2)X∼N(μ,σ2),也可以表示为X=μ+σ×Z,Z∼N(0,1)X=\mu+\sigma\times Z,Z\sim N(0,1)X=μ+σ×Z,Z∼N(0,1)。
在反向去噪部分我们得到了q(xt−1∣xt)∼N(xt−1;μ~(xt,x0),β~tI)q(x_{t-1}|x_t)\sim \mathcal{N}\left(x_{t-1} ; \tilde{\mu}\left(x_{t}, x_{0}\right), \tilde{\beta}_{t} \mathbf{I}\right)q(xt−1∣xt)∼N(xt−1;μ~(xt,x0),β~tI)其中μ~=1αtxt−1−αt1−αˉtαtϵ0,β~=βt(1−αˉt−1)1−αtˉ\tilde{\mu}=\frac{1}{\sqrt{\alpha_{t}}} \boldsymbol{x}_{t}-\frac{1-\alpha_{t}}{\sqrt{1-\bar{\alpha}_{t}} \sqrt{\alpha_{t}}} \boldsymbol{\epsilon}_{0},\tilde{\beta}=\frac{\beta_t(1-\bar{\alpha}_{t-1})}{1-\bar{\alpha_t}}μ~=αt1xt−1−αˉtαt1−αtϵ0,β~=1−αtˉβt(1−αˉt−1),故xt−1x_{t-1}xt−1可表示为:
xt−1=μ~+β~⋅z=1αt(xt−1−αt1−αtˉϵ0)+βt(1−αˉt−1)1−αtˉ⋅zx_{t-1}=\tilde{\mu}+\tilde{\beta}\cdot z=\frac{1}{\sqrt{\alpha_t}}(x_t-\frac{1-\alpha_t}{\sqrt{1-\bar{\alpha_t}}}\epsilon_0)+\frac{\beta_t(1-\bar{\alpha}_{t-1})}{1-\bar{\alpha_t}}\cdot zxt−1=μ~+β~⋅z=αt1(xt−1−αtˉ1−αtϵ0)+1−αtˉβt(1−αˉt−1)⋅z
生成图像的过程从高斯噪声开始,不断重复上面过程,直到t=0t=0t=0,即恢复出初始图像x0x_0x0,伪代码如下:
数学原理上是均值+方差x标准正态分布,可直观理解上又有不同。
生成过程更像是(噪声图像−预测噪声×系数)×缩放系数+方差×标准噪声z(噪声图像-预测噪声\times系数)\times缩放系数+方差\times 标准噪声z(噪声图像−预测噪声×系数)×缩放系数+方差×标准噪声z
源码分析
源码来自diffusers
库的scheduling_ddpm.py
文件。
代码首先定义了一个输出格式:
class DDPMSchedulerOutput(BaseOutput):"""Output class for the scheduler's `step` function output.Args:prev_sample (`torch.Tensor` of shape `(batch_size, num_channels, height, width)` for images):Computed sample `(x_{t-1})` of previous timestep. `prev_sample` should be used as next model input in thedenoising loop.pred_original_sample (`torch.Tensor` of shape `(batch_size, num_channels, height, width)` for images):The predicted denoised sample `(x_{0})` based on the model output from the current timestep.`pred_original_sample` can be used to preview progress or for guidance."""prev_sample: torch.Tensorpred_original_sample: Optional[torch.Tensor] = None
DDPM输出继承自BaseOutput,注释中说明prev_sample
是反向过程计算出的xt−1x_{t-1}xt−1步去噪图像,作为下一次输入使用,pred_original_sample
表示当前时间步预测的x0x_0x0去噪样本。
最核心的方法是DDPMScheduler
类,注释中说明:
该方法继承自SchedulerMixin和ConfigMixin
参数:
num_train_timesteps (int, 默认值为 1000):
训练模型的扩散步数。
beta_start (float, 默认值为 0.0001):
推理的初始 beta 值。
beta_end (float, 默认值为 0.02):
最终 beta 值。
beta_schedule (str, 默认值为 "linear"):
beta 调度策略,即从 beta 范围到用于推进模型的 beta 序列的映射。可选值包括 linear(线性)、scaled_linear(缩放线性)、squaredcos_cap_v2(平方余弦帽v2)或 sigmoid(sigmoid)。
trained_betas (np.ndarray, 可选):
直接传递给构造函数的 beta 数组(无需使用 beta_start 和 beta_end)。
variance_type (str, 默认值为 "fixed_small"):
在向去噪样本添加噪声时裁剪方差。可选值包括 fixed_small(固定小方差)、fixed_small_log(固定小方差对数)、fixed_large(固定大方差)、fixed_large_log(固定大方差对数)、learned(可学习)或 learned_range(可学习范围)。
clip_sample (bool, 默认值为 True):
裁剪预测样本以确保数值稳定性。
clip_sample_range (float, 默认值为 1.0):
样本裁剪的最大幅度。仅在 clip_sample=True 时有效。
prediction_type (str, 默认值为 epsilon, 可选):
调度器函数的预测类型;可选 epsilon(预测扩散过程的噪声)、sample(直接预测含噪样本)或 v_prediction(参见 Imagen Video 论文第2.4节)。
thresholding (bool, 默认值为 False):
是否使用"动态阈值处理"方法。此方法不适用于 Stable Diffusion 等潜在空间扩散模型。
dynamic_thresholding_ratio (float, 默认值为 0.995):
动态阈值处理的比例。仅在 thresholding=True 时有效。
sample_max_value (float, 默认值为 1.0):
动态阈值处理的阈值。仅在 thresholding=True 时有效。
timestep_spacing (str, 默认值为 "leading"):
时间步的缩放方式。更多信息请参考 Common Diffusion Noise Schedules and Sample Steps are Flawed 论文的表2。
steps_offset (int, 默认值为 0):
添加到推理步的偏移量(部分模型家族需要此设置)。
rescale_betas_zero_snr (bool, 默认值为 False):
是否重新缩放 beta 以获得零终端信噪比。这使模型能够生成非常明亮或黑暗的样本,而非限制为中亮度样本。与 --offset_noise 参数有松散关联。
省略一些参数初始化等设置操作,反向去噪过程通过反向求解随机微分方程(SDE)从上一时间步预测样本。此函数从学习到的模型输出(通常为预测噪声)传播扩散过程。
输入的参数有:
model_output (torch.Tensor):
学习到的扩散模型的直接输出,通常对应噪声epsilon
timestep (float):
扩散链中的当前离散时间步。
sample (torch.Tensor):
扩散过程生成的当前样本实例,对应中间去噪样本x_t
generator (torch.Generator, 可选):
随机数生成器。
return_dict (bool, 可选, 默认值为 True):
是否返回 [~schedulers.scheduling_ddpm.DDPMSchedulerOutput] 对象。
下面对具体操作进行分块解释:
- 获取时间步与输出分类
获取当前时间步并计算下一时间步,若方差类型为learned(可学习方差)或 learned_range(可学习范围方差)时,模型输出会同时包含预测的噪声和方差(形状为两倍特征维度)。此时需要将 model_output 拆分为 model_output(预测噪声)和 predicted_variance(预测方差)。
代码如下:
t = timestep
prev_t = self.previous_timestep(t)
if model_output.shape[1] == sample.shape[1] * 2 and self.variance_type in ["learned", "learned_range"]:model_output, predicted_variance = torch.split(model_output, sample.shape[1], dim=1)
else:predicted_variance = None
- 计算α\alphaα和β\betaβ
alpha和beta是扩散过程的核心超参数,代码为:
# 1. compute alphas, betas
alpha_prod_t = self.alphas_cumprod[t]
# 累乘获得bar(alpha_t)
alpha_prod_t_prev = self.alphas_cumprod[prev_t] if prev_t >= 0 else self.one
# 累乘获得bar(alpha_{t-1})
beta_prod_t = 1 - alpha_prod_t
# bar(beta_t)=1-bar(alpha_t)
beta_prod_t_prev = 1 - alpha_prod_t_prev
# bar(beta_{t-1})=1-bar(alpha_{t-1})
current_alpha_t = alpha_prod_t / alpha_prod_t_prev
# alpha_t=bar(alpha_t)/bar(alpha_{t-1})
current_beta_t = 1 - current_alpha_t
# beta_t=1-alpha_t
- 从预测噪声计算预测原始样本x0x_0x0
该过程来自文章的公式15,x0≈x^0=(xt−1−αˉtϵθ(xt))/αˉt\mathbf{x}_{0} \approx \hat{\mathbf{x}}_{0}=\left(\mathbf{x}_{t}-\sqrt{1-\bar{\alpha}_{t}} \boldsymbol{\epsilon}_{\theta}\left(\mathbf{x}_{t}\right)\right) / \sqrt{\bar{\alpha}_{t}}x0≈x^0=(xt−1−αˉtϵθ(xt))/αˉt,其实就是正向过程xt=αtˉx0+1−αtˉϵ0x_t=\sqrt{\bar{\alpha_t}}x_0+\sqrt{1-\bar{\alpha_t}}\epsilon_0xt=αtˉx0+1−αtˉϵ0解出的x0x_0x0的表达式。
if self.config.prediction_type == "epsilon":# 预测模式为噪声,(x_t-\sqrt(1-/bar{alpha}epsilon))//sqrt{/bar{alpha}}pred_original_sample = (sample - beta_prod_t ** (0.5) * model_output) / alpha_prod_t ** (0.5)
elif self.config.prediction_type == "sample":# 预测含噪样本,就无需计算,直接赋值pred_original_sample = model_output
elif self.config.prediction_type == "v_prediction":# 预测改进噪声项,让模型学习噪声与初始图像的关系,用以增强训练稳定性pred_original_sample = (alpha_prod_t**0.5) * sample - (beta_prod_t**0.5) * model_output
else:raise ValueError(f"prediction_type given as {self.config.prediction_type} must be one of `epsilon`, `sample` or"" `v_prediction` for the DDPMScheduler.")
- 裁剪或约束预测的x0x_0x0
对原始预测样本进行数值约束,避免出现异常值,确保生成样本的质量和训练稳定性。
# 3. Clip or threshold "predicted x_0"
if self.config.thresholding:# 如果配置中thresholding=true,截断到阈值水平# 异常值可能导致出现伪影,截断也是动态算法,保留大部分正常数据,仅截断异常值pred_original_sample = self._threshold_sample(pred_original_sample)
elif self.config.clip_sample:# 固定范围剪裁clip_sample=ture,则将数值限制在+-clip_sample_range范围内pred_original_sample = pred_original_sample.clamp(-self.config.clip_sample_range, self.config.clip_sample_range)
- 根据公式μ~t(xt,x0):=αˉt−1βt1−αˉtx0+αt(1−αˉt−1)1−αˉtxt\tilde{\boldsymbol{\mu}}_{t}\left(\mathbf{x}_{t}, \mathbf{x}_{0}\right):=\frac{\sqrt{\bar{\alpha}_{t-1}} \beta_{t}}{1-\bar{\alpha}_{t}} \mathbf{x}_{0}+\frac{\sqrt{\alpha_{t}}\left(1-\bar{\alpha}_{t-1}\right)}{1-\bar{\alpha}_{t}} \mathbf{x}_{t}μ~t(xt,x0):=1−αˉtαˉt−1βtx0+1−αˉtαt(1−αˉt−1)xt计算系数。前者控制预测原始样本x^0\hat{x}_0x^0在生成xt−1x_{t-1}xt−1的权重,后者控制xtx_txt在生成过程中的比重。
t较小时,噪声较弱(βt\beta_tβt较小),原始信号多αt\alpha_tαt大,因此x^0\hat{x}_0x^0的权重较大,模型更信任预测的干净样本。
t趋近于T,xtx_txt的权重更大,模型更依赖当前含噪样本。
# 4. Compute coefficients for pred_original_sample x_0 and current sample x_t
# See formula (7) from https://arxiv.org/pdf/2006.11239.pdf
# \bar{x_0}的系数
pred_original_sample_coeff = (alpha_prod_t_prev ** (0.5) * current_beta_t) / beta_prod_t
# x_t的系数
current_sample_coeff = current_alpha_t ** (0.5) * beta_prod_t_prev / beta_prod_t
- 根据上面得到的系数计算均值utu_tut
# 5. Compute predicted previous sample µ_t
# See formula (7) from https://arxiv.org/pdf/2006.11239.pdf
pred_prev_sample = pred_original_sample_coeff * pred_original_sample + current_sample_coeff * sample
- 添加噪声,返回预测值。该部分对应最终的生成部分,均值+方差x标准正态,只是均值的形式没有采用最终噪声预测器的模式,而是结合xtx_txt和x0x_0x0的版本。
# 6. Add noise
# t=0时无需添加噪声
variance = 0
if t > 0:device = model_output.device
# 正态分布噪声variance_noise = randn_tensor(model_output.shape, generator=generator, device=device, dtype=model_output.dtype)
if self.variance_type == "fixed_small_log":# 固定方差,噪声乘方差variance = self._get_variance(t, predicted_variance=predicted_variance) * variance_noise
elif self.variance_type == "learned_range":# 学习方差,预测的对数方差转化为标准差 variance = self._get_variance(t, predicted_variance=predicted_variance)variance = torch.exp(0.5 * variance) * variance_noise
else:# 默认直接将方差开方,获取标准差variance = (self._get_variance(t, predicted_variance=predicted_variance) ** 0.5) * variance_noise
# 预测加噪声,均值+方差x标准正态分布噪声
pred_prev_sample = pred_prev_sample + varianceif not return_dict:
# 返回x_{t-1}和x_0
return (pred_prev_sample,pred_original_sample,)
# 返回封装好的输出对象
return DDPMSchedulerOutput(prev_sample=pred_prev_sample, pred_original_sample=pred_original_sample)
结合生成代码来看:
with torch.no_grad():noise_pred = self.unet(latent_model_input, t, encoder_hidden_states=text_embeddings).sample.to(dtype=torch.float16)
# 根据指导权重调整噪声预测
noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
noise_pred = noise_pred_uncond + self.guidance_scale * (noise_pred_text - noise_pred_uncond)
-# 计算前一个噪声样本,继续去噪
self.latents = self.scheduler.step(noise_pred, t, self.latents).prev_sample
可以看到,最终潜空间latents
的生成是由调度器的step
方法直接实现的,unet
网络完成噪声预测部分,将预测的噪声noise_pred
作为参数model_output
传给调度器scheduler
,最终获得返回对象中的预测潜向量prev_sample
。
DDPM小结
DDPM通过模仿热运动的扩散过程提出了一种图像生成新范式,正向是将噪声逐渐添加到图像直到图像完全变成噪声,反向是使用神经网络学习出每次噪声分布的均值和方差,用以估计噪声,从当前图像中去除,直到恢复出图像。
DDPM最主要的问题是慢,一步步加噪去噪要执行大量操作,有时只需要进行几个像素点的操作却要对整个图像进行判定,所以有了后续跳步的DDIM、和目前最先进的DPM-sovler。
ddpm代码可以说是亦步亦趋地实现了文章的方法,最主要的是计算均值,均值与超参数、x0x_0x0和xtx_txt有关,图像生成采用均值+方差x标准正态的方式实现,其中均值和方差来自DDPM中推导的计算式,噪声来自unet的预测。