
一聊到 1x1 卷积核很多刚接触深度学习的人第一反应是这不就是一个“看了等于没看”的卷积吗明明叫卷积却连相邻像素都不看那还有什么用我最早也这么想过直到某次调网络时发现自己以为的瓶颈卡在通道维度上而不是空间维度才真正意识到 1x1 卷积核的价值它不负责发现空间特征而是专门负责把不同通道之间的信息打通。这篇文章会把 1x1 卷积核的定义、数学机制、设计动机、典型用法、代码实现和踩坑经验一次性讲清楚。适合那些已经能看懂普通卷积、但始终搞不懂“为什么要在网络里塞一堆 1x1 卷积”的人也适合准备自己搭网络、做轻量化改造的工程师。看完之后你会发现1x1 卷积核不是花架子它是很多经典骨架里的隐形功臣。1. 1x1卷积核到底在做什么1.1 从基本定义说起先回到卷积的定义。一个普通卷积核通常会有三个维度高、宽、输入通道数再加上输出通道数。常见的 3x3 卷积就是在输入特征图的高和宽方向上滑动一个 3x3 的窗口每走到一个位置就把窗口内所有像素、所有通道的值做加权求和得到输出特征图上的一个值。1x1 卷积核高和宽都是 1输入通道数和输出通道数可以任意设置。它在空间上不滑动任何邻居只看当前坐标点这一个像素。换句话说对于输入特征图上的每个位置 (h, w)1x1 卷积做的事情是把这一点的所有通道值取出来组成一个长度为 C_in 的向量然后用一组权重把这个向量映射成长度为 C_out 的另一个向量。用公式写就是y_{h,w,k} b_k Σ_{c1}^{C_in} x_{h,w,c} * w_{k,c}其中 x 是输入特征图y 是输出特征图k 是输出通道编号。这个公式对每个空间位置都独立执行不涉及相邻像素。所以说1x1 卷积对空间的感受野就是 1但它对通道的感受野是完整的 C_in。这决定了它最核心的职责通道维度的重组合。1.2 和 3x3 卷积的本质差别很多新手拿 1x1 卷积和 3x3 卷积直接对比会发现两者表面上看只是核大小不同但实际定位差异非常大。维度1x1 卷积3x3 卷积空间感受野只有当前像素周围 3x3 区域空间特征提取基本不提取能提取边缘、纹理等局部模式通道信息融合完整融合所有通道同时融合空间邻域和通道典型用途通道变换、升降维、融合特征提取、下采样前的空间建模参数量C_in * C_out9 * C_in * C_out你可以这样理解3x3 卷积像一场小组讨论每个像素要参考周围邻居的意见1x1 卷积更像给每个像素单独开一场内部会议把它的多个通道属性重新投票加权最后输出一个新的属性向量。它没有邻居参与自然也就不包含空间上下文。1.3 从全连接层的视角重新认识如果只看单点上的计算1x1 卷积其实就是一个共享权重的全连接层。输入向量长度是 C_in输出向量长度是 C_out中间没有隐藏层就是一次线性变换。但关键是“共享权重”四个字。普通全连接层如果作用在图像上一般要把整张图展开成一维向量参数数量会爆炸而 1x1 卷积是在每个空间位置共享同一套权重参数只由通道数决定和图片尺寸 H、W 完全无关。所以它保留了卷积网络最重要的平移不变性又实现了全连接层那种“全局通道交互”的能力。如果后面再接一个 ReLU它就成了一个非线性的通道映射。这也解释了为什么很多网络里喜欢连续堆两个 1x1 卷积第一个负责降维中间过激活第二个负责升维本质上就是一个两层的通道 MLP只不过作用在每个像素点上。2. 为什么网络里需要大量 1x1 卷积2.1 降维与升维的基本动机随着网络层数加深特征图的通道数通常会越来越大。比如某层输出可能从 128 升到 256、512 甚至更多。通道多的一般含义是“信息丰富”但同时也带来两个问题计算量急剧增加。因为卷积的算子和 C_in 成正比。冗余信息多通道之间可能存在强相关性。1x1 卷积可以在保持空间尺寸不变的前提下把通道数压下去。比如输入是 256 通道我用一个 1x1 卷积把它变到 64 通道然后再去做后续的 3x3 卷积。这时候 3x3 卷积的输入通道大幅减少整体计算量就降下来了。这就是瓶颈结构的基本思路。瓶颈这个名字很形象先把通道“掐细”让信息通过一个狭窄的管道之后再“撑开”恢复成高维表达。好处是计算量下降坏处是有信息损失所以压缩比例不能太夸张。2.2 参数量和计算量的真实对比光说抽象不如直接算一笔账。假设输入特征图是 56x56x256现在要得到一个 56x56x128 的输出。方案一直接用 3x3 卷积输入 256 通道输出 128 通道。参数量256 * 9 * 128 294,912计算量56 * 56 * 294,912 ≈ 9.25 亿次乘加方案二用瓶颈结构1x1 卷积先降到 64 通道再 3x3 卷积保持 64 通道最后 1x1 卷积升到 128 通道。1x1 降维参数256 * 1 * 64 16,3843x3 卷积参数64 * 9 * 64 36,8641x1 升维参数64 * 1 * 128 8,192总参数61,440总计算量56 * 56 * 61,440 ≈ 1.93 亿次乘加同样是输入输出尺寸完全一样瓶颈结构参数减少了接近 80%计算量减少了接近 80%。这是非常可观的收益而且是在不改变输入输出形状、不牺牲空间尺寸的前提下得到的。当然这不是说 1x1 卷积没有成本。它本身也有参数和计算量只是相对于 3x3 卷积便宜得多。你可以把 1x1 卷积看成“廉价通道变换器”3x3 卷积看成“昂贵的空间特征提取器”两者搭配才能各司其职。2.3 增加非线性表达能力每个 1x1 卷积后面通常都会接一个激活函数比如 ReLU。原本卷积本身是线性变换就算堆一百层线性卷积理论上也能合并成一次线性变换没有增加表达力。但每过一层激活函数网络就能学到非线性的通道关系。两个 1x1 卷积中间夹一个 ReLU相当于在通道维度上构造了一个非线性映射。这个映射可以组合通道、抑制某些无用通道、放大某些关键通道。如果网络早期就遇到通道间复杂关联的场景这个能力非常有用。2.4 真正意义上的跨通道信息融合普通卷积天然就在做空间和通道的联合融合但如果你刻意观察会发现深度可分离卷积这类结构里逐通道卷积只在每个通道内部做空间卷积通道之间没有交互。这时候 1x1 卷积就派上用场了它把逐通道卷积输出的多个通道重新混合起来。换句话说1x1 卷积在很多结构里扮演的是“汇合节点”的角色。没有它各个通道就像各自为战的几条独立管道有了它这些管道的信息才能汇聚成一整套表达。3. 1x1卷积在典型结构里的几种用法3.1 瓶颈结构里的降维桥很多分类网络、检测网络里都有类似的块先用 1x1 把大通道压成小通道然后过一个 3x3 卷积提取空间特征最后再用 1x1 把小通道恢复成大通道。这种设计在视觉上一眼就能看出来特征图的通道数先变小再变大像沙漏一样。这里的设计逻辑很清晰3x3 卷积负责空间建模但它的计算成本高所以要先把通道数降下来减少它的输入规模。而 1x1 卷积成本低完全可以在瓶颈前后自由调整通道数。我在实际搭网络时通常把瓶颈的中间通道数设为输入通道的 1/4 或 1/2效果比较稳妥。3.2 残差结构中的捷径匹配如果网络中有跨层相加的结构两个分支的通道数必须一致。输入通道和输出通道不一致的时候最常见的做法就是用一个 1x1 卷积把输入支路的通道数投影到目标通道数再去做相加。这里的 1x1 不参与特征提取只是简单地做通道对齐。你可能会问为什么不用 3x3 来投影因为捷径分支本来就不应该承担太多非线性变换如果用了 3x3就变成了一个额外的特征提取分支反而干扰残差语义。1x1 的线性特性在这里反而成了优势。3.3 分类头里的通道整理在常见的分类网络里最后一般会把特征图做全局平均池化得到一个 1x1xC 的向量然后再接全连接层输出类别分数。但在一些全卷积网络或者分割、检测任务里最后一个阶段不愿意用全连接层因为全连接层会把空间结构丢掉这时候就会用 1x1 卷积作为输出层。比如输入特征图是 32x32x256想要 32x32x21 的输出直接用 1x1 卷积输出通道设为 21就能得到每个像素点上的 21 类预测。这种方式保留了空间位置信息是像素级任务里特别常用的操作。3.4 注意力模块中的通道权重生成在通道注意力类的模块出现之后1x1 卷积几乎成了标配。先把空间维度压缩成全局统计量然后用 1x1 卷积或者更准确地说用通道维度的线性变换生成一组权重最后把权重和原始特征图逐通道相乘。这里如果不用 1x1 卷积而是用全连接层效果也能做到但 1x1 卷积对输入尺寸不敏感模块写起来更通用。而且 1x1 卷积还方便先降维再升维中间加激活形成一种“瓶颈式”的注意力映射既能降低参数又比简单全连接更容易训练。3.5 多尺度融合时的统一通道数当网络里有多个分支每个分支的特征图空间大小一样、但通道数不一样时通常需要统一通道数才能拼接或者相加。这时候用 1x1 卷积把每个分支都投影到相同的通道数是最自然的做法。这种写法在手工设计多分支结构时特别常见。我自己的习惯是只要遇到“通道数不一致但空间信息等价”的融合需求第一反应就是用 1x1 卷积对齐而不是直接拼接或者强制 broadcasting因为后者在实际实现里很容易埋 bug。4. 动手实操自己搭一个1x1卷积块4.1 用常见框架跑一个最小示例先看一个最直白的代码用常见的深度学习框架随手搭一个 1x1 卷积。以 PyTorch 风格为例import torch import torch.nn as nn # 输入4张图3通道64x64 x torch.randn(4, 3, 64, 64) # 1x1卷积输入3通道输出16通道 conv nn.Conv2d(3, 16, kernel_size1, stride1, padding0) y conv(x) print(y.shape)这段代码的输出形状会是torch.Size([4, 16, 64, 64])你会发现输入是 3 通道输出变成 16 通道但空间尺寸 64x64 完全没变。这就是 1x1 卷积最直观的外在表现只改变通道数不改变空间尺寸。如果你把 stride 设置为 2空间尺寸会缩小一半。但通常情况下1x1 卷积就是用来调整通道的很少有人拿它做下采样。4.2 参数量计算和打印验证接着看参数。1x1 卷积的权重形状是 (C_out, C_in, 1, 1)偏置形状是 (C_out,)可以通过下面的方式打印出来for name, param in conv.named_parameters(): print(name, param.shape)输出结果weight torch.Size([16, 3, 1, 1]) bias torch.Size([16])weight 参数量是 16 * 3 * 1 * 1 48bias 是 16总共 64。这和输入图片是 64x64 还是 256x256 没有任何关系只和输入输出通道数有关。这也是 1x1 卷积大小恒定、容易替换的原因。4.3 做一个带 BN 和 ReLU 的标准通道混合块真正用的时候很少有人只用裸卷积。标准写法一般是“1x1 卷积 批归一化 ReLU”。下面是一个可以塞进任意网络的小块class ChannelMixBlock(nn.Module): def __init__(self, in_channels, mid_channels, out_channels): super().__init__() self.conv1 nn.Conv2d(in_channels, mid_channels, kernel_size1, biasFalse) self.bn1 nn.BatchNorm2d(mid_channels) self.relu nn.ReLU(inplaceTrue) self.conv2 nn.Conv2d(mid_channels, out_channels, kernel_size1, biasFalse) self.bn2 nn.BatchNorm2d(out_channels) def forward(self, x): x self.conv1(x) x self.bn1(x) x self.relu(x) x self.conv2(x) x self.bn2(x) return x这个块先用第一个 1x1 把通道压缩到 mid_channels再经过激活再用第二个 1x1 把通道恢复到 out_channels。它做的事情就是前面反复提到的瓶颈式通道变换。如果你想可视化中间结果可以直接在 forward 里把 x 的 shape 打出来往往能看到 64x64 的空间尺寸始终不变。4.4 权重初始化和训练经验1x1 卷积的训练稳定性和普通卷积不太一样。因为它没有空间感受野初始阶段非常依赖通道维度的有效信息如果通道被压得太狠前面的层可能一开始学不到东西。我的经验是如果输入通道很多比如 1024中间通道最好不要小于 128否则信息瓶颈太严重。初始化策略可以用默认方式但如果是接近分类头的 1x1 卷积可以适当把标准差调小一点让初期输出更温和。1x1 卷积后面一定接 BN 加激活否则线性变换堆叠没有意义。在训练初期观察 loss 是否下降如果前若干轮完全没有变化优先检查这个 1x1 的输出通道是不是堵死了。5. 常见误区与避坑指南5.1 把1x1卷积等同于全连接层虽然 1x1 卷积对单个像素来说等价于全连接但它和“全局全连接层”有本质区别。全局全连接层会把所有空间位置都拉平破坏局部位置关系而 1x1 卷积是逐像素共享同一个线性映射空间结构保持不变。所以在网络中间替换 FC 时要确认前一层是全局池化后的 1x1xC而不是带着 HxW 的特征图。5.2 只降维不升维通道信息越压越扁我自己见过很多初学者在简化网络时疯狂叠加 1x1 卷积降维最后通道数越来越低训练精度直接崩掉。通道数代表信息的并行容量降维是个压缩操作压缩过度就会丢信息。一般单次压缩比控制在 4 倍以内比较安全如果必须要压更低建议加残差结构来保留原始通道信息。5.3 把 groups 参数理解错1x1 卷积里有一个坑是设置分组卷积时的行为。如果groups1默认是跨通道融合但如果设置groupsin_channels每一个通道会被独立处理1x1 卷积就退化成了逐通道的缩放和偏移完全失去跨通道融合能力。需要分组的时候最好先确认预期是“局部通道融合”还是“完全独立通道”否则很容易在实验里莫名其妙地掉点。5.4 卷积和BN之间的bias取舍在 1x1 卷积后面接 BN 的时候卷积层的 bias 可以设置为 False。因为 BN 本身带有可学习的偏置项卷积再接 BN 的情况下卷积 bias 的作用会被 BN 抵消留着只会白白增加参数。很多现成框架里的标准块都是这么写的我在代码里也习惯把 biasFalse 写死除非这个卷积后面不接 BN。5.5 算力不高时如何优化1x1卷积1x1卷积虽然算得少但它常常是访存密集型的操作因为每个输出位置都要读取所有输入通道的数据。在端侧设备上如果 1x1 卷积太多反而可能变成瓶颈。这种情况下可以试试把多个 1x1 卷积的权重合并成矩阵乘法减少算子切换成本。尽量让 1x1 卷积前后的通道数对齐避免频繁做大跨度通道变换。用现成的算子融合方案有些框架会自动把 BN 和卷积融合推理阶段更省时间。5.6 连续多个1x1卷积不等于更大的感受野这是最容易被误解的一点。多个 3x3 卷积堆叠可以扩大感受野但多个 1x1 卷积堆叠空间感受野永远是 1。它只能加深通道维度的非线性映射不能获取周边信息。如果你的任务需要上下文比如图像分割中的大目标单独堆 1x1 卷积是没有用的必须结合空间卷积或者空洞卷积。6. 我踩过几次坑之后的体会1x1 卷积核并不是那种一眼看上去很惊艳的算子它更像是网络里的“协调员”。我最早入门时总觉得卷积必须要有大感受野才有价值于是对 1x1 结构不屑一顾。后来在复现一个轻量结构时发现同样的精度目标用了瓶颈设计之后训练显存占用直接少了一大截才意识到通道维度的优化往往比空间维度的优化更关键。如果你正在设计自己的网络我建议把 1x1 卷积当作第一选择来调整通道而不是上来就换更复杂的算子。它的成本低、效果好、兼容性强几乎可以在任何位置嵌入。真正需要谨慎的是把通道压得太低或者把 1x1 用在不该用的地方比如纯空间特征提取任务里。希望这篇关于 1x1 卷积核的总结能帮你少走一些我当时走过的弯路。对于那些还在困惑“为什么那么多网络都喜欢塞 1x1”的朋友建议你亲自动手跑一下上面的小例子改一改输入输出通道看看参数量和训练速度变化你的体会会比只看文章深得多。