阅读 CNN 代码时,我先给每个张量写下形状,再检查卷积核、步长和通道数。这样比只记“提取局部特征”更容易发现实现错误。
四个维度分别是什么
PyTorch 的常见输入排列是 (N, C, H, W):批大小、通道数、高度、宽度。普通二维卷积的权重排列为 (C_out, C_in / groups, K_h, K_w);输出通道数来自不同的可学习卷积核,并不是输入 RGB 通道数的另一种叫法。
对单个空间方向,输出尺寸为:
L_out = floor((L_in + 2 * padding - dilation * (kernel - 1) - 1) / stride) + 1
使用偏置时,参数数量为:
C_out * (C_in / groups) * K_h * K_w + C_out
这些定义与分组约束见 PyTorch Conv2d 官方文档。框架通常实现的是互相关计算;不要因为接口叫卷积,就额外在代码里翻转一次卷积核。
一个不依赖深度学习框架的小检查
def output_size(length, kernel, stride=1, padding=0, dilation=1):
if min(length, kernel, stride, dilation) <= 0 or padding < 0:
raise ValueError("invalid convolution geometry")
result = (length + 2 * padding - dilation * (kernel - 1) - 1) // stride + 1
if result <= 0:
raise ValueError("kernel does not fit")
return result
def parameter_count(cin, cout, kernel, groups=1, bias=True):
if min(cin, cout, kernel, groups) <= 0 or cin % groups or cout % groups:
raise ValueError("invalid channels or groups")
return cout * (cin // groups) * kernel * kernel + (cout if bias else 0)
assert output_size(32, 3, stride=2, padding=1) == 16
assert output_size(32, 3, dilation=2) == 28
assert parameter_count(3, 16, 3) == 448
assert parameter_count(3, 12, 3, groups=3) == 120
这只是形状和参数的算术检查,不是一个卷积训练实验。通过这些断言不代表模型已经学到了有效特征。
三处概念纠偏
- 卷积不等于天然的平移不变性。 共享权重意味着同一局部运算用于不同位置;输入移动时,特征也可能随之移动。边界、步长和池化会进一步影响结果,不能据此保证整网输出始终不变。
- 展平不等于信息在数学上被删除。 位置仍可由向量索引表示;问题主要在于普通全连接层没有直接采用卷积的局部连接、共享参数等归纳偏置。
- 训练与推理不是同一套 BN 行为。 常见设置下,训练使用当前批次统计并更新运行统计,推理使用运行统计。细节及
track_running_stats=False的例外见 BatchNorm2d 官方文档。
练习
用纸笔计算两层卷积后的尺寸,再在框架中核对。把 groups 改成无法整除输入或输出通道的值,确认程序会拒绝;然后检查模型切换到 eval() 后,哪些层的行为改变。
课程对应与整理边界
对应疏锦行《26 计算机视觉专题》的《02_卷积神经网络》讲义与卷积、池化、BN、感受野视频目录。本文围绕实现核验重组知识,未复制讲义图示;视频口头补充尚未完整核验。
原创
卷积层学习笔记:先算清形状和参数,再理解特征
本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
评论交流
欢迎留下你的想法