ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

字节技术总监30讲 AI课:4|导数、梯度与优化——机器为什么能自己学?

字节技术总监30讲 AI课:4|导数、梯度与优化——机器为什么能自己学? 字节技术总监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 会怎么变化因此把变化范围缩小到无限接近 0f(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.2LossL_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 3LossL (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(xh)-f(x)] / hPythondeff(x):returnx*x x3.0h1e-5numerical_grad(f(xh)-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如果中间大量导数都小于 10.5 × 0.5 × 0.5 × 0.5 × ...连续乘下去会越来越小。最后可能变成gradient ≈ 0这就是梯度消失的基本数学来源。十七、为什么会梯度爆炸反过来如果某些局部导数长期大于 12 × 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 到底在解决什么普通 SGDw(t1) w(t) - η g(t)它主要看当前梯度如果不同 batch 带来的梯度方向变化很大参数轨迹可能出现明显抖动。Momentum 的思路是不仅看当前梯度也保留过去梯度形成的运动趋势。一种常见写法v_t β v_(t-1) g_t然后w_t w_(t-1) - η v_t直觉上SGD → 只看当前一步 Momentum → 当前一步 历史趋势因此它可以在某些优化地形上减少震荡 保持连续方向 提高某些方向上的更新效率二十、Adam 又在做什么AdamAdaptive 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对于普通 SGDL2 正则与 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()predmodel(x)losscriterion(pred,y)loss.backward()optimizer.step()现在应该能逐行翻译。optimizer.zero_grad()清空上一轮累积的梯度。model(x)进行 Forwardx ↓ 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 w4.0lr0.1forstepinrange(10):current_lossloss(w)current_gradgrad(w)print(fstep{step:2d}, fw{w:.6f}, floss{current_loss:.6f}, fgrad{current_grad:.6f})ww-lr*current_grad你应该观察到w → 不断向 0 靠近 Loss → 不断下降 Gradient → 绝对值逐渐减小这就是Forward → Loss → Gradient → Update最小化版本。二十七、再用 C 程序员视角看一次如果把参数想成floatw[n];梯度floatgrad[n];那么最基本的更新就是for(inti0;in;i){w[i]-lr*grad[i];}这非常重要。因为从系统角度看神经网络训练本质上就是一个超高维参数数组的数值迭代优化过程。只不过真实模型的参数数量 计算量 矩阵规模 梯度数量巨大到必须使用Tensor GPU CUDA Kernel 分布式训练来完成。这也是为什么本课程后面会从数学一路进入GPU → CUDA → Tensor Core → HBM → 分布式训练 → LLM 推理二十八、本讲最容易混淆的 10 个概念1. Loss 和 GradientLoss → 当前错多少 Gradient → 参数往哪里改、局部变化有多强2. Gradient 和 UpdateGradient → 提供更新信息 Update → 真正修改参数3. Backward 和 Updatebackward() → 算梯度 step() → 更新参数4. Learning Rate 和 GradientGradient → 方向和局部变化率 Learning Rate → 一步走多远5. Batch 和 EpochBatch → 一次拿多少数据 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学习
返回列表