\[ \begin{align}\begin{aligned}\newcommand{\ba}{\boldsymbol{a}} \newcommand{\bb}{\boldsymbol{b}} \newcommand{\be}{\boldsymbol{e}} \newcommand{\bq}{\boldsymbol{q}} \newcommand{\bk}{\boldsymbol{k}} \newcommand{\bw}{\boldsymbol{w}} \newcommand{\bx}{\boldsymbol{x}} \newcommand{\by}{\boldsymbol{y}} \newcommand{\bz}{\boldsymbol{z}} \newcommand{\bd}{\boldsymbol{d}} \newcommand{\bv}{\boldsymbol{v}} \newcommand{\bs}{\boldsymbol{s}}\\\newcommand{\btheta}{\boldsymbol{\theta}} \newcommand{\bbeta}{\boldsymbol{\beta}} \newcommand{\bgamma}{\boldsymbol{\gamma}} \newcommand{\bsigma}{\boldsymbol{\sigma}} \newcommand{\md}{\mbox{d}} \newcommand{\bmu}{\boldsymbol{\mu}} \newcommand{\bone}{\boldsymbol{1}} \newcommand{\bzero}{\boldsymbol{0}} \newcommand{\bepsilon}{\boldsymbol{\epsilon}} \newcommand{\bphi}{\boldsymbol{\phi}} \newcommand{\bh}{\boldsymbol{h}} \newcommand{\bc}{\boldsymbol{c}} \newcommand{\br}{\boldsymbol{r}} \newcommand{\bQ}{\boldsymbol{Q}} \newcommand{\bK}{\boldsymbol{K}} \newcommand{\bV}{\boldsymbol{V}} \newcommand{\bSigma}{\boldsymbol{\Sigma}} \newcommand{\bg}{\boldsymbol{g}} \newcommand{\bxi}{\boldsymbol{\xi}} \newcommand{\bvarepsilon}{\boldsymbol{\varepsilon}} \newcommand{\bdelta}{\boldsymbol{\delta}} \newcommand{\bq}{\boldsymbol{q}} \newcommand{\bk}{\boldsymbol{k}} \newcommand{\bJ}{\boldsymbol{J}} \newcommand{\bp}{\boldsymbol{p}} \newcommand{\bi}{\boldsymbol{i}} \newcommand{\bo}{\boldsymbol{o}} \newcommand{\bE}{\boldsymbol{E}} \newcommand{\bH}{\boldsymbol{H}} \newcommand{\bL}{\boldsymbol{L}} \newcommand{\bu}{\boldsymbol{u}} \newcommand{\bLambda}{\boldsymbol{\Lambda}} \newcommand{\trans}{^{\rm\scriptsize T}} \newcommand{\var}{\mathrm{var}}\\\newcommand{\bA}{\boldsymbol{A}} \newcommand{\bB}{\boldsymbol{B}} \newcommand{\bC}{\boldsymbol{C}} \newcommand{\bD}{\boldsymbol{D}} \newcommand{\bG}{\boldsymbol{G}} \newcommand{\bI}{\boldsymbol{I}} \newcommand{\bM}{\boldsymbol{M}} \newcommand{\bP}{\boldsymbol{P}} \newcommand{\bS}{\boldsymbol{S}} \newcommand{\bU}{\boldsymbol{U}} \newcommand{\bW}{\boldsymbol{W}} \newcommand{\bX}{\boldsymbol{X}} \newcommand{\bY}{\boldsymbol{Y}} \newcommand{\bZ}{\boldsymbol{Z}} \newcommand{\cotp}{\textcolor[RGB]{48,209,88}{TP}} \newcommand{\cotn}{\textcolor[RGB]{100,210,255}{TN}} \newcommand{\cofp}{\textcolor[RGB]{94,92,230}{FP}} \newcommand{\cofn}{\textcolor[RGB]{191,90,242}{FN}}\\\newcommand{\numcotp}{\textcolor[RGB]{48,209,88}{50}} \newcommand{\numcotn}{\textcolor[RGB]{100,210,255}{30}} \newcommand{\numcofp}{\textcolor[RGB]{94,92,230}{10}} \newcommand{\numcofn}{\textcolor[RGB]{191,90,242}{10}} \DeclareMathOperator*{\argmin}{arg\,min}\end{aligned}\end{align} \]

用 AlexNet 风格网络分类 CIFAR-10#

学习目标与记号#

  1. 准确解释 AlexNet 结构、输入分辨率适配与 ReLU;

  2. 根据公式和张量维度分析 Dropout,并识别常见实现错误;

  3. Python 实现或验证训练流程;在其他条件相同的情况下,只改变一个需要研究的因素进行比较,并根据实验结果分析该因素可能带来的影响。

  本节沿用统一记号:普通小写字母表示标量,粗体小写字母表示向量,粗体大写字母表示矩阵或高阶张量;层编号写作上标 \([l]\)⁠,样本或时间编号写作下标;转置写作 \(\trans\)⁠。第 \(l\) 层特征图的通道数、高和宽分别记为 \(d_C^{[l]}\)⁠、\(d_H^{[l]}\)\(d_W^{[l]}\)⁠。除非另有说明,程序中的一批图像按照“样本数、通道数、高、宽”的顺序存放,维度写作 \(m\times d_C^{[l]}\times d_H^{[l]}\times d_W^{[l]}\)⁠。正文与练习中的程序都应同时检查数值结果和数组维度。本节练习的参考答案见 AlexNet 与 CIFAR-10 实现答案⁠。

  实现前建议回顾 AlexNet 结构⁠、ReLU⁠、DropoutAdam⁠;数据划分原则见 训练集、验证集与测试集⁠。

  现在,我们将使用 AlexNet 教学实现对 CIFAR-10 数据集进行分类。CIFAR-10 (Canadian Institute for Advanced Research, 10 classes)是一个广泛使用的小型图像分类数据集,由加拿大多伦多大学的 Alex Krizhevsky、Vinod Nair 和 Geoffrey Hinton 开发。该数据集包含 60,000 张 \(32\times32\) 像素的彩色图像,分为 10 个类别,每个类别有 6,000 张图像。CIFAR-10 数据集的图像涵盖了现实生活中的 10 种常见物体类别:飞机、汽车、鸟类、猫、鹿、狗、青蛙、马、船和卡车。CIFAR-10 数据集通常用于计算机视觉领域的图像分类任务,因为其图像尺寸较小且类别数量适中,非常适合作为模型训练和评估的标准基准。数据集被分为 50,000 张训练图像和 10,000 张测试图像,用于评估模型在新图像上的表现。

  AlexNet 的模型规模和输入分辨率都明显大于前面的 LeNet 风格网络,本例同时演示设备选择。GPU 可以加速训练,但代码仍应在没有 CUDA 的环境中正确退回 CPU。

图形处理单元(Graphics Processing Unit, GPU)

  图形处理器(Graphics Processing Unit, GPU)是一种专门用于图形处理的处理器,由于其高并行性和强大的计算能力,GPU 也被广泛应用于深度学习领域。在深度学习中,GPU 可以加速神经网络的训练和推理过程,提高计算效率。PyTorch 支持使用 GPU 进行计算,可以通过 torch.cuda 模块来实现。CUDA 是 NVIDIA 公司推出的并行计算平台和编程模型,它允许开发者使用 C/C++Python 等语言直接调用 GPU 的计算能力。PyTorch 通过 torch.cuda 模块封装了 CUDA 的接口,提供了一系列的函数和类,方便用户在 PyTorch 中使用 GPU 进行计算。

  首先导入数组、绘图、PyTorch 模型与 CIFAR-10 数据加载所需的库,并把 NumPy、PyTorch 以及可用 CUDA 设备的随机种子设为 42。随后选择 CUDA 或 CPU 作为统一计算设备;这段准备代码不接收图像,也不会打印训练结果,后面的模型与每批数据都要移动到这里得到的 device

 1import numpy as np
 2import matplotlib.pyplot as plt
 3import torch
 4import torch.nn as nn
 5import torch.optim as optim
 6import torch.nn.functional as F
 7from torch.utils.data import DataLoader
 8from torchvision import datasets, transforms
 9
10# 设置随机数种子
11np.random.seed(42)
12torch.manual_seed(42)
13if torch.cuda.is_available():
14    torch.cuda.manual_seed_all(42)
15
16# 设置 GPU
17device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

  torch.cuda.is_available() 判断当前环境是否支持 NVIDIA CUDA;若支持,devicecuda否则为 cpu固定种子只能减少随机初始化等因素造成的差异,不能保证跨硬件和软件版本逐位复现;这段选择逻辑也不会自动使用 Apple MPS 等其他加速后端。

AlexNet 教学版模型搭建#

  接下来搭建 AlexNet 教学版模型。在上一小节中,我们实现了卷积层和池化层,这里改用 torch.nn 模块中的 Conv2dMaxPool2d 类。forward 接收 NCHW 排列的三通道图像批量;本例随后提供 \(m\times3\times227\times227\) 输入,预期得到 \(m\times10\) 的线性类别得分。下面的代码保留五个卷积层、LRN、三个全连接层和 Dropout 等经典结构,但把历史原版分布在两个 GPU 上的通道合并到一个设备,并加入自适应平均池化,将卷积层输出的特征图调整为固定大小,从而确定最后一个全连接分类层的输入维度。因此,它适合用于理解 AlexNet 的主要计算过程,但不应被视为逐项复现历史原版。

 1class AlexNet(nn.Module):
 2    def __init__(self, num_classes=10):
 3        super().__init__()
 4        self.features = nn.Sequential(
 5            nn.Conv2d(3, 96, kernel_size=11, stride=4),
 6            nn.ReLU(inplace=True),
 7            nn.LocalResponseNorm(size=5, alpha=0.0001, beta=0.75, k=2),  # LRN层
 8            nn.MaxPool2d(kernel_size=3, stride=2),
 9            nn.Conv2d(96, 256, kernel_size=5, padding=2),
10            nn.ReLU(inplace=True),
11            nn.LocalResponseNorm(size=5, alpha=0.0001, beta=0.75, k=2),  # LRN层
12            nn.MaxPool2d(kernel_size=3, stride=2),
13            nn.Conv2d(256, 384, kernel_size=3, padding=1),
14            nn.ReLU(inplace=True),
15            nn.Conv2d(384, 384, kernel_size=3, padding=1),
16            nn.ReLU(inplace=True),
17            nn.Conv2d(384, 256, kernel_size=3, padding=1),
18            nn.ReLU(inplace=True),
19            nn.MaxPool2d(kernel_size=3, stride=2),
20        )
21        self.avgpool = nn.AdaptiveAvgPool2d((6, 6))
22        self.classifier = nn.Sequential(
23            nn.Dropout(),
24            nn.Linear(256 * 6 * 6, 4096),
25            nn.ReLU(inplace=True),
26            nn.Dropout(),
27            nn.Linear(4096, 4096),
28            nn.ReLU(inplace=True),
29            nn.Linear(4096, num_classes),
30        )
31
32def forward(self, x):
33    x = self.features(x)
34    x = self.avgpool(x)
35    x = torch.flatten(x, 1)
36    x = self.classifier(x)
37    return x

  对输入维度为 \(m\times3\times227\times227\) 的一批图像,features 依次完成五次卷积和三次最大池化,得到 \(m\times256\times6\times6\)⁠;自适应平均池化在本例中保持该空间大小,并使分类器接口在其他合法输入尺寸下仍固定为 \(6\times6\)⁠。展平后,三个线性层最终返回 \(m\times\text{num\_classes}\) 的线性输出,默认即每张图像的 10 个类别得分;它们不是概率,交叉熵损失会在内部完成所需的 log-softmax。

  LRN 会在相邻通道间归一化响应,是 AlexNet 的历史组件,但不应简单称为防止过拟合的正则化层。现代架构更常使用 BatchNorm、LayerNorm 等方法;这里保留 LRN 只是为了接近经典结构。该模型还保留了两个 4096 维全连接层,参数和内存开销很大,主要适合教学或结构对照,而不是小图像任务的高效方案。

  下面定义通用训练和测试函数。两者都接收模型、按批提供 (data, target) 的加载器、交叉熵损失和计算设备;训练函数还接收优化器与轮数,返回每轮每隔 100 批记录一次的损失列表,测试函数则遍历全部测试样本并打印平均损失和准确率。

# 训练模型
def train(model, train_loader, criterion, optimizer, num_epochs, device):
    model.train()
    loss_list = []
    for epoch in range(num_epochs):
        loss_list.append([])
        for batch_idx, (data, target) in enumerate(train_loader):
            data, target = data.to(device), target.to(device)
            optimizer.zero_grad()
            output = model(data)
            loss = criterion(output, target)
            loss.backward()
            optimizer.step()
            if batch_idx % 100 == 0:
                print(f'Epoch {epoch}, Batch {batch_idx}, Loss: {loss.item()}')
                loss_list[-1].append(loss.item())
    return loss_list


# 测试模型
def test(model, test_loader, criterion, device):
    model.eval()
    test_loss = 0
    correct = 0
    with torch.no_grad():
        for data, target in test_loader:
            data, target = data.to(device), target.to(device)
            output = model(data)
            test_loss += criterion(output, target).item() * data.size(0)
            pred = output.argmax(dim=1, keepdim=True)
            correct += pred.eq(target.view_as(pred)).sum().item()
    test_loss /= len(test_loader.dataset)
    accuracy = 100. * correct / len(test_loader.dataset)
    print(f'Test Loss: {test_loss:.4f}, Accuracy: {accuracy:.2f}%')

  train 在每个小批量中依次清空梯度、前向计算、反向传播并更新参数;返回的 loss_list 只包含抽样记录,并不是逐轮平均损失。test 使用评估模式和 torch.no_grad()因此 Dropout 会关闭且不会保存梯度,再把批损失按样本数加权,得到整个测试集的平均值。若使用 GPU,模型参数与参与运算的数据必须位于同一设备;函数通过 data.to(device)target.to(device) 移动数据,模型则稍后通过 model.to(device) 移动。若 device 为 CPU,同一套代码仍可直接运行。

  接下来加载 CIFAR-10 数据集并进行预处理。输入是数据目录与两套变换规则,输出是训练、测试数据集及按批迭代的加载器。每批图像的维度预期为 \(128\times3\times227\times227\)⁠;最后一个批次的样本数可能较小。标签是一维类别编号。

 1transform_train = transforms.Compose([
 2    transforms.Resize((227, 227)),
 3    transforms.RandomHorizontalFlip(),
 4    transforms.ToTensor(),
 5    transforms.Normalize(
 6        (0.4914, 0.4822, 0.4465),
 7        (0.2470, 0.2435, 0.2616),
 8    ),
 9])
10transform_test = transforms.Compose([
11    transforms.Resize((227, 227)),
12    transforms.ToTensor(),
13    transforms.Normalize(
14        (0.4914, 0.4822, 0.4465),
15        (0.2470, 0.2435, 0.2616),
16    ),
17])
18train_dataset = datasets.CIFAR10(
19    root='./data', train=True, download=True, transform=transform_train
20)
21trainloader = DataLoader(
22    train_dataset, batch_size=128, shuffle=True, num_workers=2
23)
24
25test_dataset = datasets.CIFAR10(
26    root='./data', train=False, download=True, transform=transform_test
27)
28testloader = DataLoader(
29    test_dataset, batch_size=128, shuffle=False, num_workers=2
30)

  训练和测试采用相同的缩放与标准化,只有训练集启用随机翻转。标准化常数来自 CIFAR-10 的通道统计量;如果改用预训练 ImageNet 权重,则应使用与该权重配套的预处理。

    1课件中的 AlexNet 结构尺寸链从 \(227\times227\) 输入开始;一些文字资料也常把输入概括为约 \(224\times224\)⁠。这一差异通常来自裁剪方式、首层填充和边界处理约定。本节采用 \(227\times227\)⁠,是为了与首层 \(11\times11\)⁠、步幅 4、无填充时得到 \(55\times55\) 输出的计算一致。CIFAR-10 的原始图像只有 \(32\times32\)⁠;把它放大不会增加新的图像细节,却会明显增加计算量,因此实际应用中更适合调整网络结构以直接处理原始分辨率。
  1. Resize 用于将图像大小调整为 \(227\times227\) 1课件中的 AlexNet 结构尺寸链从 \(227\times227\) 输入开始;一些文字资料也常把输入概括为约 \(224\times224\)⁠。这一差异通常来自裁剪方式、首层填充和边界处理约定。本节采用 \(227\times227\)⁠,是为了与首层 \(11\times11\)⁠、步幅 4、无填充时得到 \(55\times55\) 输出的计算一致。CIFAR-10 的原始图像只有 \(32\times32\)⁠;把它放大不会增加新的图像细节,却会明显增加计算量,因此实际应用中更适合调整网络结构以直接处理原始分辨率。⁠,以复现本节采用的 AlexNet 尺寸链;

  2. ToTensor 用于将 PIL 图像或者 numpy.ndarray 转换为 torch.Tensor

  3. Normalize 按通道执行 \((x-\mu)/\sigma\)⁠,其均值和标准差应由训练数据或预训练模型规范确定,不能使用测试集重新拟合。

  接着,我们使用 datasets.CIFAR10 类加载 CIFAR-10 数据集,其中 root 参数指定了数据集的存储路径,train 参数指定了是否加载训练集,transform 参数指定了对图像进行的预处理操作,download 参数指定了是否下载数据集。然后,我们使用 DataLoader 类创建了训练集和测试集的数据加载器 trainloadertestloader其中 batch_size 参数指定了每个批次的样本数量,shuffle 参数指定了是否打乱数据集,对于训练集我们打乱数据集,对于测试集我们不打乱数据集。

  下面实例化一个 AlexNet 教学版模型,用前面创建的 trainloader 作为训练数据,交叉熵作为目标,并训练 25 轮。调用 train 后模型参数会被更新,终端会打印抽样批损失,返回值 loss_list 保存这些记录。在 Krizhevsky 等(2012) 的原始论文中,AlexNet 使用了 SGD 优化器;这里为了演示另一种训练设置,改用 Adam 优化器,并把学习率设为 \(5\times10^{-4}\)⁠。因此,下面的训练结果属于本节教学设置,不能作为历史原版 AlexNet 的复现实验。

1# 实例化模型
2model = AlexNet().to(device)
3# 定义损失函数
4criterion = nn.CrossEntropyLoss()
5# 定义优化器
6optimizer = optim.Adam(model.parameters(), lr=5e-4)
7# 训练模型
8loss_list = train(model, trainloader, criterion, optimizer, 25, device)

  这段代码把模型参数移动到 device用交叉熵直接接收模型的 10 维线性输出,并让 Adam 以学习率 \(5\times10^{-4}\) 更新全部可训练参数。train 运行 25 轮并把抽样批损失保存为 loss_list同时在终端打印进度;它没有保存最佳检查点,也没有使用验证集早停,因此不宜根据后面的测试结果反复调整设置。

  训练的部分输出如下;省略号表示中间日志未全部列出:

Epoch 0, Batch 0, Loss: 2.3031015396118164
Epoch 0, Batch 100, Loss: 1.8027007579803467
Epoch 0, Batch 200, Loss: 1.5465164184570312
Epoch 0, Batch 300, Loss: 1.406746745109558
Epoch 1, Batch 0, Loss: 1.16340172290802
Epoch 1, Batch 100, Loss: 1.1709628105163574
Epoch 1, Batch 200, Loss: 0.8591127395629883
Epoch 1, Batch 300, Loss: 0.9119205474853516
Epoch 2, Batch 0, Loss: 0.9254726767539978
Epoch 2, Batch 100, Loss: 1.0733277797698975
Epoch 2, Batch 200, Loss: 0.9020097255706787
Epoch 2, Batch 300, Loss: 0.9042439460754395
Epoch 3, Batch 0, Loss: 0.8871455192565918
Epoch 3, Batch 100, Loss: 0.7843882441520691
Epoch 3, Batch 200, Loss: 0.8064925670623779
Epoch 3, Batch 300, Loss: 0.7478532195091248
Epoch 4, Batch 0, Loss: 0.705902636051178
...
Epoch 24, Batch 200, Loss: 0.2242554873228073
Epoch 24, Batch 300, Loss: 0.22900965809822083

  每行是某一小批量的交叉熵,不是整轮平均值;总体下降说明模型正在拟合训练数据,但局部波动是正常的,仅凭这些抽样点不能断言已经收敛或没有过拟合。下面把训练后的模型交给 test预期打印整个测试集的平均交叉熵和准确率。

1test(model, testloader, criterion, device)

  一次运行的测试输出如下,其中 Test Loss 是按全部测试样本平均的交叉熵,Accuracy 是预测类别等于真实标签的样本比例:

Test Loss: 0.5253, Accuracy: 83.42%

  精确结果会随随机性和训练环境变化。由于把 \(32\times32\) 图像放大到 \(227\times227\) 会显著增加计算量,却不会创造新的图像细节,本例主要用于理解原始 AlexNet 的尺寸流程。针对 CIFAR-10 的实际实验,更合理的做法是缩小首层卷积和分类器,直接在原分辨率上训练,并另外划出验证集选择超参数。

核心推导与实现核验#

核心关系

\[\text{结构适配}\neq\text{直接照搬原始超参数}.\]

  推导路径。 原始 AlexNet 面向较大输入;用于 \(32\times32\) 图像时,首层大卷积和大步幅会过快压缩空间信息,因此需重新计算每层输出并调整核、步幅或池化。

关键条件

  只要某层有效核大于其输入空间尺寸,输出不存在;逐层应用输出尺寸公式可证明直接照搬配置可能在 CIFAR-10 上失效。

数据规模

  每次改变输入图像的高或宽后,都要重新计算最后一个卷积层的通道数 \(d_C^{[l]}\)⁠、高 \(d_H^{[l]}\) 和宽 \(d_W^{[l]}\)⁠。分类输出层接收的输入数应为 \(d_C^{[l]}d_H^{[l]}d_W^{[l]}\)⁠,不能直接照搬 AlexNet 针对另一种图像尺寸设置的固定数值。

常见误区

  测试集只用于最终评价,不能用于反复调整模型。随机裁剪、翻转等数据增强通常只用于训练数据;测试数据应采用固定的处理方式。此外,训练集和测试集应使用相同的通道归一化参数。否则,模型之间的结果差异可能来自数据处理方式,而不是真正来自模型本身。

动手检查

  用一批随机生成的 \(32\times32\) 图像逐层记录 \(m\times d_C^{[l]}\times d_H^{[l]}\times d_W^{[l]}\) 维度与参数量,确认空间尺寸始终为正、分类输出层输入维度正确,并完成一次前向传播、损失计算和后向传播。

数值稳定性与规模

  训练损失直接接收输出层的线性运算结果。若把本节的局部响应归一化替换为 Batch Normalization,应使用足够的精度计算其批量统计量。采用混合精度训练时,可使用损失缩放,并检查每层输出、梯度和学习率是否为有限数值。

本节小结#

  1. 迁移经典架构时必须根据输入分辨率重算维度。

  2. 架构名称与具体训练配方应清楚区分。

  3. 增强、归一化和数据划分必须在实验间保持一致。

综合练习#

  前四题用于推导、证明和具体计算,第 5--7 题用于编程实现与核验,第 8--10 题要求在其他条件相同的情况下,只改变一个需要研究的因素进行比较,并根据实验结果分析该因素可能带来的影响。程序题应固定随机种子、写出维度断言并报告运行环境;比较题还应固定数据划分、随机种子集合和训练预算。全部参考答案见 AlexNet 与 CIFAR-10 实现答案⁠。

  1. 小输入失效计算。 把原始 AlexNet 风格的首层 \(11\times11\)⁠、步幅 4 卷积和 \(3\times3\)⁠、步幅 2 池化直接用于 \(32\times32\) 输入,随后使用保持尺寸的 \(5\times5\) 卷积和第二个 \(3\times3\)⁠、步幅 2 池化。逐层计算空间尺寸并指出在哪一步出现非法输出。

  2. 经典尺寸链。\(227\times227\) 输入,依次计算“\(11\times11\) 步幅 4 卷积”“\(3\times3\) 步幅 2 池化”“填充 2 的 \(5\times5\) 卷积”“第二次 \(3\times3\) 步幅 2 池化”“三个保持尺寸的 \(3\times3\) 卷积”和最后一次 \(3\times3\) 步幅 2 池化”的空间尺寸。

  3. 分类输出层参数量。 经典分类输出层把 \(256\times6\times6\) 展平后依次映射到 4096、4096 和 10 维。计算三个含偏置线性层各自与合计的参数量,并说明为什么该分类输出层可能主导整个模型的存储。

  4. Dropout 计算。 分类输出层使用保留概率 \(q=0.5\) 的倒置 Dropout。对激活向量 \((2,0,-4,6)\) 和掩码 \((1,0,1,0)\)⁠,计算训练输出及其条件期望;说明评估模式下应输出什么。

  5. CIFAR-10 适配实现。 实现一个直接接收 \(32\times32\) 图像的 AlexNet 变体:使用较小首层卷积,避免把图像强制放大到 227,并用自适应池化或虚拟前向确定分类输出层维度。

  6. 维度与模式核验。 用前向传播监听函数(forward hook)打印第 5 题中每一层的输入、输出维度,并与公式计算结果对照;检查模型和数据在同一设备,验证 Dropout 在训练模式下随机、评估模式下确定,并把参数量与框架统计核对。

  7. 测试设计。 测试原始结构处理空间尺寸为 \(32\times32\) 的输入图像时,会在预期的网络层因特征图尺寸过小而失败;同时测试适配结构能处理多个批量大小、输出维度为 \(m\times10\)⁠、训练与评估模式切换正确、CPU/GPU 设备一致,以及一次后向传播所得梯度均为有限数值。

  8. 输入适配方案比较。 比较“放大到 \(227\times227\) 的经典风格网络”和“直接处理 \(32\times32\) 的适配网络”。固定 CIFAR-10 划分、随机种子集合、训练预算和增强选择预算;报告任务性能、训练时间、固定批量预测时间、参数量、乘加量及峰值内存。

  9. Dropout 概率比较。 在适配 AlexNet 中比较 Dropout 概率 \(0,0.25,0.5\)⁠。各组使用相同的数据划分、随机种子、初始化方法和训练预算,只改变 Dropout 概率;报告任务性能、训练—验证差距、训练时间、固定批量预测时间、参数量及峰值内存,并根据结果分析 Dropout 概率可能带来的影响。

  10. 归一化方法比较。 在相同 AlexNet 骨干上比较不使用归一化、保留经典 LRN 和使用 BatchNorm。固定数据划分、随机种子集合、训练预算和增强;报告任务性能、训练时间、固定批量预测时间、参数量及峰值内存,并区分历史结构与现代训练配方。