课程0基础Agent开发课 / 数学基础 / 微积分与梯度下降-AI训练的数学引擎
— 14 min read

微积分与梯度下降-AI训练的数学引擎

> **本文适合谁**:完全不懂微积分的读者。目标不是让你会计算导数,而是让你理解"模型是怎么学习的"这件事背后的数学直觉。读完后你应该能明白:为什么学习率太大会让训练不稳定,为什么需要反向传播,以及`loss.backward()`这行代码在做什么。

微积分与梯度下降:AI训练的数学引擎

本文适合谁:完全不懂微积分的读者。目标不是让你会计算导数,而是让你理解"模型是怎么学习的"这件事背后的数学直觉。读完后你应该能明白:为什么学习率太大会让训练不稳定,为什么需要反向传播,以及loss.backward()这行代码在做什么。

本文阅读时间:约16分钟


一、为什么AI训练需要微积分

先想一个具体问题:你训练了一个模型来预测房价,它的预测结果和真实房价有偏差。现在你想改进这个模型——你会怎么做?

一个朴素的想法是:随机调整模型的参数,看看误差有没有减小,如果减小了就保留,否则放弃。这个方法理论上可行,但有一个致命缺陷:一个大模型有几十亿个参数,随机尝试的空间是天文数字,根本不可能穷举

这就是优化问题的本质困境:在极其巨大的参数空间里,如何高效找到让误差最小的那组参数?

微积分给出了答案:不需要随机猜,可以计算出"每个参数应该往哪个方向调整、调整多少"

具体来说,微积分在AI训练中解决了三个关键问题:

第一,量化误差。用损失函数把"模型有多差"变成一个具体的数字。

第二,指明方向。用梯度告诉我们:当前参数下,哪个方向能让误差下降最快。把"在巨大参数空间里盲目搜索"变成"沿着明确方向有目的地移动"。

第三,分配责任。神经网络有很多层,每层都有参数。反向传播(基于微积分的链式法则)能计算每个参数对最终误差的"贡献度",从而准确地调整每个参数。

这就是为什么说微积分是AI训练的数学引擎——没有它,大模型的训练根本无从实现。


二、导数:变化率的直觉

先从生活说起

你开车,踩下油门1厘米,车速增加5公里/小时;踩下2厘米,增加10公里/小时。

这个"输入变化多少,输出变化多少"的关系,就是导数的直觉含义。

导数描述的是:当输入发生一个极小的变化时,输出会变化多少。 在几何上,这就是函数曲线在某一点处的斜率——曲线在那一点有多"陡"。

导数的价值在于它告诉我们"变化的方向和速度"。在AI训练里,我们关心的是:当我把某个参数调大一点点,损失(误差)会增大还是减小?增大/减小多少?导数直接回答了这个问题。

python
import numpy as np

# f(x) = x^2 的导数是 f'(x) = 2x
# 在x=3处,导数=6,意味着x每增加1,f(x)大约增加6
def f(x):
    return x**2

def df(x):
    return 2*x

print(df(3))  # 6
# 验证:f(3.001) - f(3) = 9.006001 - 9 = 0.006001 ≈ 6 × 0.001
print(f(3.001) - f(3))  # 约0.006001,和6 × 0.001几乎一样

为什么AI需要导数?

训练模型时,我们需要知道:调整哪个参数能让模型变好?调整多少?往哪个方向调?导数告诉我们这些问题的答案。


三、梯度:多维世界里的方向感

从一维到多维

一维的导数好理解——函数只有一个输入,导数就是那一个方向上的斜率。

但神经网络有数十亿个参数,不是一维的x,而是一个高维向量。这时候,每个参数都有自己的"斜率",我们需要知道的是:在所有参数构成的高维空间里,沿哪个方向移动,损失下降最快?

这就引出了偏导数和梯度的概念。

偏导数:在多个变量同时存在时,固定其他变量不动,只看某一个变量的变化率。比如一个函数同时受温度和湿度影响,偏导数分别告诉你"仅改变温度时,结果怎么变"和"仅改变湿度时,结果怎么变"。

python
# 二维函数 f(x, y) = x^2 + 2*y^2
# 对x的偏导数:∂f/∂x = 2x(把y当常数)
# 对y的偏导数:∂f/∂y = 4y(把x当常数)

def f_2d(x, y):
    return x**2 + 2 * y**2

def partial_x(x, y):
    return 2 * x  # ∂f/∂x

def partial_y(x, y):
    return 4 * y  # ∂f/∂y

# 在点(3, 1)处
x, y = 3.0, 1.0
print(f"∂f/∂x = {partial_x(x, y)}")  # 6.0:x增加,f增加
print(f"∂f/∂y = {partial_y(x, y)}")  # 4.0:y增加,f增加

运行后你会看到在(3,1)这个点,x方向的偏导数是6,y方向的偏导数是4。意思是:在这个点,x增加1个单位,f(x,y)大约增加6;y增加1个单位,f大约增加4。

梯度是所有偏导数组成的向量:

code
∇f(x, y) = [∂f/∂x, ∂f/∂y] = [2x, 4y]

在点(3, 1)处,梯度 = [6, 4],这是一个指向"f增长最快方向"的向量。

把梯度想象成站在山上,梯度就像指南针,指向"上坡最陡的方向"。如果你想爬到山顶,就沿梯度方向走;如果你想下山(让损失减小),就沿梯度反方向走。


四、梯度下降:找最低点的方法

蒙眼在山上找最低点

想象这样一个场景:你被蒙着眼睛,放在一座连绵起伏的山上,任务是找到山谷的最低点。你既看不见远处,也不知道地图——你唯一能做的是感受脚下地面的坡度

这时候最自然的策略是:每次感受当前位置脚下最陡的下坡方向,沿着那个方向走一步,然后再次感受,再走一步……反复迭代,最终抵达山谷底部。

这正是梯度下降的完整逻辑。用数学语言表达:

  1. 计算当前参数下损失函数的梯度(相当于"感受坡度")
  2. 沿梯度的反方向移动一小步(相当于"往最陡的下坡方向走")
  3. 重复步骤1和2,直到损失不再明显下降

关键:沿梯度反方向走,是所有方向中损失下降最快的。这不是经验,而是数学保证。

梯度下降等高线图
梯度下降在损失函数"山谷"中寻找最低点

python
# 简单的梯度下降示例:找 f(x) = x^2 的最小值
def gradient_descent():
    x = 10.0  # 初始位置(离最小值点很远)
    learning_rate = 0.1  # 步长(每次走多大一步)

    for i in range(50):
        gradient = 2 * x  # f(x) = x^2 的梯度
        x = x - learning_rate * gradient  # 往梯度反方向走

        if i % 10 == 0:
            print(f"Step {i}: x={x:.4f}, f(x)={x**2:.4f}")

    return x

result = gradient_descent()
print(f"最终: x={result:.6f}")
# 你应该看到x从10.0逐渐趋近于0(最小值点)

运行这段代码,你会看到x从10.0开始,每步都在减小,最终接近0。这就是梯度下降:沿梯度反方向一步一步走向最小值。


五、学习率:步长为什么重要

步子太大扯着蛋,步子太小走一年

回到山谷类比。你在山上往下走,每一步走多大合适?

步子太大(学习率过高):有可能直接跨过山谷最低点,跳到对面山坡,然后又跳回来,来回震荡,永远到不了最低点,甚至越走越高(发散)。

步子太小(学习率过低):方向正确,但每次移动非常微小,要走几百万步才能到达最低点,训练成本极高。

合适的步长:稳定、快速地向最低点收敛。

这个"步长"在梯度下降中叫做学习率(Learning Rate),是训练中最重要的超参数。

学习率对比
学习率过大/合适/过小时的训练损失曲线对比

python
# 用代码验证不同学习率的效果
def gradient_descent_compare(learning_rate, steps=20):
    x = 10.0
    trajectory = [x]
    for _ in range(steps):
        gradient = 2 * x
        x = x - learning_rate * gradient
        trajectory.append(x)
    return trajectory

# 三种学习率
traj_large = gradient_descent_compare(0.9)   # 太大:震荡
traj_good  = gradient_descent_compare(0.1)   # 合适:收敛
traj_small = gradient_descent_compare(0.01)  # 太小:收敛太慢

print(f"学习率0.9(前5步): {traj_large[:5]}")   # 震荡严重
print(f"学习率0.1(前5步): {traj_good[:5]}")    # 平稳下降
print(f"学习率0.01(前5步): {traj_small[:5]}")  # 下降很慢

运行后你会看到:学习率0.9的x会在正负之间来回跳;学习率0.1的x每步都平稳减小;学习率0.01的x也在减小,但速度很慢。

常用学习率参考(实践中的经验值):

  • 1e-3 (0.001):Adam优化器从头训练常用起点
  • 1e-4 (0.0001):微调预训练模型常用
  • 1e-5 (0.00001):微调大模型常用(第16章会详细说)

六、损失函数:衡量模型有多差

为什么需要损失函数

在优化之前,必须先定义"什么叫好,什么叫差"。

如果模型预测房价是100万,真实价格是120万,误差是20万。但这只是一个样本。训练集有10000个样本,每个样本的误差各不相同,如何把这10000个误差合并成一个统一的"模型好坏"指标?

这就是**损失函数(Loss Function)**的作用:把所有样本上的误差汇总成一个单一的数值,数值越小,说明模型整体预测越准。

python
# 均方误差(MSE):预测值和真实值之差的平方的平均值,回归任务常用
def mse_loss(predictions, targets):
    return np.mean((predictions - targets) ** 2)

# 示例:预测值和真实值
predictions = np.array([100, 200, 300, 400])  # 万元
targets     = np.array([120, 190, 310, 390])  # 万元

print(f"MSE损失: {mse_loss(predictions, targets):.2f}")
# 输出:MSE损失: 187.50
# 越小越好,0表示完美预测

# 交叉熵损失(Cross Entropy):分类任务常用,衡量预测概率分布和真实分布的差距
def cross_entropy_loss(probs, targets):
    return -np.mean(targets * np.log(probs + 1e-8))

训练目标:最小化损失函数


七、反向传播:多层网络的梯度计算

多层网络的挑战

单层网络的梯度计算还比较直接,但神经网络通常有很多层,每层都有自己的参数。

挑战在于:最终损失是所有层共同作用的结果,如何把"最终误差"追溯回去,计算出每一层、每个参数对误差的"贡献度"?

**链式法则(Chain Rule)**提供了高效的解法:

如果 y = f(g(x)),那么 dy/dx = (dy/dg) × (dg/dx)

直觉就是:链条上每一节的导数连乘在一起,就得到整条链的导数。

反向传播从输出层开始,一层一层向输入层"反向传播"梯度,这个过程从输出层到第一层,每步只需要用上一步的结果和本层的局部导数,高效且精确。

python
# 概念示意(实际用PyTorch自动计算)
x = 2.0
w1 = 0.5
w2 = 0.3

h = w1 * x           # 第一层
y = w2 * h           # 第二层
loss = (y - 1) ** 2  # 损失

# 反向传播:手动应用链式法则
dloss_dy = 2 * (y - 1)   # d(loss)/d(y)
dy_dw2   = h              # d(y)/d(w2)
dy_dh    = w2             # d(y)/d(h)
dh_dw1   = x              # d(h)/d(w1)

# 链式法则:对w2的梯度
dloss_dw2 = dloss_dy * dy_dw2
# 链式法则:对w1的梯度(经过两层)
dloss_dw1 = dloss_dy * dy_dh * dh_dw1

print(f"w2的梯度: {dloss_dw2:.4f}")
print(f"w1的梯度: {dloss_dw1:.4f}")

PyTorch的autograd自动完成这个计算——你只需要写前向传播的代码,PyTorch会自动追踪所有运算,在你调用.backward()时自动计算所有参数的梯度。这是深度学习框架最核心的价值之一。

输入 x
正向传播

第1层: y1 = w1*x+b1
求偏导 ∂y1/∂w1, ∂y1/∂b1

第2层: y2 = σ(y1)
求偏导 ∂y2/∂y1

第3层: y3 = w2*y2+b2
求偏导 ∂y3/∂w2, ∂y3/∂b2

输出层: ŷ = w3*y3+b3
求偏导 ∂L/∂ŷ

反向传播
链式法则: ∂L/∂w = ∂L/∂ŷ * ∂ŷ/∂y3 * ∂y3/∂y2 * ... * ∂y1/∂w1

逐层更新参数
w := w - α * ∂L/∂w

反向传播通过链式法则逐层计算梯度,从输出层向输入层回传

核心原理:前向传播算输出 → 反向传播链式求导 → 逐层更新参数。PyTorch autograd 自动完成。


八、学习率调度:让学习率随训练动态变化

固定学习率往往不是最优的。

直觉上:训练前期参数离最优点还很远,用较大学习率快速接近;训练后期接近最优点了,步子还是很大容易在最优点附近来回跳跃,应该用小学习率精细调整。

这就是**学习率调度器(LR Scheduler)**的动机。

python
import torch.optim as optim

# 假设已有模型 model
# optimizer = optim.AdamW(model.parameters(), lr=1e-3)

# 策略1:余弦退火——学习率按余弦曲线从初始值降到接近0
# scheduler_cosine = optim.lr_scheduler.CosineAnnealingLR(
#     optimizer, T_max=100, eta_min=1e-6
# )

# 策略2:线性预热 + 余弦衰减(LLM训练和微调最常用的策略)
# 先从小学习率线性增加到最大值,再余弦衰减
# from transformers import get_cosine_schedule_with_warmup
# scheduler_warmup = get_cosine_schedule_with_warmup(
#     optimizer,
#     num_warmup_steps=100,    # 前100步线性预热
#     num_training_steps=1000  # 总步数
# )

为什么需要预热(Warmup)?

训练初期,模型参数是随机初始化的,梯度方差很大。如果这时候就用大学习率,相当于每次都迈一大步而且方向还不稳定,很容易走向错误的地方。预热阶段用小学习率,让模型先"摸清地形",等梯度估计稳定之后,再加大步伐快速优化。


九、常用优化器

梯度下降有多种变体,实践中用得最多的是Adam和AdamW:

优化器 特点 适用场景
SGD(随机梯度下降) 简单,需要仔细调参 图像模型
Adam(自适应矩估计) 自适应学习率,稳定 大多数场景
AdamW Adam + 权重衰减 Transformer微调

AdamW是目前训练Transformer模型最常用的优化器。它为每个参数自适应地调整学习率——历史上梯度大的参数,步长自动变小;历史上梯度小的参数,步长自动变大。效果是对学习率不那么敏感,收敛更稳定。

python
import torch.optim as optim

# PyTorch中使用优化器
# weight_decay是L2正则化,防止过拟合
optimizer = optim.AdamW(model.parameters(), lr=1e-4, weight_decay=0.01)

小结

概念 直觉 作用
导数 变化率:踩油门,车速怎么变 告诉我们参数怎么调整
梯度 所有偏导数打包成一个方向向量 指向损失增长最快的方向
损失函数 衡量模型当前有多差的单一数字 训练的优化目标
梯度下降 蒙眼在山上找最低点:沿下坡方向走 让参数一步步走向最优解
学习率 每步走多大 太大震荡,太小太慢
反向传播 链式法则:从后往前分配责任 高效计算每个参数的梯度

理解了梯度下降,你就理解了模型训练的本质:训练不是魔法,而是在一个高维空间里,用导数指路,一步一步走向误差最小的那个点

每次调用 loss.backward()optimizer.step(),背后都是这篇文章介绍的数学在运行。

后续应用:本文知识将在以下章节直接用到:

本页目录