Skip to content

WARNING

🧪 Beta公测版本提示:教程主体已完成,正在优化细节,欢迎大家提Issue反馈问题或建议。

s05 前向传播与计算图 — exercise.py 练习指南

Download exercise.py

练习目标

通过实现三个核心模块——单层全连接前向传播、GELU 激活函数、计算图追踪,从代码层面深入理解神经网络前向传播的完整流程和计算图的概念。

预备知识

在开始练习前,确保你已经理解了以下概念(参见 demo.py 代码详解 中的详细解释):

  • 线性变换 z=Waprev+b 的矩阵维度规则
  • ReLU、Sigmoid、Tanh 三种激活函数的定义和实现
  • 中间值缓存(cache)的概念:存储 zaprevWb 供反向传播使用
  • GELU 的数学定义:GELU(z)=zΦ(z),其中 Φ(z) 是标准正态 CDF
  • 计算图的概念:将复杂表达式分解为基本运算节点的有向无环图

任务清单

任务1:实现单层全连接层的前向传播 dense_layer_forward(A_prev, W, b, activation)

  • 用到的公式
    • 线性变换:Z=WAprev+b
    • ReLU:ReLU(z)=max(0,z)
    • Sigmoid:σ(z)=11+ez
    • Tanh:tanh(z)
  • 实现步骤
    1. 计算 Z=W@Aprev+b(矩阵乘法 + 广播加法)
    2. 根据 activation 参数选择激活函数:
      • "relu"np.maximum(0, Z)
      • "sigmoid"1.0 / (1.0 + np.exp(-Z))(注意数值稳定:可先用 np.clip(Z, -500, 500)
      • "tanh"np.tanh(Z)(NumPy 内置)
      • "none"A=Z(恒等映射,无激活)
    3. 创建 cache 字典,包含 {Z: 线性输出, A_prev: 上一层激活, W: 权重矩阵, b: 偏置向量}
  • 需要调用的函数@ 运算符(矩阵乘法)、np.maximum()np.exp()np.tanh()np.clip()
  • 返回(Z, A, cache) 三元组
  • 期望输出Z.shape(n_curr, m)A.shape 也为 (n_curr, m),cache 包含四个键

任务2:实现 GELU 激活函数 gelu_exact(z)gelu_derivative(z)

  • GELU 的精确数学定义

    GELU(z)=zΦ(z)

    其中 Φ(z) 是标准正态分布的累积分布函数(CDF):

    Φ(z)=12[1+erf(z2)]

    erf(x) 是误差函数(error function)。

  • GELU 的近似实现(如果需要避免引入 scipy):

    GELU(z)0.5z[1+tanh(2π(z+0.044715z3))]
  • GELU 导数的精确形式

    GELU(z)=Φ(z)+zϕ(z)

    其中 ϕ(z) 是标准正态分布的概率密度函数(PDF):

    ϕ(z)=12πez2/2
  • 实现提示

    • 如果使用 scipy:from scipy.special import erfΦ(z)=0.5(1+erf(z/2))
    • 如果不想引入 scipy:使用 tanh 近似公式
    • 导数实现:需要同时用到 Φ(z)ϕ(z)
  • 验证GELU(0)=0GELU(2)1.95z 大时行为接近 ReLU),GELU(2)0.05

任务3:手动追踪计算图 trace_computational_graph(X)

给定表达式:

f(x1,x2,x3)=σ((x1w1+x2w2+b)w3+x3)

其中 w1=0.5, w2=0.3, w3=2.0, b=0.1σ 是 Sigmoid。

  • 任务:把这个复合函数分解为 7 个基本运算节点,每个节点执行一个简单操作(加减乘除、Sigmoid)

  • 计算图的节点链

    节点计算操作类型输入
    u1x1w1multiplyx1, w1
    u2x2w2multiplyx2, w2
    u3u1+u2addu1, u2
    u4u3+baddu3, b
    u5u4w3multiplyu4, w3
    u6u5+x3addu5, x3
    u7σ(u6)sigmoidu6
  • 实现:对每个节点,计算并记录到 graph_nodes 字典中

  • 每个节点的记录格式

    python
    graph_nodes["u1"] = {
        "value": 计算结果,
        "inputs": ["x1", "w1"],
        "op": "multiply"
    }
  • Sigmoid 实现提示σ(z)=1/(1+ez),可使用 np.exp()。为了防止数值溢出,建议先 clip 输入

  • 核心理解:这个练习让你手动体验 PyTorch/TensorFlow 底层在做什么——自动将复杂的数学表达式分解为计算图上的基本操作节点,每个节点只需要知道自己的局部操作和链式法则导数规则。

验证标准

运行 python exercise.py

  1. TODO 1:对 4 种激活函数(relu, sigmoid, tanh, none)都应输出正确的 Z.shapeA 的范围
  2. TODO 2GELU(0)=0GELU(z)z for z0(如 z=2 时约等于 1.95
  3. TODO 3:打印出 7 个节点的计算图,最终输出 u7 是一个在 (0,1) 之间的值(因为经过了 Sigmoid)

源码位置

clone 后打开(相对仓库根目录):

docs/nn-decision/dl/forward-graph/code/exercise.py