知识点卡片:矩阵微分
基本信息
| 属性 | 内容 |
|---|---|
| 知识点 | 矩阵微分 (Matrix Calculus) |
| 掌握程度 | ★★★★☆ |
| 学习优先级 | P1 |
| 预估时间 | 6小时 |
| 面试频率 | ★★★☆☆ |
核心原理
矩阵微分是反向传播的数学基础。在深度学习中,我们需要计算标量损失对矩阵参数的梯度。
布局约定
- 分母布局(Denominator layout):∂y/∂x 的形状与 x^T 相同
- 分子布局(Numerator layout):∂y/∂x 的形状与 y 相同
深度学习中通常使用分母布局。
核心公式速查
标量对向量:
∂(a^T x)/∂x = a
∂(x^T A x)/∂x = (A + A^T)x
∂‖x‖²/∂x = 2x
标量对矩阵:
∂(a^T X b)/∂X = a b^T
∂(tr(AX))/∂X = A^T
∂(tr(X^T A X))/∂X = (A + A^T)X
∂(log|X|)/∂X = (X^{-1})^T
向量对向量(雅可比矩阵):
∂(Ax)/∂x = A^T (分母布局)
∂(softmax(x))/∂x = diag(p) - p p^T代码实现
手写关键梯度推导
python
import numpy as np
# 1. MSE损失对W的梯度
# L = ‖XW - y‖² / (2n)
# ∂L/∂W = X^T (XW - y) / n
def mse_gradient(X, y, W):
n = len(y)
pred = X @ W
error = pred - y
return X.T @ error / n
# 2. 交叉熵 + softmax 对logits的梯度
# L = -Σ y log(softmax(z))
# ∂L/∂z = softmax(z) - y
def softmax(x):
exp_x = np.exp(x - np.max(x, axis=-1, keepdims=True))
return exp_x / exp_x.sum(axis=-1, keepdims=True)
def cross_entropy_gradient(logits, targets):
probs = softmax(logits)
return probs - targets # 简洁形式!
# 3. 验证数值梯度
def numerical_gradient(f, x, eps=1e-5):
grad = np.zeros_like(x)
for i in range(x.size):
x_flat = x.flatten()
x_flat[i] += eps
f_plus = f(x_flat.reshape(x.shape))
x_flat[i] -= 2 * eps
f_minus = f(x_flat.reshape(x.shape))
grad.flat[i] = (f_plus - f_minus) / (2 * eps)
return grad深度学习中的关键梯度
python
import torch
import torch.nn.functional as F
# 1. Linear层梯度
# y = x @ W^T + b
# ∂L/∂W = ∂L/∂y^T @ x
# ∂L/∂x = ∂L/∂y @ W
# 2. BatchNorm梯度(简化)
# y = γ * (x - μ) / √(σ² + ε) + β
# ∂L/∂γ = Σ ∂L/∂y * (x - μ) / √(σ² + ε)
# ∂L/∂β = Σ ∂L/∂y
# 3. Attention梯度
# scores = Q @ K^T / √d_k
# attn = softmax(scores)
# ∂L/∂Q = (∂L/∂attn @ V^T) 的复杂形式
# 验证PyTorch自动微分
x = torch.randn(3, 4, requires_grad=True)
W = torch.randn(4, 2, requires_grad=True)
y = x @ W
loss = y.sum()
loss.backward()
print(f"∂L/∂x shape: {x.grad.shape}") # (3, 4)
print(f"∂L/∂W shape: {W.grad.shape}") # (4, 2)
# 解析解:∂L/∂x = ∂L/∂y @ W^T
# torch.allclose(x.grad, torch.ones(3, 2) @ W.T) # Truesoftmax梯度完整推导
python
"""
关键推导:∂softmax(z_i) / ∂z_j
设 p_i = softmax(z)_i = exp(z_i) / Σ_k exp(z_k)
情况1: i = j
∂p_i/∂z_i = p_i * (1 - p_i)
情况2: i ≠ j
∂p_i/∂z_j = -p_i * p_j
统一形式:
∂p/∂z = diag(p) - p p^T
交叉熵梯度:
L = -Σ y_k log(p_k)
∂L/∂z = p - y (极简形式!)
"""链式法则在矩阵上的应用
python
# 反向传播的核心是链式法则
# 设 L = f(g(h(x, W)))
# 则 ∂L/∂W = ∂L/∂f · ∂f/∂g · ∂g/∂h · ∂h/∂W
# 神经网络中的链式法则
class Layer:
def forward(self, x):
self.x = x # 保存输入
return self._forward(x)
def backward(self, grad_output):
# grad_output = ∂L/∂(本层输出)
# 需要计算:
# 1. ∂L/∂(本层参数) = grad_output * ∂output/∂param
# 2. ∂L/∂(本层输入) = grad_output * ∂output/∂input
return self._backward(grad_output)
# 示例: Linear层反向传播
class Linear:
def __init__(self, in_features, out_features):
self.W = np.random.randn(in_features, out_features) * 0.01
self.b = np.zeros(out_features)
def forward(self, x):
self.x = x
return x @ self.W + self.b
def backward(self, grad_output):
# grad_output: ∂L/∂y, shape (batch, out_features)
self.grad_W = self.x.T @ grad_output # (in, out)
self.grad_b = grad_output.sum(axis=0) # (out,)
grad_input = grad_output @ self.W.T # (batch, in)
return grad_input面试高频问题
Q1: 推导softmax + cross_entropy对logits的梯度
答:
设 z = logits, p = softmax(z), L = -Σ y_k log(p_k)
∂L/∂z_i = -Σ_k y_k * ∂log(p_k)/∂z_i
= -Σ_k y_k * (1/p_k) * ∂p_k/∂z_i
当 k = i: ∂p_i/∂z_i = p_i(1-p_i)
当 k ≠ i: ∂p_k/∂z_i = -p_k p_i
代入:
∂L/∂z_i = -y_i(1-p_i) + Σ_{k≠i} y_k p_i
= -y_i + p_i(y_i + Σ_{k≠i} y_k)
= -y_i + p_i * 1
= p_i - y_i
结论:∂L/∂z = p - y(梯度=预测-标签)Q2: 矩阵微分为什么重要?
答:
- 反向传播基础:所有参数的梯度计算都是矩阵微分
- 优化器设计:理解梯度方向才能设计更好的优化器
- 架构设计:理解梯度流动才能避免梯度消失/爆炸
- 数值稳定性:正确的微分公式保证数值稳定
Q3: PyTorch的自动微分原理?
答: PyTorch使用计算图+链式法则自动计算梯度:
- Forward时构建动态计算图
- Backward时通过拓扑排序从后向前计算梯度
- 每个节点只需知道其局部梯度(∂output/∂input)
- 使用链式法则将局部梯度与上游梯度相乘
常见矩阵微分公式表
| 表达式 | 对x求导 | 条件 |
|---|---|---|
| b^T x | b | - |
| x^T x | 2x | - |
| x^T A x | (A + A^T)x | A是矩阵 |
| ‖Ax - b‖² | 2A^T(Ax - b) | - |
| -log p(y|x) | p - y | softmax+CE |
| log|X| | (X^{-1})^T | X可逆 |
练习题
python
# 1. 手推线性回归梯度
# L = (1/2n) * ‖XW - y‖²
# ∂L/∂W = (1/n) * X^T(XW - y)
# 2. 手推逻辑回归梯度
# L = -Σ[y log(σ(z)) + (1-y)log(1-σ(z))]
# ∂L/∂W = X^T(σ(z) - y)
# 3. 验证自动微分与手算一致
import torch
x = torch.randn(3, 5, requires_grad=True)
W = torch.randn(5, 2, requires_grad=True)
y = torch.randn(3, 2)
loss = ((x @ W - y) ** 2).sum()
loss.backward()
analytical = 2 * x.T @ (x @ W - y)
print(torch.allclose(W.grad, analytical, atol=1e-5)) # True