PyTorch 自动微分与反向传播机制原理解析

本文为用户与 DeepSeek 的问答。主题为 PyTorch 的反向传播机制。本文从数学原理、计算图机制和实际需求三个方面深入解释其工作原理,并结合 PyTorch 的自动微分(Autograd)系统进行说明。

Q; 深入解析 PyTorch 中的反向传播机制

Query

深入解释 torch 中反向传播机制的数学原理与为何需要反向传播机制。提示:你可以使用公式、Mermaid 图表、表格等方式使内容更容易理解。

Reasoning

深入解析 PyTorch 中的反向传播机制

反向传播(Backpropagation)是神经网络训练的核心算法。本文将从数学原理、计算图机制和实际需求三个方面深入解释其工作原理,并结合 PyTorch 的自动微分(Autograd)系统进行说明。


一、反向传播的数学原理

1. 链式法则(Chain Rule)

反向传播本质上是链式法则的高效实现。假设神经网络的计算流程为:

L=f(g(h(x)))L = f(g(h(x)))

其中:

  • h(x)=W1x+b1h(x) = W_1x + b_1
  • g(z)=ReLU(z)g(z) = \text{ReLU}(z)
  • f(y)=12(yt)2f(y) = \frac{1}{2}(y - t)^2 (均方误差损失)

则梯度计算为:

LW1=LffgghhW1\frac{\partial L}{\partial W_1} = \frac{\partial L}{\partial f} \cdot \frac{\partial f}{\partial g} \cdot \frac{\partial g}{\partial h} \cdot \frac{\partial h}{\partial W_1}
2. 梯度累积公式

对于任意参数 θ\theta,梯度计算可表示为:

Lθ=path θL(iopiinputi)\frac{\partial \mathcal{L}}{\partial \theta} = \sum_{\text{path } \theta \to \mathcal{L}} \left( \prod_{i} \frac{\partial \text{op}_i}{\partial \text{input}_i} \right)

二、PyTorch 的实现机制

1. 动态计算图(Dynamic Computation Graph)

PyTorch 在前向传播时自动构建计算图,节点为张量,边为运算操作:

2. Autograd 核心组件
组件作用
Tensor.requires_grad标记需要跟踪梯度的张量
Tensor.grad_fn记录创建该张量的操作(如 MulBackward
Tensor.grad存储计算得到的梯度
3. 反向传播过程
import torch
 
x = torch.tensor([2.0], requires_grad=True)
W = torch.tensor([3.0], requires_grad=True)
y = W * x
y.backward()
 
print(W.grad)  # 输出: tensor([2.])
print(x.grad)  # 输出: tensor([3.])

三、为何需要反向传播?

1. 手动计算不可行性

对于包含百万参数的现代神经网络,手动计算梯度的时间复杂度为 O(n2)O(n^2) ,而反向传播只需 O(n)O(n)

2. 计算效率对比
方法时间复杂度空间复杂度数值稳定性
数值微分O(n2)O(n^2)O(1)O(1)
符号微分O(1)O(1)O(n)O(n)
自动微分(反向传播)O(n)O(n)O(n)O(n)优秀
3. 内存优化策略

PyTorch 采用以下技术降低内存占用:

  • 梯度检查点:只保留关键节点的中间结果
  • 即时释放中间梯度:默认只保留叶子节点的梯度

四、反向传播的数学扩展

1. 矩阵求导示例

对于全连接层 Y=XW+bY = XW + b ,梯度计算为:

LW=XTLY\frac{\partial \mathcal{L}}{\partial W} = X^T \cdot \frac{\partial \mathcal{L}}{\partial Y}
2. 复杂运算处理
# PyTorch 自动处理不可导点
x = torch.tensor([-1.0, 0.0, 2.0], requires_grad=True)
y = torch.relu(x)
y.backward(torch.tensor([1.0, 1.0, 1.0]))
print(x.grad)  # 输出: tensor([0., 0., 1.])

五、总结

反向传播的必要性

  1. 自动微分:避免手工推导复杂导数
  2. 计算高效:利用链式法则的分解特性
  3. 内存优化:动态图的即时构建与销毁
  4. 框架支撑:支持任意复杂网络结构

PyTorch 通过动态计算图和 Autograd 系统,将反向传播的数学原理高效实现,使得深度学习模型的训练变得可行且高效。理解这一机制对于调试模型、优化内存使用和实现自定义算子至关重要。

Q: 参数的梯度累加计算公式

Query

你提到对于任意参数 θ\theta,梯度计算可表示为:

Lθ=path θL(iopiinputi)\frac{\partial \mathcal{L}}{\partial \theta} = \sum_{\text{path } \theta \to \mathcal{L}} \left( \prod_{i} \frac{\partial \text{op}_i}{\partial \text{input}_i} \right)

这个公式我没懂,再深入解释一下。我只有本科程度的高等数学知识与线性代数知识,建议一步一步解释推理过程。

Reasoning

好的!我将用最直观的方式,通过一个具体例子逐步解释这个公式的数学原理。我们结合图示和分步推导来理解链式法则在反向传播中的应用。


一、问题场景设定

假设我们有一个简单的计算图,其中参数 θ\theta 通过两条路径影响最终损失 LL

  • 路径 1θOp1AOp3L\theta \xrightarrow{\text{Op1}} A \xrightarrow{\text{Op3}} L
  • 路径 2θOp2BOp3L\theta \xrightarrow{\text{Op2}} B \xrightarrow{\text{Op3}} L

目标是计算 Lθ\frac{\partial L}{\partial \theta}


二、分步推导

步骤 1:前向传播表达式

假设具体操作为:

  • A=θ2A = \theta^2 (Op1:平方操作)
  • B=2θB = 2\theta (Op2:线性操作)
  • L=A+BL = A + B (Op3:加法操作)

则最终损失为:

L=θ2+2θL = \theta^2 + 2\theta

步骤 2:直接求导(对比验证)

直接对 LL 求导:

Lθ=2θ+2\frac{\partial L}{\partial \theta} = 2\theta + 2

步骤 3:反向传播视角

根据计算图,梯度需要沿着两条路径反向传播:

路径 1 的梯度计算(θ → A → L)
  1. A=θ2Aθ=2θA = \theta^2 \quad \Rightarrow \quad \frac{\partial A}{\partial \theta} = 2\theta
  2. L=A+BLA=1L = A + B \quad \Rightarrow \quad \frac{\partial L}{\partial A} = 1
  3. 路径 1 的梯度贡献: Lθ路径1=LAAθ=12θ=2θ\frac{\partial L}{\partial \theta} \bigg|_{\text{路径1}} = \frac{\partial L}{\partial A} \cdot \frac{\partial A}{\partial \theta} = 1 \cdot 2\theta = 2\theta
路径 2 的梯度计算(θ → B → L)
  1. B=2θBθ=2B = 2\theta \quad \Rightarrow \quad \frac{\partial B}{\partial \theta} = 2
  2. L=A+BLB=1L = A + B \quad \Rightarrow \quad \frac{\partial L}{\partial B} = 1
  3. 路径 2 的梯度贡献: Lθ路径2=LBBθ=12=2\frac{\partial L}{\partial \theta} \bigg|_{\text{路径2}} = \frac{\partial L}{\partial B} \cdot \frac{\partial B}{\partial \theta} = 1 \cdot 2 = 2
总梯度 = 路径 1 + 路径 2
Lθ=2θ+2\frac{\partial L}{\partial \theta} = 2\theta + 2

三、通用公式解析

原公式:

Lθ=path θL(iopiinputi)\frac{\partial \mathcal{L}}{\partial \theta} = \sum_{\text{path } \theta \to \mathcal{L}} \left( \prod_{i} \frac{\partial \text{op}_i}{\partial \text{input}_i} \right)

1. 符号解释

  • 路径(Path):参数 θ\theta 到损失 L\mathcal{L} 的所有可能数据流动路径。
  • 乘积项 \prod:沿某条路径的所有操作(op)的导数连乘(链式法则)。
  • 求和项 \sum:将所有路径的梯度贡献相加(多路径时梯度累加)。

2. 关键性质

  • 链式法则:复合函数的导数 = 外层导数 × 内层导数。
  • 多路径叠加:如果参数影响输出的多个分支,梯度是各分支贡献的总和。

四、复杂案例验证

考虑一个更复杂的计算图:

前向传播表达式

  • C=θ3C = \theta^3(Op1)
  • D=sin(θ)D = \sin(\theta)(Op2)
  • E=CDE = C \cdot D(Op3)
  • L=ln(E)L = \ln(E)(Op4)

反向传播计算

路径 1:θ → C → E → L
Lθ路径1=LEECCθ=1ED3θ2\frac{\partial L}{\partial \theta} \bigg|_{\text{路径1}} = \frac{\partial L}{\partial E} \cdot \frac{\partial E}{\partial C} \cdot \frac{\partial C}{\partial \theta} = \frac{1}{E} \cdot D \cdot 3\theta^2
路径 2:θ → D → E → L
Lθ路径2=LEEDDθ=1ECcos(θ)\frac{\partial L}{\partial \theta} \bigg|_{\text{路径2}} = \frac{\partial L}{\partial E} \cdot \frac{\partial E}{\partial D} \cdot \frac{\partial D}{\partial \theta} = \frac{1}{E} \cdot C \cdot \cos(\theta)
总梯度
Lθ=D3θ2+Ccos(θ)E\frac{\partial L}{\partial \theta} = \frac{D \cdot 3\theta^2 + C \cdot \cos(\theta)}{E}

五、反向传播的本质

  1. 自动化的链式法则:框架通过计算图自动追踪操作路径。
  2. 梯度累加规则:当一个参数影响多个分支时,梯度是所有路径贡献的总和。
  3. 计算效率:避免重复计算,时间复杂度与网络深度成线性关系(O(n)O(n))。

六、总结

  • 为什么需要求和(\sum:因为参数可能通过多个路径影响损失,每个路径贡献一部分梯度。
  • 为什么需要连乘(\prod:链式法则要求将路径上的所有局部导数相乘。
  • 反向传播的优势:将复杂的全局求导分解为局部导数的连乘与路径求和,大幅降低计算复杂度。

通过这种分路径计算再累加的方式,反向传播巧妙地利用计算图的结构信息,实现了高效的梯度计算。

Q: 从 PyTorch 代码层面深入解释一下 PyTorch 的反向传播如何实现

Query

从 PyTorch 代码层面深入解释一下 PyTorch 的反向传播如何实现

Reasoning

以下是关于 PyTorch 反向传播实现机制的代码级深入解析,结合关键代码段和底层原理说明:


一、PyTorch 反向传播的三大核心组件

1. Tensor 的梯度追踪机制

import torch
 
# 创建需要梯度追踪的张量
x = torch.tensor([2.0], requires_grad=True)  # 叶子节点
W = torch.tensor([3.0], requires_grad=True)  # 叶子节点
 
# 执行计算(自动记录操作)
y = W * x           # grad_fn=<MulBackward0>
z = y + 1           # grad_fn=<AddBackward0>
loss = z.mean()      # grad_fn=<MeanBackward0>
 
print(loss.grad_fn)  # 输出: <MeanBackward0 object>
  • requires_grad=True:标记需要追踪梯度的张量(叶子节点)
  • grad_fn:记录创建该张量的操作(反向传播时知道如何计算梯度)

2. 计算图的动态构建

PyTorch 的计算图是动态构建的,每次前向传播都会新建一个图:

# 示例:动态图特性
def forward(x):
    if x.sum() > 0:
        return x * 2
    else:
        return x * (-1)
 
x = torch.tensor([3.0], requires_grad=True)
out = forward(x)  # 根据输入值动态创建不同的计算图
out.backward()    # 自动适应不同的分支路径

3. 反向传播引擎(Autograd Engine)

当调用 .backward() 时,PyTorch 会执行以下操作:

loss.backward()  # 触发反向传播
  • 反向遍历计算图:从 lossgrad_fn 开始,沿着 next_functions 递归
  • 计算局部梯度:在每个操作节点调用已注册的 backward() 方法
  • 梯度累加:将计算结果累积到对应张量的 .grad 属性中

二、代码级反向传播流程分解

步骤 1:前向传播构建计算图

a = torch.tensor(2.0, requires_grad=True)
b = torch.tensor(3.0, requires_grad=True)
c = a * b      # grad_fn=<MulBackward0>
d = c + 1      # grad_fn=<AddBackward0>
loss = d**2    # grad_fn=<PowBackward0>

此时的计算图结构:

步骤 2:执行反向传播

loss.backward()  # 触发反向传播
底层操作分解
  1. 初始化梯度:设置 loss.grad = 1.0(标量损失的默认梯度)
  2. 反向传播到 d
    • 调用 PowBackward0backward() 方法:
      d_grad = 2 * d  # 导数公式:d(loss)/d(d) = 2d
  3. 反向传播到 c
    • 调用 AddBackward0backward() 方法:
      c_grad = d_grad * 1  # 加法操作的导数为1
  4. 反向传播到 a 和 b
    • 调用 MulBackward0backward() 方法:
      a_grad = c_grad * b  # 导数公式:d(c)/d(a) = b
      b_grad = c_grad * a  # 导数公式:d(c)/d(b) = a
  5. 梯度累加
    a.grad += a_grad  # 实际为 in-place 操作
    b.grad += b_grad

步骤 3:查看结果

print(a.grad)  # 输出: 2*d* b = 2*(2*3+1)*3 = 42
print(b.grad)  # 输出: 2*d* a = 2*(2*3+1)*2 = 28

三、PyTorch 反向传播的关键实现细节

1. 梯度计算规则注册

每个 torch 操作都有预定义的梯度计算规则:

# 示例:自定义操作的梯度规则
class MyAdd(torch.autograd.Function):
    @staticmethod
    def forward(ctx, x, y):
        ctx.save_for_backward(x, y)  # 保存前向传播的输入
        return x + y
 
    @staticmethod
    def backward(ctx, grad_output):
        x, y = ctx.saved_tensors
        return grad_output * 1, grad_output * 1  # 加法操作的导数
 
# 使用自定义操作
a = torch.tensor(2.0, requires_grad=True)
b = torch.tensor(3.0, requires_grad=True)
c = MyAdd.apply(a, b)
c.backward()

2. 梯度累加机制

PyTorch 默认会累加梯度,因此每次反向传播前需要手动清零:

optimizer.zero_grad()  # 标准训练循环中的必要操作
loss.backward()        # 梯度会累加到 .grad 属性中
optimizer.step()

3. 非标量张量的反向传播

当输出为非标量时,需要提供 gradient 参数:

x = torch.tensor([1.0, 2.0], requires_grad=True)
y = x * 2
 
# 假设 y 是向量,需要提供梯度初始值
y.backward(gradient=torch.tensor([0.1, 0.2]))
print(x.grad)  # 输出: tensor([0.2, 0.4])

4. 内存优化策略

  • 中间变量释放:默认只保留叶子节点的梯度
  • 梯度检查点:通过 torch.utils.checkpoint 减少内存占用
  • 禁用梯度计算:使用 with torch.no_grad():@torch.inference_mode()

四、可视化反向传播过程

示例代码

import torchviz
 
a = torch.tensor(3.0, requires_grad=True)
b = torch.tensor(4.0, requires_grad=True)
c = a * b
d = c.sin()
d.backward()
 
# 生成计算图可视化
torchviz.make_dot(d, params={'a':a, 'b':b}).render("graph")

生成的计算图结构


五、PyTorch 反向传播的性能优化

1. 异步执行

# 使用 CUDA 流实现异步计算
with torch.cuda.stream(torch.cuda.Stream()):
    loss.backward()  # 在非默认流中执行反向传播

2. 混合精度训练

scaler = torch.cuda.amp.GradScaler()
 
with torch.cuda.amp.autocast():
    output = model(input)
    loss = loss_fn(output, target)
 
scaler.scale(loss).backward()  # 自动缩放梯度
scaler.step(optimizer)
scaler.update()

3. 梯度裁剪

torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)

六、调试技巧

1. 检查梯度是否存在

print([p.requires_grad for p in model.parameters()])  # 检查参数是否启用梯度

2. 梯度数值检查

for name, param in model.named_parameters():
    if param.grad is not None:
        print(f"{name} 梯度均值: {param.grad.mean().item()}")

3. 保留中间梯度

y = x * 2
y.retain_grad()  # 强制保留中间变量的梯度
y.backward()
print(y.grad)    # 可以查看中间梯度

七、底层实现原理

1. C++ 核心引擎

PyTorch 的反向传播引擎主要由以下 C++ 组件构成:

  • torch::autograd::Engine:管理反向传播任务的执行
  • torch::autograd::Node:表示计算图中的操作节点
  • torch::autograd::Edge:表示节点之间的连接关系

2. 反向传播任务调度

// 伪代码:引擎执行流程
void Engine::execute(const EdgeList& roots) {
    // 1. 构建任务队列
    // 2. 使用线程池并行执行反向任务
    // 3. 处理依赖关系(拓扑排序)
    // 4. 调用每个 Node 的 apply() 方法
}

八、总结:PyTorch 反向传播的设计哲学

  1. 动态计算图:即时构建、即时销毁,灵活适应可变网络结构
  2. 延迟计算:只在需要时执行梯度计算,优化内存使用
  3. 分离式设计:前向传播记录操作,反向传播时执行实际计算
  4. 可扩展性:通过注册自定义 Function 支持任意复杂操作

理解这些底层机制,可以帮助开发者:

  • 更高效地调试梯度相关问题
  • 合理控制内存使用(如使用 detach() 截断计算图)
  • 实现自定义神经网络层和损失函数
  • 优化训练过程的性能

Q: 深入讲解几种微分方法的复杂度与数值稳定性

Query

你之前提到数值微分、符号微分、自动微分的时间空间复杂度以及数值稳定性的对比。这些复杂度是如何计算出的结果?数值稳定性又如何评估?

Reasoning

以下是关于数值微分、符号微分和自动微分的时间/空间复杂度计算原理,以及数值稳定性评估方法的详细解释:


一、时间复杂度计算原理

1. 数值微分(Numerical Differentiation)

实现方式:通过有限差分近似导数 f(x)f(x+h)f(xh)2hf'(x) \approx \frac{f(x+h)-f(x-h)}{2h}

时间复杂度分析

  • 单参数计算:每次计算需要 2 次前向传播
  • n 个参数:需要 2n 次前向传播
  • 前向传播复杂度:假设单次前向传播为 O(k)O(k)(k 为计算步骤数)
  • 总时间复杂度O(2n×k)=O(nk)O(2n \times k) = O(nk)

关键结论:时间复杂度与参数数量成线性关系,但前向传播的 kk 通常与网络规模相关,实际复杂度为 O(n2)O(n^2)

示例:计算 f(w1,w2,...,w100)f(w*1,w_2,...,w*{100}) 的梯度,需要 200 次前向传播


2. 符号微分(Symbolic Differentiation)

实现方式:通过代数规则推导闭式表达式

时间复杂度分析

  • 表达式膨胀:对复合函数 f(g(h(x)))f(g(h(x))) 求导会产生嵌套乘积项
  • 最坏情况:每层操作导致表达式长度指数级增长 O(2d)O(2^d)(d 为网络深度)
  • 优化情况:现代系统会进行表达式简化,典型复杂度为 O(d)O(d)

示例:对 f(x)=sin(ex2)f(x) = \sin(e^{x^2}) 求导: f(x)=cos(ex2)ex22xf'(x) = \cos(e^{x^2}) \cdot e^{x^2} \cdot 2x 表达式长度随求导次数线性增长


3. 自动微分(Automatic Differentiation)

实现方式:基于计算图分解求导过程

时间复杂度分析

  • 前向传播O(d)O(d)(d 为计算图深度)
  • 反向传播:每个节点执行固定时间的梯度计算,复杂度 O(d)O(d)
  • 总时间复杂度O(d)+O(d)=O(d)O(d) + O(d) = O(d)

关键优化:计算图节点数通常与参数数量 nn 成线性关系,因此复杂度可表示为 O(n)O(n)

示例:全连接网络的反向传播时间与参数数量成正比


二、空间复杂度计算原理

对比表格

方法空间复杂度原因
数值微分O(1)O(1)只需存储当前参数值和扰动结果
符号微分O(n)O(n)需要存储完整的符号表达式(可能指数膨胀,但优化后线性增长)
自动微分O(n)O(n)需要存储计算图中所有中间变量的前向值

详细解释

  • 数值微分:仅需保持原始参数和扰动后的参数副本,内存占用恒定
  • 符号微分:导数的符号表达式可能非常庞大,例如: xi=1nxi=j=1n_ijxi\frac{\partial}{\partial x} \prod*{i=1}^n x_i = \sum*{j=1}^n \prod\_{i \neq j} x_i 需要存储 nn 个乘积项
  • 自动微分:需要保存前向传播的所有中间结果用于反向计算,例如:
    # 前向计算需要保存这些值
    a = W1 * x
    b = a + b1
    c = relu(b)

三、数值稳定性评估方法

1. 数值微分稳定性

误差来源

  • 截断误差O(h2)O(h^2)(来自泰勒展开的近似)
  • 舍入误差O(ϵ/h)O(\epsilon/h)ϵ\epsilon 为机器精度)

稳定性分析总误差=O(h2)+O(ϵ/h)\text{总误差} = O(h^2) + O(\epsilon/h)

  • 最优步长选择:当 hϵ3h \approx \sqrt[3]{\epsilon} 时误差最小
  • 典型问题:对于病态条件数的问题(如指数函数),小步长会放大舍入误差

示例:计算 f(x)=exf(x) = e^xx=20x=20 处的导数:

  • 真实导数:e204.85×108e^{20} \approx 4.85 \times 10^8
  • 数值计算可能产生显著相对误差

2. 符号微分稳定性

特点

  • 精确计算:理论上没有数值误差
  • 实际限制
    • 符号化简可能导致数值不稳定表达式
    • 转换为浮点运算时仍会引入舍入误差

评估方法

  • 表达式复杂度:检查导数表达式是否包含病态操作(如大数相减) x(exex)=ex+ex\frac{\partial}{\partial x} (e^x - e^{-x}) = e^x + e^{-x} 直接计算可能在 xx 较大时出现精度损失

3. 自动微分稳定性

误差来源

  • 前向传播舍入误差:与符号微分相同量级
  • 反向传播稳定性:取决于梯度计算路径

稳定性优势

  • 避免误差累积:通过保持原始计算路径的数值特性
  • 典型对比
    • 直接计算 f(x)=x2+1xf(x) = \sqrt{x^2 + 1} - x 的导数
    • 符号微分可能得到不稳定的 xx2+11\frac{x}{\sqrt{x^2+1}} - 1
    • 自动微分在计算时保持原始表达式结构

四、综合对比与工程选择

1. 方法选择决策树

2. 实际框架实现

  • PyTorch/TensorFlow:采用自动微分(反向传播)
  • Mathematica:使用符号微分
  • 有限差分库:常用于验证其他方法的正确性

五、深度学习中的特殊考量

1. GPU 内存瓶颈

  • 自动微分优化:PyTorch 的 checkpoint 机制通过牺牲计算换内存
    # 减少中间变量存储
    from torch.utils.checkpoint import checkpoint
    x = checkpoint(layer1, x)
    x = checkpoint(layer2, x)

2. 混合精度训练

  • 稳定性挑战:float16 类型下的梯度计算需要特殊处理
  • 解决方案:梯度缩放(Gradient Scaling)
    scaler = torch.cuda.amp.GradScaler()
    with torch.cuda.amp.autocast():
        output = model(input)
    scaler.scale(loss).backward()

六、数学证明示例

数值微分误差分析

设真实导数为 f(x)f'(x),数值近似为: f~(x)=f(x+h)f(xh)2h\tilde{f}'(x) = \frac{f(x+h) - f(x-h)}{2h}

泰勒展开f(x+h)=f(x)+hf(x)+h22f(x)+h36f(ξ1)f(x+h) = f(x) + hf'(x) + \frac{h^2}{2}f''(x) + \frac{h^3}{6}f'''(ξ_1) f(xh)=f(x)hf(x)+h22f(x)h36f(ξ2)f(x-h) = f(x) - hf'(x) + \frac{h^2}{2}f''(x) - \frac{h^3}{6}f'''(ξ_2)

相减得f~(x)=f(x)+h212[f(ξ1)+f(ξ2)]\tilde{f}'(x) = f'(x) + \frac{h^2}{12}[f'''(ξ_1) + f'''(ξ_2)]

误差项截断误差=O(h2)\text{截断误差} = O(h^2)


七、总结理解

  1. 复杂度计算本质

    • 时间复杂度:计算代价随问题规模的增长率
    • 空间复杂度:内存需求随问题规模的增长率
  2. 稳定性评估核心

    • 对舍入误差和截断误差的敏感度
    • 保持数值计算精度的能力
  3. 工程实践指导

    • 小规模问题:优先符号微分(如物理仿真)
    • 大规模可微编程:必选自动微分(深度学习框架)
    • 快速验证:使用数值微分(原型开发阶段)
亮曰:
  • 数值微分:参数做 Delta ,计算最终值的差,没有反向传播过程
  • 符号微分:直接推导出导数表达式,再代入计算,表达式复杂
  • 自动微分:前向传播计算每个节点的值,再反向传播用链式法则计算每个节点的梯度。

自动微分可以算是一种动态规划,暂存计算中重复用到的中间数据,用空间换时间。更进一步,反向传播时,用层序遍历计算图,当节点的所有入边(反向时的出边)都计算完后,即可将当前节点从队列中移除,从而节省内存。