知识点卡片:导数与梯度
基本信息
| 属性 | 内容 |
|---|---|
| 知识点 | 导数、偏导数与梯度 |
| 掌握程度 | ★★★★★ |
| 学习优先级 | P0 |
| 预估时间 | 6小时 |
| 面试频率 | ★★★★★ |
核心原理
导数定义
f'(x) = lim_{h→0} [f(x+h) - f(x)] / h
几何意义:函数在x处的切线斜率
物理意义:瞬时变化率偏导数
对于多元函数 f(x₁, x₂, ..., xₙ),偏导数 ∂f/∂xᵢ 表示仅xᵢ变化时的变化率:
∂f/∂x₁ = lim_{h→0} [f(x₁+h, x₂, ..., xₙ) - f(x₁, x₂, ..., xₙ)] / h梯度
梯度是所有偏导数组成的向量:
∇f = [∂f/∂x₁, ∂f/∂x₂, ..., ∂f/∂xₙ]^T关键性质:梯度指向函数增长最快的方向,负梯度指向函数下降最快的方向。
方向导数
方向导数 D_u f = ∇f · u = ‖∇f‖ cos(θ)
- 当u与∇f同向(θ=0):D_u f = ‖∇f‖,增长最快
- 当u与∇f反向(θ=π):D_u f = -‖∇f‖,下降最快
- 当u⊥∇f(θ=π/2):D_u f = 0,不变代码实现
数值梯度
python
import numpy as np
def numerical_gradient(f, x, eps=1e-5):
"""数值计算梯度(中心差分)"""
grad = np.zeros_like(x)
x_flat = x.flatten()
for i in range(x_flat.size):
# f(x + eps*e_i)
x_flat[i] += eps
f_plus = f(x_flat.reshape(x.shape))
# f(x - eps*e_i)
x_flat[i] -= 2 * eps
f_minus = f(x_flat.reshape(x.shape))
# 中心差分:精度O(eps²)
grad.flat[i] = (f_plus - f_minus) / (2 * eps)
# 恢复
x_flat[i] += eps
return grad
# 测试
def f(x):
return (x ** 2).sum()
x = np.array([1.0, 2.0, 3.0])
grad_numerical = numerical_gradient(f, x)
grad_analytical = 2 * x # ∂(Σx²)/∂x = 2x
print(f"数值梯度: {grad_numerical}")
print(f"解析梯度: {grad_analytical}")
print(f"误差: {np.abs(grad_numerical - grad_analytical).max():.10f}")深度学习中的梯度
python
import torch
import torch.nn as nn
import torch.nn.functional as F
# 计算图与自动梯度
x = torch.tensor([3.0], requires_grad=True)
# y = x² + 2x + 1
y = x ** 2 + 2 * x + 1
# ∂y/∂x = 2x + 2 = 8
y.backward()
print(f"dy/dx: {x.grad}") # tensor([8.])
# 神经网络中的梯度
model = nn.Linear(10, 5)
x = torch.randn(3, 10)
target = torch.randn(3, 5)
# Forward
output = model(x)
loss = F.mse_loss(output, target)
# Backward
loss.backward()
# 获取梯度
print(f"W.grad shape: {model.weight.grad.shape}") # (5, 10)
print(f"b.grad shape: {model.bias.grad.shape}") # (5,)链式法则
python
# 一维链式法则
# z = f(g(x))
# dz/dx = f'(g(x)) * g'(x)
# 多维链式法则(反向传播的核心)
# 设 L = loss(output), output = layer2(layer1(x))
# ∂L/∂x = ∂L/∂output * ∂output/∂layer1 * ∂layer1/∂x
# 神经网络中的链式法则示意
def forward_pass(x, W1, b1, W2, b2):
"""两层神经网络前向传播"""
# Layer 1
z1 = x @ W1 + b1 # (batch, hidden)
a1 = sigmoid(z1) # (batch, hidden)
# Layer 2
z2 = a1 @ W2 + b2 # (batch, output)
a2 = softmax(z2) # (batch, output)
# Loss
loss = cross_entropy(a2, y_true)
# 保存中间结果用于反向传播
cache = (x, z1, a1, z2, a2)
return loss, cache
def backward_pass(cache, y_true, W2):
"""反向传播(链式法则)"""
x, z1, a1, z2, a2 = cache
# ∂L/∂z2 = a2 - y_true(softmax + CE的简洁梯度)
d_z2 = a2 - y_true
# ∂L/∂W2 = a1^T @ d_z2
d_W2 = a1.T @ d_z2
d_b2 = d_z2.sum(axis=0)
# ∂L/∂a1 = d_z2 @ W2^T
d_a1 = d_z2 @ W2.T
# ∂L/∂z1 = d_a1 * σ'(z1)
d_z1 = d_a1 * sigmoid_derivative(z1)
# ∂L/∂W1 = x^T @ d_z1
d_W1 = x.T @ d_z1
d_b1 = d_z1.sum(axis=0)
return d_W1, d_b1, d_W2, d_b2面试高频问题
Q1: 梯度和方向导数的关系?
答:
- 梯度 ∇f 是一个向量,指向函数增长最快的方向
- 方向导数 D_u f = ∇f · u = ‖∇f‖ cos(θ),沿任意方向u的导数
- 最大方向导数 = ‖∇f‖,在u与∇f同向时取得
- 梯度下降中,我们沿负梯度方向移动,因为这是下降最快的方向
Q2: 链式法则在BP中的作用?
答: 反向传播利用链式法则将损失从输出层向输入层传播:
∂L/∂W₁ = ∂L/∂y · ∂y/∂h · ∂h/∂z · ∂z/∂W₁每层只需知道:
- 上游传来的梯度(来自后一层)
- 本层激活函数的局部导数(forward时保存)
- 本层的输入(forward时保存)
Q3: 为什么梯度下降沿负梯度方向?
答: 泰勒一阶展开:f(x - α∇f) ≈ f(x) - α‖∇f‖²
当α>0充分小时,f(x - α∇f) < f(x),即函数值下降。负梯度是使函数值下降最快的方向(在欧氏距离度量下)。
梯度消失与梯度爆炸
python
# 梯度消失:sigmoid导数最大0.25,多层连乘后趋近0
# 例如10层sigmoid网络:最大梯度 = 0.25^10 ≈ 9.5e-7
# 梯度爆炸:权重初始化过大导致梯度指数增长
# 例如每层放大2倍,10层后梯度放大2^10 = 1024倍
# 可视化
import matplotlib.pyplot as plt
x = np.linspace(-5, 5, 100)
sigmoid = 1 / (1 + np.exp(-x))
sigmoid_grad = sigmoid * (1 - sigmoid)
relu = np.maximum(0, x)
relu_grad = (x > 0).astype(float)
print(f"Sigmoid最大梯度: {sigmoid_grad.max():.4f}") # 0.2500
print(f"ReLU最大梯度: {relu_grad.max():.4f}") # 1.0000练习题
python
# 1. 手推 y = (w*x + b - y_true)² 对w和b的梯度
# 验证:∂L/∂w = 2x(wx+b-y_true), ∂L/∂b = 2(wx+b-y_true)
# 2. 验证数值梯度与解析梯度
def check_gradient(f, grad_f, x, eps=1e-5):
num_grad = numerical_gradient(f, x, eps)
ana_grad = grad_f(x)
diff = np.linalg.norm(num_grad - ana_grad) / max(np.linalg.norm(num_grad), 1e-10)
print(f"相对误差: {diff:.10f}")
return diff < 1e-4
# 3. 实现简单的梯度下降
def gradient_descent(f, grad_f, x0, lr=0.01, n_iters=100):
x = x0.copy()
losses = [f(x)]
for _ in range(n_iters):
x = x - lr * grad_f(x)
losses.append(f(x))
return x, losses