☰
字节技术总监30讲 AI课:4|导数、梯度与优化——机器为什么能自己学?
2026/10/1 10:32:03 网站建设 项目流程

字节技术总监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 ... wn

Loss 是:

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.2

Loss:

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 ↓ ... ↓ 0

Loss 也不断下降。

这就是最基本的“机器学习”。

没有魔法。

本质就是:

计算误差 → 计算梯度 → 调整参数 → 再算一次 → 重复

七、为什么链式法则是深度学习的核心?

真实神经网络不是:

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 = 3

Loss:

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)] / h

Python:

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_t
v_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 ↓ Prediction

criterion(pred, y)

根据预测结果和真实标签得到 Loss。

前一讲我们已经知道:

Logits → Cross-Entropy → Loss

loss.backward()

执行:

Backward

根据计算图和链式法则计算:

dLoss / dParameter

optimizer.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 > SGD

10. 反向传播 ≠ 反向预测

反向传播解决的是:

怎么得到参数梯度

不是:

怎么反过来生成输入

二十九、这一讲真正建立的知识树

第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)] / Δx
2. 梯度 ∇L = [ ∂L/∂w1, ∂L/∂w2, ..., ∂L/∂wn ]
3. 梯度下降 w ← w - η∇L
4. 链式法则 dL/dw = dL/dz × dz/dw
5. Momentum v_t = βv_(t-1) + g_t
6. 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 ...

参考资料

  1. Ian Goodfellow, Yoshua Bengio, Aaron Courville,Deep Learning.
  2. Christopher M. Bishop,Pattern Recognition and Machine Learning.
  3. Diederik P. Kingma, Jimmy Ba,Adam: A Method for Stochastic Optimization, 2014.
  4. Ilya Loshchilov, Frank Hutter,Decoupled Weight Decay Regularization, 2019.
  5. PyTorch Autograd、Optimizer、Gradient Clipping 官方文档。

标签

#人工智能 #AI #大模型 #机器学习 #深度学习 #神经网络 #梯度下降 #反向传播 #Gradient #Backpropagation #Adam #AdamW #PyTorch #Python #程序员 #AI学习

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询