Skip to content

知识点卡片:导数与梯度

基本信息

属性内容
知识点导数、偏导数与梯度
掌握程度★★★★★
学习优先级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₁

每层只需知道:

  1. 上游传来的梯度(来自后一层)
  2. 本层激活函数的局部导数(forward时保存)
  3. 本层的输入(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

相关知识点