资讯详情 NYU-DLSP20 第 5 周深度解读:优化算法(GD/SGD/Momentum/ADAM)、归一化层与卷积·自动微分实战
📅 2026/10/10 13:22:40
示例工程【免费下载链接】NYU-DLSP20NYU Deep Learning Spring 2020项目地址https://gitcode.com/gh_mirrors/pyt/pytorch-Deep-Learning点击查看免费下载导读本篇技术指南围绕 NYU Deep Learning Spring 2020NYU-DLSP20课程第 5 周的完整内容展开核心覆盖三大部分优化技术 I梯度下降、随机梯度下降 SGD、动量 Momentum、优化技术 IIRMSprop、ADAM 等自适应方法、归一化层、以及神经网络加速 MRI 重建的工业案例与Practicum 实战一维/二维卷积的核维度与输出宽度计算、PyTorch 自动微分原理、自定义梯度模块。读完本文你将掌握从梯度下降到自适应优化器的完整推导脉络、归一化层选型依据BatchNorm vs GroupNorm 等并能直接在 PyTorch 中复现卷积维度计算、验证反向传播手算结果、以及通过torch.autograd.Function编写可插入计算图的自定义算子。课程原文依次存放在 docs/en/week05/05.md、docs/en/week05/05-1.md、docs/en/week05/05-2.md 与 docs/en/week05/05-3.md仓库配套的 extra/optimization.ipynb、extra/utils/optim.py、extra/b-custom_grads.ipynb 提供了可直接运行的实现练习。第 5 周课程总览第 5 周分为 Lecture Part A、Lecture Part B 与 Practicum 三部分Lecture part A优化技术 I从最基础的梯度下降Gradient Descent讲起讨论步长step size在求解过程中的关键作用随后引入随机梯度下降SGD并对比其与全批量梯度下降Full Batch GD的性能差异最后介绍动量更新Momentum Updates的两种更新规则、动量背后的直觉及其对收敛的影响。Lecture part B优化技术 II讨论 RMSprop、ADAM 等自适应方法介绍归一化层及其对神经网络训练过程的影响最后以一个真实工业案例收尾——用神经网络让 MRI 扫描更快、更高效。Practicum简要回顾矩阵乘法后深入卷积——通过堆叠与平移使用核kernels先手算理解一维卷积再用 PyTorch 学习一维与二维卷积中核的维度与输出宽度随后用 PyTorch 理解自动梯度autograd如何工作并实现自定义梯度custom-grads。一、优化技术 I从梯度下降到动量1.1 梯度下降Gradient Descent我们从优化方法中最基础、同时也是如后文所述效果最差的方法——梯度下降——开始对优化算法的研究。问题定义$$ \min_w f(w) $$迭代求解$$ w_{k1} w_k - \gamma_k \nabla f(w_k) $$其中$w_{k1}$第 $k$ 次迭代后的更新值$w_k$第 $k$ 次迭代前的初始值$\gamma_k$步长step size$\nabla f(w_k)$$f$ 在 $w_k$ 处的梯度。这里的假设是函数 $f$ 连续且可微。我们的目标是找到优化函数的谷底最低点但通往谷底的真实方向未知——我们只能进行局部观察因此负梯度方向是我们所拥有的最好信息。沿该方向迈出一小步只会让我们更接近最小值迈出小步后我们重新计算新梯度再次沿该方向移动一小步如此反复直到抵达谷底。本质上梯度下降所做的就是沿最陡下降方向负梯度前进。步长step size的选择迭代更新方程中的 $\gamma$ 参数被称为步长。通常我们不知道最优步长的取值因此需要尝试不同的值。标准做法是在对数尺度上尝试一组取值然后选用效果最好的一个。对于一维二次函数可能出现如下几种情形学习率过低会朝最小值稳步前进但耗时可能远超理想情况略高于最优实践中最理想的取值通常带来最快的收敛一般很难、甚至不可能一步直达最小值因此希望步长比最优值稍大一点学习率过大迭代点会离最小值越来越远出现发散divergence。实际工程中我们通常希望使用一个略小于发散临界值的学习率。1.2 随机梯度下降Stochastic Gradient Descent在 SGD 中我们用梯度的随机估计替代真实梯度向量。对神经网络而言这个随机估计就是单个数据点单个样本的损失梯度。设 $f_i$ 表示网络在第 $i$ 个样本上的损失$$ f_i l(x_i, y_i, w) $$我们最终要最小化的函数 $f$ 是所有样本上的总损失$$ f \frac{1}{n}\sum_i^n f_i $$SGD 依据 $f_i$ 的梯度而非总损失 $f$ 的梯度更新权重$$ \begin{aligned} w_{k1} w_k - \gamma_k \nabla f_i(w_k) \quad\text{(i 均匀随机选取)} \end{aligned} $$如果 $i$ 是随机选取的那么 $f_i$ 是 $f$ 的一个有噪声但无偏的估计数学上写为$$ \mathbb{E}[\nabla f_i(w_k)] \nabla f(w_k) $$因此 SGD 第 $k$ 步的期望与全梯度下降第 $k$ 步相同$$ \mathbb{E}[w_{k1}] w_k - \gamma_k \mathbb{E}[\nabla f_i(w_k)] w_k - \gamma_k \nabla f(w_k) $$即任何 SGD 更新在期望意义上都等价于全批量更新。但 SGD 不只是带噪声的更快梯度下降——除了更快之外它还能取得比全批量梯度下降更好的结果SGD 中的噪声能帮助我们避开较浅的局部最小值找到更深、更好的最小值这一现象称为退火annealing。概括而言随机梯度下降的优势如下各样本之间存在大量冗余信息SGD 避免了大量冗余计算在训练早期噪声相对梯度中的信息而言很小因此 SGD 的一步几乎等同于GD 的一步退火——SGD 更新中的噪声可以防止收敛到较差的浅层局部最小值SGD 的计算成本大幅降低无需遍历所有数据点。小批量Mini-batching小批量方法不是只计算单个样本的损失而是计算多个随机选取样本上的损失从而降低单步更新的噪声$$ w_{k1} w_k - \gamma_k \frac{1}{|B_i|} \sum_{j \in B_i}\nabla f_j(w_k) $$相比单样本小批量通常能更好地利用硬件例如 GPU 在单样本训练时利用率很低。分布式训练技术会把一个很大的 mini-batch 拆分到集群的各台机器上再聚合产生的梯度课程中举例Facebook 曾借助分布式训练在一小时内完成 ImageNet 上的网络训练。重要提示不要对全尺寸批量使用梯度下降。如果你确实想在全批量上训练请使用名为LBFGS的优化技术——PyTorch 和 SciPy 都提供了该方法的实现。1.3 动量Momentum在动量方法中我们维护两个迭代量$p$ 和 $w$而不是一个更新规则如下$$ \begin{aligned} p_{k1} \hat{\beta_k}p_k \nabla f_i(w_k) \ w_{k1} w_k - \gamma_kp_{k1} \ \end{aligned} $$$p$ 被称为SGD 动量SGD momentum。每一步更新时我们先把旧动量乘以衰减因子 $\beta$取值在 0 与 1 之间然后加上当前随机梯度。$p$ 可以看作梯度的运行平均running average最后让 $w$ 沿新动量 $p$ 的方向移动。等价形式随机重球法Stochastic Heavy Ball Method$$ \begin{aligned} w_{k1} w_k - \gamma_k\nabla f_i(w_k) \beta_k(w_k - w_{k-1}) 0 \leq \beta 1 \end{aligned} $$该形式与前一形式数学等价。这里下一步移动是上一步方向$w_k - w_{k-1}$与新负梯度的组合。直觉物理中的动量SGD 动量与物理中的动量概念类似优化过程像一个滚下山的重球。动量让球保持既有的运动方向而梯度可以看作把球推向其他方向的力。与剧烈改变行进方向纯 SGD 常见相比动量只做适度调整抑制了纯 SGD 中常见的震荡。$\beta$ 参数被称为阻尼因子Dampening Factor$\beta$ 必须大于 0若等于 0退化为普通梯度下降$\beta$ 必须小于 1否则一切都会爆炸发散$\beta$ 较小能更快改变方向$\beta$ 较大转弯所需时间更长。实用指南Practical guidelines动量几乎必须与随机梯度下降搭配使用$\beta 0.9$ 或 $0.99$ 几乎总是效果良好增大动量参数时通常需要降低步长以维持收敛。例如 $\beta$ 从 0.9 变为 0.99 时学习率需除以 10。为什么动量有效1加速Acceleration以下是 Nesterov 动量的更新规则$$ p_{k1} \hat{\beta_k}p_k \nabla f_i(w_k) \ w_{k1} w_k - \gamma_k(\nabla f_i(w_k) \hat{\beta_k}p_{k1}) $$Nesterov 动量在精心选择常数的情况下可以获得加速收敛但仅适用于凸问题不适用于神经网络。很多人说普通动量也是加速方法但实际上它只对二次函数加速且加速与 SGD 的噪声不相容。因此虽然 Momentum SGD 确实带有一定加速成分但仅用加速不足以解释该方法的高性能。2噪声平滑Noise smoothing更实际、更可能的解释是噪声平滑动量对梯度求平均——我们每步使用的都是梯度的运行平均。理论上为了让 SGD 正常工作应当对所有步更新取平均$$ \bar w_k \frac{1}{K} \sum_{k1}^K w_k $$带动量的 SGD 妙处在于不再需要这种平均动量给优化过程增加了平滑性使每次更新都成为对解的良好逼近。加速与噪声平滑共同造就了动量方法的高性能。纯 SGD 在初期向解前进顺利但到达碗底谷底后会在底部来回弹跳调整学习率只会让弹跳变慢。动量则平滑了每一步不再出现弹跳。仓库中的可运行实现课程仓库的 extra/optimization.ipynb 提供了手写优化器的练习其基类 extra/utils/optim.py 定义了Optim(Optimizer)step()遍历参数组并调用待实现的my_step(p, state, group)未实现时抛出NotImplementedError并内置了二次目标函数objective(x, y)与可视化函数output(...)便于直接观察不同优化器、不同学习率下的路径差异。练习中给出的 vanilla SGD 实现为class SGDOptimizer(Optim): def __init__(self, params, lr): defaults dict(lrlr) super(SGDOptimizer, self).__init__(params, defaults) def my_step(self, p, state, group): lr group[lr] d_p p.grad.data p.data.add_(-group[lr], d_p)练习笔记明确指出学习率过大时优化方向方差很大学习率过小时又无法有效最小化目标函数——这正是本讲步长选择的直观演示。动量版实现则维护状态state[v]每步按v d_p momentum * v累积方向再更新参数对应讲义中 $p_{k1} \beta p_k \nabla f_i(w_k)$ 的更新规则。二、优化技术 II自适应方法、归一化层与神经网络加速 MRI2.1 自适应方法Adaptive Methods带动量的 SGD 目前是许多机器学习问题上的最先进优化方法但在实践中还会用到其他方法——统称自适应方法它们对条件不佳poorly conditioned的问题尤其有用即 SGD 失效的场景。在 SGD 公式中网络中每个权重都使用同一个全局学习率 $\gamma$更新。而自适应方法为每个权重单独适配学习率其依据来自每个权重各自得到的梯度信息。实际使用的网络通常在不同部分具有不同结构例如 CNN 前部可能是在大尺寸图像上的很浅的卷积层而网络后部可能是小图像上的大量通道卷积。两种运算差异很大因此对网络起始部分合适的学习率不一定适合后部——按层自适应学习率是有用的。以 VGG16 为例后部权重如图中 4096 维全连接层直接决定输出、对输出影响极强需要更小的学习率而前部权重尤其是随机初始化时对输出的个体影响较小。RMSprop**均方根传播Root Mean Square Propagation**的核心思想是用梯度的均方根对梯度做归一化。下面方程中对梯度取平方表示对向量每个元素分别平方$$ \begin{aligned} v_{t1} {\alpha}v_t (1 - \alpha) \nabla f_i(w_t)^2 \ w_{t1} w_t - \gamma \frac {\nabla f_i(w_t)}{ \sqrt{v_{t1}} \epsilon} \end{aligned} $$其中 $\gamma$ 是全局学习率$\epsilon$ 是接近机器精度的小值量级约为 $10^{-7}$ 或 $10^{-8}$用于避免除零错误$v_{t1}$ 是二阶矩估计。$v$ 通过指数移动平均exponential moving average来估计这个带噪声的量这是维护一个会随时间变化之量的平均值的标准方法。我们需要给较新的值更大的权重因为它们携带更多信息一种做法是指数式地降低旧值的权重。$v$ 计算中非常古老的值每步都被 $\alpha$ 常数取值 0 到 1 之间降低权重直到它们不再是指数移动平均的重要组成部分。原始方法维护的是非中心二阶矩的指数移动平均因此这里不减均值。二阶矩被用于**逐元素element-wise**归一化梯度梯度的每个元素都除以二阶矩估计的平方根。若梯度的期望值很小这一过程近似于用标准差去除梯度。分母中的小 $\epsilon$ 不会引起发散因为当 $v$ 很小时动量也很小。ADAMADAMAdaptive Moment Estimation即RMSprop 加动量是更常用的方法。动量更新被转换为指数移动平均且调整 $\beta$ 时无需改变学习率与 RMSprop 一样这里也对平方梯度取指数移动平均$$ \begin{aligned} m_{t1} {\beta}m_t (1 - \beta) \nabla f_i(w_t) \ v_{t1} {\alpha}v_t (1 - \alpha) \nabla f_i(w_t)^2 \ w_{t1} w_t - \gamma \frac {m_{t}}{ \sqrt{v_{t1}} \epsilon} \end{aligned} $$其中 $m_{t1}$ 是动量的指数移动平均。公式中未展示的**偏差校正bias correction**用于在早期迭代中保持移动平均无偏。实践对比与 ADAM 的缺点训练神经网络时SGD 在训练初期常常走错方向而 RMSprop 能较快对准正确方向但 RMSprop 与普通 SGD 一样受噪声困扰接近局部极小值时会在最优点附近明显弹跳。正如在 SGD 上添加动量能得到改进一样ADAM 也会带来同类改进——它是解的良好、低噪声估计因此一般推荐使用 ADAM 而非 RMSprop。ADAM 对训练某些语言模型类网络是必要的优化神经网络时一般优先选择带动量的 SGD或ADAM。但 ADAM 的理论在论文中仍未被很好理解并且存在若干缺点在非常简单的测试问题上可以证明该方法不收敛已知会产生泛化误差若网络在训练数据上训练到零损失它不会在从未见过的数据点上达到零损失。在图像问题上尤其常见其泛化误差常比使用 SGD 更差。可能的原因包括它找到的是最近的局部最小值、ADAM 噪声更少、或其结构本身等ADAM 需要维护3 个缓冲区而 SGD 只需 2 个。模型达到数 GB 量级时这可能使其无法装入内存需要调2 个动量参数而非 1 个。2.2 归一化层Normalization Layers与改进优化算法不同归一化层改进的是网络结构本身它们是插在既有层之间的额外层目标是改善优化与泛化性能。神经网络中通常交替进行线性运算与非线性运算激活函数如 ReLU。归一化层可以放在线性层之前也可以放在激活函数之后最常见的做法是放在线性层与激活函数之间例如图 3(c) 中卷积线性层→ 批归一化 → ReLU。注意归一化层影响流经的数据但不会削弱网络的表达能力通过适当配置权重未归一化的网络也能给出与归一化网络相同的输出。归一化运算的通用形式$$ y \frac{a}{\sigma}(x - \mu) b $$其中 $x$ 是输入向量$y$ 是输出向量$\mu$ 是 $x$ 的均值估计$\sigma$ 是 $x$ 的标准差估计$a$ 是可学习的缩放因子$b$ 是可学习的偏置项。若去掉可学习参数 $a$、$b$输出向量 $y$ 的分布将固定为均值 0、标准差 1。缩放因子 $a$ 与偏置 $b$ 维持了网络的表征能力——输出值仍可处于任意范围。注意 $a$、$b$并不会逆转归一化因为它们是可学习参数比 $\mu$、$\sigma$ 稳定得多。针对一批 $N$ 张高 $H$、宽 $W$、$C$ 通道的图像根据如何选取样本做归一化有 4 种不同的归一化方式批归一化Batch norm仅对输入的某一个通道做归一化。这是最早提出、最广为人知的方法层归一化Layer norm在一张图像内、跨所有通道做归一化实例归一化Instance norm仅对一张图像、一个通道做归一化组归一化Group norm在一张图像内、跨一组通道做归一化。例如通道 0 到 9 是一组通道 10 到 19 是另一组以此类推。实践中组大小几乎总是32。这是课程讲师 Aaron Defazio 推荐的方法实践中性能好且不与 SGD 冲突。实践中批归一化与组归一化在计算机视觉问题上效果良好而层归一化与实例归一化在语言问题上被大量使用。为什么归一化有帮助尽管归一化在实践中效果好其有效的确切原因仍存争议。最初提出归一化是为了减少内部协变量偏移internal covariate shift但一些学者通过实验证明该解释有误。不过归一化显然综合了以下因素优化效果带归一化层的网络更容易优化允许使用更大的学习率从而加速神经网络训练正则化效果由于批内样本的随机性均值/标准差估计带有噪声。这种额外噪声在某些情况下带来更好的泛化归一化降低了对权重初始化的敏感性。因此归一化让你可以更随意几乎可以把任意神经网络构件组合在一起而无需担心网络条件有多差就能有很大机会训练成功。实践注意事项必须通过均值与标准差的计算以及归一化的应用来做反向传播否则网络训练会发散。该反向传播计算相当困难且易出错但PyTorch 能自动为我们计算非常有帮助。PyTorch 中两个归一化层类如下torch.nn.BatchNorm2d(num_features, ...) torch.nn.GroupNorm(num_groups, num_channels, ...)批归一化是最早开发、最广为人知的方法但Aaron Defazio 推荐使用组归一化它更稳定、理论上更简单、通常效果更好组大小 32 是很好的默认值。注意对于批归一化和实例归一化训练结束后使用的均值/标准差是固定的而不是每次评估网络时重新计算——因为做归一化需要多个训练样本。组归一化和层归一化则无此必要因为它们的归一化只针对单个训练样本。2.3 优化的终结The Death of Optimization神经网络加速 MRI有时我们闯入一个完全陌生的领域却能改善其现有做法。一个例子就是在**磁共振成像MRI**领域用深度神经网络加速 MRI 图像重建。MRI 重建问题传统 MRI 重建问题中原始数据来自 MRI 机器通过简单管线/算法重建出图像。MRI 机器在二维傅里叶域中一次捕获一行或一列每隔几毫秒。原始输入由频率与相位两个通道组成值代表具有该频率与相位的正弦波的幅度。简单地说它可以被看作一幅复值图像具有实部与虚部两个通道。若对该输入施加逆傅里叶变换即把所有正弦波按其值加权相加就能得到原始的解剖图像。目前从傅里叶域到图像域存在一个线性映射且非常高效——无论图像多大只需几毫秒。问题是还能更快吗加速 MRI加速 MRI 的目标是让重建过程更快机器运行更快同时仍产出相同质量的图像。目前最成功的方式是不采集 MRI 扫描的所有列可以随机跳过一些列但实践中保留中间的列很有用它们包含横跨图像的大量信息其余部分随机采集。问题是不再能用线性映射重建图像——对子采样傅里叶空间施加线性映射的输出图 7 最右图显然没有实用价值这给了我们做点更聪明事情的空间。压缩感知Compressed sensing压缩感知是理论数学领域长期以来的重大突破之一。Candes 等人的论文表明理论上可以从子采样的傅里叶域图像获得完美重建。换言之当要重建的信号是稀疏的或具有稀疏结构时可以从更少的测量中完美重建它。但实际应用有若干要求不需要随机采样而是需要非相干incoherent采样——虽然实践中人们最终还是会随机采样。另外采集整列与半列耗时相同因此实践中我们总是采集整列。另一个条件是图像需要稀疏性——即图像中有大量零黑色像素。对原始输入做波长分解可以使其稀疏表示但这种分解得到的只是近似稀疏而非精确稀疏因此该方法给出相当好但并不完美的重建。不过如果输入在波长域中非常稀疏我们一定能得到完美图像。压缩感知基于优化理论其重建通过求解一个带额外正则化项的小型优化问题得到$$ \hat{x} \arg\min_x \frac{1}{2} \Vert M (\mathcal{F}(x)) - y \Vert^2 \lambda TV(x) $$其中 $M$ 是将未采样项置零的掩码函数$\mathcal{F}$ 是傅里叶变换$y$ 是观测到的傅里叶域数据$\lambda$ 是正则化惩罚强度$V$ 是正则化函数。该优化问题必须为 MRI 扫描中的**每个时间步或每个切片**求解一次耗时往往远超扫描本身——这给了我们寻找更好方法的又一个理由。谁还需要优化与其在每个时间步求解小优化问题为何不用一个大神经网络直接产出所需解我们的希望是训练一个复杂度足够的神经网络使其一步之内本质性地解决该优化问题并产出与逐步求解优化问题同样好的结果$$ \hat{x} B(y) $$其中 $B$ 是我们的深度学习模型$y$ 是观测到的傅里叶域数据。15 年前这种做法很难如今则容易得多深度学习方法的输出明显优于压缩感知方法且看起来与真实扫描非常相似。课程中用于生成该重建的模型采用ADAM 优化器、组归一化层、基于 U-Net 的卷积神经网络。这类方法已经非常接近实际应用有望在数年内看到加速 MRI 扫描进入临床实践。三、Practicum理解卷积与自动微分引擎3.1 理解一维卷积本部分讨论卷积因为我们想探索数据的稀疏性sparsity、平稳性stationarity与组合性compositionality。上一周docs/en/week04/04-1.md讨论的线性变换使用矩阵 $A$这里我们把矩阵宽度改为核大小 $k$于是矩阵的每一行都是一个核。我们可以通过**堆叠与平移stacking and shifting**来使用这些核从而得到 $m$ 个高度为 $n-k1$ 的层。输出是 $m$厚度个大小为 $n-k1$ 的向量。此外单个输入向量可以看作一个单声道信号monophonic signal。现在输入 $x$ 是一个映射$$ x:\Omega\rightarrow\mathbb{R}^{c} $$其中 $\Omega \lbrace 1, 2, 3, \cdots \rbrace \subset \mathbb{N}^1$因为这是一维信号其定义域是一维的此时通道数 $c1$当 $c2$ 时就是立体声信号stereophonic signal。对于一维卷积我们只需逐核计算标量积见下图。3.2 PyTorch 中核的维度与输出宽度技巧在 IPython 中使用问号可以查看函数的文档。例如Init signature: nn.Conv1d( in_channels, # 输入图像中的通道数 out_channels, # 卷积产生的通道数 kernel_size, # 卷积核大小 stride1, # 卷积步长 padding0, # 加在输入两侧的零填充 dilation1, # 核元素之间的间距 groups1, # 从输入到输出的阻塞连接数 biasTrue, # 若为 True则向输出添加可学习的偏置 padding_modezeros, # 可选值 zeros 和 circular )一维卷积示例我们做一维卷积从 $2$ 个通道立体声信号到 $16$ 个通道$16$ 个核核大小 $3$、步长 $1$。于是有 $16$ 个厚度为 $2$、长度为 $3$ 的核。假设输入信号批次大小为 $1$一个信号、$2$ 个通道、$64$ 个采样点则输出层有 $1$ 个信号、$16$ 个通道信号长度为 $62$$64-31$。若输出偏置大小会发现偏置大小为 $16$——因为每个权重对应一个偏置conv nn.Conv1d(2, 16, 3) # 2 通道立体声信号16 个大小为 3 的核 conv.weight.size() # 输出: torch.Size([16, 2, 3]) conv.bias.size() # 输出: torch.Size([16]) x torch.rand(1, 2, 64) # 批次大小 12 通道64 个采样点 conv(x).size() # 输出: torch.Size([1, 16, 62]) conv nn.Conv1d(2, 16, 5) # 2 通道16 个大小为 5 的核 conv(x).size() # 输出: torch.Size([1, 16, 60])二维卷积示例先定义输入数据$1$ 个样本、$20$ 个通道例如高光谱图像、高 $64$、宽 $128$。二维卷积从输入的 $20$ 个通道得到 $16$ 个大小为 $3 \times 5$ 的核。卷积后输出数据为 $1$ 个样本、$16$ 个通道高 $62$$64-31$、宽 $124$$128-51$x torch.rand(1, 20, 64, 128) # 1 样本20 通道高 64宽 128 conv nn.Conv2d(20, 16, (3, 5)) # 20 通道16 个核核大小为 3 x 5 conv.weight.size() # 输出: torch.Size([16, 20, 3, 5]) conv(x).size() # 输出: torch.Size([1, 16, 62, 124])如果想保持相同维度可以添加填充padding。继续上面的代码给卷积函数添加新参数stride1和padding(1, 2)表示 $y$ 方向填充 $1$上下各 1、$x$ 方向填充 $2$。此时输出信号与输入信号尺寸相同。进行二维卷积时存储核集合所需的维度数是 $4$# 20 通道16 个 3 x 5 的核步长 1填充为 1 和 2 conv nn.Conv2d(20, 16, (3, 5), 1, (1, 2)) conv(x).size() # 输出: torch.Size([1, 16, 64, 128])3.3 自动梯度Autograd如何工作本节我们让 torch 跟踪张量上的所有计算以便执行偏导数计算。创建一个 $2\times2$ 张量 $\boldsymbol{x}$具备梯度累积能力将 $\boldsymbol{x}$ 的所有元素减 $2$ 得到 $\boldsymbol{y}$打印y.grad_fn会得到SubBackward0 object at 0x12904b290说明y由减法模块 $\boldsymbol{x}-2$ 生成也可以用y.grad_fn.next_functions[0][0].variable反推原始张量。继续运算$\boldsymbol{z} 3\boldsymbol{y}^2$计算 $\boldsymbol{z}$ 的均值。反向传播用于计算梯度。本示例中反向传播过程可看作计算 $\frac{d\boldsymbol{a}}{d\boldsymbol{x}}$。手算 $\frac{d\boldsymbol{a}}{d\boldsymbol{x}}$ 作为验证后可以发现执行a.backward()得到的x.grad与我们手算的值一致。手算反向传播的过程如下$$ \begin{aligned} a \frac{1}{4} (z_1 z_2 z_3 z_4) \ z_i 3y_i^2 3(x_i-2)^2 \ \frac{da}{dx_i} \frac{1}{4}\times3\times2(x_i-2) \frac{3}{2}x_i-3 \ x \begin{pmatrix} 12\34\end{pmatrix} \ \left(\frac{da}{dx_i}\right)^\top \begin{pmatrix} 1.5-33-3\[2mm]4.5-36-3\end{pmatrix}\begin{pmatrix} -1.50\[2mm]1.53\end{pmatrix} \end{aligned} $$在 PyTorch 中任何使用偏导数的地方你得到的形状都与原始数据相同但正确的 Jacobian 应该是转置。从基础到更疯狂的例子现在有一个 $1\times3$ 向量 $x$令 $y$ 为 $x$ 的两倍并持续让 $y$ 翻倍直到其范数小于 $1000$。由于 $x$ 的随机性无法直接知道过程终止时的迭代次数x torch.randn(3, requires_gradTrue) y x * 2 i 0 while y.data.norm() 1000: y y * 2 i 1但通过已知的梯度可以轻松推断出来gradients torch.FloatTensor([0.1, 1.0, 0.0001]) y.backward(gradients) print(x.grad) tensor([1.0240e02, 1.0240e03, 1.0240e-01]) print(i) 9对于推断可以用requires_gradTrue标记我们想要跟踪梯度累积如下所示。如果在 $x$ 或 $w$ 的声明中省略requires_gradTrue并对 $z$ 调用backward()就会因为 $x$ 或 $w$ 没有梯度累积而出现运行时错误# x 和 w 都允许梯度累积 x torch.arange(1., n 1, requires_gradTrue) w torch.ones(n, requires_gradTrue) z w x z.backward() print(x.grad, w.grad, sep\n)此外可以用with torch.no_grad()省略梯度累积x torch.arange(1., n 1) w torch.ones(n, requires_gradTrue) # 所有 torch 张量都不进行梯度累积 with torch.no_grad(): z w x try: z.backward() # 这里 PyTorch 会报错因为 z 没有梯度累积 except RuntimeError as e: print(RuntimeError!!! :[) print(e)3.4 自定义梯度Custom gradients除了基础数值运算我们还可以生成自定义的模块/函数并把它插入神经计算图中。完整可运行的 Notebook 见 extra/b-custom_grads.ipynb。做法是继承torch.autograd.Function并重写forward()与backward()函数。例如训练网络时我们需要前向传播并知道输入关于输出的偏导数这样就能在代码的任何位置使用该模块。只要知道输入关于输出的偏导数就可以借助反向传播链式法则把该模块插入运算链的任意位置。Notebook 中给出了三个自定义模块示例add、split与max。例如自定义加法模块# 自定义加法模块 class MyAdd(torch.autograd.Function): staticmethod def forward(ctx, x1, x2): # ctx 是一个上下文可以在其中保存 # 供 backward 使用的计算结果 ctx.save_for_backward(x1, x2) return x1 x2 staticmethod def backward(ctx, grad_output): x1, x2 ctx.saved_tensors grad_x1 grad_output * torch.ones_like(x1) grad_x2 grad_output * torch.ones_like(x2) # 返回梯度的顺序需与 forward 的输入顺序一致排除 ctx return grad_x1, grad_x2如果两个东西相加得到一个输出就需要像这样重写 forward 函数反向传播时梯度被复制到两侧因此重写 backward 时就是复制。split与max的 forward/backward 重写方式见 NotebookSplit从同一个东西分裂出来向下传播梯度时应相加求和argmax选出最高项的索引因此最高项的梯度应为 $1$其余为 $0$。记住不同的自定义模块需要重写各自的前向传播以及backward 中的梯度计算方式。Notebook 中还演示了MySplit用x.clone()复制输入并返回两个输出、MyMax用x.detach().numpy().max()显式使用非 torch 代码求最大值、再以argmax掩码传播梯度的写法——这些是理解ctx.save_for_backward与梯度返回顺序约定返回张量数必须与 forward 输入数一致的最佳范例。四、小结与后续深入路径第 5 周从三个层面打通了训练神经网络的核心链路算法层梯度下降 → SGD无偏噪声估计、退火、小批量→ 动量重球法、阻尼因子、噪声平滑再到 RMSprop/ADAM 的逐权重自适应学习率并明确各自的适用场景与代价ADAM 的 3 个缓冲区、2 个待调参数、泛化问题等结构层归一化层作为插入既有层之间的结构性改进从通用公式 $y \frac{a}{\sigma}(x - \mu) b$ 出发厘清 Batch/Layer/Instance/Group 四种归一化的采样范围差异并给出 PyTorch 的BatchNorm2d/GroupNorm实践建议组大小 32 为良好默认值实现层通过手算 1D 卷积与Conv1d/Conv2d的维度推演、手算自动梯度并与a.backward()对照、以及继承torch.autograd.Function编写自定义算子把理论与实践对齐。可以继续深入仓库以下资源课程主索引与安装方式docs/en/week05/05.md、README.md含 Miniconda 环境与environment.yml创建、jupyter lab/jupyter notebook启动方式优化器手写练习extra/optimization.ipynb、extra/utils/optim.py自定义梯度 Notebookextra/b-custom_grads.ipynb上一周线性代数与卷积基础docs/en/week04/04-1.md。赞分享示例工程【免费下载链接】NYU-DLSP20NYU Deep Learning Spring 2020项目地址https://gitcode.com/gh_mirrors/pyt/pytorch-Deep-Learning点击查看免费下载相关推荐NYU-DLSP20 第 5 周技术解析PyTorch 优化方法、归一化层与卷积/自动微分实战NYU DLSP20 第 5 周技术解析PyTorch 优化方法、归一化层与卷积/自动微分实战 本文基于 docs/fr/week05/05.md https示例工程NYU-DLSP20 第五周精读梯度下降、Momentum 与自适应优化、归一化层以及卷积与自动微分实战NYU DLSP20 第五周精读梯度下降、Momentum 与自适应优化、归一化层以及卷积与自动微分实战 本文基于开源课程仓库 pytorch Deep L示例工程NYU-DLSP20 第 5 周深度笔记梯度下降、SGD 与动量、自适应优化、归一化层与 PyTorch 卷积/自动微分NYU DLSP20 第 5 周深度笔记梯度下降、SGD 与动量、自适应优化、归一化层与 PyTorch 卷积/自动微分 本文是 NYU Deep Learn示例工程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考