跳到主要内容
约 12 分钟4 张卡

卷积神经网络

卷积神经网络(Convolutional Neural Network,CNN)是在网络中使用卷积层处理局部模式的神经网络。卷积核在不同位置复用同一组参数,使模型可以用相同的规则识别图像各处的局部特征,再逐层组合这些特征。

简历中出现图像分类、识别等项目时,需要解释每层接收什么、输出什么,以及这种结构为什么适合项目数据。只列出“卷积、激活、池化、全连接”还不够,尺寸变化、参数从哪里来、效果怎样验证,都要能对应到实际模型。下文以二维图像分类为例,代码使用 PyTorch 2.6.0。

1. 面试核心​

先记住这几条,再读正文
  • 卷积层用局部连接和参数共享提取特征:一个输出位置只读取局部区域,同一个输出通道在不同空间位置使用同一组权重。卡 · 综合题
  • 卷积核数量决定输出通道数,步幅、填充和核尺寸共同决定空间尺寸。计算参数量时还要计入输入通道,不能只算二维核的面积。卡 · 多通道与参数共享
  • 激活函数提供非线性,池化汇总局部信息,分类层输出类别分数。池化不一定是必需层,也不保证任何平移都不改变结果。卡 · 平移等变与平移不变
  • 感受野是某个特征位置可能依赖的输入区域。堆叠卷积和下采样会改变它,不能只看最后一层卷积核有多大。卡 · 感受野
  • CNN 的结构优势来自对局部模式的假设,是否优于其他模型仍要在可比数据和预算下验证。训练损失下降,也不能替代独立测试集上的效果评估。卡 · 综合题

2. 卷积层​

2.1 输入、卷积核与特征图​

张量是按多个维度组织的数值数组。在这里,一批图像用 (N, C, H, W) 表示:N 是批量大小,C 是通道数,H、W 是高和宽。RGB 图像通常有 3 个输入通道;中间层的通道则是模型计算出的特征,不再分别等同于红、绿、蓝。

卷积核是一组在空间位置之间共享的权重。它覆盖一个小区域,将对应数值相乘再相加,得到一个输出值;随后移动到下一个位置,继续使用同一组权重。输出按位置排列,就形成特征图。

先看单输入通道、单输出通道,没有偏置、填充的例子:

import torch
from torch.nn import functional as F

x = torch.tensor([[
[[1., 2., 0.],
[0., 3., 1.],
[2., 1., 4.]]
]])
weight = torch.tensor([[
[[1., -1.],
[0., 2.]]
]])
y = F.conv2d(x, weight, stride=1, padding=0)
print(tuple(y.shape))
print(y[0, 0].tolist())

输出形状是 (1, 1, 2, 2),数值是 [[5.0, 4.0], [-1.0, 10.0]]。左上角结果为 1×1 + 2×(-1) + 0×0 + 3×2 = 5。右移一格后,读取另一块输入,但仍然使用 weight 中的同一组权重。

这里没有翻转卷积核,数学上称为互相关。PyTorch 的 Conv2d 使用这种计算,深度学习中仍习惯称它为卷积。例子手动指定了权重,实际训练会根据任务调整权重;不能预先认定某个通道一定学到“边缘”或某个物体。

2.2 多通道与参数共享​

普通卷积的一个输出通道会读取所有输入通道。在每个输入通道的局部区域做乘加,再把这些结果相加,最后加上这个输出通道的偏置。

假设输入有 3 个通道,输出有 8 个通道,卷积核空间尺寸为 3×3。groups=1 时,权重形状是 (8, 3, 3, 3):8 组输出,每组都包含覆盖 3 个输入通道的权重。带偏置的参数量为:

参数量 = Cout × Cin × Kh × Kw + Cout
= 8 × 3 × 3 × 3 + 8
= 224

共享发生在空间位置之间,不是所有输出通道共用一套权重。 每个输出通道可以学习不同的特征,但这个通道在左上角和右下角使用相同的核。固定通道数和核尺寸时,图像从 32×32 变成 64×64,不会使这层卷积的参数数量增加;输出元素和计算量仍会增加。

上述公式限定普通卷积。分组卷积会把输入、输出通道分组,每个输出只连接到本组输入,权重数量相应改变;不能把普通卷积公式不加条件地套用。项目没有用到分组卷积时,先把普通卷积的连接关系讲清楚即可。

2.3 输出尺寸​

步幅(stride)是卷积核相邻两次放置的位置间隔;填充(padding)是在输入边界补出的区域;膨胀率(dilation)是核中相邻采样点的间隔,1 表示连续采样。

对高、宽分别计算,若两侧填充相同,记输入尺寸为 I,核尺寸为 K,步幅为 S,每侧填充为 P,膨胀率为 D:

输出尺寸 = floor((I + 2P - D(K - 1) - 1) / S) + 1

floor 表示向下取整。比如 I=32、K=3、P=1、S=1、D=1,输出仍是 32;改成 S=2,输出变成 16。若有效核比填充后的输入还大,则不能产生正常输出,应该检查配置,而不是把负数当成张量尺寸。

填充可以保留更多边缘位置,但补出的零不是真实图像信息。PyTorch 2.6 的 padding='same' 只支持步幅 1,不能仅凭名字推断任意步幅都能保持输入尺寸。

3. 各层的作用​

CNN 的结构由任务决定,不要求每个网络都有完全相同的层。下面是一种用于说明图像分类过程的小网络,输入是 N 张 3×32×32 的图像:

层输出形状作用
3×3 卷积,3→8 通道,填充 1(N, 8, 32, 32)在局部区域计算 8 类特征响应
ReLU(N, 8, 32, 32)逐元素执行 max(0, x),引入非线性
2×2 最大池化,步幅 2(N, 8, 16, 16)在每个通道内汇总局部最大值并下采样
3×3 卷积,8→16 通道,填充 1;ReLU(N, 16, 16, 16)组合上一层特征并作非线性变换
全局平均池化(N, 16, 1, 1)每个通道将所有空间位置取平均
展平;全连接层,16→10(N, 10)将通道汇总结果映射成 10 类分数

激活函数在加权计算后加入非线性。若只串联线性或仿射变换,整体仍能合并成一个仿射变换,不能仅靠堆叠层数得到这种非线性表达能力。ReLU 没有可训练参数,但它的输出取决于输入。

池化按固定规则汇总一个区域内的值。最大池化取最大值,平均池化取平均值;常见的这两种池化本身没有可训练权重。上表中的最大池化不改变通道数,但缩小高、宽。下采样减少后续空间计算,也可能丢掉细节,不能认为只会带来收益。带步幅的卷积也可以下采样,CNN 不一定必须包含池化层。

全局平均池化将每个通道压成一个数,减弱分类头对精确位置的依赖,但也丢掉了位置布局。需要像素级分割、定位或区分特定空间关系时,不能不加判断地把所有空间信息都平均掉。

最后的类别分数(logits)是模型为各类别输出的未归一化数值。它们不是概率;需要展示概率时,可以沿类别维做 softmax。输出维度、标签含义和使用的损失函数必须一致。

3.1 感受野​

感受野描述某个输出位置在结构上可能受到原始输入哪些位置的影响。第二层卷积读取的是第一层特征图,所以它虽然也使用 3×3 核,却可能间接读取比 3×3 更大的原图区域。

对单一路径、膨胀率 1 的网络,可以同时维护两个量:r 是感受野边长,j 是相邻特征位置在原图上的间距。输入处 r=1、j=1;经过核尺寸 K、步幅 S 的层后:

新的 r = 原来的 r + (K - 1) × 原来的 j
新的 j = 原来的 j × S

上表的第一层卷积得到 r=3、j=1;2×2、步幅 2 的池化后 r=4、j=2;第二层 3×3 卷积后 r=8、j=2。ReLU 逐位置变换,不扩大感受野。因此,第二层的一个内部特征位置可能依赖原图的 8×8 区域。

这里是结构允许的范围,不表示每个像素的实际影响相同。最大池化在某次输入上只选中局部最大值,权重和激活也会改变实际影响;边缘位置的范围还可能包含填充。后面的全局平均池化会汇总各个位置,整个分类输出的输入范围不能继续只算成 8×8。

4. 结构优势与限制​

局部连接减少直接连接,参数共享让同一种局部规则在不同位置复用。 如果有用的模式会出现在图像多个位置,这种结构就符合数据特点,不必为每个位置分别学习一整套规则。

以 32×32 输入、输出 8 通道的 3×3 普通卷积为例,它使用 224 个参数。若保留相同的局部连接,却为每个输出空间位置分别配置 224 个参数,就需要 32×32×224 = 229376 个参数。这个对照说明共享减少了多少独立参数,不是证明共享一定让准确率提高。

CNN 适合利用网格数据中的局部结构,但存在条件:

  • 局部核无法一层直接连接任意远的两个位置,远距离关系需要更深的结构、扩大感受野或其他机制。
  • 参数共享约束不同位置使用同样的规则;若位置本身有特殊含义,还需要让模型获得相应信息。
  • 下采样和全局汇总会损失细节,模型设计要看分类、检测、分割等任务需要保留什么。
  • 数据量、训练方法、预训练、输入分辨率和硬件预算都会影响比较结果,不能说 CNN 必然比全连接网络或 Transformer 更好。

自注意力可以按输入内容建立位置之间的关系,具体机制见 Transformer 与注意力机制。比较时要落到任务和约束,而不是只比较两个结构名称。

4.1 平移等变与平移不变​

平移等变是输入平移后,输出特征也按对应方式移动;平移不变是输入移动了,输出结果仍不变。二者不是同一个要求。

在步幅 1、不受裁剪或边界补值影响的内部区域,共享卷积核可以使局部响应随输入平移。有限图像的裁剪、填充和带步幅采样会影响这个关系,不能无条件推广成整张分类结果对任意平移都不变。

池化也不提供这样的无条件保证。把一行输入 [0, 1, 0, 0] 按宽度 2、步幅 2 做最大池化,得到 [1, 0];向右移动一个位置后变成 [0, 0, 1, 0],池化结果变成 [0, 1]。数值跨过窗口边界,结果就改变了。

训练时适当的数据增强可以帮助模型适应任务允许的变化,但增强必须保持标签语义。比如某种旋转会改变字符类别,就不能把旋转后的样本继续当成同一标签。

5. 训练与评估​

训练根据样本和目标调整参数。一次基本训练步骤是:前向计算类别分数,用损失函数衡量分数与标签的差距,再通过反向传播计算梯度,由优化器更新参数。

下面把前文的小网络写成可运行程序,并执行一个构造批次的更新。随机输入和临时标签只用于说明计算链路,不用于证明模型能识别任何真实图像:

import torch
from torch import nn

torch.manual_seed(0)
model = nn.Sequential(
nn.Conv2d(3, 8, 3, padding=1),
nn.ReLU(),
nn.MaxPool2d(2, stride=2),
nn.Conv2d(8, 16, 3, padding=1),
nn.ReLU(),
nn.AdaptiveAvgPool2d(1),
nn.Flatten(),
nn.Linear(16, 10),
)
x = torch.randn(2, 3, 32, 32)
target = torch.tensor([0, 1], dtype=torch.long)

with torch.no_grad():
value = x
for layer in model:
value = layer(value)
print(type(layer).__name__, tuple(value.shape))
print("parameters", sum(p.numel() for p in model.parameters()))

model.train()
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
before = model[0].weight.detach().clone()
optimizer.zero_grad(set_to_none=True)
logits = model(x)
loss = nn.CrossEntropyLoss()(logits, target)
loss.backward()
print("grad_finite", bool(torch.isfinite(model[0].weight.grad).all()))
optimizer.step()
print("weight_changed", not torch.equal(before, model[0].weight))

model.eval()
with torch.no_grad():
probabilities = model(x).softmax(dim=1)
print("probabilities", tuple(probabilities.shape))

各层形状与前面的表一致,N 为 2。参数总量是 1562,由两个卷积层的 224、1168 和全连接层的 170 组成。最后三项输出是 grad_finite True、weight_changed True、probabilities (2, 10),表明梯度和一次更新实际发生;这仍然不是模型效果指标。

zero_grad 清理上一轮梯度,backward 计算当前梯度,step 使用梯度更新参数。PyTorch 默认会累加叶子参数上的梯度,普通逐批训练不能忘记清理;需要梯度累积时,则应有意控制清理和更新时机。

对这里的单标签多分类任务,CrossEntropyLoss 直接接收 logits 和类别索引,不要先给它做 softmax。示例标签是长度为 N 的整数张量,类别取值在 0 到 9;具体损失还可以支持其他标签形式,不能把这一个例子的接口约束推广到所有任务。

model.eval() 切换模块的评估模式,主要影响 Dropout、BatchNorm 等训练和评估行为不同的层,它不等于关闭梯度记录。torch.no_grad() 才控制此处是否记录反向传播所需的计算。本例没有 Dropout、BatchNorm,但仍明确区分两个动作。

项目结果还要说明数据来源、划分方式、指标和对照模型。训练集用于学习参数,验证集用于选配置,独立测试集用于评估最终方案;同一对象的近似照片或相邻视频帧不能随意拆到两侧。分类不平衡时也不能只看准确率,具体划分和指标见 模型评测及其分类指标。

解释“CNN 更适合这个项目”时,应提供同一数据划分、预处理与评价口径下的对照,说明效果、资源和延迟的取舍。训练损失下降但验证效果变差,可能出现过拟合,也应检查分布、标签和训练评估流程,不能仅凭一条曲线确定原因。

6 · 面试题卡片​

本页题目与参考回答

6.1 CNN 项目中每层有什么作用,相比其他模型有什么优势?​

题型:综合题

考察重点:输入输出、各层分工、局部连接与共享参数,以及项目结论的依据。

相关内容:卷积层、各层的作用、结构优势与限制、训练与评估。

参考回答

沿着实际模型说明形状和信息怎样变化,再解释结构选择。 卷积提取局部特征,激活引入非线性,池化或带步幅卷积汇总、下采样,分类头把特征映射为类别分数。不是每个 CNN 都必须包含相同的层。

局部连接减少直接连接,参数共享让同样的特征提取规则复用于不同位置,适合局部模式反复出现的图像数据。更深的层可以组合局部信息,但下采样也会损失细节,远距离关系和位置要求需要额外考虑。

项目优势应由可比实验支持:说明数据划分、预处理、对照模型、指标与资源预算,不把结构上的参数节省直接等同于效果更好。

6.2 输入为 3×32×32,3×3 卷积输出 8 通道,怎样计算形状和参数量?​

考察重点:通道、空间尺寸、偏置和共享范围。

相关内容:多通道与参数共享、输出尺寸。

参考回答

还需确认步幅、填充、膨胀率、分组和是否带偏置。 普通卷积取步幅 1、每侧填充 1、膨胀率 1 时,输出为 8×32×32;批量维保持不变。

带偏置时参数量为 8×3×3×3+8=224,权重形状为 (8, 3, 3, 3)。同一个输出通道的核在不同空间位置共享,所以参数量不再乘输出的高和宽。若只把步幅改为 2,空间尺寸变为 16×16,参数量仍是 224。

6.3 CNN 的卷积和池化能保证平移不变吗?​

考察重点:等变与不变、边界和采样窗口。

相关内容:平移等变与平移不变。

参考回答

不能无条件保证。 步幅 1 的共享卷积在不受边界影响的区域,使特征响应随输入移动,这是等变,不是输出不变。

池化可以减少对局部细节的依赖,但输入跨过池化窗口边界时,结果仍可能改变。填充、裁剪、下采样和分类头也会影响最终结果,需要针对任务允许的变化进行训练和测试。

6.4 为什么第二层 3×3 卷积的感受野不一定是 3×3?​

考察重点:层间组合、特征间距,以及结构范围与实际影响。

相关内容:感受野。

参考回答

第二层读取的是上一层特征,每个特征已经对应一片原始输入。 新一层组合这些特征,会扩大它对原图的依赖范围。

例如 3×3、步幅 1 卷积之后接 2×2、步幅 2 池化,再接 3×3、步幅 1 卷积,感受野边长依次为 3、4、8。这个 8×8 是单个特征位置的结构范围,不表示所有像素贡献相同,也不是后续全局汇总后的整个分类范围。

7. 参考资料​

来源:Coding 101 原文 · 免费阅读,引用请保留出处。内容使用说明