「用一个线性层」,在 CNN 的语境下至少能对应三种写法,参数量差三个数量级,讨论时很容易互相错位。题面给的中间特征是 14×14×512,目标 14×14×128,下面都按这个尺寸算。
第一种是逐位置的线性映射,每个空间位置共用同一套 512→128 的权重,位置之间共享。这就是 1×1 卷积,参数量 512×128+128 = 65,664,单图乘加 12,845,056 次。
第二种也是逐位置的线性映射,但每个位置各学一套自己的权重,位置之间不共享。参数量变成 196 倍,12,870,144;计算量一点没变,还是 12,845,056 次。
第三种把整张 14×14×512 展平成一个 100352 维向量,用一个 Linear 映射到 128×14×14 = 25088 维,再 reshape 回 4D。参数量 2,517,656,064,是 1×1 卷积的三万八千多倍,计算量同时涨到 196 倍。
三种写法在连接方式上的差别就是这张图里的三种连线,参数量的差距全部来自「一套权重被复用了多少次」和「每个输出位置看到了多大的输入范围」。
这道题下面已经有回答算过这笔账,一个给出 196 倍,另一个给出 195 倍,看着像在说同一件事,其实对上的不是同一组量。196 倍是 1284 万参数比 6.5 万参数,195 倍是 25.2 亿参数比 1285 万次乘加。把口径摊平之后就是上面那张图里的关系。第二种写法参数涨 196 倍,计算量一分不多;第三种两头一起涨。
还有第四种写法值得单拎出来,把整张图展平后只映射到一个 128 维向量,参数量 12,845,184,是 1×1 卷积的 195.6 倍,单图乘加 12,845,056 次,和 1×1 卷积一模一样。付出同样的计算和两百倍的参数,换来的输出只有 128 个数,14×14 的网格没有了。
这个方案用在分类网络最后一步是标准做法,放在中间层等于把空间维一次性丢掉,后面接不上任何卷积。
第一种写法本身就是线性层。nn.Conv2d(512, 128, 1) 的权重形状是 (128, 512, 1, 1),nn.Linear(512, 128) 是 (128, 512),元素个数一致,做的事也一致,都是把每个位置上 512 维的向量乘成一个 128 维的向量。
取同一份权重,一条路走 F.conv2d,一条路把输入换成 channels-last 之后走 F.linear:
import torch, torch.nn as nn, torch.nn.functional as F
torch.manual_seed(0)
conv = nn.Conv2d(512, 128, kernel_size=1)
x = torch.randn(1, 512, 14, 14)
y_conv = conv(x) # (1, 128, 14, 14)
W = conv.weight.reshape(128, 512)
y_lin = F.linear(x.permute(0, 2, 3, 1), W, conv.bias) # (1, 14, 14, 128)
y_lin = y_lin.permute(0, 3, 1, 2)
print((y_conv - y_lin).abs().max().item(


1113

被折叠的 条评论
为什么被折叠?



