张量链式法则(上篇):任意维度反向传播公式推导与常见算子解析

📅 2026/7/27 4:08:07
张量链式法则(上篇):任意维度反向传播公式推导与常见算子解析
张量链式法则上篇任意维度反向传播公式推导与常见算子解析在深度学习框架的底层自动微分Autograd是支撑反向传播的核心引擎。理解张量Tensor上的链式法则是掌握神经网络训练机制的关键。本文将深入剖析任意维度张量的反向传播公式并通过可运行的代码示例解析常见算子的梯度计算。## 标量链式法则到张量链式法则的推广在微积分中标量函数的链式法则形式简洁若 ( y f(u) )( u g(x) )则 ( \frac{dy}{dx} \frac{dy}{du} \cdot \frac{du}{dx} )。当推广到张量时我们需要考虑多维输入和多维输出之间的雅可比矩阵。对于张量函数 ( \mathbf{y} f(\mathbf{x}) )其中 ( \mathbf{x} \in \mathbb{R}^{m_1 \times m_2 \times \cdots} )( \mathbf{y} \in \mathbb{R}^{n_1 \times n_2 \times \cdots} )反向传播的核心是计算梯度 ( \frac{\partial L}{\partial \mathbf{x}} )其中 ( L ) 是最终标量损失。通过链式法则[\frac{\partial L}{\partial \mathbf{x}} \frac{\partial L}{\partial \mathbf{y}} \cdot \frac{\partial \mathbf{y}}{\partial \mathbf{x}}]这里 ( \frac{\partial L}{\partial \mathbf{y}} ) 是上游梯度形状与 ( \mathbf{y} ) 相同( \frac{\partial \mathbf{y}}{\partial \mathbf{x}} ) 是雅可比矩阵形状为 ( \text{shape}(\mathbf{y}) \times \text{shape}(\mathbf{x}) )。实际计算时我们通常直接使用向量-雅可比积VJP形式避免显式构造巨大的雅可比矩阵。## 任意维度反向传播的通用公式假设在前向传播中张量 ( \mathbf{z} f(\mathbf{x}, \mathbf{w}) )其中 ( \mathbf{x} ) 和 ( \mathbf{w} ) 可能是不同维度的输入。反向传播时我们已知上游梯度 ( \text{grad_z} \frac{\partial L}{\partial \mathbf{z}} )需要计算[\text{grad_x} \text{grad_z} \cdot \frac{\partial \mathbf{z}}{\partial \mathbf{x}}, \quad \text{grad_w} \text{grad_z} \cdot \frac{\partial \mathbf{z}}{\partial \mathbf{w}}]对于大多数常见算子这些梯度可以通过形状适配和广播规则高效计算。核心思想是梯度形状与输入形状相同值取决于算子对输入的导数并考虑广播broadcasting对维度的影响。## 常见算子反向传播解析### 1. 矩阵乘法MatMul矩阵乘法 ( \mathbf{C} \mathbf{A} \cdot \mathbf{B} )其中 ( \mathbf{A} \in \mathbb{R}^{m \times k} )( \mathbf{B} \in \mathbb{R}^{k \times n} )的梯度计算如下- 对 ( \mathbf{A} ) 的梯度( \frac{\partial L}{\partial \mathbf{A}} \frac{\partial L}{\partial \mathbf{C}} \cdot \mathbf{B}^T )- 对 ( \mathbf{B} ) 的梯度( \frac{\partial L}{\partial \mathbf{B}} \mathbf{A}^T \cdot \frac{\partial L}{\partial \mathbf{C}} )下面是可运行的代码示例手动实现矩阵乘法的反向传播pythonimport numpy as npdef matmul_forward_backward(A, B, grad_C): 矩阵乘法前向和反向传播 A: shape (m, k) B: shape (k, n) grad_C: 上游梯度shape (m, n) 返回: (C, grad_A, grad_B) # 前向传播 C np.dot(A, B) # 反向传播计算对 A 和 B 的梯度 grad_A np.dot(grad_C, B.T) # (m, n) (n, k) - (m, k) grad_B np.dot(A.T, grad_C) # (k, m) (m, n) - (k, n) return C, grad_A, grad_B# 测试用例np.random.seed(42)A np.random.randn(3, 4)B np.random.randn(4, 2)grad_C np.ones((3, 2)) # 假设上游梯度全为1C, grad_A, grad_B matmul_forward_backward(A, B, grad_C)print(前向结果 C shape:, C.shape)print(梯度 grad_A shape:, grad_A.shape)print(梯度 grad_B shape:, grad_B.shape)print(grad_A 示例值:\n, grad_A)### 2. 广播加法Broadcast Add当两个张量进行加法时如果形状不同numpy/PyTorch 会自动广播。反向传播时梯度需要逆广播reduce to original shape即对广播后多余的维度求和。例如( \mathbf{Z} \mathbf{X} \mathbf{b} )其中 ( \mathbf{X} \in \mathbb{R}^{m \times n} )( \mathbf{b} \in \mathbb{R}^{n} )。前向时( \mathbf{b} ) 被广播为 ( (m, n) )。反向传播时- ( \text{grad_X} \text{grad_Z} )形状相同- ( \text{grad_b} \sum_{i0}^{m-1} \text{grad_Z}[i, :] )对广播维度求和下面是实现广播加法和反向传播的代码pythonimport numpy as npdef broadcast_add_backward(X, b, grad_Z): 广播加法反向传播 X: shape (m, n) b: shape (n,) grad_Z: 上游梯度shape (m, n) 返回: (grad_X, grad_b) # 对 X 的梯度就是 grad_Z 本身形状相同 grad_X grad_Z.copy() # 对 b 的梯度对广播维度第0维求和 grad_b np.sum(grad_Z, axis0) # shape (n,) return grad_X, grad_b# 测试用例X np.random.randn(3, 4)b np.array([1.0, 2.0, 3.0, 4.0])grad_Z np.ones((3, 4)) # 假设上游梯度全为1grad_X, grad_b broadcast_add_backward(X, b, grad_Z)print(梯度 grad_X shape:, grad_X.shape)print(梯度 grad_b:, grad_b)# 验证由于 grad_Z 全为1grad_b 应该是 [3, 3, 3, 3]3行求和### 3. 逐元素运算如 ReLU逐元素运算的梯度计算最为直接梯度形状与输入相同每个位置的梯度等于上游梯度乘以该位置的局部导数。以 ReLU 激活函数为例[\text{ReLU}(x) \max(0, x), \quad \frac{\partial \text{ReLU}}{\partial x} \begin{cases} 1 x 0 \ 0 x \leq 0 \end{cases}]反向传播时( \text{grad_x} \text{grad_y} \odot \text{mask} )其中 ( \text{mask} ) 是输入大于0的指示器。pythonimport numpy as npdef relu_forward_backward(x, grad_y): ReLU 激活函数前向和反向 x: 输入张量任意形状 grad_y: 上游梯度形状与 x 相同 返回: (y, grad_x) # 前向传播 y np.maximum(0, x) # 反向传播局部梯度为 mask (x 0) mask (x 0).astype(np.float32) grad_x grad_y * mask # 逐元素乘法 return y, grad_x# 测试用例x np.array([[-1.0, 2.0], [0.5, -0.3]])grad_y np.ones((2, 2))y, grad_x relu_forward_backward(x, grad_y)print(前向结果 y:\n, y)print(梯度 grad_x:\n, grad_x)# 预期: 负位置梯度为0正位置梯度为1## 链式法则在复合算子中的应用当多个算子串联时反向传播就是逐层应用上述 VJP 计算。例如一个简单的两层网络[\mathbf{h} \text{ReLU}(\mathbf{X} \mathbf{W}_1 \mathbf{b}_1)][\mathbf{y} \mathbf{h} \mathbf{W}_2 \mathbf{b}_2]它的反向传播顺序是先计算对 ( \mathbf{W}_2, \mathbf{b}_2 ) 的梯度然后通过 ReLU 的梯度传播到 ( \mathbf{h} )最后计算对 ( \mathbf{W}_1, \mathbf{b}_1 ) 的梯度。这种逐层反向的过程正是深度学习框架中自动求导的实现基础。## 总结本文从标量链式法则出发推广到任意维度张量的反向传播并深入解析了矩阵乘法、广播加法和逐元素运算ReLU这三种常见算子的梯度计算原理。通过可运行的代码示例我们展示了如何手动实现这些算子的反向传播揭示了向量-雅可比积VJP在具体计算中的高效形式。理解这些基础原理有助于开发者深入掌握自动微分机制并为后续学习更复杂的算子如卷积、归一化和框架源码打下坚实基础。在下一篇中我们将继续探讨更高维度的算子如卷积和池化的反向传播公式并展示它们在现代深度学习框架中的实现细节。