一、卷积神经网络核心概念

1. 卷积操作

  • 卷积核在输入图像上滑动,计算对应位置元素相乘再求和,得到输出特征图的一个像素。

  • 卷积核大小(kernel_size):通常是奇数,如 3×3、5×5。

2. 填充(Padding)

  • 在输入边界周围补充像素(通常补0),作用:控制输出尺寸,保护边缘信息。

  • padding=1 表示上下左右各补1行/列0。

  • Same paddingpadding = (kernel_size - 1) // 2,当 stride=1 时,输出尺寸等于输入尺寸。

3. 步幅(Stride)

  • 卷积核每次滑动的步长。步幅越大,输出尺寸越小。

4. 卷积输出尺寸计算公式(必考)

H_{\text{out}} = \left\lfloor \frac{H_{\text{in}} + 2 \times \text{padding} - \text{kernel\_size}}{\text{stride}} \right\rfloor + 1

(宽度同理)

5. 池化层(Pooling)

  • 最大池化:取窗口内的最大值。

  • 平均池化:取窗口内的平均值。

  • 池化层参数:kernel_size(窗口大小)、stride(步幅)。

  • 输出尺寸公式:(H_in - kernel_size) // stride + 1

  • 池化层作用:降维、减少计算量、增强平移不变性。

6. 多输入 / 多输出通道

  • 输入通道(in_channels):彩色图像为3(RGB),灰度图为1。

  • 输出通道(out_channels):每个输出通道对应一组独立的卷积核,提取不同特征。

  • 卷积层构造参数nn.Conv2d(in_channels, out_channels, kernel_size, ...)

  • 卷积核权重张量的形状(通过 conv.weight.shape 查看):(out_channels, in_channels, kernel_height, kernel_width)

    • 注意:第一个维度是输出通道数,第二个是输入通道数。

    • 例如:Conv2d(3, 16, 3) 的权重形状为 (16, 3, 3, 3)

7. 感受野(Receptive Field)

  • 定义:某一层的一个神经元在原始输入图像上所能看到的区域大小。

  • 计算公式(从最后一层向前递推):

    • 初始 RF = 1

    • 从后向前遍历每一层:RF = (RF - 1) * stride + kernel_size

  • 作用:感受野越大,能捕捉越全局的信息。

8. 数据增强

  • 对训练图像进行随机变换(旋转、翻转、缩放等),增加样本多样性,防止过拟合。

  • 测试集不使用数据增强,以保持评估的公平性。


二、LeNet 网络结构(适配MNIST 28×28)

参数 输入形状 输出形状
conv1 1→6, k5, s1, p0 1×28×28 6×24×24
pool1 2×2, s2 6×24×24 6×12×12
conv2 6→16, k5, s1, p0 6×12×12 16×8×8
pool2 2×2, s2 16×8×8 16×4×4
flatten view(x.size(0), -1) 16×4×4 256
fc1 256→120 256 120
fc2 120→84 120 84
fc3 84→10 84 10
  • x.view(x.size(0), -1) 的作用:将每个样本的特征图展平成一维向量,以输入全连接层。


三、PyTorch 中维度操作

  • unsqueeze(dim):在指定位置增加一个大小为1的维度。

  • squeeze():移除所有大小为1的维度(或指定维度)。

  • 池化和卷积层要求输入为4维:(batch, channels, height, width)


《人工智能概论》实验5 考试题

一、单选题(每题3分)

  1. 给定输入尺寸 28×28,卷积核大小 5×5,步幅 1,填充 0,卷积输出尺寸是( )
    A. 24×24
    B. 28×28
    C. 26×26
    D. 20×20

  2. 关于填充(padding)的说法,正确的是( )
    A. 填充只能在输入四周补0
    B. 填充会降低输出特征图的分辨率
    C. padding=1 表示上下左右各补1圈
    D. 填充不影响感受野大小

  3. 最大池化层 MaxPool2d(kernel_size=2, stride=2) 对 4×4 特征图池化后,输出尺寸为( )
    A. 2×2
    B. 3×3
    C. 4×4
    D. 1×1

  4. 在LeNet中,x.view(x.size(0), -1) 的作用是( )
    A. 增加一个维度
    B. 将多维特征图展平成一维向量,保留batch维度
    C. 转置特征图
    D. 复制特征图

  5. 数据增强通常只应用于( )
    A. 训练集
    B. 测试集
    C. 验证集
    D. 所有数据集


二、填空题(每空2分)

  1. 卷积输出尺寸计算公式为:

  2. 一个 Conv2d(3, 16, kernel_size=3, stride=1, padding=1) 层的卷积核形状是 (16, _, _, _)

  3. 感受野计算函数中,从最后一层向前递推:rf = rf + (k - 1) * s。若两层卷积核均为3,步幅第一层2、第二层1,则最终感受野大小为 ____。

  4. LeNet中,第二个池化层 pool2 的输入通道数是 ____,输出通道数不变。池化层不会改变通道数。

  5. 在PyTorch中,unsqueeze(0) 是在第0维增加一个维度,squeeze() 用于移除维度大小为 ____ 的维度。


三、判断题(正确打“√”,错误打“×”,每题2分)

  1. ( )卷积核的数量决定输出特征图的通道数。

  2. ( )池化层具有可训练参数。

  3. ( )感受野只与卷积核大小有关,与步幅无关。

  4. ( )数据增强可以帮助提高模型泛化能力。

  5. ( )在LeNet的forward中,x = x.view(x.size(0), -1) 之前不需要展平,因为全连接层会自动处理。


四、简答题(将原实验中的思考题整合,共35分)

简答题1(3分,来自3.1(1)) 写出卷积输出尺寸的计算公式。

简答题2(4分,来自3.1(3)) 使用给定参数(stride=1,padding=0),写出输出的第2行第1个数是如何计算出来的(可参考代码中的输入和卷积核)。

简答题3(4分,来自3.2(3)) 当 padding = (kernel_size - 1) // 2 且 stride=1 时,输出尺寸等于什么?这种填充方式叫什么?

简答题4(4分,来自3.2(4)) 在 nn.Conv2d 中,可以直接使用 padding='same' 参数实现相同效果吗?为什么?

简答题5(3分,来自3.3(5)) 代码中的 unsqueeze 和 squeeze 分别做了什么?为什么池化前需要增加维度?

简答题6(4分,来自3.3(6)) 最大池化和平均池化的输出结果分别是什么(针对给定的4×4特征图)?池化层的主要作用是什么?

简答题7(3分,来自3.4(7)) 为什么感受野很重要?如果网络更深,感受野会如何变化?

简答题8(4分,来自3.5(8)) 卷积核的形状 (16, 3, 3, 3) 四个数字分别代表什么?

简答题9(3分,来自4.1(9)) 数据增强的作用是什么?为什么测试集不使用数据增强?

简答题10(3分,来自4.2(11)) x.view(x.size(0), -1) 这一步的作用是什么?


五、计算题(20分)

5.1 特征图尺寸计算(8分)

完成下表(每空2分):

输入 操作 输出尺寸
32×32×3 Conv2d(3,16,3,stride=1,padding=0) ______
30×30×16 MaxPool2d(2,stride=2) ______
32×32×3 Conv2d(3,16,5,stride=2,padding=1) ______
28×28×6 Conv2d(6,16,5,stride=1,padding=0) ______

5.2 感受野计算(6分)

一个三层卷积网络:第一层 kernel=3, stride=2;第二层 kernel=3, stride=1;第三层 kernel=3, stride=1。请计算第三层(最后一层)特征图上每个神经元相对于原始输入的感受野大小。

5.3 LeNet特征图尺寸填空(6分)

补全LeNet各层输出形状(已知输入1×28×28):

输出形状
conv1 (k5,s1,p0, 1→6) ______
pool1 (2×2,s2) ______
conv2 (k5,s1,p0, 6→16) ______
pool2 (2×2,s2) ______
flatten后 ______

六、代码填空题(每空2分)

卷积层定义

import torch
import torch.nn as nn

# 定义卷积层:输入通道1,输出通道6,卷积核5×5,步幅1,填充0
conv1 = nn.Conv2d(in_channels=______, out_channels=______, kernel_size=______, stride=1, padding=0)

# 创建一个模拟输入 (batch=1, channel=1, height=28, width=28)
x = torch.randn(1, 1, 28, 28)
out = conv1(x)
print("输出形状:", out.shape)   # 期望输出: torch.Size([1, 6, 24, 24])

池化层

# 定义最大池化层:核大小2×2,步幅2
pool = nn.MaxPool2d(kernel_size=______, stride=______)

# 对上面卷积的输出进行池化
pooled = pool(out)
print("池化后形状:", pooled.shape)   # 期望输出: torch.Size([1, 6, 12, 12])

感受野函数

def receptive_field(kernel_sizes, strides):
    """
    计算输出层相对于原始输入的感受野大小
    kernel_sizes: 各层卷积核大小列表,从输入层到输出层顺序
    strides: 各层步幅列表,从输入层到输出层顺序
    """
    rf = 1
    # 从最后一层向第一层遍历
    for k, s in zip(reversed(kernel_sizes), reversed(strides)):
        rf = rf + (______ - 1) * ______
    return rf

# 示例:两层卷积,核大小均为3,步幅分别为2和1
rf = receptive_field([3, 3], [2, 1])
print("感受野大小:", rf)   # 期望输出: 7

LeNet 展平

class LeNet(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(1, 6, 5)          # 输入1→6通道,核5
        self.pool1 = nn.MaxPool2d(2, 2)          # 2×2池化,步幅2
        self.conv2 = nn.Conv2d(6, 16, 5)         # 输入6→16通道,核5
        self.pool2 = nn.MaxPool2d(2, 2)          # 2×2池化,步幅2
        self.fc1 = nn.Linear(16 * 4 * 4, 120)    # 全连接层
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)
    
    def forward(self, x):
        x = self.pool1(torch.relu(self.conv1(x)))   # 输出形状: (batch, 6, 12, 12)
        x = self.pool2(torch.relu(self.conv2(x)))   # 输出形状: (batch, 16, 4, 4)
        x = x.______(x.size(0), -1)                 # 展平操作
        x = torch.relu(self.fc1(x))
        x = torch.relu(self.fc2(x))
        x = self.fc3(x)
        return x

# 测试模型
model = LeNet()
dummy = torch.randn(1, 1, 28, 28)
output = model(dummy)
print("输出形状:", output.shape)   # 期望: torch.Size([1, 10])

数据增强旋转角度

from torchvision import transforms

train_transform = transforms.Compose([
    transforms.RandomRotation(______),          # 随机旋转 ±10度
    transforms.RandomHorizontalFlip(),          # 随机水平翻转
    transforms.ToTensor(),
    transforms.Normalize((0.1307,), (0.3081,))  # MNIST归一化参数
])

# 测试一下变换(假设有一张PIL图像)
# from PIL import Image
# img = Image.open('test.png')
# img_tensor = train_transform(img)

参考答案与超详细解析(小白版)

一、单选题

第1题

题目:给定输入尺寸 28×28,卷积核大小 5×5,步幅 1,填充 0,卷积输出尺寸是( )
A. 24×24
B. 28×28
C. 26×26
D. 20×20

答案:A

详细解析

  • 首先回忆卷积输出尺寸公式:
    输出尺寸 = (输入尺寸 + 2×填充 - 卷积核大小) ÷ 步幅 + 1
    这里所有数字都是整数,除法是整数除法(向下取整)。

  • 代入数字:输入=28,填充=0,核=5,步幅=1。
    计算括号内:28 + 0 - 5 = 23。
    除以步幅1:23 ÷ 1 = 23。
    加1:23 + 1 = 24。

  • 所以输出是 24×24。

  • 为什么不是 28?因为卷积核滑动的过程中,边缘的像素只能被卷积核覆盖一部分,每滑动一步,输出尺寸就会减少 (核尺寸-1),这里 5-1=4,28-4=24。


第2题

题目:关于填充(padding)的说法,正确的是( )
A. 填充只能在输入四周补0
B. 填充会降低输出特征图的分辨率
C. padding=1 表示上下左右各补1行/列0
D. 填充不影响感受野大小

答案:C

详细解析

  • A选项:填充通常使用0,但不是“只能”。PyTorch 的卷积层 nn.Conv2d 的 padding 参数默认是补0,但也可以使用其他填充模式(如反射、重复等)。而且 padding 只是一个数值,表示补多少圈0,但如果你用 torch.nn.functional.pad 可以指定不同的填充值。所以“只能”这个词太绝对,错误。

  • B选项:填充是增加输出尺寸的。如果填充足够大,可以让输出尺寸等于输入尺寸(same padding)。即使填充很小,输出尺寸也比不填充时要大或相等。例如:输入5×5,核3,步幅1,无填充输出3×3;加上1圈填充(变成7×7),输出5×5。所以填充不会降低分辨率,反而可能维持或增大。

  • C选项padding=1 表示在输入的四周各补1行/列(通常补0)。对于二维图像,上下各补1行,左右各补1列,所以总高度增加2,总宽度增加2。这个说法是正确的。

  • D选项:填充会影响感受野。因为填充相当于扩展了输入的有效范围,使得卷积核可以覆盖到原来边界的区域,从而影响后续层神经元能看到的原始输入范围。所以感受野会变化。

因此只有 C 正确。


第3题

题目:最大池化层 MaxPool2d(kernel_size=2, stride=2) 对 4×4 特征图池化后,输出尺寸为( )
A. 2×2
B. 3×3
C. 4×4
D. 1×1

答案:A

详细解析

  • 池化层输出尺寸公式与卷积类似:
    输出尺寸 = (输入尺寸 - 核尺寸) ÷ 步幅 + 1(除法是整数除法,向下取整)

  • 输入=4,核=2,步幅=2。
    (4 - 2) ÷ 2 = 2 ÷ 2 = 1,再加1得2。

  • 或者直观理解:2×2的窗口,步长2,刚好不重叠地滑过4×4区域,可以分成2行2列窗口,所以输出2×2。

  • 最大池化取每个窗口的最大值,所以输出四个值,排成2×2。


第4题

题目:在LeNet中,x.view(x.size(0), -1) 的作用是( )
A. 增加一个维度
B. 将多维特征图展平成一维向量,保留batch维度
C. 转置特征图
D. 复制特征图

答案:B

详细解析

  • x 的形状通常是 (batch, channels, height, width),比如 (64, 16, 4, 4)

  • x.size(0) 是 batch 大小(64)。

  • view(64, -1) 表示将每个样本(每个64中的一行)的所有元素排成一列,-1 表示自动计算该维度大小。因为原本每个样本有 16*4*4=256 个元素,所以输出形状为 (64, 256)

  • 这样就将卷积层输出的特征图“展平”成二维矩阵,每一行对应一个样本的特征向量,以便输入全连接层。

  • A错:不是增加维度,而是合并后面的维度。

  • C错:不是转置。

  • D错:不是复制。


第5题

题目:数据增强通常只应用于( )
A. 训练集
B. 测试集
C. 验证集
D. 所有数据集

答案:A

详细解析

  • 数据增强是为了增加训练样本的多样性,防止过拟合,提升泛化能力。

  • 测试集必须保持原始、未经修改的状态,因为我们要评估模型在真实数据上的表现。如果对测试集也做随机变换,那么评估结果就不稳定且不公平。

  • 验证集同理,一般不使用数据增强,以保证其能真实反映模型性能。

  • 所以只用于训练集。


二、填空题

第6题

题目:卷积输出尺寸计算公式为:Hout=(____+2×padding−kernel_size)//____+1Hout​=(____+2×padding−kernel_size)//____+1。

答案H_instride

详细解析

  • 标准公式中的分子部分是 H_in + 2*padding - kernel_size,分母是 stride,然后向下取整再加1。

  • 这里 // 表示整数除法(向下取整)。


第7题

题目:一个 Conv2d(3, 16, kernel_size=3, stride=1, padding=1) 层的卷积核形状是 (16, _, _, _)

答案333

详细解析

  • nn.Conv2d 的卷积核形状为 (out_channels, in_channels, kernel_height, kernel_width)

  • 这里 out_channels=16,in_channels=3(输入是RGB图像,3通道),kernel_size=3(正方形,所以高=3,宽=3)。

  • 因此形状是 (16, 3, 3, 3)

  • 注意:顺序不能写错,第一个数是输出通道,第二个是输入通道。


第8题

题目:感受野计算函数中,从最后一层向前递推:rf = rf + (k - 1) * s。若两层卷积核均为3,步幅第一层2、第二层1,则最终感受野大小为 ______。

答案:7

详细解析

  • 感受野计算需要从最后一层往输入层方向递推。

  • 初始 rf = 1(最后一层的输出神经元)。

  • 考虑最后一层(第二层,因为它是最靠近输出的一层,通常我们说的“第一层”是靠近输入)。题目说“两层卷积核均为3,步幅第一层2、第二层1”,意思是第一层(靠近输入)stride=2,第二层(靠近输出)stride=1。

  • 我们从最后一层(第二层)开始向前推:

    • 处理第二层(从后往前第一个卷积层):k=3,s=1 → rf = 1 + (3-1)*1 = 1 + 2 = 3

    • 再往前处理第一层:k=3,s=2 → rf = 3 + (3-1)*2 = 3 + 4 = 7

  • 所以感受野大小为 7×7。

  • 注意:如果题目说的是两层,顺序是输入→第一层(3×3, stride=2)→第二层(3×3, stride=1)→输出。那么计算正确。


第9题

题目:LeNet中,第二个池化层 pool2 的输入通道数是 ______,输出通道数不变。

答案:16

详细解析

  • LeNet 中 conv2 的输出通道是16,然后 pool2 紧接着 conv2,所以 pool2 的输入通道数就是16。

  • 池化层不会改变通道数,只改变空间尺寸。


第10题

题目:在PyTorch中,unsqueeze(0) 是在第0维增加一个维度,squeeze() 用于移除维度大小为 ______ 的维度。

答案:1

详细解析

  • squeeze() 默认移除所有长度为1的维度。例如形状 (1, 3, 1, 5) 经过 squeeze() 后变成 (3, 5)

  • 若指定 squeeze(dim),则只移除该维度(前提是它长度为1)。

  • 所以填空填 1


三、判断题

第11题

题目:卷积核的数量决定输出特征图的通道数。
答案:√

详细解析

  • 卷积层中,每个输出通道对应一个独立的卷积核(一组权重)。

  • out_channels 参数就是卷积核的数量,输出特征图的通道数等于 out_channels


第12题

题目:池化层具有可训练参数。
答案:×

详细解析

  • 池化层(最大池化、平均池化)没有权重和偏置,只是固定操作(取最大值或平均值),不参与梯度更新。

  • 因此没有可训练参数。


第13题

题目:感受野只与卷积核大小有关,与步幅无关。
答案:×

详细解析

  • 感受野与卷积核大小和步幅都有关。步幅越大,每层向前跳跃的步长越大,感受野增长越快。

  • 例如:单层卷积,核3,步幅1,感受野3;步幅2,感受野3?这里单层感受野就是核大小,步幅不影响单层的感受野,但影响多层叠加后的感受野。对多层网络,步幅会放大感受野。


第14题

题目:数据增强可以帮助提高模型泛化能力。
答案:√

详细解析

  • 数据增强通过对训练图像进行随机变换(旋转、翻转、缩放等),创建更多样化的样本,使模型学习到更鲁棒的特征,从而减少过拟合,提高泛化能力。


第15题

题目:在LeNet的forward中,x = x.view(x.size(0), -1) 之前不需要展平,因为全连接层会自动处理。
答案:×

详细解析

  • nn.Linear 层的输入要求是二维:(batch, features)

  • 卷积层输出的特征是四维:(batch, channels, height, width),必须手动展平成二维,否则 Linear 会报错或产生错误结果。

  • 因此展平是必要的。


四、简答题(仅选部分详细展开)

由于篇幅限制,这里只展示 简答题1、2、3 的超详细解析,其余同理。

简答题1

题目:写出卷积输出尺寸的计算公式。

答案

H_{\text{out}} = \left\lfloor \frac{H_{\text{in}} + 2 \times \text{padding} - \text{kernel\_size}}{\text{stride}} \right\rfloor + 1

其中 H_in 是输入高度,padding 是填充的圈数,kernel_size 是卷积核高度,stride 是步幅。宽度相同公式。


简答题2

题目:使用给定参数(stride=1,padding=0),写出输出的第2行第1个数是如何计算出来的(可参考代码中的输入和卷积核)。

详细答案

  • 输入是一个5×5矩阵(值1到25),卷积核是3×3(值 [[1,0,-1],[1,0,-1],[1,0,-1]])。

  • 输出第2行第1个数对应卷积核中心与输入的第2行第1列对齐,此时卷积核覆盖输入的区域是:
    输入的行索引1~3,列索引0~2(从0开始)。
    这些位置的值为:
    [ [6,7,8],
    [11,12,13],
    [16,17,18] ]

  • 与卷积核逐元素相乘再求和:
    (6×1)+(7×0)+(8×(-1)) + (11×1)+(12×0)+(13×(-1)) + (16×1)+(17×0)+(18×(-1))
    = (6 -8) + (11-13) + (16-18)
    = (-2) + (-2) + (-2) = -6

  • 因此程序输出的该位置值为 -6。


简答题3

题目:当 padding = (kernel_size - 1) // 2 且 stride=1 时,输出尺寸等于什么?这种填充方式叫什么?

答案

  • 代入公式:
    H_out = (H_in + 2*((k-1)//2) - k) // 1 + 1
    如果 k 是奇数,(k-1)//2 = (k-1)/2,则分子 H_in + (k-1) - k = H_in -1,除以1得 H_in -1,再加1得 H_in
    所以输出尺寸等于输入尺寸。

  • 这种填充方式称为 Same Padding(或保形填充)。

简答题4 在 nn.Conv2d 中,可以直接使用 padding='same' 参数实现相同效果吗?为什么?

答案:不能。nn.Conv2d 的 padding 参数只能接收整数或元组,不支持字符串 'same'。需要手动计算 padding = (kernel_size - 1) // 2 来实现 same padding(当 stride=1 时)。

详细解析

  • PyTorch的 nn.Conv2d 设计上 padding 参数接受 int 或 tuple,表示填充的行/列数。

  • 一些高级框架(如 TensorFlow)的卷积层支持 padding='same',但 PyTorch 没有直接支持。

  • 如果希望自动计算 same padding,可以自己写函数或使用 torch.nn.functional.pad

  • 在 stride 不为1时,same padding 的定义更复杂(需要保证输出尺寸 = ceil(输入/步长)),此时不能简单用 (k-1)//2 计算。

  • 本实验只讨论 stride=1 的情况。


简答题5 unsqueeze 和 squeeze 的作用?为什么池化前需要增加维度?

答案

  • unsqueeze(dim):在指定维度插入一个大小为1的新维度。

  • squeeze():移除所有大小为1的维度(或指定维度)。

  • 池化前需要增加维度是因为 PyTorch 的池化层(nn.MaxPool2d 等)要求输入为4维张量:(batch, channels, height, width)。而手动创建的特征图通常是2维 (height, width) 或3维 (channels, height, width),缺少 batch 或 channel 维度,因此需要用 unsqueeze 补充。

详细解析

  • 例如一个单通道特征图 (28,28),需要先 unsqueeze(0) 变为 (1,28,28) 添加 batch 维度,再 unsqueeze(0) 变为 (1,1,28,28) 添加 channel 维度。实际更常见的是直接构建4维张量。

  • 在数据加载器中,返回的 batch 已经是 (batch, channels, H, W),不需要手动添加。但单独测试某个特征图时需要。

  • squeeze 常用于移除不必要的单维度,以便显示或与全连接层对接。


简答题6 最大池化和平均池化对4×4特征图的结果(分别写出)?池化的主要作用?

题目中特征图:实验中给出的4×4矩阵(值1~16):

1   2   3   4
5   6   7   8
9   10  11  12
13  14  15  16

池化参数:kernel_size=2, stride=2(不重叠)。

答案

  • 最大池化结果

    6   8
    14  16
  • 平均池化结果

    3.5   5.5
    11.5  13.5
  • 池化的主要作用

    1. 降低特征图的空间尺寸(下采样),减少计算量和参数量。

    2. 增强平移不变性(对微小位移不敏感)。

    3. 最大池化保留最显著的特征,平均池化保留整体信息。

详细计算过程

  • 将4×4分成2×2不重叠的4个窗口:

    • 窗口1(左上):[1,2;5,6] → 最大值6,平均值(1+2+5+6)/4=3.5

    • 窗口2(右上):[3,4;7,8] → 最大值8,平均值(3+4+7+8)/4=5.5

    • 窗口3(左下):[9,10;13,14] → 最大值14,平均值(9+10+13+14)/4=11.5

    • 窗口4(右下):[11,12;15,16] → 最大值16,平均值(11+12+15+16)/4=13.5

  • 池化输出尺寸 = 2×2。


简答题7 感受野为什么重要?网络更深时感受野如何变化?

答案

  • 感受野决定了某一层的神经元在原始输入图像上能看到的区域大小。感受野越大,神经元能整合更全局的上下文信息。

  • 网络越深,感受野越大。因为每一层卷积都会扩大感受野(尤其是步幅大于1时)。

详细解析

  • 在目标检测中,如果感受野太小,可能看不清大物体;如果感受野太大,可能对小物体不敏感。设计网络时需要平衡。

  • 感受野的计算公式(从后向前):RF_prev = (RF_cur - 1) * stride + kernel_size

  • 随着层数增加,感受野呈指数级增长(如果步幅>1)或线性增长(步幅=1)。

  • 例如:单层3×3卷积,步幅1,感受野=3;两层3×3卷积,步幅1,感受野=5;若步幅为2,感受野增长更快。


简答题8 对于卷积层 Conv2d(3, 16, kernel_size=3),其权重张量的形状是什么?各数字的含义是什么?

答案

  • 权重张量形状:(16, 3, 3, 3)

  • 含义:

    • 第一个 16:输出通道数(卷积核的个数)

    • 第二个 3:输入通道数(例如RGB图像的3个通道)

    • 第三个 3:卷积核的高度

    • 第四个 3:卷积核的宽度

详细解析

  • PyTorch中,卷积层的权重通过 conv.weight 访问,形状为 (out_channels, in_channels, kernel_h, kernel_w)

  • 构造函数参数顺序是 (in_channels, out_channels, kernel_size),容易混淆。但权重张量的第一个维度是 out_channels

  • 每个输出通道对应一组独立的卷积核,该组内有 in_channels 个二维核(每个输入通道一个)。

  • 所以总参数量 = 16 × 3 × 3 × 3 = 432(不含偏置)。


简答题9 数据增强的作用?测试集为何不用?

答案

  • 作用:通过对训练图像进行随机变换(旋转、翻转、缩放、裁剪等),增加训练样本的多样性,防止模型过拟合,提高泛化能力。

  • 测试集不用:测试集需要反映真实、未变形的数据分布,以公平评估模型的性能。对测试集使用数据增强会导致结果不稳定且不可重复,无法与其他模型公平比较。

详细解析

  • 数据增强相当于一种正则化方法,让模型看到更多变体,学会提取鲁棒特征。

  • 常见增强:随机旋转±10°,水平翻转,随机裁剪,颜色抖动等。

  • 测试集应保持原始状态,不做任何随机变换,通常只做归一化。


简答题10 x.view(x.size(0), -1) 的作用?

答案:将卷积输出的多维特征图展平为一维向量,同时保留 batch 维度,以便输入全连接层。

详细解析

  • 假设经过卷积和池化后,x 的形状为 (batch, channels, height, width),例如 (64, 16, 4, 4)

  • x.size(0) 是 batch 大小(64)。

  • -1 表示自动计算该维度的大小,结果为 channels * height * width = 16*4*4=256

  • 因此 x.view(64, -1) 输出形状 (64, 256),每个样本的特征被拉直成一维向量。

  • 全连接层(nn.Linear)要求输入是2维 (batch, features),所以这一步是必须的。

  • 不使用 reshape 而用 view 是因为 view 要求内存连续,且不会复制数据,效率更高。如果内存不连续可用 reshape


五、计算题(详细步骤)

5.1 特征图尺寸计算(每题2分)

  1. 输入32×32×3,Conv2d(3,16,3, stride=1, padding=0)

    • 公式:(32 + 0 - 3)/1 + 1 = 30 → 30×30(通道数16不变,但题目只要求尺寸,答30×30)

  2. 输入30×30×16,MaxPool2d(2, stride=2)

    • 公式:(30 - 2)/2 + 1 = 28/2=14 +1=15 → 15×15(通道数16不变)

  3. 输入32×32×3,Conv2d(3,16,5, stride=2, padding=1)

    • 公式:(32 + 2×1 - 5)/2 + 1 = (32+2-5)=29,29÷2=14(整数除法),+1=15 → 15×15

  4. 输入28×28×6,Conv2d(6,16,5, stride=1, padding=0)

    • (28 + 0 - 5)/1 + 1 = 24 → 24×24

注意:题目中要求输出尺寸,不需要写通道数,只写高×宽。


5.2 感受野计算(6分)

题目:三层卷积网络:第一层 kernel=3, stride=2;第二层 kernel=3, stride=1;第三层 kernel=3, stride=1。求第三层特征图上每个神经元相对于原始输入的感受野。

步骤

  • 从最后一层(第三层)开始向前计算。

  • 初始 RF = 1(第三层输出神经元)。

  • 处理第三层(这是反向第一层,即从后往前第一个卷积层):k=3, s=1
    RF = 1 + (3-1)*1 = 3

  • 处理第二层:k=3, s=1
    RF = 3 + (3-1)*1 = 5

  • 处理第一层:k=3, s=2
    RF = 5 + (3-1)*2 = 5 + 4 = 9

  • 所以感受野大小为 9×9

解析:为什么这样算?因为感受野的计算可以理解为:当前层的神经元在上一层的输入区域大小是 (RF_prev - 1)*stride + kernel,从后向前递推。实验代码中的函数 receptive_field 正是这样实现的。


5.3 LeNet特征图填空(6分)

已知输入 1×28×28:

计算过程 输出形状
conv1 (k5,s1,p0, 1→6) (28-5)/1+1=24 6×24×24
pool1 (2×2,s2) 24/2=12 6×12×12
conv2 (k5,s1,p0, 6→16) (12-5)/1+1=8 16×8×8
pool2 (2×2,s2) 8/2=4 16×4×4
flatten 16×4×4=256 256(一维)

六、代码填空题答案

  1. 1(in_channels)

  2. 6(out_channels)

  3. 5(kernel_size)

  4. 2(kernel_size)

  5. 2(stride)

  6. k

  7. s

  8. view

  9. 10(旋转 ±10 度)

解析

空1、空2、空3:分别填 165

详细解析:
  • 空1(in_channels):输入通道数。MNIST数据集的图像是灰度图,只有一个颜色通道,所以 in_channels=1

  • 空2(out_channels):输出通道数,也等于卷积核的个数。这里我们希望输出6个特征图(原LeNet设计),所以填 6

  • 空3(kernel_size):卷积核大小。LeNet中第一个卷积核是5×5,所以填 5

  • 为什么输出形状是 (1,6,24,24)?
    使用公式:H_out = (H_in + 2*padding - kernel_size) // stride + 1
    代入:H_in=28, padding=0, kernel_size=5, stride=1 → (28+0-5)//1 +1 = 23+1=24
    输出通道数等于 out_channels=6,batch=1,所以形状 (1,6,24,24)

空4、空5:分别填 22

详细解析:
  • 池化层的 kernel_size=2 表示池化窗口是 2×2,stride=2 表示窗口每次滑动2步(不重叠)。

  • 输入特征图尺寸是 24×24,池化后尺寸减半:24//2=12。通道数不变(仍为6),所以输出 (1,6,12,12)

  • 公式验证:(24 - 2)//2 +1 = 22//2+1=11+1=12。正确。

空6、空7:分别填 ks

详细解析:
  • 感受野递推公式(从最后一层向第一层):
    RF_prev = (RF_cur - 1) * stride + kernel_size

  • 在循环中,k 是当前层的卷积核大小,s 是当前层的步幅。公式写成增量形式:
    RF_new = RF_old + (k - 1) * s

  • 所以 (______ - 1) 应该填入 k* ______ 应该填入 s

  • 如果填其他数字,计算结果就会错误。

空8:填 view

详细解析:
  • 经过两个卷积-池化块后,x 的形状是 (batch, 16, 4, 4)。全连接层(nn.Linear)要求输入是二维:(batch, features)

  • x.size(0) 是 batch 大小,-1 表示自动计算剩下的维度总数(16*4*4=256)。

  • view(x.size(0), -1) 将每个样本的特征图拉直成一维,得到 (batch, 256)

  • PyTorch 中改变形状用 view,不能用 reshape 吗?也可以,但 LeNet 经典写法用 view。题目填 view 是正确的。

空9:填 10

详细解析:
  • RandomRotation(degrees) 中 degrees 表示旋转角度的范围。如果填 10,表示在 [-10, +10] 度之间随机旋转。

  • 实验要求“随机旋转 ±10度”,所以填 10

  • 如果填 20,就变成 ±20 度,会旋转过大,可能影响识别。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐