
坦白说很多人啃人工智能数学教材时会卡在第十八章。前面十七章把线性代数、概率论、微积分这些工具一样样递到你手里每章都像认识一位新朋友还不用想太多。但到了第十八章画风突变——教材开始要求你用这些工具理解“模型是怎么被训练出来的”比如为什么梯度下降能收敛、学习率为什么不能拍脑袋、随机梯度下降为什么噪声很大却反而是常态。我见过不少同学前十七章刷得风生水起一到大作业阶段就手足无措本质上就是缺了这一章建立的“优化视角”。这一章的核心并不难但它把之前学过的向量、矩阵、导数、期望全部缝合到一个场景里也就是用数学语言描述“学习”这件事。这篇文章我会按教材后半段的常见编排思路把第十八章拆开讲清楚目标函数怎么构造、梯度下降的数学原理、随机梯度与mini-batch的动机、自适应优化器在补偿什么以及训练现场最常见的一批数值异常该怎么排查。无论你是在校生应付人工智能大作业还是即将转岗做人工智能训练师这套框架都应该能直接派上用场。1. 第十八章为什么卡在最优化从模型表达走到损失曲面1.1 “修炼工具”到“缝合工具”的转折点前面学线代时你会算矩阵乘法、特征分解但意识不到这和“训练一个模型”有什么直接关系。第十八章的第一个目标就是让你建立这样一个视角一个人工智能模型本质上是一个带参数 θ 的函数 f(x; θ)。训练过程就是在一个高维参数空间里移动 θ 这个点让某个数值指标变小。这个指标通常叫损失函数也叫目标函数。把参数想象成坐标系里的坐标点那么模型训练就是在这个坐标系里走出一条路径。教材前面讲的向量空间、矩阵求导、偏导数链式法则到了这一章才真正变成“会说话的工具”梯度是一个向量方向指向损失增长最快的方向Hessian矩阵是一个矩阵描述损失曲面的弯曲程度。所以我一直觉得第十八章不是新知识而是“缝合”。它会反复出现同一个动作先写出一个带参数的函数再构造一个衡量好坏的标量然后用梯度方向去移动参数。1.2 一个具体例子线性回归的损失曲面长什么样拿最简单的房价预测举例。假设你有 N 套房子的数据每套有面积、房间数这些特征合成一个特征向量 x真实价格是 y。模型假设是[ \hat{y} w^T x b ]训练目标是最小化预测价和真实价的平均平方误差[ L(w,b) \frac{1}{N} \sum_{i1}^{N} \left( y_i - (w^T x_i b) \right)^2 ]如果只有两三个参数你完全可以把 L 画成一个 3D 曲面找到曲面的最低点。这就是“损失曲面”的直观含义。参数越多曲面维度越高人眼看不见但数学性质照常成立我们需要在百万维空间里找那个最低点。这里有个值得敲黑板的点为什么深度学习不用线性回归那套闭式解公式 ((X^T X)^{-1} X^T y)道理很朴素矩阵求逆的复杂度大约是 O(n^3)模型参数到百万级别时这个操作直接不可行深度学习模型根本不是线性的f(x; θ) 是一层套一层的函数组合根本写不出解析解。所以从第十八章开始你要接受一个现实大多数人工智能模型的最优参数没有公式只能靠迭代逼近。1.3 正则项为什么要给损失函数加“惩罚”另一个在第十八章反复出现的细节是正则化。光看训练集上的损失还不行因为当参数数量比样本数量还多时往往存在无穷多组参数都能把训练损失压到接近零。这属于数学上的欠定问题。解决办法是在目标函数里加一项惩罚比如最常见的 L2 正则[ \min_w ; \frac{1}{N} \sum_{i1}^{N} \left( y_i - f(x_i; w) \right)^2 \lambda |w|^2 ]这背后的数学直觉是模型希望参数 w 的“长度”不要太大。参数太大通常意味着函数对输入过于敏感一点点噪声就能让输出剧烈变化这样的模型没见过新样本时容易翻车。正则项的几何效果是把原来的“无约束优化”变成对参数空间的一种约束——参数被限制在一个球体附近解从“无穷多”变成“唯一”。如果你之前只把正则当成一个“技巧”到这一章一定要升级认知它是优化问题结构的一部分直接改变最优解的位置和稳定性。人工智能训练师在实际调模型时第一个想到的欠拟合、过拟合处置手段就是动这一项。2. 负梯度、学习率与“下山”梯度下降里的一阶数学2.1 为什么偏要选负梯度方向深度学习里八成以上的优化算法底层都是梯度下降虽然看起来有很多变体但核心更新规则就一行[ w_{t1} w_t - \eta \nabla L(w_t) ]为什么是负梯度方向而不是其他方向这不是拍脑袋选的可以从一阶泰勒展开严格推出来。假设当前在点 w沿单位方向 d 走一小步 ε损失近似为[ L(w \varepsilon d) \approx L(w) \varepsilon , d^T \nabla L(w) ]我们想让 L 下降得最快也就是让一阶变化项 d^T ∇L 尽量小。在单位方向约束 |d|1 下由柯西不等式[ d^T \nabla L \ge -|\nabla L| ]等号成立的唯一条件就是[ d -\frac{\nabla L}{|\nabla L|} ]也就是说负梯度方向是下降最陡的方向。这段推导我建议你亲手在纸上写一遍因为后面所有优化器的改进本质上都是对“方向 d”或“步长”的修正根子都在这条泰勒展开里。2.2 学习率到底在控制什么学习率 η 表面上是“步长”但更准确地说它控制的是我们对一阶泰勒展开的信任程度。把更新式代回泰勒展开[ L(w - \eta g) \approx L(w) - \eta |g|^2 ]前提是 η 足够小小到二阶项忽略不计时成立。如果 η 取得太大一阶近似失效损失不但不降还可能直接跳动甚至发散。最经典的例子是二次函数 f(x) x²梯度下降迭代式为[ x_{t1} (1 - 2\eta) x_t ]这要收敛必须满足 |1 - 2η| 1也就是 0 η 1。这个例子虽然简单但揭示了一个通用规律每个方向都有一个“安全学习率上限”上限由该方向的曲率决定。曲率越大允许的学习率越小。这也为后面讲自适应优化埋下伏笔。2.3 梯度为零不代表到了最低点刚学梯度下降时容易产生一个误解看到梯度为 0就觉得找到了最优解。其实梯度为 0 的点有三种情况局部极小值周围都比它高局部极大值周围都比它低鞍点有些方向在下坡有些方向在上坡。在高维空间里鞍点才是最常见的东西。原因也很数学一个点要成为局部极小值需要 Hessian 矩阵的所有特征值都大于 0但在高维随机函数中特征值有正有负的概率极高你撞上一个处处都是“山谷”的概率反而很低。所以第十八章会反复强调一个概念我们在高维非凸问题上找到的“最小值”往往只是损失非常低的谷底而不是全局最优。这听起来有点让人沮丧但也是人工智能模型训练能成立的数学依据——我们不需要全局最优只需要足够低的损失。2.4 手写一个最简梯度下降看学习率如何捣乱空谈不如跑代码。下面这个 Python 示例用一个二维凸函数 ( f(x,y) x^2 0.1y^2 ) 演示梯度下降。注意两个方向的曲率不同一个是 2一个是 0.2学习率取大了会在曲率大的方向震荡。import numpy as np def f(w): x, y w return x**2 0.1 * y**2 def grad(w): x, y w return np.array([2 * x, 0.2 * y]) def gd(start, lr, steps): w np.array(start, dtypefloat) path [w.copy()] for _ in range(steps): w w - lr * grad(w) path.append(w.copy()) return path w0 [1.0, 1.0] for lr in [0.05, 0.5, 1.9, 2.1]: path gd(w0, lr, 20) last path[-1] print(flr{lr:5} 最终位置({last[0]:.3f}, {last[1]:.3f}))你亲自跑一遍就会发现lr0.5 收敛平稳lr2.1 直接发散。原因就是 x 方向曲率对应的安全上限是 1超过后每一步都离最优值越来越远。这是整个第十八章里最值得亲手做的小实验。3. 全样本、随机样本与 mini-batch随机梯度下降的动机和代价3.1 全批量梯度的昂贵之处上一节讲的梯度下降默认用的是全部训练数据算出一个整体梯度。这被称为全批量梯度下降。它的数学性质最好每步方向都是真实下降方向步长也不需要特别小心。但代价非常大。真实的人工智能训练集少则几万多则上亿样本。每更新一次参数都要把全部样本的正向计算和反向传播做一遍计算量正比于样本总量 N。更现实的问题是很多训练场景里数据是源源不断产生的根本没有一个静态的“全量数据集”等在那里。这时候你需要的不是“每步精确”而是“每步快、多走几步”。3.2 随机梯度的无偏性数学上的关键保证随机梯度下降SGD是从训练集中随机抽一个样本 i用这个样本的梯度 ( g_i \nabla \ell_i(w) ) 来近似全批量梯度。它为什么能工作靠的是无偏性[ \mathbb{E}{i}[g_i(w)] \frac{1}{N}\sum{i1}^{N}\nabla \ell_i(w) \nabla L(w) ]翻译成人话就是单次梯度方向噪声可能很大但期望方向是对的。这就像用体重秤单次读数判断趋势不可靠但如果每天都固定时间称一次长期看噪声会互相抵消趋势会被看清。有了这个保证迭代更新的收敛性才能成立。数学里管这叫“噪声但无偏的梯度估计”它是随机优化理论的地基。3.3 噪声不仅是麻烦还有正面作用很多人第一次跑 SGD 时会疑惑为什么损失曲线这么抖不像全批量那样平滑下降这是正常的因为每一步都在用随机样本的梯度天然带有噪声。但它不全是坏处。噪声有一个被低估的好处它让参数有机会从差的局部极小点和鞍点附近逃出来。全批量梯度下降会老老实实走进它看到的第一个谷底而随机梯度下降因为步脚不稳反而可能“意外”迈过一些坎走到更低的谷底。代价则是收敛后期损失持续震荡。解决办法通常有两个逐步降低学习率即学习率退火让后期噪声方差变小配合动量机制把轨迹磨平。3.4 mini-batch折中方案与实用参数把全批量和随机两个极端折中就是 mini-batch 梯度下降每次抽一小批样本计算平均梯度。这是目前所有主流深度学习框架的默认做法。方式每步计算量梯度噪声适用场景全批量高正比于 N无小数据集、可二次求导的问题随机batch1极低很大流式数据、在线学习mini-batch中正比于 batch size适中绝大多数深度学习训练实用经验上batch size 取 32、64、128 都是常见区间。一个通则是batch size 翻倍时学习率可以按比例增大但有一个上限。比如原来 batch32、lr0.001换成 batch128 可以试着把 lr 提到 0.002 或 0.003却很少会直接翻四倍。原因在于学习率太大时过去的理论和实测都会指向发散或收敛异常。这一小节学完你应该把“选 batch size”和“选学习率”当成一对耦合的超参数来看而不是两个孤立变量。4. Hessian、动量与 Adam自适应优化在补偿什么4.1 病态曲率为什么梯度方向会“来回打脸”上一节我们说到每个方向都有自己允许的学习率上限。如果一个损失函数在不同方向的曲率差异很大比如[ f(u,v) u^2 100v^2 ]那么 v 方向曲率很大梯度更新时 v 分量会被迅速拉回零而 u 方向推进极慢。给个比喻你站在一个极窄极长的山谷里梯度会把你左右两侧的墙壁来回反弹真正想前进的方向却只能一点点挪。用数学语言说这叫病态曲率描述它的指标是 Hessian 矩阵最大与最小特征值之比也叫条件数。条件数越大普通梯度下降越难走学习率必须小到满足最陡方向的约束而小学习率又让缓坡方向几乎停滞。这是极早期优化算法面临的核心瓶颈。4.2 牛顿方向看一步曲率再迈腿的“理想方案”二阶优化方法给出了一个完美的思路。对损失做二阶泰勒展开并令梯度为 0可以得到更新方向[ \Delta w -H^{-1} \nabla L(w) ]其中 H 是 Hessian 矩阵。这个方向被称为牛顿方向它的特点是在曲率大的方向上自动迈小步在曲率小的方向上自动迈大步一步就能跨过病态曲率。但为什么深度学习里几乎没人直接算牛顿方向原因很现实H 是百万 × 百万矩阵直接存储和求逆都不可能非凸区域里 H 可能不可逆甚至负定牛顿方向反而会把人带上山每步都重新计算 H计算量远超训练本身。所以十八章节里学习二阶方法的重点不是让你去实现牛顿法而是理解一个思路优化器应该感知每个方向的曲率并据此调整步长。后来的动量、RMSProp、Adam全是在用“近似”的方式追这个理想。4.3 动量用历史梯度平均掉振荡动量是最简单有效的修正。它的更新规则长这样[ v_{t1} \beta v_t (1-\beta) g_t ][ w_{t1} w_t - \eta v_{t1} ]这里的 v 可以理解为历史梯度的指数移动平均β 常见取值 0.9。当梯度方向反复横跳时正负分量在平均里互相抵消真正持续的方向则被保留并积累速度。有一个直觉很有用把参数更新想象成小球从山坡滚下动量就是小球的惯性。它不会因为路过一个小坑就立刻停住能越过局部小起伏。这个性质让动量不仅在病态曲率下表现更好也在一定程度上应付了前面提到的鞍点问题。4.4 Adam逐参数曲率估计与实用调参建议RMSProp 和 Adam 的思路更进一层它们用梯度的平方的指数平均近似每个坐标方向的曲率大小然后在每个参数上做“归一化”。Adam 的更新可以理解成一阶矩估计动量项决定方向二阶矩估计决定每个坐标方向的步长缩放每个参数被自适应地分配一个适合自己的学习率。这也是为什么 Adam 在很多模型上比普通 SGD “无脑”地更好用。实际工程中的参数设置我常用的是一组基准值超参数常用值作用learning rate1e-3基准步长太大梯度震荡太小收敛慢β₁0.9一阶动量衰减系数β₂0.999二阶矩衰减系数ε1e-8防止除零的极小值不过你要记住一个反面经验Adam 收敛前期很快但最终泛化误差有时不如调好的 SGD 动量。最近几年大家常用的 AdamW 就是在 Adam 基础上修正了权重衰减的实现方式如果你训练 Transformer 类模型优先试它。5. 损失不降、梯度爆炸与 NaN训练异常背后的数学信号5.1 损失完全不动先区分“优化没到位”还是“模型有问题”训练中的第一类异常是损失一直原地踏步几乎没变化。排查时别急着换优化器。我建议按顺序走用一小批数据比如 32 个样本做一次前向确认损失数值不是在正常范围内看反向传播后各层梯度范数如果输出层梯度正常、浅层梯度几乎为 0那大概率是梯度消失如果所有层梯度都接近 0检查是不是某个激活函数输出饱和比如 ReLU 大批量死亡如果整套流程和数值都正常再怀疑学习率是不是调得太小。还有一个百试百灵的骚操作先只用一个样本训练把 batch 设成 1看模型能不能把这个样本的损失压到接近 0。如果连这个都做不到说明代码、数据或模型结构有根本问题这时候加数据、调学习率都没用。5.2 梯度爆炸与梯度消失连乘效应和它的克星梯度爆炸和消失本质上都是链式法则的连乘效应。设网络有 L 层损失对第 1 层参数的梯度里会出现 L-1 项 Jacobian 矩阵连乘[ \frac{\partial L}{\partial W^{(1)}} \frac{\partial L}{\partial h^{(L)}} \frac{\partial h^{(L)}}{\partial h^{(L-1)}} \cdots \frac{\partial h^{(2)}}{\partial h^{(1)}} \frac{\partial h^{(1)}}{\partial W^{(1)}} ]每一项乘积的特征值如果普遍大于 1连乘后梯度指数增长轻则训练震荡重则直接溢出为 NaN如果普遍小于 1梯度指数衰减浅层参数根本得不到更新。处理梯度爆炸最直接的办法是梯度裁剪gradient clipping把梯度范数强行限制在一个阈值内。处理梯度消失则更多靠结构设计比如残差连接把上一层的输出直接加到本层输出来保证梯度有捷径、归一化层以及用 ReLU 这类的激活函数降低饱和风险。这些手段表面上是工程技巧底层都离不开链式法则那套乘法结构。5.3 训练出现 NaN找第一个出现异常的位置NaN 是所有训练异常里最令人头大的一种因为一旦出现后续所有数值都会被污染你看到的损失可能是一个吓人的 nan。排查思路可以用“二分定位”在损失计算处加检查确认是不是算出了 NaN如果是向前找检查交叉熵里是否出现了 log(0)或者预测概率后面是否有未加 epsilon 的除零如果前向没问题看反向后的梯度范数是不是 inf如果确实是梯度爆炸优先调低学习率或加梯度裁剪如果数据里混入了 NaN也需要在数据加载阶段过滤掉不然后续再稳定的计算图都会崩。浮点计算里还有一个常见现象两个极大值相减会丢失精度或者某个中间结果超出 float32 的表示范围。这类问题在模型变深、学习率偏大时很容易引爆。我的习惯是给损失函数里容易出问题的操作都加一个极小量 epsilon同时在训练脚本里每隔固定步数打印一下“参数最大值、最小值、梯度范数”。这一手能让你在问题刚冒头时就发现而不是等到损失变成 NaN 才靠猜。结尾这一章真正留给你的东西我自己实际训练模型的体会是第十八章最宝贵的不是某个优化器的公式而是建立一套“先诊断再换药方”的思考方式。看到损失不降先分清是学习率问题、数据问题还是模型结构问题看到梯度爆炸先算梯度范数看到收敛异常再考虑换优化器。这个顺序比盲目从 SGD 换到 Adam 有效得多。如果你正卡在人工智能大作业上建议把这一章的内容浓缩成三张纸带去实验室第一张写梯度下降的更新式和一阶泰勒解释第二张写 SGD、动量、Adam 的更新对应关系第三张写训练异常的排查流程。把这三张纸吃透后面再看源码、调模型都会顺手很多。没有哪种优化器是万能的损失曲面复杂到超出人眼想象的时候数学给我们的不是“完美答案”而是一套可靠的诊断工具。这大概就是第十八章存在的意义。