Melaton's Blog

从高斯模糊到卷积再到diffusion模型

Table of Contents

卷积好有意思,于是就写了

# 高斯模糊

高斯模糊是一种模糊图像的算法,但你有没有想过算法具体是如何运作的?

让我们从高斯分布开始说起

大多数人都肯定听过正态分布,即随机变量的平均值的概率密度函数,自然界中的随机变量在样本增加后会逐渐收敛成正态分布。你们可能已经猜到了,高斯模糊的核心正是高斯分布

一维高斯分布的概率密度函数为:

$$ G(x) = \frac{1}{\sqrt{2\pi}\sigma} e^{-\frac{(x-\mu)^2}{2\sigma^2}} $$

其中,μ 是均值,σ 是标准差。

对于图像处理,我们通常使用二维高斯分布: $$ G(x, y) = \frac{1}{2\pi\sigma^2} e^{-\frac{x^2+y^2}{2\sigma^2}} $$ (这里假设中心点为 (0,0),即 μ=0)

想象一个 3 * 3 的网格,平铺到图片的左上角,网格与图片的像素对齐。现在有一种算法,将 3 * 3 网格中的9个像素的R G B值分别取平均值,处理结束后将这个平均值像素放到网格的正中心。我们完成了一次图像处理!接下来只要将所有像素都这样操作一遍,我们就能得到一张平均模糊后的图像!太神奇了!

高斯模糊就是这样的操作,只不过使用了其它尺寸的网格,且平均方式是加权平均。想象将正态分布的图像沿y轴旋转,形成了一个凸起的平面。网格的加权方式就是采用这个平面对应点的高度作为权重,使用这样的方式进行加权平均。这个网格通常也称为高斯核

# 卷积

刚才我们提到的那个网格其实叫卷积核convolution kernel),简单来说,卷积核是一种滑动+计算的操作,通过设计不同的卷积核算法,我们就可以用来处理图像或提取图像的特征。例如一个3*3的网格,只不过这次 左边一列的权重为正,右边一列的权重为负。通过这个卷积核,我们就可以知道这个图像中哪些地方是左边缘,哪些地方是右边缘。

⚠️注意!计算机中的卷积和数学中的卷积原理相同,但计算机中的卷积没有翻转的操作,因为大家发现翻不翻转都一样!

我们再来讲讲数学中的卷积想出卷积这个名字的人真是个天才! 数学中的卷积正如其名字所描述,对两组数进行操作时,将其中一组数字翻转过来,倒过来进行挨个相乘并相加。

离散卷积的通用数学定义(一维)为: $$ (f * g)[n] = \sum_{m=-\infty}^{\infty} f[m] g[n-m] $$ 或者,在有限信号且考虑翻转的情况下: $$ (f * g)[n] = \sum_{m} f[m] g[n-m] \quad \text{(注意 g 的索引是 n-m,体现翻转和滑动)} $$

让我们看一个例子:

将一维数组 [1, 2, 3, 4, 5] 使用卷积核 [-1, 0, 1] 进行卷积。首先,我们要将卷积核 卷过来,得到处理后的卷积核 [1, 0, -1]。我们再把数组与卷积核对齐。进行运算

1 2 3 4 5
1 0 -1

$$ \text{output}[0] = (1 \times 1) + (2 \times 0) + (3 \times -1) = 1 + 0 - 3 = -2 $$

1 2 3 4 5
1 0 -1

$$ \text{output}[1] = (2 \times 1) + (3 \times 0) + (4 \times -1) = 2 + 0 - 4 = -2 $$

1 2 3 4 5
1 0 -1

$$ \text{output}[2] = (3 \times 1) + (4 \times 0) + (5 \times -1) = 3 + 0 - 5 = -2 $$

最后,我们可以得到数组[-2, -2, -2],这就是经过卷积核计算后的结果。

值得注意的是,在这里我为了简化计算流程(计算机也是这么干的),两个数组完全对齐。在严谨的数学计算中,需要从数组的第一个数和卷积核的最后一个数开始卷积(或者根据定义调整边界)。

再提一嘴,你虽然不知道卷积是什么意思,但你肯定用过。回想一下多项式乘法中是不是用到了卷积的思想?例如 $$ P(x) = a_0 + a_1 x + a_2 x^2 $$

$$ Q(x) = b_0 + b_1 x $$

$$ R(x) = P(x) \times Q(x) = c_0 + c_1 x + c_2 x^2 + c_3 x^3 $$

其中系数 ck 的计算方式为: $$ c_k = \sum_{i=0}^{k} a_i b_{k-i} \quad (\text{假设 } a_i=0 \text{ for } i>2, b_j=0 \text{ for } j>1) $$ 例如:

  • c0 = a0 b0
  • c1 = a0 b1 + a1 b0
  • c2 = a1 b1 + a2 b0
  • c3 = a2 b1

发现了吗,R 的系数 [c0, c1, c2, c3] 是系数序列 [a0, a1, a2][b0, b1]卷积

如果你想知道如何如何快速运算两个超大的多项式的乘积,你可以接着往下看。但我想先说说diffusion模型

# Diffusion

再来讲讲卷积的应用。其中最出名的就是diffusion模型,在这里我只想聊聊图片生成。

由于卷积捕捉图像特征的特性,人们很快就想到了将其运用到图像处理上。其中最著名的发明就是 U-Net。通过卷积核在图像上滑动并捕捉图像特征,计算机可以提取到图像特别之处再进行存储。通过 U-Net 的下采样(downsampling),可以增加卷积层的感受野(receptive field),让模型可以捕捉更大的上下文信息。

简单来说,diffusion模型是一个预测模型,我们人为创造一幅沙画,再通过吹气的方式为图像添加噪声,让模型学习噪声添加后的沙画和原本的沙画有什么区别。重复吹沙画好多次,我们就可以得到一幅完全混乱的图像,也称作高斯噪声(居然又是高斯!)。通过让模型学习不同的沙画,最后的模型就可以通过完全随机的噪声生成图像。这就是diffusion模型的原理。

接下来我想聊聊更有意思的

## 快速傅立叶变换 (FFT)

卷积定理提出,两个函数在时域(或空域)的卷积,等于它们各自进行傅立叶变换后在频域的乘积。反之亦然。

用公式表示卷积定理: $$ \mathcal{F}{f * g} = \mathcal{F}{f} \cdot \mathcal{F}{g} $$ 以及 $$ f * g = \mathcal{F}^{-1}{\mathcal{F}{f} \cdot \mathcal{F}{g}} $$ 其中, $\mathcal{F}$ 表示傅立叶变换 (Fourier Transform), $\mathcal{F}^{-1}$ 表示傅立叶反变换 (Inverse Fourier Transform), $*$ 表示卷积, $\cdot$ 表示逐点乘积。

很显然,大部分人都不知道这句话是什么意思。

简单来说,将两袋混合口味的糖果直接进行卷积很难,但通过傅立叶变换,就可以将这袋糖果拆开,告诉你有多少种口味,以及对应的糖果数量(对应频域信息)。将这两袋糖果的成分对应位置的成分量相乘,就得到了新的成分列表。通过傅立叶反变换,就可以将糖果再混合成一袋新的糖果。这个过程与卷积完全无关,但得到了与卷积完全一样的结果!

这个过程被称为快速傅立叶变换Fast Fourier Transform),可以很快速地计算卷积。它可以将计算的复杂度从 O(N²) 降低到 O(N log N)太神奇了!

对于diffusion模型中的大型计算,快速傅立叶变换极大地提高了计算效率!

数学真的是太神奇了