一家中央厨房接到一单大宴席,招牌菜“三鲜烩”的配方像一道神秘公式 z=(a+b)×c:先取两盘食材 a、b 混合,再和第三盘食材 c 一起翻倍入味。主厨把十几个灶台的工序画成一张满是箭头的流程图:切配台、炒锅、装盘台……半成品一道接一道往下传,最后端出成品等客人打分。客人皱眉说“太咸了!”主厨不慌不忙,拿起图纸从成品一路倒查——是调味台盐放多了,还是上游高汤本身就咸?每一道工序该背多少“锅”、该往哪个方向调整配方,全都写在图里。这张图,就是深度学习里大名鼎鼎的计算图。本文会陪你走进这张“厨房图纸”,看懂 AI 如何照图做菜(前向传播)、按图追责(反向传播),最终把配方越调越好。

岗位关联

理解计算图,等于拿到深度学习框架的“内部地图”:能读懂 PyTorch 与 TensorFlow 的梯度魔法,遇到 loss 不降、梯度消失时精准定位,还能自己动手实现自定义算子。这是算法工程师从“调包侠”进阶为“破局者”的关键一步。

目录
  1. 可视化演示
  2. 认识计算图:一张看得见流程的菜谱
  3. 构建计算图:给流水线画图纸
  4. 前向传播:照图出菜,一步一托盘
  5. 反向传播:顺着差评逐级追责
  6. 梯度流动:改配方要“柔着改”
  7. 框架实现:动态与静态的厨房哲学
  8. 模型调试:厨房里的质检巡检
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示计算图DAG结构的前向传播(拓扑序执行)、反向传播(链式法则梯度回传)及静态图与动态图执行机制对比,帮助理解深度学习框架底层原理。

本节配有交互动效(进入视口后自动加载,约 3KB)。

'+captions.forward+'

认识计算图:一张看得见流程的菜谱

中央厨房最值钱的东西,不是昂贵的食材,而是墙上那张“工序流程图”:洗切、腌制、下锅、调味、装盘,每道工序由谁负责、用到什么、产出去哪儿,全都清清楚楚。在深度学习里,这张图就叫计算图(Computational Graph)。任何一个复杂的数学表达式,都可以像菜谱一样被拆成小步骤:招牌菜配方 z=(a+b)×c,就是“先混合 a、b,再与 c 翻倍”。图中每个圆圈是一个节点,代表一次加工动作;箭头是边,代表半成品(数据)流动的方向。

节点和边组合起来,就形成了一张有向无环图(DAG)。“有向”是说箭头只能沿着一个方向走,菜从切菜台流向炒锅,绝不会倒流回切菜台;“无环”是说流程里不能出现首尾相接的圆圈,否则工序会无限循环,厨房永远无法收工。深度学习里所有前向、反向操作,都严格建立在这个“单行道”前提上。

有了这张图纸,谁负责哪道工序一目了然,也为后面“按图追责”(反向传播)打好了地基。没有计算图,深度学习模型的训练就像一锅乱炖——只知味道不对,却说不清是哪一味调料出了问题。

z=(a+b)×c\displaystyle z=(a+b)\times c
这个公式只有两步运算,却完整演示了计算图的拆解逻辑:一个复杂表达式 = 若干小节点 + 数据流向。
  1. 加法节点先执行 a+b,得到中间结果 m,对应图上“混合台”
    m=a+b\displaystyle m = a + b
  2. 乘法节点把 m 与 c 相乘,得到最终结果 z,对应图上“调味台”
    z=m×c\displaystyle z = m \times c

构建计算图:给流水线画图纸

动工之前,主厨先清点食材托盘。在深度学习里,这个托盘叫张量(Tensor),它不只是一堆数字,还带着形状、类型等“产地标签”,走到哪个灶台都不会混淆。图纸上每一道工序是一张工序卡,叫算子(Operator):混合、调味、分装,算子吃下几个托盘,加工后产出新的托盘。

图纸上的连线不是随便画的,它隐含着拓扑排序(Topological Order)——必须先完成前置工序,才能开始后续工序。任何一个节点的输入,都必须来自“已经出锅”的半成品,绝不能依赖一个还没算出来的未来值。构建图的本质,就是把一个个算子按依赖关系串成链条。

图纸画得越规范,后面自动做菜(前向传播)和自动追责(反向传播)就越省心。一张混乱的流程图,轻则算错步骤,重则让联动工序全部瘫痪——这正是真实工程里“先设计计算图、再写代码”的意义。

xk=fk(xp1, xp2, …)\displaystyle x_{k}=f_{k}\big(x_{p_{1}},\,x_{p_{2}},\,\dots\big)
每个节点都是一个函数:吃进若干前驱节点的输出,吐出一个新结果。公式里的下标 p₁、p₂ 就是“食材从哪里来”的依赖清单。
  1. 函数运算节点 k 执行算子 f_k,把输入加工成输出
    xk=fk(xp1,xp2,…)\displaystyle x_{k}=f_{k}\big(x_{p_{1}},x_{p_{2}},\dots\big)
  2. 依赖约束所有输入 x_{pᵢ} 必须来自 k 的前驱节点,保证拓扑序合法
    pi∈predecessors⁡(k)\displaystyle p_{i}\in\operatorname{predecessors}(k)

前向传播:照图出菜,一步一托盘

图纸挂上墙,开工!前向传播(Forward Propagation)就是把输入数据从图的起点灌进去,沿着箭头方向逐道工序走到底。每经过一个节点,算子读取输入、按当前参数完成计算,再把半成品托盘传给下一道工序。这一步和我们平时写代码做数学运算没什么两样,只是框架会自动把每一步“记账”。

关键在“记账”二字:中间结果必须缓存。每个节点算出的值都会被记住,因为反向传播时要按原路取用这些值来计算局部导数。就像厨师把每道半成品都留一勺样品放在台秤边,日后追查偏差时随时拿样品复检。如果丢掉这些缓存,反向传播就得把整条链路重算一遍,代价极其高昂。

最后一步是损失计算(Loss):端出成品让顾客打分。均方误差(MSE)是最常见的打分表之一——真实答案与预测值差得越远,分数越差。

菜全部上桌后,顾客的打分(损失)成了衡量整条流水线的唯一标尺。接下来,主厨就要拿着这张“差评单”,沿着流程图反向找责任人了。

L=1N∑i=1N(f(xi;θ)−yi)2\displaystyle L=\frac{1}{N}\sum_{i=1}^{N}\big(f(x_{i};\theta)-y_{i}\big)^{2}
损失 L 是一个标量,代表模型在整批数据上的“总差评程度”。反向传播的所有起点,都是这一个数字。
  1. 单样本误差对第 i 个样本,算出模型预测值 f(xᵢ;θ) 与真实值 yᵢ 的差
    ei=f(xi;θ)−yi\displaystyle e_{i}=f(x_{i};\theta)-y_{i}
  2. 平方取平均把每个误差平方(消除正负抵消),再对所有样本取平均,得到总损失 L
    L=1N∑i=1Nei2\displaystyle L=\frac{1}{N}\sum_{i=1}^{N}e_{i}^{2}

反向传播:顺着差评逐级追责

顾客说“太咸了!”主厨不会只骂装盘工,而是沿流水线倒着查:装盘的菜盐分超标,是因为最后调味台多加了一勺盐,还是上游高汤本身就咸?这个“从成品倒查源头”的过程,就是反向传播(Backpropagation)。它从损失节点出发,沿图反向计算每一个节点对损失的“责任大小”,这个责任大小叫做梯度(Gradient)。

核心数学工具是链式法则(Chain Rule):假设损失 L 依赖中间量 m,m 又依赖输入 x,那么 L 对 x 的导数等于两段导数相乘。每个节点只需记住两件事:自己的输出对输入的局部梯度,以及上游传来的梯度。二者相乘,传给下游,层层接力,就能算出所有参数的梯度。

差评从顾客一路传回配菜台,每个环节都清楚自己该背多少锅。梯度越大,说明这个参数对改变损失越“有话语权”;梯度接近零,则说明这味调料对当前口味几乎没影响——后面该加盐还是减糖,全靠这份责任清单。

∂L∂x=∂L∂m×∂m∂x\displaystyle \frac{\partial L}{\partial x}=\frac{\partial L}{\partial m}\times\frac{\partial m}{\partial x}
反向传播不是新魔法,它只是链式法则在计算图上的机械化执行:人人只需算自己的“局部责任”,然后乘以上游传来的“总责任”。
  1. 上游梯度损失对中间量 m 的偏导,由后一层节点回传而来
    ∂L∂m\displaystyle \frac{\partial L}{\partial m}
  2. 局部梯度中间量 m 对输入 x 的偏导,在本节点内直接计算
    ∂m∂x\displaystyle \frac{\partial m}{\partial x}
  3. 相乘回传上游梯度乘局部梯度,得到 L 对 x 的偏导,继续向前传递
    ∂L∂x=∂L∂m×∂m∂x\displaystyle \frac{\partial L}{\partial x}=\frac{\partial L}{\partial m}\times\frac{\partial m}{\partial x}

梯度流动:改配方要“柔着改”

现在每个环节都拿到了“责任大小”,也就是梯度。梯度告诉你:如果把这个参数稍微调大,损失会变大还是变小、变化多少。顺着梯度反方向微调,损失就会下降,这就是梯度下降(Gradient Descent)。但主厨深知,改配方不能太激进:每次只调一小勺,尝一口,再决定下一步。

这里藏着一个麻烦:梯度在反向传播中要沿路径不断相乘。如果每段局部梯度都小于 1,乘着乘着梯度指数级缩小,传到浅层几乎为 0,这叫梯度消失——像一条水管,越靠近源头水流越细,远端灶台根本接不到调整指令;反过来,如果每段都大于 1,梯度成倍放大,参数一步跳飞,这叫梯度爆炸。对策之一是梯度裁剪(Gradient Clipping):把过大的梯度按比例压回阈值内,像给水管装上减压阀。

主厨在配料表上做微调,每次只动几克盐,尝一口再动下一勺。梯度流动是否顺畅,直接决定模型能否收敛,这也是为什么工程中要密切关注每一层的梯度统计值。

wt+1=wt−η⋅∂L∂wt\displaystyle w_{t+1}=w_{t}-\eta\cdot\frac{\partial L}{\partial w_{t}}
学习率 η 是“每次改配方的小勺大小”,梯度是“该往哪个方向调、调多少”。二者相乘,再对参数做一次温柔的修正。
  1. 当前梯度∂L/∂w_t 表示第 t 步时,该参数对损失的影响方向和幅度
    ∂L∂wt\displaystyle \frac{\partial L}{\partial w_{t}}
  2. 学习率η 控制每次调整的步幅,太大则震荡发散,太小则龟速收敛
    η\displaystyle \eta
  3. 更新规则向梯度反方向迈一小步,得到下一轮参数 w_{t+1}
    wt+1=wt−η⋅∂L∂wt\displaystyle w_{t+1}=w_{t}-\eta\cdot\frac{\partial L}{\partial w_{t}}

框架实现:动态与静态的厨房哲学

两大主流框架,代表着两种经营哲学。PyTorch 的动态图像“现点现做的小炒店”:代码执行到哪,计算图就搭到哪,每一次前向都动态重建,想打印中间结果就打印、想打断调试就打断,对研究者极其友好,这种模式叫 define-by-run。TensorFlow 的静态图(尤其 1.x 时代)像“先排产再开工的中央厨房”:先把整张工序图完整定义好、编译成执行计划,然后数据灌进去按计划高速运转,性能更优、便于部署,这种模式叫 define-and-run。

无论哪种模式,框架内部都藏着一个自动微分引擎(Autograd Engine):前向时缓存中间值,反向时按拓扑逆序把链式法则的乘法一个个完成。我们要做的只是搭好模型、写好前向逻辑,梯度这种“脏活累活”全部交给引擎。

小炒店的灵活与中央厨房的性能各有拥趸,但底层那本“菜谱”——链式法则,一模一样。近年两大框架也在互相靠近:TensorFlow 有了即时执行模式,PyTorch 也能把动态图编译成静态图部署,哲学终归要服务于实战。

∂L∂x=∑y∈children(x)∂L∂y⋅∂y∂x\displaystyle \frac{\partial L}{\partial x}=\sum_{y\in\mathrm{children}(x)}\frac{\partial L}{\partial y}\cdot\frac{\partial y}{\partial x}
自动微分的通用公式:一个节点的梯度,等于所有下游节点传回梯度的总和。每个下游分支的梯度,再乘上分支链路对应的局部导数。
  1. 子节点回传x 的每个下游节点 y 先把自己的梯度传回来
    ∂L∂y\displaystyle \frac{\partial L}{\partial y}
  2. 乘以局部导数乘上 x→y 这条边上的局部导数,完成“接力”
    ∂y∂x\displaystyle \frac{\partial y}{\partial x}
  3. 跨路径求和x 被多个下游使用时,各路梯度相加,得到总梯度
    ∑y∈children(x)(∂L∂y⋅∂y∂x)\displaystyle \sum_{y\in\mathrm{children}(x)}\Big(\frac{\partial L}{\partial y}\cdot\frac{\partial y}{\partial x}\Big)

模型调试:厨房里的质检巡检

流水线跑得不顺,loss 不降反升甚至冒出 NaN,怎么办?现代框架提供了成套“厨房巡检”工具。第一件利器是梯度检查(Gradient Check):用数值方法近似验证框架算出的解析梯度是否正确。原理很简单——导数本来就是“参数微动时,函数值的变化率”,所以分别给参数加一点、减一点,算出函数值之差再除以间距,就得到数值梯度。如果它和反向传播算出的梯度高度一致,说明反向实现没毛病。

第二件利器是图可视化:TensorBoard 等工具把计算图渲染成可交互的流程图,哪条分支数据流异常、哪个算子耗时异常、哪段梯度常年接近于 0(断流点),一眼就能圈出来。第三件是算子验证:对新增的自定义算子单独喂已知输入,把手算结果和框架输出对比,确保前向、反向都正确后再接入大模型。

质检师傅拿着温度计、盐度计逐台巡检,哪道工序数值离谱,图纸上一圈就能锁死范围。学会这些调试手段,你在真实项目中遇到“梯度神秘消失”或“loss 乱跳”时,就不再是无头苍蝇,而是拿着计算图精确排雷的资深主厨。

f′(x)≈f(x+ε)−f(x−ε)2ε\displaystyle f'(x)\approx\frac{f(x+\varepsilon)-f(x-\varepsilon)}{2\varepsilon}
数值梯度像“用尺子量斜率”,虽然慢但非常可靠。它是验证反向传播实现是否正确的黄金标尺。
  1. 两侧差分采样在 x 左右两侧分别加、减一个极小量 ε,算出两个函数值
    f(x+ε),f(x−ε)\displaystyle f(x+\varepsilon),\quad f(x-\varepsilon)
  2. 中心差商近似两个函数值之差除以间距 2ε,得到导数近似值
    f(x+ε)−f(x−ε)2ε\displaystyle \frac{f(x+\varepsilon)-f(x-\varepsilon)}{2\varepsilon}

AI 应用场景

自定义损失函数与梯度验证

阶段:模型研发 → 自定义算子/损失实现

解决的问题:论文或业务中常需要全新损失函数,手写梯度极容易出错;用计算图思维,把前向与反向拆成两个函数,让框架自动接上反向链

具体案例:算法工程师在 PyTorch 中继承 torch.autograd.Function,重写 forward 与 backward,然后调用 gradcheck 做数值梯度比对,误差小于 1e-6 才敢合入训练流程。这套流程的每一步,都是“计算图节点 + 链式法则”的标准实践

训练故障定位:loss 不降与梯度断流

阶段:训练调优 → 模型调试

解决的问题:训练数小时后 loss 突然变 NaN 或长期不降,传统调参靠猜;利用计算图逐层检查梯度分布,能精准定位断流层

具体案例:平台工程师在 TensorBoard 中查看各层梯度直方图,发现第 4 层之后梯度全部为 0,顺着计算图检查发现该层接的是已饱和的 ReLU,负输入全部被截断,梯度无法回流。更换激活函数后训练恢复

研究模型转部署:动态图改静态图

阶段:模型部署 → 推理优化

解决的问题:研究阶段用动态图方便调试,但部署环境对延迟和显存敏感,静态图可以预先编译、融合算子,大幅提速

具体案例:推荐系统团队在 PyTorch 中完成 CTR 模型训练后,用 torch.jit.trace 将模型导出为静态计算图,再转 ONNX 交给推理引擎,单次推理耗时下降 40%。理解计算图执行机制,是完成这一转换的前提

Python 演示

python63 行
class Value:    def __init__(self, data, label=""):        self.data = data        self.grad = 0.0        self.label = label        self.prev = set()        self.grad_fn = lambda: None     def __add__(self, other):        other = other if isinstance(other, Value) else Value(other)        out = Value(self.data + other.data)        out.prev = {self, other}        def _backward():            self.grad += out.grad            other.grad += out.grad        out.grad_fn = _backward        return out     def __mul__(self, other):        other = other if isinstance(other, Value) else Value(other)        out = Value(self.data * other.data)        out.prev = {self, other}        def _backward():            self.grad += other.data * out.grad            other.grad += self.data * out.grad        out.grad_fn = _backward        return out     def relu(self):        out = Value(self.data if self.data > 0 else 0.0)        out.prev = {self}        def _backward():            self.grad += (out.data > 0) * out.grad        out.grad_fn = _backward        return out     def backward(self):        topo = []        visited = set()        def build(v):            if v not in visited:                visited.add(v)                for p in v.prev:                    build(p)                topo.append(v)        build(self)        self.grad = 1.0        for v in reversed(topo):            v.grad_fn()  def run_demo():    a = Value(2.0, "a")    b = Value(3.0, "b")    c = Value(4.0, "c")    z = (a + b) * c    z.backward()    print("z =", z.data)    print("dz/da =", a.grad, " dz/db =", b.grad, " dz/dc =", c.grad)  if __name__ == "__main__":    run_demo()
目的:用约 60 行代码实现一个微型自动微分引擎,直观演示“前向建图、反向回传梯度”的全流程,输出 z=20 以及 dz/da=4、dz/db=4、dz/dc=5。
  1. out.prev = {self, other}记录当前节点的“前驱节点”集合,这就是计算图的边
  2. out.grad_fn = _backward把反向小函数挂到新节点上,相当于给每个灶台配一张“追责卡”
  3. self.grad += other.data * out.grad乘法节点的局部梯度是“对方的数值”,梯度必须累加,因为一个节点可能有多个下游
  4. def _backward():闭包捕获输入节点,反向时直接更新它们身上的梯度
  5. for v in reversed(topo): v.grad_fn()逆拓扑序执行所有反向函数,等价于框架的自动微分引擎
  6. z = (a + b) * c一次普通四则运算,就在内存中建好了一张微型计算图
  7. z.backward()从输出节点出发,把梯度 1.0 沿着图一路传回所有叶子节点

常见误区

误区我觉得前向传播时保存每个节点的输出,主要是为了减少内存占用,跟反向传播没什么关系。

正解恰好相反,缓存中间值是为了反向传播时“复用局部梯度”。比如乘法节点的反向需要用到另一个输入的值,如果丢掉缓存,反向时整条前向链都要重算。它本质是“空间换时间”——用内存换速度,而不是省内存。就像厨师留样品是为了快速复核,不是为了省冰箱空间。

为何常见:初学时常把“缓存”简单理解为“存储优化”,忽略了它是链式法则得以高效执行的关键前提。

误区我听说反向传播很厉害,以为它是一个深度学习独有的特殊算法,跟数学没关系。

正解反向传播只是链式法则在计算图上的工程化实现。数学内核几百年前就有,框架做的只是把“局部梯度相乘 + 路径求和”自动化。理解了这一点,你就能解释为什么任何可导函数组成的图都能自动求梯度。

为何常见:框架把反向封装得像魔法,使用者只看到一行 .backward(),自然误以为存在某种神秘新算法。

误区我写 PyTorch 代码时从没手动建过图,所以动态图模式下应该没有计算图吧?

正解计算图一直都在,只是 PyTorch 采用“边执行边搭建”的方式,让框架在背后自动建图。你每做一次张量运算,图就悄悄长出一个新节点。没有计算图,自动微分引擎就无处发力。

为何常见:“显式定义”与“存在”被混为一谈,看不到手动建图步骤就误以为图不存在。

误区如果某个参数梯度是 0,说明它对模型没用,我可以直接删掉它。

正解梯度为 0 只表示“在当前点附近,损失对这个参数的变化不敏感”。它可能是局部极小值、鞍点,也可能是梯度消失或数值精度问题。贸然删除可能导致模型永久失去表达力。

为何常见:把“梯度大小”误等同于“参数重要性”,忽略了梯度携带的是局部信息而非全局信息。

误区梯度裁剪会把梯度缩小,那不是改变了梯度原本的方向,导致训练变歪吗?

正解按范数缩放的梯度裁剪只改变梯度的模长(长度),不改变方向。它就像把一根过长的水管直径调细,水流方向不变,但不再冲垮下游设备。如果裁剪后训练效果变差,通常是阈值设得太小。

为何常见:对“向量 = 方向 + 长度”的分解不熟悉,误以为任何数值修改都会破坏方向。

面试题

Q1. 计算图必须是无环的,最直接的原因是什么?

单选题 · easy

  • A.节省内存减少冗余计算
  • B.保证梯度回传必然终止← 正确答案
  • C.让图的结构更加对称美观
  • D.方便用户手工绘制示意图

答案:B。有环意味着反向传播会顺着回路无限绕圈,梯度永远传不回输入;无环(DAG)才能保证拓扑序存在、回传一定终止。就像厨房流水线不可能让菜从炒锅倒流回切菜台,否则流程永远结束不了。A、D 是附带好处而非根本原因,C 与工程无关。

Q2. 反向传播依赖的核心数学工具是哪一个?

单选题 · easy

  • A.泰勒展开公式
  • B.拉格朗日乘子法
  • C.链式求导法则← 正确答案
  • D.贝叶斯全概率公式

答案:C。反向传播的每一步都是“上游梯度 × 局部梯度”,这正是链式法则的标准形式。泰勒展开用于近似和梯度检查,拉格朗日乘子用于约束优化,贝叶斯公式涉及概率推断,都与反向传播的核心机制无关。

Q3. 前向传播中缓存每个节点的输出,主要目的是什么?

单选题 · mid

  • A.减少模型占用的磁盘空间
  • B.反向传播时复用局部梯度← 正确答案
  • C.提高数据增强的运算速度
  • D.让损失曲线变得更加平滑

答案:B。反向传播计算局部梯度时,需要用到前向的输入输出值(如乘法节点要用到另一边的数值)。缓存就是为反向准备的“样品台”,是空间换时间的策略。A 说反了,缓存反而增加内存占用;C、D 与缓存逻辑无关。

Q4. 关于动态图与静态图,下列说法正确的有哪些?

多选题 · mid

  • A.动态图支持边执行边建图← 正确答案
  • B.静态图通常利于部署优化← 正确答案
  • C.静态图无法实现自动微分
  • D.两种图都不需要拓扑排序

答案:AB。动态图(define-by-run)边执行边搭图,调试灵活;静态图(define-and-run)先编译后执行,便于算子融合和部署优化。两种图都需要拓扑排序,也都能做自动微分——C 和 D 是常见误解。就像小炒店现点现做、中央厨房提前排产,各有千秋但都离不开工序顺序。

Q5. 下列哪些情况容易造成梯度消失?

多选题 · mid

  • A.反向路径上连乘许多小数值局部导数← 正确答案
  • B.网络层数很深且激活函数饱和← 正确答案
  • C.损失函数中使用了交叉熵
  • D.学习率设置得过大偏激

答案:AB。链式法则要求梯度沿路径连乘,每段局部导数小于 1 时乘积指数级缩小;深层网络的饱和激活(如 Sigmoid 两端)局部导数趋近于 0,会加剧消失。交叉熵本身与梯度消失没有必然关系,学习率过大的典型症状是震荡发散而非梯度消失。

Q6. 某参数的梯度累计结果为 0,下列说法正确的是?

单选题 · hard

  • A.该参数一定可以安全删除
  • B.该参数当前对损失不敏感← 正确答案
  • C.该参数必定处于全局最优
  • D.该点损失函数一定不可导

答案:B。梯度为 0 只代表当前点附近损失对该参数的变化不敏感,可能是驻点、鞍点、梯度消失或数值错误,绝不等于“可以删”或“全局最优”。就像盐度计在某处读数没变化,不代表这块食材不需要盐,可能只是测量位置不对。

Q7. 梯度裁剪主要解决什么问题?

单选题 · mid

  • A.彻底消除梯度消失现象
  • B.抑制梯度爆炸造成的发散← 正确答案
  • C.加速模型收敛的速度
  • D.替代学习率的自动调整

答案:B。梯度爆炸时参数更新步长过大,模型容易发散。梯度裁剪把梯度范数限制在阈值内,相当于给水管装减压阀,只限制冲击力不改变方向。它不能消除梯度消失,也不是收敛加速器,更无法替代学习率调度。

Q8. 做梯度检查(数值梯度比对)时,正确的注意事项有哪些?

多选题 · hard

  • A.在测试模式下使用小网络← 正确答案
  • B.ε 要取得足够小但不能过小← 正确答案
  • C.采用相对误差作为判定依据← 正确答案
  • D.必须在训练若干轮之后进行

答案:ABC。梯度检查应在小网络、测试模式下进行,避免 BatchNorm 等训练态干扰;ε 太小会触发浮点精度问题,太大则近似误差大;相对误差能公平比较不同量级的梯度。是否先训练几轮不是必要项,D 错误。就像校验菜谱时用最小份量试做,火候和用量都要恰到好处。

Q9. 学习率 η 设置过大会导致什么典型现象?

单选题 · easy

  • A.损失快速下降后持续走低
  • B.损失震荡甚至发散无法收敛← 正确答案
  • C.梯度一定全部变成零值
  • D.计算图退化为静态图

答案:B。η 太大时,参数每次迈过谷底、来回震荡,损失不降反升甚至溢出为 NaN。它就像主厨每次加盐都加一大勺,成品口味忽咸忽淡,永远调不到位。A 是理想情形,C、D 与学习率大小无直接关系。

Q10. 自定义算子的反向函数中,为什么通常用梯度累加 += 而非直接赋值 =?

单选题 · extreme

  • A.输出可能被多个上游节点引用← 正确答案
  • B.因为 GPU 内存需要手动释放
  • C.无环图只能执行一次反向
  • D.浮点误差必须累积抵消掉

答案:A。一个节点的输出可能被多个下游分支使用(例如共享权重、多路连接),每个下游都会传回一份梯度,总梯度必须是所有路径梯度的和。这正是自动微分公式里的跨路径求和。厨房里同一盘高汤可能同时供三道菜用,三边反馈的修正量自然要叠加。B、C、D 与累加的数学意义无关。