2026/10/18 10:11:07

卷积层为什么是线性映射?从加权求和到矩阵乘法

卷积层为什么是线性映射?从加权求和到矩阵乘法 很多初学者第一次接触卷积神经网络时都会被“卷积”这个名词带偏觉得它是一个“高级”的、带有某种“特征提取魔法”的操作甚至是某种非线性变换。直到某天看到一句话——卷积层本质上是一个线性映射——才突然卡住卷积核里明明有非线性激活函数为什么还说它是线性的这个问题如果不搞清楚后面所有关于CNN可视化、网络剪枝、频域分析、甚至是模型等价变换的讨论都会悬空。本文就从这个最基础的问题入手从结构展开和统计等价两个角度把“卷积层为什么是线性映射”这件事彻底讲透并附上可以直接运行的验证代码帮你建立一套清晰、可迁移的理解框架。1. 线性映射一个不动摇的基准定义要回答“卷积层是不是线性映射”先得有一个不会含糊的标尺。数学上一个映射 (f: \mathbb{R}^n \to \mathbb{R}^m) 是线性映射当且仅当它同时满足两条性质可加性(f(\mathbf{x}_1 \mathbf{x}_2) f(\mathbf{x}_1) f(\mathbf{x}_2))齐次性(f(c \cdot \mathbf{x}) c \cdot f(\mathbf{x}))对任意标量 (c) 成立这两条合在一起就是教科书里常写的叠加原理对输入的任意线性组合输出等于各自输出的同一线性组合即 [ f(\alpha \mathbf{x}_1 \beta \mathbf{x}_2) \alpha f(\mathbf{x}_1) \beta f(\mathbf{x}_2) ]这个定义看似简单却非常管用。它有两个重要推论第一线性映射与矩阵乘法是同一件事。只要在有限维空间里选定一组基任何线性映射都能写成 (\mathbf{y} \mathbf{W}\mathbf{x}) 的矩阵形式。这个推论是后面“结构展开”的支点如果我们能证明卷积的输出就是输入向量与某个矩阵相乘那卷积层的线性性就无可辩驳。第二线性映射没有任何“形状变换”能力。它不能把一个线性可分的分布扭曲成非线性可分也不存在数据的交互项比如 (x_1 x_2) 这种组合。所以一个纯卷积层不带激活堆得再深整体仍然是一个线性函数这就是后来很多人讨论“去掉激活的深网络等价于一个单层线性网络”的直接原因。判断一个运算是否线性不需要看它的实现细节只需要检验它对输入加法和数乘的行为。这个基准再强调也不为过网络里的很多层全连接、批归一化的推理阶段、卷积都在这个定义下都是线性映射而ReLU、Sigmoid、MaxPooling严格说它是分片线性则不是。有了这把尺子下面就可以放心地拆卷积了。2. 卷积层的数学结构加权求和而不是某种神秘变换2.1 单通道情况的核心公式先看最简单的情形单通道输入、单卷积核、无填充、步长为1。设输入是二维矩阵 (\mathbf{X})卷积核是 (\mathbf{K})则输出在位置 ((i,j)) 的值为[ \mathbf{Y}(i,j) \sum_{m0}^{K_h-1} \sum_{n0}^{K_w-1} \mathbf{X}(im, jn) \cdot \mathbf{K}(m,n) ]稍微翻译一下输出特征图上的每一个像素都是输入图上一个小邻域内像素的加权和权重就是卷积核上对应位置的数值。这本质上就是一个滑动窗口内的加权求和。这个公式告诉你三件事第一输出值的计算是“一次乘加”操作没有任何取阈值、归一化、指数之类的非线性函数混在里面。第二卷积核的系数是固定的不随输入位置变化而变化这就是参数共享。第三每个输出元素“看”到的输入范围是整个输入的一个局部窗口这就是局部连接。这三条合起来已经能嗅到线性的味道因为它对输入的依赖方式是“乘一个固定权重视角下做加和”。2.2 从定义上验证可加性与齐次性不要只在直觉层面打转直接代入定义验证。先验证可加性。把输入换成 (\mathbf{X}_1 \mathbf{X}_2)[ \begin{aligned} \mathbf{Y}{\text{sum}}(i,j) \sum{m,n} \big(\mathbf{X}_1(im,jn) \mathbf{X}2(im,jn)\big) \cdot \mathbf{K}(m,n) \ \sum{m,n} \mathbf{X}1(im,jn) \cdot \mathbf{K}(m,n) \sum{m,n} \mathbf{X}_2(im,jn) \cdot \mathbf{K}(m,n) \ \mathbf{Y}_1(i,j) \mathbf{Y}_2(i,j) \end{aligned} ]第二个等号只是把加法和乘法交换了顺序这个操作没有任何前提条件。齐次性也同理把 (\mathbf{X}) 换成 (c\mathbf{X})常数因子 (c) 可以直接从求和号里提出来于是输出也缩放 (c) 倍。所以从算子角度卷积是线性算子。这个过程不需要知道卷积核是什么值——哪怕核里全是随机数线性性依然成立。这也意味着“线性”是卷积这个运算本身的结构属性与训练出来的参数无关。有趣的是很多教材在讲卷积神经网络时会加一句“卷积层可以视为线性映射”但这其实不算是一种简化而是一个精确的数学结论。出于这个原因我们接下来可以考虑用矩阵乘法的视角重新描述卷积。3. 从结构展开看“卷积等于矩阵乘法”3.1 把输入展平构造等效矩阵如果卷积是线性的那么按照线性代数基本定理它一定能写成矩阵形式。这个矩阵长什么样答案藏在“结构展开”里。设输入是一个 (3 \times 3) 的单通道矩阵卷积核是 (2 \times 2)步长为1忽略填充。输入可以展平成9维向量import numpy as np x np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtypenp.float64) x_vec x.flatten() print(x_vec) # [1. 2. 3. 4. 5. 6. 7. 8. 9.]卷积核展平成4维kernel np.array([[0.5, -1.0], [0.25, 0.75]], dtypenp.float64) k_vec kernel.flatten() print(k_vec) # [0.5 -1. 0.25 0.75]输出尺寸为 (2 \times 2)展平后得到4个输出值。每个输出值都是4个输入像素与4个核系数相乘后求和。按照滑动窗口的位置把每个输出对应的输入位置记录下来就能构造出等效矩阵 (\mathbf{W})。手算一下输出输出(0,0)窗口覆盖输入左上角2×2区域即 ([1,2,4,5])对应核 ([0.5,-1,0.25,0.75])所以 (y_{00} 1 \times 0.5 2 \times (-1) 4 \times 0.25 5 \times 0.75 2.75)输出(0,1)窗口右移一格覆盖 ([2,3,5,6]) (y_{01} 2 \times 0.5 3 \times (-1) 5 \times 0.25 6 \times 0.75 3.75)输出(1,0)窗口下移一格覆盖 ([4,5,7,8]) (y_{10} 4 \times 0.5 5 \times (-1) 7 \times 0.25 8 \times 0.75 5.25)输出(1,1)窗口右下覆盖 ([5,6,8,9]) (y_{11} 5 \times 0.5 6 \times (-1) 8 \times 0.25 9 \times 0.75 6.75)现在构造矩阵。(\mathbf{W}) 的大小是 (4 \times 9)因为从9维输入映射到4维输出。每一行对应一个输出位置只在被窗口覆盖的输入位置上有核权值其余位置为0。W np.zeros((4, 9), dtypenp.float64) # 输出位置 (0,0)窗口覆盖输入索引 [0,1,3,4]原始坐标 (0,0),(0,1),(1,0),(1,1) W[0, 0] 0.5 W[0, 1] -1.0 W[0, 3] 0.25 W[0, 4] 0.75 # 输出位置 (0,1)窗口覆盖索引 [1,2,4,5] W[1, 1] 0.5 W[1, 2] -1.0 W[1, 4] 0.25 W[1, 5] 0.75 # 输出位置 (1,0)窗口覆盖索引 [3,4,6,7] W[2, 3] 0.5 W[2, 4] -1.0 W[2, 6] 0.25 W[2, 7] 0.75 # 输出位置 (1,1)窗口覆盖索引 [4,5,7,8] W[3, 4] 0.5 W[3, 5] -1.0 W[3, 7] 0.25 W[3, 8] 0.75 result W x_vec print(result) # [2.75 3.75 5.25 6.75]用scipy的卷积结果比对一下from scipy.signal import correlate2d out correlate2d(x, kernel, modevalid) print(out.flatten()) # [2.75 3.75 5.25 6.75]结果完全一致。这一步验证了核心结论卷积层的输出等价于把输入展平后乘以一个稀疏矩阵。3.2 多通道与批量维度怎么变真实的卷积层几乎都是多通道输入、多卷积核、批量处理。这种情况下等效矩阵依然存在只不过维度更大。输入展平长度 (d_{\text{in}} N \times C_{\text{in}} \times H_{\text{in}} \times W_{\text{in}})输出展平长度 (d_{\text{out}} N \times C_{\text{out}} \times H_{\text{out}} \times W_{\text{out}})于是等效矩阵 (\mathbf{W}) 的维度是 (d_{\text{out}} \times d_{\text{in}})。这个矩阵的大部分元素是0只有少数位置对应卷积核系数。用具体数字感受一下规模。假设输入是 (32 \times 32 \times 3)输出通道16卷积核 (3 \times 3)输出空间尺寸仍是 (32 \times 32)假设填充1、步长1(d_{\text{in}} 3 \times 32 \times 32 3072)(d_{\text{out}} 16 \times 32 \times 32 16384)等效矩阵元素总量 ( 16384 \times 3072 50331648)约5000万而实际卷积核参数只有 (16 \times 3 \times 3 \times 3 432) 个。也就是说等效矩阵里超过99.99%的元素是0。这种矩阵称为稀疏结构矩阵。每一行非零元素的个数恰好等于一个卷积核覆盖的输入元素个数即 (C_{\text{in}} \times K_h \times K_w)。在这个例子里单行非零数是 (3 \times 3 \times 3 27)。稀疏度约为 (1 - 27/3072 \approx 0.9912)也就是99.12%的稀疏率。这些非零元素的位置不是随机散落的——它们被“滑动窗口参数共享”这两个规则严格约束。所以从结构展开的视角看卷积层就是一个在参数上加了强结构化约束的稀疏线性层等效矩阵呈现分块的带状结构也就是 Toeplitz 矩阵在多通道情形下的扩展。3.3 im2col工程上就是这么干的看到这里有经验的读者应该已经联想到深度学习框架里卷积的经典实现方式im2col。im2col做的事情就是把输入按滑动窗口“展开”成一个大矩阵让每一次窗口操作变成矩阵里的一列或一行从而把卷积运算转化为通用矩阵乘法。这个思路和上面的等效矩阵推导完全一致输入展开成 ( (C_{\text{in}} \times K_h \times K_w) \times (C_{\text{out}} \times H_{\text{out}} \times W_{\text{out}}?)) 的 patch 矩阵不同框架的排布方向略有差异卷积核重排成权重矩阵两者做 GEMM。GPU 对 GEMM 的优化极其成熟所以主流框架在卷积实现上都会选择这条“展开为矩阵乘法”的路线。这不只是巧合而是卷积线性结构的直接工程红利——你证明它是线性的就可以用矩阵乘法的高性能库来加速它。理解了这个结构你还会得到一个额外的好处用等效矩阵做梯度推导会非常清晰。卷积层的反向传播可以理解为等效矩阵的转置相乘这在数学上几乎不用额外记公式。4. 统计等价卷积与全连接的差别不在线性而在于约束4.1 全连接层是“最一般”的线性映射另一个常见的线性层是全连接层记为 (\mathbf{y} \mathbf{W}\mathbf{x} \mathbf{b})。同样大小的输入展平向量 (\mathbf{x})全连接层的权重矩阵 (\mathbf{W}) 没有任何结构限制——任意一个元素都可以非零。这意味着全连接层代表了线性映射的最一般形态每个输出神经元都和所有输入神经元相连每个连接都有独立的权重。从统计角度看它对应的参数空间是所有可能的矩阵没有任何先验假设。卷积层的等效矩阵则完全不同。同样是形如 (\mathbf{y} \mathbf{W}\mathbf{x}) 的线性表示但 (\mathbf{W}) 被限制为局部连接每一个输出只与输入中一个局部patch相连等效矩阵每一行非零元素只出现在与空间位置相邻的列上权重共享同一个卷积核在空间上滑动等效矩阵中不同行的非零权重来自同一组数值的重复使用通道结构性多通道输入下每个输出通道只连接到输入通道的一个固定子集。所以卷积层在参数空间上不是“另一种”线性层而是全连接层的一个受约束子集。你可以说任何卷积层都能表示为一个特定的全连接层就是把权重矩阵填上稀疏结构但反过来不成立——全连接层表达的函数空间严格大于卷积层。4.2 参数空间的维度对比5千万 vs 432把上面例子中的数字放在一起全连接层参数数量(d_{\text{out}} \times d_{\text{in}} 16384 \times 3072 \approx 5033万)卷积层参数数量(C_{\text{out}} \times C_{\text{in}} \times K_h \times K_w 16 \times 3 \times 3 \times 3 432)参数压缩比约11.6万倍。卷积层能够用如此少的自由度去表达一个“看似很庞大”的线性映射靠的就是结构约束。这种约束在统计上会带来什么效果先说结论它显著降低了模型的方差同时引入了归纳偏置相当于给模型增加了先验。局部连接的偏置假设是“目标像素只与其空间邻域相关”参数共享的偏置假设是“同一特征在不同位置具有统计平移不变性”。这两个假设对图像任务来说是极其合理的先验因此CNN在样本量远小于全连接网络需求量的情况下就能学到可泛化的特征。但也要清楚地看到代价如果输入数据的结构不符合这两个假设比如对于像素位置高度敏感的乱序数据卷积层的先验就会变成误导表达能力反而受限。这就是“任何先验都有代价”的统计本质。4.3 “统计等价”是什么意思标题里有个词是“统计等价”。这里并不是说卷积层和全连接层的输出分布永远相同而是指在有限维线性映射的意义下它们都属于同一个函数族线性函数区别仅仅在于参数集的结构性约束不同因此在统计学习的语境下我们可以把卷积层看作“在全连接层参数空间的一个低维流形上做搜索”而不是一个全新的模型族。这个视角对很多实际问题都有帮助。比如模型剪枝时分析一个卷积层的“有效秩”本质上是在研究这个约束流形在数据驱动下的退化情况又比如神经网络的可解释性工作里把某一层的特征图反演回输入空间依赖的正是“卷积层可写为矩阵运算”这一前提。在表里把两者的核心差异列清楚对比维度全连接层卷积层权重形式稠密矩阵稀疏结构矩阵带状/Toeplitz拓展参数共享无卷积核在空间上共享局部性无全局连接有局部感受野先验假设无局部性 平移不变性参数量同规模极大极小是否线性是是这种“线性但受约束”的定位让卷积层在深度学习理论里拥有了一个特殊位置它既保留了线性模型的可分析性可以展开成矩阵、可以做谱分析又比纯线性模型更具样本效率。5. 亲手验证一次完整的线性性测试5.1 准备一个“卷积线性检查器”前面说的都是理论推导实际动手测一下会更踏实。你可以用一段不长的小程序来检查任意一个“声称是线性层”的操作是否满足定义。思路很简单生成两组随机输入分别计算以下三个量直接计算 (f(\mathbf{x}_1 \mathbf{x}_2))分别计算 (f(\mathbf{x}_1) f(\mathbf{x}_2))计算 (f(c \cdot \mathbf{x}_1)) 与 (c \cdot f(\mathbf{x}_1))如果两组结果在浮点误差范围内一致就说明该操作对这两条测试成立。import numpy as np from scipy.signal import correlate2d def conv_linear_map(x, kernel): return correlate2d(x, kernel, modevalid) np.random.seed(42) x1 np.random.randn(8, 8) x2 np.random.randn(8, 8) k np.random.randn(3, 3) c 2.5 # 可加性 lhs_add conv_linear_map(x1 x2, k) rhs_add conv_linear_map(x1, k) conv_linear_map(x2, k) print(Additivity max error:, np.max(np.abs(lhs_add - rhs_add))) # 输出大约是 1e-15 量级 # 齐次性 lhs_scale conv_linear_map(c * x1, k) rhs_scale c * conv_linear_map(x1, k) print(Homogeneity max error:, np.max(np.abs(lhs_scale - rhs_scale))) # 输出同样是 1e-15 量级两次检查都在 (10^{-15}) 量级基本就是浮点运算的舍入误差说明该操作线性成立。这个“线性检查器”非常通用——不信某个层是线性的就用这个逻辑直接测比翻文档快得多。5.2 从张量运算层面验证等效矩阵上面的测试验证了卷积运算的算子线性性但还没有构造等效矩阵。再把上一节的一维矩阵构造思想泛化一下。def conv_as_matrix(x, kernel): h_in, w_in x.shape kh, kw kernel.shape h_out, w_out h_in - kh 1, w_in - kw 1 x_flat x.flatten() W np.zeros((h_out * w_out, h_in * w_in)) idx 0 for i in range(h_out): for j in range(w_out): for m in range(kh): for n in range(kw): # 输入矩阵里的坐标 r, c i m, j n W[idx, r * w_in c] kernel[m, n] idx 1 y_mat (W x_flat).reshape(h_out, w_out) y_direct correlate2d(x, kernel, modevalid) print(Matrix-form max error:, np.max(np.abs(y_mat - y_direct))) x np.random.randn(5, 5) k np.random.randn(3, 3) conv_as_matrix(x, k)结果同样在浮点误差范围内。这说明解析推导和数值实验完全互相印证。看到这里你可以自己试试给这个矩阵加上偏置项就会得到 (\mathbf{y} \mathbf{W}\mathbf{x} \mathbf{b}) 的标准线性仿射形式。严格来说这是一个“仿射映射”不是齐次线性映射但由于偏置不依赖于输入把输入扩充一维常数1之后仿射就变成了齐次线性。这也是很多深度学习教材把所有带偏置的全连接层、卷积层都直接称呼为线性层的原因——在约定的意义下完全成立。5.3 对比ReLU打破线性只需要一个非线性为了加深印象可以做一个对照实验在卷积输出上接一个ReLU再看线性性测试是否还成立。def conv_relu(x, kernel): return np.maximum(0, correlate2d(x, kernel, modevalid)) lhs conv_relu(x1 x2, k) rhs conv_relu(x1, k) conv_relu(x2, k) print(ReLU breaks additivity:, np.max(np.abs(lhs - rhs))) # 大概率是一个不小的数值由于ReLU会把负数清零(f(x_1x_2)) 和 (f(x_1)f(x_2)) 在哪个位置应该被截断这件事上会发生交错线性不再成立。这正是网络非线性能力的来源。实际网络里卷积层与ReLU通常成对出现但两者的角色截然不同卷积层只负责做线性特征重组ReLU负责引入非线性。这两种操作分开理解整个网络的行为会清晰得多。6. 常见疑问与容易踩的坑6.1 “卷积核本身也在不断更新为什么还算固定系数”这里面有一个角色混淆线性映射里的“固定”指的是在一次前向计算中对输入 (\mathbf{x}) 来说是固定不变的操作与“参数在训练过程中通过梯度更新不断变化”并不矛盾。训练过程改变的是映射本身而不是映射的类型。就好比不同的人手里拿同一把刻度不同的尺子每个人量出来的数值不一样但“用尺子量长度”这个操作仍然是线性的。只要在任意时刻把网络参数冻结住卷积层对输入的操作就严格满足线性映射的定义。这种区分对理解模型训练很重要梯度下降是在一个线性映射空间里搜索合适的参数搜索的对象是映射的参数不是映射的形状。6.2 “卷积核里有非线性函数吗”没有。卷积核里面存的就是一组标量权重它只是一个 (K_h \times K_w \times C_{\text{in}} \times C_{\text{out}}) 形状的张量直接参与乘加计算。容易混淆是因为很多流程图把“卷积ReLU”画成一个整体模块导致新人误以为卷积核里包含激活函数。正确的拆法是先做线性映射卷积/全连接再加偏置线性仿射再过一个非线性激活ReLU等池化、归一化等按具体设计穿插。每一层在网络图中可能是一个节点但数学上是多个连续函数的复合。拆开来分析时只有激活函数这一层不是线性的。还有一个认知误区是把批归一化BatchNorm当成非线性层。实话说BN在推理阶段就是一个“缩放平移”训练阶段还包含一个基于mini-batch统计量的归一化但最终作用在输入上仍然是先减均值、再除方差、再缩放和平移——全是线性仿射操作。所以纯卷积BN全连接不带激活的深度网络仍然是一个线性系统。6.3 “空洞卷积、分组卷积、转置卷积还是线性映射吗”全部都是线性的因为它们本质上没有改变“输出等于输入局部加权求和”这一核心结构。空洞卷积用带“空洞”的采样网格来收集输入像素等价于一个系数带零的更大卷积核仍然是加权求和分组卷积把通道分成若干组每组内部独立做卷积从整体看依然是一个分块对角结构的稀疏矩阵转置卷积反卷积它本身就是某个普通卷积的等效矩阵转置矩阵转置不改变线性性质。这些变体改变的只是等效矩阵 (\mathbf{W}) 的结构稀疏模式、权重共享方式并不改变线性算子这个身份。它们的非线性统统来自后面挂的激活函数。这一点在模型设计时很有用如果你在某种“特殊卷积”后面忘了加激活函数那它和普通卷积一样仍然只提供了线性表达能力多堆几层并不会增加网络的非线性容量。6.4 “既然卷积是线性映射为什么不能只用线性代数完成CNN的一切分析”这是个好问题也常被忽略。严格来说单个卷积层是线性的可以等效为矩阵多层卷积堆叠没有激活还是线性的整体等效为一个单层线性矩阵但现代CNN里每一层卷积后面都有ReLU复合函数不再是线性函数因此对完整网络的分析必须依赖非线性模型工具如神经正切核、分段线性分解等不能简化为单一矩阵。也就是说“卷积层是线性映射”说的是卷积层自身的算子属性不是整个网络的属性。这个边界要时刻记在脑子里否则容易推出错误的结论——比如“所有CNN都可以用一个巨大的矩阵来等价”。只有无激活网络的宽度足够大时这种等价才在严格意义上成立。6.5 “线性层这么好分析为什么不用纯线性网络”因为线性网络叠加再多层的表达能力也停留在单层矩阵的水平无法解决非线性可分问题。分类任务里常见的“异或问题”就是最简单的反例线性模型永远无法用一条直线把四个点分开只有引入激活函数之类的非线性处理才能做到。卷积层的价值在于高效地、结构化地提取局部特征而线性的本质让它可以被矩阵工具分析激活函数的价值在于提供非线性表达。两者是互补关系缺一不可。7. 在实际项目中的一点个人体会想通了“卷积层为什么是线性映射”这件事之后我在实际项目里会顺手用到几个小技巧这里一并分享。其一做特征图可视化和可解释性分析时如果把某一层卷积等效为矩阵就可以直接计算输入到该层输出的梯度或相关性矩阵而不必依赖框架里复杂的自动求导链路。特别是在做基于线性假设的归因分析时等效矩阵能够直接给出解析解比数值近似快很多。其二做网络剪枝时我经常先看卷积层等效矩阵的秩结构。如果一个卷积层的等效矩阵呈现明显的低秩特征说明很多卷积核是冗余的这比单纯看权重大小更可靠。这种分析依赖的正是“卷积层有矩阵表示”这个事实。其三做频域加速时卷积定理也源于线性时不变系统的性质。一旦你接受卷积层的线性本质离散余弦变换或傅里叶变换下做卷积的操作就非常自然了变换本身是线性映射卷积在频域变成逐元素乘法计算复杂度可以有效下降。工程上不一定常用但理解原理会帮你判断什么时候可以尝试这种策略。最后再提一个很小的实操建议当你对自己实现的某个“新颖卷积变体”是否线性拿不准时直接运行本文第5节的线性检查器10行代码就能给出结论。很多论文里声称的新操作一测就会发现它是仿射或分段线性的并不像文字描述的那样神秘。基础概念清清楚楚后面读论文、复现模型、调参分析都会顺很多。