\[ \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} \]
代表性 CNN 架构:参考答案
返回正文练习 · 返回答案索引
说明
以下答案与正文9道题逐题对应。推导题给出主要步骤;编程和实验题给出参考程序和检查方法,并说明结果适用于哪些条件。
雅可比为 \(\partial\widetilde{\ba}^{[l+2]}/\partial\ba^{[l]}=\partial\mathcal F/\partial\ba^{[l]}+\bI\),所以 \(\partial\mathcal J/\partial\ba^{[l]}=(\partial\mathcal F/\partial\ba^{[l]}+\bI)\trans\cdot(\partial\mathcal J/\partial\widetilde{\ba}^{[l+2]})\)。其中 \(\bI\) 是恒等直连分支提供的直接项,不需要经过残差分支的导数。但是,多个残差块的总雅可比仍包含这些矩阵的连续乘积,残差分支、激活函数、归一化和优化过程都可能改变梯度大小,因此不能据此保证梯度永远稳定。
标准卷积参数量为 \(32(64)(3)(3)=18432\)。逐通道卷积为 \(32(3)(3)=288\),逐点卷积为 \(32(64)=2048\),合计 2336,比值为 \(2336/18432\approx0.1267\)。两者在每个输出空间位置重复相同数量的核运算,因此输出高度和宽度相同时,乘加量也分别乘以共同的空间位置数,比值不变。
一个 \(5\times5\) 卷积有 \(64(64)(25)=102400\) 个参数;两个 \(3\times3\) 卷积有 \(2(64)(64)(9)=73728\) 个参数。步幅 1 时第一个 \(3\times3\) 输出的每个位置覆盖 3 个像素,第二个再向两侧各扩 1 个位置,总感受野为 \(5\times5\)。两个小核之间可加入激活函数,从而以较少参数表达更复杂的非线性变换。
残差分支输出为 \(m\times128\times16\times16\)。原输入的通道数和空间尺寸都不匹配,不能直接相加;直连分支用 \(1\times1\)、步幅 2 的卷积投影到相同维度。忽略偏置时投影参数量为 \(128(64)=8192\)。相加前应明确检查两条分支的维度,不能依赖广播机制自动补齐。
三个模块的核心结构可以写成:
vgg = nn.Sequential(nn.Conv2d(cin, cout, 3, padding=1), nn.ReLU(),
nn.Conv2d(cout, cout, 3, padding=1), nn.ReLU())
mobile = nn.Sequential(nn.Conv2d(cin, cin, 3, padding=1, groups=cin),
nn.ReLU(), nn.Conv2d(cin, cout, 1), nn.ReLU())
class BasicBlock(nn.Module):
def __init__(self, cin, cout, stride=1):
super().__init__()
self.f = nn.Sequential(nn.Conv2d(cin, cout, 3, stride, 1), nn.ReLU(),
nn.Conv2d(cout, cout, 3, 1, 1))
self.p = (nn.Identity() if cin == cout and stride == 1
else nn.Conv2d(cin, cout, 1, stride))
def forward(self, x):
residual, shortcut = self.f(x), self.p(x)
if residual.shape != shortcut.shape:
raise RuntimeError("残差两分支维度不同")
return torch.relu(residual + shortcut)
钩子可统一统计输出元素数,参数量用实际张量元素数求和。
权重对照要求相同的核排列、偏置和评估模式,输出使用 allclose 比较。零残差测试应在最终激活函数之前检查 \(\mathcal F(\ba^{[l]})+\ba^{[l]}=\ba^{[l]}\);若在 ReLU 函数之后检查,负输入会被截断,不再是恒等映射。逐通道层必须设置 groups=cin,其权重维度为 \(d_C^{[l-1]}\times1\times f\times f\);再接 \(1\times1\) 层混合通道。
三种网络应共享划分、增强、种子、更新次数和超参数选择预算;参数无法精确匹配时如实报告差异。VGG 结构规则,MobileNet 通常计算量较低,ResNet 更利于训练深层网络,但实际延迟还受硬件内核影响。表中给出任务指标均值与标准差、训练秒数、固定批量预测时间、参数量、乘加量和峰值内存,不能只用理论乘加量替代实测速度。
普通网络与 ResNet 使用相同的卷积层数和通道数;若投影层增加参数,应单独列出,还可以增加一组通过调整通道数使参数量接近的比较。直连路径通常使深层网络更容易训练,可以通过梯度范数和达到同一验证指标所需时间观察这一现象。对彼此接近的输入,还可把输入梯度展平后计算相关系数;如果普通深层网络的相关系数随深度迅速接近 0,而 ResNet 衰减较慢,就与正文所述的梯度破碎现象一致。报告多个随机种子下的任务性能、梯度相关程度、训练时间、固定批量预测时间、参数量和峰值内存;不能根据一次运行中收敛更快,就认定所有残差网络都更好。
保持原宽度的比较用于观察直接替换卷积形式后的压缩效果;调整宽度使参数量接近的比较用于判断结果差异更可能来自卷积形式还是模型容量。所有组使用相同的数据划分、随机种子、训练轮数和尝试超参数的次数,报告任务指标、训练时间、固定批量预测时间、参数量、乘加量与峰值内存。深度可分离卷积常显著减少理论计算,但较小的运算和内存访问可能使实际加速幅度小于乘加量的下降幅度;若加宽后精度恢复,也要计入新增成本。