张量链式法则(上篇):任意维度反向传播公式推导与常见算子解析
2026/7/27 4:08:02 网站建设 项目流程

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

在深度学习框架的底层,自动微分(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_{i=0}^{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, axis=0) # 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 全为1,grad_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)在具体计算中的高效形式。理解这些基础原理,有助于开发者深入掌握自动微分机制,并为后续学习更复杂的算子(如卷积、归一化)和框架源码打下坚实基础。在下一篇中,我们将继续探讨更高维度的算子(如卷积和池化)的反向传播公式,并展示它们在现代深度学习框架中的实现细节。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询