16 分钟阅读

前言

决定现在开始先学习一些卷积操作,为后期的卷积神经网络的学习打牢基本功。

还要抽时间搞王俊刚老师的课题 《随机矩阵理论视角下的神经网络训练动力学实验研究》 感觉很高大上,但又不难理解,很有意思。

卷积核

构造kernel

kernel = np.ones((3,3), np.float32) / 9

数据上,生成了一个全为$1\over9$的$3\times3$矩阵

\[\frac{1}{9} \begin{pmatrix} 1 & 1 & 1 \\ 1 & 1 & 1 \\ 1 & 1 & 1 \end{pmatrix}\]

这是一个 均值滤波核Mean Filter

输出像素 = 周围 3×3 像素的平均值

卷积核可以自己构造,或通过机器学习(如反向传播)来学习参数。


经典的卷积核包括:

低通卷积核(平滑处理核)

  • 均值滤波
\[\frac{1}{9} \begin{bmatrix} 1 & 1 & 1 \\ 1 & 1 & 1 \\ 1 & 1 & 1 \end{bmatrix}\]
  • 高斯滤波
\[\frac{1}{16} \begin{bmatrix} 1& 2& 1\\ 2& 4& 2\\ 1& 2& 1 \end{bmatrix}\]

高通卷积核(边缘检测核)

一阶导数

  • Sobel 算子

竖向边缘检测 \(\begin{bmatrix} -1& 0& +1\\ -2& 0& +2\\ -1& 0& +1 \end{bmatrix}\)

横向边缘检测 \(\begin{bmatrix} -1& -2& -1\\ 0& 0& 0\\ +1& +2& +1 \end{bmatrix}\)

本质是对像素值的离散一阶导数

简化后变为Prewitt 算子

\[\begin{bmatrix} -1& 0& +1\\ -1& 0& +1\\ -1& 0& +1 \end{bmatrix}\]

改进核为Scharr 算子

\[\begin{bmatrix} -3& 0& +3\\ -10& 0& +10\\ -3& 0& +3 \end{bmatrix}\]

OpenCV 推荐小核用 Scharr


二阶导数

  • Laplacian(拉普拉斯算子)

4邻域版本

\[\begin{bmatrix} 0& -1& 0\\ -1& +4& -1\\ 0& -1& 0 \end{bmatrix}\]

8邻域版本

\[\begin{bmatrix} -1& -1& -1\\ -1& +8& -1\\ -1& -1& -1 \end{bmatrix}\]

噪声非常敏感,通常先高斯平滑

  • LoG(Laplacian of Gaussian)(高斯后拉普拉斯核)
\[\begin{bmatrix} 0& 0& -1& 0 & 0\\ 0& -1& -2& -1 & 0\\ -1& -2& 16 &-2 &-1\\ 0& -1& -2& -1 & 0\\ 0& 0& -1 & 0 & 0 \end{bmatrix}\]

锐化(高通增强)

  • 锐化核(Sharpen)
\[\begin{bmatrix} 0 & -1 & 0\\ -1 & +5 & -1\\ 0 & -1 & 0 \end{bmatrix}\]
  • Unsharp Mask(反遮罩)
\[\begin{bmatrix} -1 & -1 & -1\\ -1 & +9 &-1\\ -1 & -1 & -1 \end{bmatrix}\]

等价形式是原图 − 模糊图


其他核

  • Gabor 滤波器 ⭐⭐⭐

形式:

g(x,y) = exp(-(x²+y²)/2σ²) · cos(2πfx)

特点:

  • 方向性

  • 频率选择性

  • 类似 V1 视觉皮层感受野

  • CNN 第一层学到的核 ≈ Gabor

示例代码

def smooth_filter(self):
    img = cv2.imread(self.imagePath)
    kernel = np.ones((3,3), np.float32) / 9
    det = cv2.filter2D(img, -1, kernel)

※线性代数角度的卷积操作本质

前言

首先,线性代数中,矩阵一词是有精确定义的:

矩阵 = 表示“线性映射”的对象

而图像不是线性映射,而是被作用的对象,所以图像需要先被拉成向量,再作用线性变换(即矩阵变换)

一个非常关键的区分(请记住)

角色 在线代中是什么
图像 向量(元素是像素)
卷积 / 滤波 线性变换
卷积核 变换的参数
卷积矩阵 表示该变换的矩阵

卷积运算是线性运算,符合上述定义。

那么,假设:

  • 图像是 5×5(25 个像素)
  • 使用 3×3 卷积核
  • 输出仍然是 25 个像素

那么一定存在一个 $25 × 25$ 的矩阵 $A$ ,使得:

\[y = Ax\]

其中:

  • x:原图拉直后的向量
  • y:卷积结果拉直后的向量

所以,卷积 ≡ 某个固定矩阵乘以图像向量


譬如,一个$5\times5$的图像,因为基有25个元素,所以自由度是25,是25维的向量。有一个卷积核生成的$25\times 25$的卷积矩阵 $A$,作用于图像,可以生成处理后的图像

关键点解答:

  1. 这个图像要被拉成一个向量,叫做 vectorization
  2. 基有 25 个元素

    可以这样选择标准基:

     e₁ = (1,0,0,...)
     e₂ = (0,1,0,...)
     ...
     e₂₅ = (0,0,...,1)
    

    每个基向量对应 一个像素位置

  3. 变换需要一个 25×25 的变换矩阵

    这是一个:

    \[T : ℝ^{25} → ℝ^{25}\]

    所以根据线性代数的语境,卷积核生成了一个卷积矩阵 $A$ ,使得

    \[y = Ax\]
  4. 变成另一幅向量

    输出向量经过 reshape 回 5×5,就是新图像

  5. 关于生成的卷积矩阵:
  • 这个 25×25 矩阵
    • 非常大
    • 非常稀疏
    • 结构高度重复
    • 完全由卷积核生成
  • 所以我们用 kernel + 滑动 的方式计算,等价但高效

    对于一个像素,卷积操作只和 周围 3×3 共 9 个像素有关,

    所以矩阵的一行里:

    • 只有 9 个非零数
    • 其他全是 0

    这种“沿着对角线重复出现结构”的矩阵,就叫:

    Toeplitz / Block-Toeplitz 矩阵

所以,卷积 ≈ 一个结构高度重复、非常稀疏的线性变换

小结

卷积核 = 一个局部线性算子
图像 = 高维向量
卷积 = 稀疏 Toeplitz 矩阵 × 向量

不同卷积核 ➡ 选择不同的「局部基」

这和 随机矩阵 / Jacobian / NTK 在思想上是完全同源的。

示例

接下来,使用一个例子来帮助理解:

  1. 一个 5×5 图像 + 3×3 kernel

  2. 对应的 25×25 卷积矩阵

  3. 指出哪 9 个元素非零、为什么是 Toeplitz


1. 图像表示和向量化

一张 $5\times 5$ 的图像 $X$ 记作:

\[X = \begin{bmatrix} x_1 & x_2 & x_3 & x_4 & x_5 \\ x_6 & x_7 & x_8 & x_9 & x_{10} \\ x_{11} & x_{12} & x_{13} & x_{14} & x_{15} \\ x_{16} & x_{17} & x_{18} & x_{19} & x_{20} \\ x_{21} & x_{22} & x_{23} & x_{24} & x_{25} \end{bmatrix}\]

行优先向量化(拉直到向量):

\[\mathrm{vec}(X) = \begin{bmatrix} x_1, x_2, \dots, x_{25} \end{bmatrix}^\top \in \mathbb{R}^{25}\]

2.卷积核表示

将卷积核 $K$ 记作:

\[K = \begin{bmatrix} a & b & c \\ d & e & f \\ g & h & i \end{bmatrix}\]

接下来进行卷积操作,并还原出卷积矩阵

3. 卷积方式

  • stride = 1
  • padding = 0(valid 卷积)
  • 输出大小:3×3

为了简单,先只看 valid (same 卷积只是多几行零)


输出左上角像素 $y_1$ 来自:

\[\begin{bmatrix} x_1 & x_2 & x_3 \\ x_6 & x_7 & x_8 \\ x_{11} & x_{12} & x_{13} \end{bmatrix}\]

所以:

\[y_1 = a x_1 + b x_2 + c x_3 + d x_6 + e x_7 + f x_8 + g x_{11} + h x_{12} + i x_{13}\]

把这句话写成:

\[y_1 = \begin{bmatrix} a & b & c & 0 & 0 & d & e & f & 0 & 0 & g & h & i & 0 & \cdots & 0 \end{bmatrix} \cdot \mathrm{vec}(X)\]

这就是 25×25 矩阵的第一行


输出 $y_2$ 用到:

\[\begin{bmatrix} x_2 & x_3 & x_4 \\ x_7 & x_8 & x_9 \\ x_{12} & x_{13} & x_{14} \end{bmatrix}\]

对应:

\[y_2 = a x_2 + b x_3 + c x_4 + d x_7 + e x_8 + f x_9 + g x_{12} + h x_{13} + i x_{14}\]

写成矩阵行:

\[y_2 = \begin{bmatrix} 0 & a & b & c & 0 & 0 & d & e & f & 0 & 0 & g & h & i & 0 & \cdots \end{bmatrix} \cdot \mathrm{vec}(X)\]

所以这就是卷积矩阵的第二行


可以看出,本质上是将卷积核的各元素向右平移了一格。

而对于第三行结束后,卷积窗口进行“换行”:

比如 $y_4$ (第二行第一个输出):

\[\begin{bmatrix} x_6 & x_7 & x_8 \\ x_{11} & x_{12} & x_{13} \\ x_{16} & x_{17} & x_{18} \end{bmatrix}\]

对应矩阵行:

\[[0,0,0,0,0,\; a,b,c,0,0,\; d,e,f,0,0,\; g,h,i,0,\dots]\]

整体向右跳了 5 格
(因为一行有 5 个像素)


所以,我们可以写出这个卷积矩阵的表示:

\[\underbrace{ \begin{bmatrix} \text{—— 9 个非零 ——} & 0 & \cdots \\ 0 & \text{—— 9 个非零 ——} & \cdots \\ \vdots & \vdots & \ddots \end{bmatrix} }_{\text{卷积矩阵 } A}\]

满足:

\[\mathrm{vec}(Y) = A \, \mathrm{vec}(X)\]

其中:

  • $A \in \mathbb{R}^{9 \times 25}$ (valid)
  • 或 $A \in \mathbb{R}^{25 \times 25}$ (same)

4. 小结

至此,我们可以重新理解一遍这个诠释:

卷积层 = 一个带强结构约束、权重共享的稀疏大矩阵

卷积 ≈ 一个结构高度重复、非常稀疏的线性变换

总结

在这篇文章里,我们从具体的卷积核入手,深入探讨了卷积操作的线性代数本质,为后续理解CNN理论,Jacobian矩阵,NTK构造奠定了重要的基础。

文章分为三个部分。

第一部分,列举了常见的卷积核参数,包括低通滤波,高通滤波和Gabor等特殊核,提供了操作的示例代码。

第二部分,介绍了图像的卷积处理在线性代数中的明确定义,对卷积操作做出了诠释:

卷积操作 ≈ 一个结构高度重复、非常稀疏的线性变换

第三部分,给出了$5\times 5$图像的卷积操作实例,并写出了$3\times 3$卷积操作的卷积矩阵,明确了:

卷积层 = 一个带强结构约束、权重共享的稀疏大矩阵

希望这篇文章能对我有一些启示作用,并帮助我更好地理解图像卷积处理和卷积神经网络的相关内容。

如有纰漏,请批评指正

留下评论