字节技术总监30讲 AI课:4|导数、梯度与优化——机器为什么能自己学?
《现代 AI 原理与系统工程》· 第04讲
第03讲解决的是:模型怎么把“预测得好不好”变成一个 Loss。
这一讲继续往下问:
Loss 已经算出来了,模型到底凭什么知道参数应该往哪里改?
这一讲把这条链完整打通:
参数 ↓ Forward ↓ Loss ↓ Derivative ↓ Gradient ↓ Backpropagation ↓ Optimizer ↓ Parameter Update ↓ 下一轮 Forward学懂以后,再看到:
loss.backward()optimizer.step()就不会再把它当成框架“自动帮你训练”的黑盒。
一、先从最简单的问题开始:模型知道自己错了,下一步怎么办?
假设只有一个参数:
w模型:
y_hat = w * x损失:
L = (y_hat - y)^2进一步代入:
L = (wx - y)^2现在我们已经知道:
当前 Loss = 多少但训练还没完成。
真正的问题是:
w 应该增大? 还是减小? 一次改多少?比如:
L = w²当:
w = 3有:
L = 9显然,如果希望 Loss 下降,w 应该往 0 靠。
但如果:
w = -3同样:
L = 9此时却应该让 w 增大。
所以训练真正需要的是:
当前位置的 Loss,朝哪个方向变化?变化有多快?
这就是导数开始发挥作用的地方。
二、导数到底是什么?
1. 先用“斜率”理解
函数:
y = f(x)当 x 从x1变成x2:
平均变化率 = (f(x2) - f(x1)) / (x2 - x1)这其实就是斜率:
纵坐标变化 / 横坐标变化但机器学习关心的是:
就在当前位置附近,x 只变化一点点,Loss 会怎么变化?
因此把变化范围缩小到无限接近 0:
f'(x) = lim Δx→0 [f(x + Δx) - f(x)] / Δx这就是导数。
2.L = w²的导数
L = w²所以:
dL/dw = 2w几个位置:
w = -3 → dL/dw = -6 w = -1 → dL/dw = -2 w = 0 → dL/dw = 0 w = 1 → dL/dw = 2 w = 3 → dL/dw = 6于是立刻能得到:
导数 < 0 → w 增大时 Loss 倾向下降 → w 应该往正方向调整 导数 > 0 → w 增大时 Loss 倾向上升 → w 应该往负方向调整 导数 = 0 → 当前点局部变化率为 0所以导数不仅是一个“数学计算结果”。
在优化里,它实际上提供了:
参数当前应该往哪边走的局部信息。
三、从一个参数到一百万个参数:偏导和梯度
真实神经网络不可能只有一个参数。
假设模型有:
w1 w2 w3 ... wnLoss 是:
L = f(w1, w2, ..., wn)这时我们需要分别研究:
w1 对 Loss 的影响 w2 对 Loss 的影响 ... wn 对 Loss 的影响于是出现:
偏导数(Partial Derivative)
例如:
L(x,y) = x² + 3y²分别求:
∂L/∂x = 2x ∂L/∂y = 6y偏导的核心意思就是:
固定其他变量,只看某一个变量发生变化时,Loss 怎么变。
梯度是什么?
把所有偏导数放在一起:
Gradient = [ ∂L/∂w1 ∂L/∂w2 ... ∂L/∂wn ]写成数学形式:
∇L = [ ∂L/∂w1, ∂L/∂w2, ..., ∂L/∂wn ]从程序员视角,可以直接把它想成:
floatw[n];floatgrad[n];其中:
w[i] → 第 i 个参数当前是多少 grad[i] → 当前这个参数对 Loss 的局部变化率所以:
梯度就是整个参数数组对应的一组“局部调整方向信息”。
四、为什么一定沿“负梯度”走?
梯度:
∇L指向的是:
Loss 增长最快的方向。
而我们训练模型的目标是:
让 Loss 下降所以自然应该走反方向:
-∇L也就是:
负梯度方向是局部最快下降方向。
因此最基本的梯度下降更新公式就是:
w = w - η * ∂L/∂w多参数形式:
w = w - η * ∇L这里:
η = learning rate也就是:
学习率。
五、学习率到底是什么?
学习率最容易被说成:
“学习得快不快。”
这个说法不够准确。
更准确的是:
学习率控制一次参数更新走多远。
例如:
当前 w = 4 gradient = 8如果:
η = 0.1那么:
w_new = 4 - 0.1 × 8 = 3.2如果:
η = 0.01则:
w_new = 3.92所以:
学习率小 → 一步走得近 → 通常更慢 学习率大 → 一步走得远 → 可能更快,也可能震荡甚至发散六、手算一次完整的梯度下降
还是:
L(w) = w²导数:
dL/dw = 2w初始:
w = 4 η = 0.1第一步:
gradient = 2 × 4 = 8 w_new = 4 - 0.1 × 8 = 3.2Loss:
L_old = 4² = 16 L_new = 3.2² = 10.24继续:
w = 3.2 gradient = 6.4 w_new = 3.2 - 0.1 × 6.4 = 2.56于是:
4 ↓ 3.2 ↓ 2.56 ↓ 2.048 ↓ 1.6384 ↓ ... ↓ 0Loss 也不断下降。
这就是最基本的“机器学习”。
没有魔法。
本质就是:
计算误差 → 计算梯度 → 调整参数 → 再算一次 → 重复七、为什么链式法则是深度学习的核心?
真实神经网络不是:
w → Loss而是:
w ↓ z ↓ a ↓ z2 ↓ Loss例如:
z = wx L = (z - y)²我们真正想求:
dL/dw但是 Loss 并不是直接由 w 算出来的,中间经过了:
w → z → L这时就需要:
链式法则。
dL/dw = dL/dz × dz/dw这件事非常重要。
它意味着:
复杂函数可以拆成很多个简单环节,每一环只需要知道自己的局部导数,就能把梯度一层一层传回去。
这就是深度学习反向传播的数学基础。
八、手算一个完整的链式法则例子
设:
x = 3 y = 6 w = 1模型:
z = wx所以:
z = 3Loss:
L = (z - y)² = (3 - 6)² = 9现在求:
dL/dw拆成两部分:
dL/dw = dL/dz × dz/dw第一部分:
dL/dz = 2(z-y) = 2(3-6) = -6第二部分:
dz/dw = x = 3所以:
dL/dw = -6 × 3 = -18梯度是:
-18假设:
η = 0.01更新:
w_new = 1 - 0.01 × (-18) = 1.18因为梯度是负数,所以 w 反而增大。
这正好验证了前面的规则:
gradient < 0 → 往正方向调整九、计算图:反向传播到底在“反”什么?
把:
w → z → L画成计算图:
w ───┐ ↓ z = wx ↓ L前向传播:
w ↓ z ↓ Loss算的是:
每个节点的数值反向传播:
Loss ↓ z ↓ w算的是:
每个参数对 Loss 的梯度因此:
Forward → 算值 Backward → 算梯度反向传播不是“把前向传播再反着跑一次”,而是:
沿计算图反向应用链式法则。
十、为什么自动微分可以自动得到梯度?
现代深度学习框架中的自动微分系统,会记录前向计算涉及的操作。
比如:
乘法 加法 ReLU 矩阵乘法 指数 对数 ...这些基础运算都有已知导数。
于是:
Forward → 记录计算关系和必要中间结果 Backward → 按链式法则把梯度从后往前传最后得到:
dLoss/dW1 dLoss/dW2 dLoss/db1 dLoss/db2 ...所以 PyTorch 的:
loss.backward()背后不是神秘 AI 技术,而是:
计算图 + 自动微分 + 链式法则十一、数值微分可以帮我们检查梯度
如果:
f(x) = x²理论导数:
f'(x) = 2x还可以用有限差分近似:
f'(x) ≈ [f(x+h)-f(x)] / hPython:
deff(x):returnx*x x=3.0h=1e-5numerical_grad=(f(x+h)-f(x))/hprint(numerical_grad)结果应该非常接近:
6它的价值不是拿来代替反向传播。
而是:
在自己实现复杂算子时,用有限差分检查自动求导或手写梯度是否正确。
这叫:
Gradient Check。
十二、为什么不能对每个参数都用数值微分?
因为如果模型有:
10 亿个参数最简单的有限差分思路就可能要求:
对大量参数分别做额外函数计算计算成本会非常高。
而反向传播的核心优势之一就是:
一次 backward 可以高效地得到大量参数的梯度。
因此现代深度学习训练依赖的是:
Automatic Differentiation + Backpropagation而不是逐参数做数值微分。
十三、Batch、SGD、Mini-batch 到底区别在哪?
前面的梯度是:
∇L但现实训练还有一个问题:
每次究竟拿多少数据来算梯度?
假设数据集有:
10000 条样本1. Batch Gradient Descent
每次使用:
10000 条再更新一次参数。
特点:
梯度相对稳定 单次计算量大 更新频率低2. SGD
每次只使用:
1 条样本就更新一次。
特点:
单次计算小 更新非常频繁 梯度噪声较大3. Mini-batch
现代深度学习最常见:
batch_size = 32 64 128 256 ...一次拿一小批数据。
它在:
计算效率 显存占用 梯度噪声 GPU 并行度之间取得了工程上的平衡。
十四、为什么 Mini-batch 对 GPU 特别友好?
GPU 擅长:
大量相似计算并行执行。
一条样本往往无法充分利用 GPU 的并行计算资源。
而:
Batch + 矩阵运算 + 并行计算可以把大量相同类型的运算组织起来。
例如:
X @ W当:
X包含几十、几百甚至更多样本时,矩阵乘法可以更充分地使用 GPU。
这也是第02讲中:
Shape 矩阵 GEMM最终会连接到这里的原因。
十五、Epoch 和 Step 不要再混
假设:
训练集 = 1000 条 batch_size = 100那么:
1 epoch = 完整遍历一次训练集一次 epoch 需要:
1000 / 100 = 10 steps如果训练:
20 epochs就是:
20 × 10 = 200 steps所以:
Batch → 一次拿多少样本 Step → 参数更新一次 Epoch → 完整遍历一次数据集十六、为什么会出现梯度消失?
这是深度网络非常重要的问题。
假设反向传播经过:
L ↓ z3 ↓ z2 ↓ z1 ↓ w根据链式法则:
dL/dw = dL/dz3 × dz3/dz2 × dz2/dz1 × dz1/dw如果中间大量导数都小于 1:
0.5 × 0.5 × 0.5 × 0.5 × ...连续乘下去会越来越小。
最后可能变成:
gradient ≈ 0这就是梯度消失的基本数学来源。
十七、为什么会梯度爆炸?
反过来,如果某些局部导数长期大于 1:
2 × 2 × 2 × 2 × ...梯度就可能越来越大:
2 4 8 16 32 ...最终导致:
参数更新过大 Loss 剧烈震荡 数值溢出 训练不稳定因此:
梯度消失 → 梯度链连续乘小数 梯度爆炸 → 梯度链连续乘大数这就是为什么深层网络的:
激活函数 初始化 Normalization Residual Optimizer Learning Rate都可能影响训练稳定性。
十八、Sigmoid 为什么容易让梯度变小?
Sigmoid:
σ(x) = 1 / (1 + exp(-x))导数:
σ'(x) = σ(x)(1-σ(x))其最大值为:
0.25也就是说,在很多情况下,经过 sigmoid 的梯度会被乘上一个小于等于 0.25 的因子。
如果这样的运算在很深的网络中不断出现:
0.25 × 0.25 × 0.25 × ...梯度就容易迅速衰减。
这也是深层神经网络后来大量采用:
ReLU GELU等激活函数的重要背景之一。
但不能把“换激活函数”理解成解决梯度问题的万能方案,实际训练还与:
初始化 归一化 网络结构 优化器 学习率共同相关。
十九、Momentum 到底在解决什么?
普通 SGD:
w(t+1) = w(t) - η g(t)它主要看:
当前梯度如果不同 batch 带来的梯度方向变化很大,参数轨迹可能出现明显抖动。
Momentum 的思路是:
不仅看当前梯度,也保留过去梯度形成的运动趋势。
一种常见写法:
v_t = β v_(t-1) + g_t然后:
w_t = w_(t-1) - η v_t直觉上:
SGD → 只看当前一步 Momentum → 当前一步 + 历史趋势因此它可以在某些优化地形上:
减少震荡 保持连续方向 提高某些方向上的更新效率二十、Adam 又在做什么?
Adam:
Adaptive Moment Estimation
它不仅考虑当前梯度,还维护梯度的一阶、二阶统计信息。
常见形式:
m_t = β1 m_(t-1) + (1-β1) g_tv_t = β2 v_(t-1) + (1-β2) g_t²再做偏差修正:
m_hat_t = m_t / (1-β1^t) v_hat_t = v_t / (1-β2^t)最终:
w_t = w_(t-1) - η * m_hat_t / (sqrt(v_hat_t) + ε)不用先背公式。
先理解两个东西:
m → 对历史梯度方向进行平滑估计 v → 对梯度平方规模进行估计所以 Adam 的核心思想可以理解成:
既考虑过去大致往哪个方向走,又根据不同参数近期梯度的规模调整更新幅度。
二十一、AdamW 为什么还要单独出来?
深度学习里经常看到:
weight decay对于普通 SGD,L2 正则与 weight decay 的关系比较直接。
但在 Adam 这类自适应优化器中,把 L2 项简单并入梯度,与独立执行参数衰减并不完全等价。
AdamW 的重要思想就是:
把 weight decay 与梯度更新解耦。
因此可以把它粗略理解为:
梯度更新 + 参数权重衰减两条机制分别处理。
重点不是死记:
AdamW = Adam + 一个小改动而是理解:
优化器改变的不只是公式形式,而是整个参数更新动力学。
二十二、Gradient Clipping 是干什么的?
当梯度出现异常大值时:
gradient norm可能非常大。
例如:
norm = 100但训练希望单次更新不要失控。
于是可以设置:
max_norm = 1如果梯度范数超过阈值,就整体缩放。
核心目的:
防止某一步出现极端大梯度 → 导致参数发生过大的更新注意:
Gradient Clipping主要处理:
梯度过大它不能直接解决:
梯度消失 学习率错误 数据异常 模型结构问题 Loss 设计问题所以它是数值 / 优化稳定性手段,不是万能修复器。
二十三、PyTorch 训练代码到底在干什么?
看到:
forx,yindataloader:optimizer.zero_grad()pred=model(x)loss=criterion(pred,y)loss.backward()optimizer.step()现在应该能逐行翻译。
optimizer.zero_grad()
清空上一轮累积的梯度。
model(x)
进行 Forward:
x ↓ Model ↓ Predictioncriterion(pred, y)
根据预测结果和真实标签得到 Loss。
前一讲我们已经知道:
Logits → Cross-Entropy → Lossloss.backward()
执行:
Backward根据计算图和链式法则计算:
dLoss / dParameteroptimizer.step()
真正使用这些梯度更新参数。
所以一定记住:
loss.backward() ≠ 更新参数而是:
backward → 计算梯度 step → 使用梯度更新参数二十四、训练的完整闭环是什么?
现在把第03讲和第04讲真正接起来:
输入数据 ↓ 模型 Forward ↓ Logits ↓ Loss ↓ Backward ↓ Gradient ↓ Optimizer ↓ 更新参数 ↓ 下一轮 Forward如果再把第03讲展开:
输入上下文 ↓ 模型 ↓ Logits ↓ Softmax / LogSoftmax ↓ Probability ↓ 真实 Token 概率 ↓ -log(p_true) ↓ Cross-Entropy ↓ Loss ↓ Gradient ↓ Parameter Update这就是现代神经网络训练最核心的一条计算链。
二十五、为什么只看 Loss 不够?
假设训练过程中:
Loss 突然变成 NaN不能只说:
“模型训练坏了。”
应该继续检查:
Learning Rate Gradient Norm Parameter Statistics 输入数据 Loss 数值稳定性例如:
Loss 突然 NaN可能涉及:
梯度爆炸 浮点数溢出 exp / log 数值问题 异常输入 学习率过大如果:
Loss 长时间几乎不动可能涉及:
学习率太小 梯度消失 初始化问题 数据 / 标签问题 模型结构问题所以:
训练曲线是现象,梯度、参数和数值状态才是进一步定位问题的依据。
二十六、一个最小可运行的梯度下降程序
下面不用 PyTorch,直接手写一个最小训练系统。
defloss(w):returnw*wdefgrad(w):return2*w w=4.0lr=0.1forstepinrange(10):current_loss=loss(w)current_grad=grad(w)print(f"step={step:2d}, "f"w={w:.6f}, "f"loss={current_loss:.6f}, "f"grad={current_grad:.6f}")w=w-lr*current_grad你应该观察到:
w → 不断向 0 靠近 Loss → 不断下降 Gradient → 绝对值逐渐减小这就是:
Forward → Loss → Gradient → Update最小化版本。
二十七、再用 C 程序员视角看一次
如果把参数想成:
floatw[n];梯度:
floatgrad[n];那么最基本的更新就是:
for(inti=0;i<n;i++){w[i]-=lr*grad[i];}这非常重要。
因为从系统角度看:
神经网络训练本质上就是一个超高维参数数组的数值迭代优化过程。
只不过真实模型的:
参数数量 计算量 矩阵规模 梯度数量巨大到必须使用:
Tensor GPU CUDA Kernel 分布式训练来完成。
这也是为什么本课程后面会从:
数学一路进入:
GPU → CUDA → Tensor Core → HBM → 分布式训练 → LLM 推理二十八、本讲最容易混淆的 10 个概念
1. Loss 和 Gradient
Loss → 当前错多少 Gradient → 参数往哪里改、局部变化有多强2. Gradient 和 Update
Gradient → 提供更新信息 Update → 真正修改参数3. Backward 和 Update
backward() → 算梯度 step() → 更新参数4. Learning Rate 和 Gradient
Gradient → 方向和局部变化率 Learning Rate → 一步走多远5. Batch 和 Epoch
Batch → 一次拿多少数据 Epoch → 完整看一遍数据集6. SGD 和 Mini-batch
严格来说,现代代码中的“SGD”这个词经常泛指随机 / 小批量梯度优化;工程讨论时更重要的是明确:
batch size 到底是多少不要只看优化器名字。
7. Loss 大 ≠ Gradient 一定大
Loss 是:
函数当前值Gradient 是:
函数当前局部变化率它们不是同一个量。
8. Gradient 小 ≠ Loss 小
模型可能:
Loss 很大但是处在梯度很小的区域。
因此不能只根据 Loss 大小判断优化状态。
9. Adam ≠ 一定优于 SGD
不同模型、数据、训练阶段和超参数设置下,优化器表现可能不同。
应该研究:
任务 模型 数据 学习率 训练稳定性 泛化表现而不是简单背:
Adam > SGD10. 反向传播 ≠ 反向预测
反向传播解决的是:
怎么得到参数梯度不是:
怎么反过来生成输入二十九、这一讲真正建立的知识树
第04讲:导数、梯度与优化 │ ├── 导数 │ ├── 平均变化率 │ ├── 瞬时变化率 │ └── 斜率 │ ├── 多变量 │ ├── 偏导 │ └── 梯度 │ ├── 优化 │ ├── 梯度下降 │ ├── Learning Rate │ └── Mini-batch │ ├── 反向传播 │ ├── Chain Rule │ ├── Computation Graph │ └── Automatic Differentiation │ ├── 训练稳定性 │ ├── Gradient Vanishing │ ├── Gradient Explosion │ ├── Gradient Clipping │ └── Numerical Stability │ └── Optimizer ├── SGD ├── Momentum ├── Adam └── AdamW三十、本讲最重要的几个公式
不用一开始要求自己背几十个公式,先把这几个真正理解。
1. 导数 f'(x) = lim Δx→0 [f(x+Δx)-f(x)] / Δx2. 梯度 ∇L = [ ∂L/∂w1, ∂L/∂w2, ..., ∂L/∂wn ]3. 梯度下降 w ← w - η∇L4. 链式法则 dL/dw = dL/dz × dz/dw5. Momentum v_t = βv_(t-1) + g_t6. Adam m_t = β1m_(t-1) + (1-β1)g_t v_t = β2v_(t-1) + (1-β2)g_t²三十一、学完这一讲,你应该能自己解释这句话
loss.backward()optimizer.step()不能只回答:
“反向传播,然后优化器更新。”
真正应该能展开成:
Forward ↓ 得到 Loss ↓ 计算图记录了计算关系 ↓ Backward 使用链式法则 ↓ 得到每个参数的 Gradient ↓ Optimizer 根据 Gradient、Learning Rate 等信息 ↓ 更新 Parameter ↓ 下一轮训练再往下理解:
为什么梯度能算出来? → 因为链式法则 为什么能自动算? → 因为自动微分系统 为什么可能训练不稳定? → 梯度、学习率、数值精度、模型结构等共同影响 为什么最后离不开 GPU? → 参数、梯度和矩阵计算规模巨大到这里,“机器为什么能自己学”才算真正回答。
三十二、把第03讲和第04讲压缩成一条线
第03讲 模型预测什么? ↓ P(x_t | x_<t) ↓ Logits ↓ Softmax ↓ Probability ↓ Cross-Entropy ↓ Loss 第04讲 Loss 有了以后怎么办? ↓ Derivative ↓ Gradient ↓ Chain Rule ↓ Backpropagation ↓ Optimizer ↓ Parameter Update最终只记这一条:
预测 → 衡量错误 → 计算梯度 → 修改参数 → 再预测 → 再修改 → 不断迭代这就是神经网络训练最底层的闭环。
下一讲:第05讲|机器学习、泛化、过拟合与评估
这一讲解决了:
“参数怎么根据 Loss 自动调整?”
下一讲继续追问:
“训练集上的 Loss 下降了,为什么模型到了没见过的数据上不一定同样好?”
届时会进入:
Training Set Validation Set Test Set ↓ Generalization ↓ Overfitting ↓ Underfitting ↓ Bias / Variance ↓ Regularization ↓ Evaluation这一步会把“模型学会了”与“模型真正学得好”区分开。
《现代 AI 原理与系统工程》系列
第01讲|现代 AI 为什么会走到今天 第02讲|向量、矩阵与线性变换:AI 为什么离不开它们 第03讲|概率、信息论与损失函数:模型到底在预测什么 第04讲|导数、梯度与优化:机器为什么能自己学 第05讲|机器学习:数据、泛化、过拟合与评估 第06讲|神经网络:参数、激活函数、Forward、Backward 与 BP ...参考资料
- Ian Goodfellow, Yoshua Bengio, Aaron Courville,Deep Learning.
- Christopher M. Bishop,Pattern Recognition and Machine Learning.
- Diederik P. Kingma, Jimmy Ba,Adam: A Method for Stochastic Optimization, 2014.
- Ilya Loshchilov, Frank Hutter,Decoupled Weight Decay Regularization, 2019.
- PyTorch Autograd、Optimizer、Gradient Clipping 官方文档。
标签
#人工智能 #AI #大模型 #机器学习 #深度学习 #神经网络 #梯度下降 #反向传播 #Gradient #Backpropagation #Adam #AdamW #PyTorch #Python #程序员 #AI学习