☰
人工智能数学第十八章:神经网络训练背后的数学原理与实战避坑
2026/10/27 6:19:25 网站建设 项目流程

人工智能数学教材翻到第十八章,通常已经不是入门前菜了。前面十七章把线性代数、微积分、概率统计、最优化这些工具一门一门摆出来,很多同学学到这里会有一个共同的感觉:每一章单独拿出来都能看懂,做题也还行,但一放到人工智能的语境里,就不知道怎么用。第十八章恰恰就是来“缝合”这些知识的,它通常会把前面所有数学工具串成一个完整的东西——神经网络的训练过程。这篇文章就用我实际带学员、做项目时的视角,把这一章的核心逻辑、公式推导、常见翻车点一次讲清楚。不管你是为了应付期末,还是想补足数学短板把深度学习真正跑通,这篇都值得慢慢看。

1. 第18章在整本教材里的中枢位置

1.1 前面十七章到底为这一章准备了什么

很多人学人工智能数学基础,学到第四章矩阵求导觉得“这辈子用不上了”,学到第九章概率分布觉得“这不就是高中概率换个马甲”,结果到第十八章才发现,前面所有的章节都在为一个目标服务:让机器自己找到一组参数,把输入映射到输出。

我列个对照表你就明白了:

前面章节的核心内容在第18章中的落点
线性代数:矩阵乘法、转置、逆矩阵神经网络层与层之间的线性变换,权重矩阵的存储与更新
微积分:偏导数、链式法则、梯度反向传播的数学内核,损失函数对每个参数的偏导计算
概率论:条件概率、期望、熵交叉熵损失函数的设计,softmax输出层的概率解释
最优化:梯度下降、凸优化参数更新规则,各种优化器(SGD、Adam)的原理
数值分析:浮点误差、稳定性激活函数取值域的设计,softmax与交叉熵的数值稳定技巧

看到没有,第十八章本质上是把数学工具箱全部打开,然后以“训练神经网络”这个实际任务为线索重新组织了一遍。它不引入太多全新数学概念,而是把旧知识组合成一个能直接落地的操作流程。这也是为什么我常说,如果你只能细读一章来应付人工智能大作业或者期末,那一定是第十八章。

1.2 为什么深度学习是最能体现数学价值的场景

有人说深度学习无非是“调库调参”,这句话只对了一半。调库的前提是你得知道库里那些接口背后做了什么数学运算,否则遇到报错和性能瓶颈基本抓瞎。

神经网络训练可以抽象成一句话:给定一大堆样本数据,找一个参数向量,让某个目标函数的值尽可能小。这个描述里的三个要素,分别对应三块数学:

第一,样本数据在电脑里本质是矩阵。一张 32x32 的彩色图片就是一个 32x32x3 的张量,展开成一列向量就是 3072 维。第二,模型本身是一个带参数的复合函数,从输入到输出每一步都是矩阵乘法和非线性映射的交替。第三,目标函数(损失函数)通常写成所有样本损失的平均值,而让它最小化靠的是梯度下降——每一步沿负梯度方向更新参数。

这三个要素单独拿出来都不是第十八章的原创,但组合在一起就需要一个统一的计算框架来承载。第18章引入的计算图(computational graph)就是这个框架,它让“从损失函数反推每个参数梯度”变成了一件有固定套路的事。你用 TensorFlow、PyTorch 这类框架训练模型时,框架自动帮你做的就是这件事,而第十八章就是把你从“只会点运行按钮”变成“知道按钮背后发生了什么”的那扇门。

2. 神经网络前向传播的代数本质

2.1 每一层网络都是一次矩阵变换加非线性激活

如果不看内部细节,一个最简单的全连接神经网络长这样:

输入向量 x,第一层的权重矩阵 W1,偏置 b1,经过一个激活函数 σ,得到中间结果 a1 = σ(W1 x + b1)。第二层再在 a1 的基础上做一次类似的变换,得到输出 a2,最后把 a2 和真实标签 y 比较,算出一个损失值 L。

写成公式就是:

$$ z^{[1]} = W^{[1]} x + b^{[1]} $$

$$ a^{[1]} = \sigma(z^{[1]}) $$

$$ z^{[2]} = W^{[2]} a^{[1]} + b^{[2]} $$

$$ a^{[2]} = \sigma(z^{[2]}) $$

$$ L = \text{loss}(a^{[2]}, y) $$

这组式子看着简单,但里面全是容易懵的细节。咱们逐个拆。

第一,为什么是矩阵乘法?因为如果不用矩阵,你有 n 个输入特征、m 个隐藏神经元,就得写 m 个独立线性函数,每个函数又包含 n 个权重系数,手写下来是 m 行 n 列的系数表,这就是矩阵的来历。矩阵乘法本质上让你用一套符号同时描述所有神经元的加权求和。

第二,为什么要加偏置?因为 W x 这个线性变换必须经过原点,而现实中决策边界基本不会正好过原点,b 的存在让每一层可以自由平移决策边界。没有 b,整个模型的拟合能力会差一大截。

第三,为什么要非线性激活函数?这个很多人理解不到位。如果每一层都只做线性变换,那么无论网络叠多少层,最终效果都等价于一层线性变换。你把两个线性映射套在一起:W2(W1 x + b1) + b2 = (W2 W1)x + (W2 b1 + b2),还是一个线性函数。那还不如一开始就直接用一个矩阵 W = W2 W1 来得省事,为什么要费劲去构造多层结构?正是因为激活函数把非线性引进来了,多层才能逼近任意复杂函数。这是神经网络表达能力的关键。

2.2 链式法则:神经网络反向传播的基石

学员问得最多的问题之一是:为什么要单独学链式法则?因为神经网络从输入到输出是一条层层嵌套的复合函数,损失对某一层参数的偏导,必须从损失函数开始,顺着这条链一路往回算。

我拿生活中的例子打个比方。假设你要计算“网购订单总金额”对“商品单价”的敏感程度:总金额 = 商品数 × 单价,而商品数又 = 购物车里的件数。单价变一点,会影响商品数吗?不会,但会影响总金额。这种“间接影响”在数学上就是复合函数求导,链式法则告诉你:一间接地表达这种影响,要把每一步的局部导数连乘起来。

对于三层网络的简单案例,损失对第二层某个权重 W[2]_{ij} 的偏导是:

$$ \frac{\partial L}{\partial W^{[2]}{ij}} = \frac{\partial L}{\partial a^{[2]}} \cdot \frac{\partial a^{[2]}}{\partial z^{[2]}} \cdot \frac{\partial z^{[2]}}{\partial W^{[2]}{ij}} $$

这里有三项相乘,每一项都是局部的导数。哪一项算错了,整个梯度就错了。PyTorch 自动微分帮你算了,但你自己推导时就得保持清醒:谁的导数对谁,先算哪个中间变量。

2.3 维度一致性检查:快速发现公式错误的利器

我做项目时有个习惯,任何矩阵公式写出来都要做维度核对(dimension check)。原理很简单:矩阵能相乘,必须满足左矩阵列数等于右矩阵行数;结果的行数等于左矩阵行数,列数等于右矩阵列数。

假设你有一个 3 维输入 x(形状是 3×1),第一层有 4 个神经元,那么 W1 的形状必须是 4×3,b1 是 4×1,这样 z1 = W1 x + b1 的结果才是 4×1。如果第一层输出是 4 维,第二层要输出 2 维,W2 就是 2×4。

很多同学在推导反向传播公式时写错转置,维度检查几秒就能发现问题。比如梯度更新公式里出现 W 的转置还是原矩阵,你拿维度一看:如果乘出来形状对不上,多半是转置写反了。这个习惯比任何调试工具都管用。

3. 损失函数与反向传播的核心细节

3.1 MSE 与交叉熵到底该怎么选

第18章通常会介绍两类最常用的损失函数:均方误差(MSE)和交叉熵(Cross Entropy)。

MSE 的公式是:

$$ L_{MSE} = \frac{1}{n} \sum_{i=1}^{n} (y_i - \hat{y}_i)^2 $$

它天然适合回归问题,比如预测房价、温度、销量这类连续数值。为什么选平方而不是绝对值?因为平方函数在误差为 0 附近光滑,可导;绝对值函数在 0 点有一个尖角,导数不存在。而梯度下降需要求导,光滑的损失函数能让优化过程稳定得多。

交叉熵的典型形式是:

$$ L_{CE} = -\frac{1}{n} \sum_{i=1}^{n} \left[ y_i \log \hat{y}_i + (1 - y_i) \log (1 - \hat{y}_i) \right] $$

这是二分类的形式,多分类还要套 softmax。为什么分类任务不推荐用 MSE?因为分类模型输出的是概率,概率必须落在 0 到 1 之间,而 MSE 的梯度在预测值接近 0 或 1 时会变得非常小,导致训练极慢。交叉熵的梯度结构天然缓解了这个问题,尤其是配 softmax 输出时,梯度形式会极度简化。

3.2 反向传播四步走

完整走一遍反向传播是第十八章最重要的实操训练。以二分类单隐层网络为例,操作流程是固定的四步:

第一步,前向传播。按前面写的公式,从 x 开始一直算到损失 L,每一层的 z、a 都要保留下来,因为后面求导要用它们。

第二步,计算输出层误差。定义 δ2 = ∂L/∂z2,它表示损失对第二层线性输出 z2 的敏感度。对 softmax + 交叉熵的组合,经过数学推导,δ2 会化简成一个非常漂亮的结果:预测概率减真实标签,即 (a2 - y)。这个化简不是巧合,而是交叉熵和 softmax 相互配合的结果。

第三步,向隐藏层传播误差。利用链式法则:

$$ \delta^{[1]} = (W^{[2]})^T \delta^{[2]} \odot \sigma'(z^{[1]}) $$

这里 ⊙ 表示逐元素相乘,σ' 是激活函数的导数。这一式子的含义是:第二层的误差信号,通过 W2 的转置“传回”到第一层,再乘以激活函数在 z1 位置的斜率,修正这个信号在当前神经元上的强度。

第四步,求参数梯度并更新。所有权重和偏置的梯度都能用前面算好的中间结果表示:

$$ \frac{\partial L}{\partial W^{[2]}} = \delta^{[2]} (a^{[1]})^T $$

$$ \frac{\partial L}{\partial b^{[2]}} = \delta^{[2]} $$

$$ \frac{\partial L}{\partial W^{[1]}} = \delta^{[1]} x^T $$

$$ \frac{\partial L}{\partial b^{[1]}} = \delta^{[1]} $$

更新规则是 W = W - η * ∂L/∂W,η 是学习率。这个流程看着公式多,但大多数深度学习框架已经自动完成。你在作业里手动实现时,按这四步走,基本不会乱。

3.3 数值稳定性:一个被大多教材轻描淡写但直接决定成败的细节

第十八章如果只讲公式推导,忽略数值稳定性,那实战会让你怀疑人生。最常见的问题出现在 softmax 函数里,它要算 e 的 z 次幂。如果 z 里有个 1000,e^1000 直接溢出成无穷大,整个训练立刻崩溃。

解决办法非常朴素:在计算 softmax 之前,对每个 z 减去该批数据的最大值。因为 softmax 的分子分母同时除以 e^max,结果不变,但指数的输入就变成了 z - max,最大值是 0,其他值全是负数,绝不会溢出。这个技巧叫 max-shift,几乎所有深度学习框架的底层都内置了。你手动实现分类器时,千万别省略这一步。

同理,交叉熵和 sigmoid 组合时也要避免直接计算 log(0)。如果预测概率被算成精确的 0,log(0) 直接报错。工程上是给概率加一个极小量 ε,比如 1e-12,保证对数函数输入永远大于 0。这不是为了假装数据,而是为了让数值计算在计算机的有限精度下也能稳定推进。

4. 优化算法:从梯度下降到 Adam 的数学原理

4.1 梯度下降的几何直觉与数学表达

梯度下降的出发点很简单:你在山上雾天看不见远处,只能靠脚下感知坡度,想下山就往坡度最陡的方向迈一步。数学里,一个多元函数在某点的梯度,就是它“上升最快的方向”,那反方向就是“下降最快的方向”。

参数更新公式:

$$ \theta_{t+1} = \theta_t - \eta \nabla_{\theta} L(\theta_t) $$

这里 θ 是所有参数的集合,∇L 是损失对每个参数的偏导组成的向量,η 是学习率。第十八章通常会告诉你,学习率不能太小也不能太大:太小,训练要几万步才收敛,浪费时间;太大,参数一步越过最优点,loss 可能震荡甚至发散。实践经验往往是先用 0.01 试跑,观察 loss 曲线再调。

4.2 从 SGD 到动量:为什么要考虑历史梯度

最朴素的梯度下降每一步都用全部训练数据算梯度,数据量大时太慢。随机梯度下降(SGD)每次只用一个或一小批数据来估算梯度,速度快但是方向动荡。动量方法就是解决这种动荡的。

动量的更新公式里有历史梯度信息的累积项:

$$ v_t = \beta v_{t-1} + \eta \nabla L(\theta_t) $$

$$ \theta_{t+1} = \theta_t - v_t $$

物理上的类比是“惯性”:球从山坡滚下来不会因为遇到一个突然的方向变化就立刻转向,而是带着原来的势头冲过去。β 通常取 0.9,表示当前方向 90% 参考历史动量,只有 10% 来自最新梯度。这样训练过程的 loss 曲线会平稳很多,不容易在狭长峡谷区域来回震荡。

4.3 Adam:一阶矩与二阶矩的配合

Adam 是现在项目里最常用的优化器,它把动量思想和自适应学习率结合到一起。Adam 维护两个变量:一阶矩估计 m(类似动量,反映平均梯度方向)和二阶矩估计 v(反映梯度平方的平均,用它来自适应缩放每个参数的学习率)。

更新规则大致是:

$$ m_t = \beta_1 m_{t-1} + (1 - \beta_1) g_t $$

$$ v_t = \beta_2 v_{t-1} + (1 - \beta_2) g_t^2 $$

$$ \hat{m}_t = \frac{m_t}{1 - \beta_1^t}, \quad \hat{v}_t = \frac{v_t}{1 - \beta_2^t} $$

$$ \theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{\hat{v}_t} + \epsilon} \hat{m}_t $$

为什么要做偏差修正(除以 1-β^t)?因为初始几步 m_t、v_t 都从 0 开始,估计值偏小,直接使用会让前几步更新幅度失真。偏差修正把早期估计拉回正常尺度。熟悉正则化或移动平均的同学会发现,这本质上是带权重的指数移动平均。

Adam 的默认值几乎可以适配大多数任务:β1 = 0.9,β2 = 0.999,η = 0.001。这也是框架里 torch.optim.Adam 的开箱参数。它不见得一定比精心调过的 SGD 好,但胜在鲁棒,失败概率低。你做大作业时直接上 Adam,省去大量调学习率的痛苦。

4.4 学习率、Batch Size 与梯度噪声的实际关系

第18章最后如果讲优化,一定会提到一个工程概念:batch size。你用全部数据算梯度叫全批梯度下降,用一小批叫 mini-batch。batch size 影响内存、训练速度和收敛稳定性。

batch size 越大,梯度估计越准,但每步更新成本越高;越小,梯度噪声越大,但相当于给优化过程引入了随机扰动,有时反而能帮助跳出局部极小值。经验法则是先用 32 或 64 起步。这个数字通常不讲究什么神奇原理,更多是性能和精度的折中。

还有一个很实用但文档里不常写的规律:如果增大 batch size,同时把学习率按比例放大,能在不损失收敛效果的情况下显著加速训练。这背后的数学原因是更大批次给出了更准确的梯度方向,足以支持更大步长。这个技巧叫学习率缩放,在分布式训练里尤其常见。

5. 第18章常见问题与实战避坑实录

5.1 梯度消失与梯度爆炸的数学根源

训练深层网络时最经典的报警就是 loss 不降,甚至直接 nan。原因大概率出在两个地方:梯度消失和梯度爆炸。

梯度消失的根源是连乘效应。反向传播要一路乘上每一层激活函数的导数。如果激活函数是 sigmoid,它的导数最大也只有 0.25,多层连乘后就趋近于 0。一个 10 层的网络,0.25 的 10 次方已经小到让前面的层几乎收不到更新信号。

梯度爆炸则是权重矩阵的特征值大于 1 时,连乘后梯度指数级增长。解决办法常见的几种:换用 ReLU 这类导数固定为 0 或 1 的激活函数;做权重初始化时控制方差别太大;加上梯度裁剪(gradient clipping)——当梯度的整体长度超过某个阈值时按比例压缩。深层网络配合残差连接也能大幅缓解梯度消失,因为残差结构相当于给梯度开了一条“高速公路”。

5.2 L1 和 L2 正则化:从导数角度理解为什么它们能防过拟合

正则化是损失函数后面加一个惩罚项,让模型权重不要太大。L2 惩罚是加 λ 乘以权重平方和,L1 是加 λ 乘以绝对值之和。为什么这会有效?你从梯度角度想就特别清楚。

L2 的导数就是 2λw,因此每一步更新除了原来的梯度,还要额外把权重向 0 方向拉一点,等于每个权重被“压缩”一圈。L1 的导数是 λ 乘以 w 的符号,所以它对每个参数施加的是一个恒定大小的推力,让权重无论大小都会被往 0 推。这也解释了为什么 L1 更容易产生稀疏解,很多参数被推成精确 0,相当于自动做特征选择。L2 只会让参数变小,不会变成 0。

在作业里加正则化很容易,直接在 loss 之后加一行:loss += lambda * (W.pow(2).sum())。lambda 一般从 0.0001 到 0.01 之间调。太小没效果,太大模型会欠拟合——loss 只顾着压权重,忽略了数据本身。

5.3 期末、大作业怎么利用第十八章知识拿高分

期末和大作业最常见的项目是从零实现一个手写数字识别或简单的分类器,不借助高级框架,只用 NumPy。这类任务想拿高分,拼的不是代码多花哨,而是你是否把数学环节做到了位。

第一,自己做一次完整的前向和反向传播推导,建议纸笔写一遍,再把公式翻译成代码。很多网上的博客直接给了代码,你复制跑通也行,但一旦面试问你某个维度为什么是转置,你就暴露了。

第二,数据预处理至少要做标准化。原始像素值在 0 到 255 之间,直接送进网络会让初始梯度量级很大。减均值除以标准差,数值上更平滑,训练也更稳。

第三,训练时保存一份 loss 曲线。这一点特别重要,因为老师看作业时会根据 loss 曲线判断你是真的理解了过程,还是仅仅“调包运行成功”。一个正常收敛的曲线是前几百步快速下降,后面平缓。如果曲线乱跳,说明学习率太大,或者数据没有洗牌。

第四,多跑几组对比实验,比如不同学习率、不同隐藏层宽度、加不加正则化。用表格记录最终准确率。这种对照实验是区分高分作业和及格作业的重要标志。

5.4 第十八章高频公式速查

这里把最容易被出成填空或简答题的公式集中列一下,考试前半小时过一遍很管用:

  • 单层前向传播:z = Wx + b,a = σ(z)
  • 均方误差:L = (1/n) Σ(y - a)^2
  • 交叉熵:L = -(1/n) Σ[y log a + (1-y) log(1-a)]
  • sigmoid 导数:σ'(z) = σ(z)(1 - σ(z))
  • tanh 导数:tanh'(z) = 1 - tanh^2(z)
  • ReLU 导数:z > 0 时为 1,z ≤ 0 时为 0
  • softmax 交叉熵输出层误差:δ = a - y
  • 隐藏层误差:δ^[l] = (W^[l+1])^T δ^[l+1] ⊙ σ'(z^[l])
  • 权重梯度:∂L/∂W^[l] = δ^[l] (a^[l-1])^T
  • 偏置梯度:∂L/∂b^[l] = δ^[l]
  • L2 正则化损失:L_total = L_data + λ Σ W^2
  • L1 正则化损失:L_total = L_data + λ Σ |W|

第18章的内容拆开看,每一块都没有超出前面章节的难度,但组合起来就是一个完整可运行的智能系统雏形。我个人带过不少从零起步的学员,他们的一个共同变化发生在真正自己动手推导一次反向传播后:在此之前,神经网络对他们是一个黑盒;在此之后,见到模型报错或者效果差,第一反应不是去网上盲目改参数,而是从数学上判断问题大概出在哪一层、是梯度的问题还是损失函数的问题。这种判断力,就是第十八章存在的意义。如果你正在学这一章,别急着跳代码,先用纸笔把三层网络的前向和反向推导完整写一遍,再打开编辑器实现,你会发现后面所有内容都顺了。 # 人工智能数学基础第十八章:从公式到神经网络的最后一公里

一本三百多页的人工智能数学教材,翻到第十八章时通常已经是收尾阶段了。但根据我的教学和项目落地经验,很多同学恰恰是在这一章开始掉队,因为前面的线性代数、概率论还能靠背公式应付过去,到了这里突然要面对矩阵求导、链式法则、梯度下降、损失函数这一整套串联起来的东西,稍不留神就断片。这一章是典型的“学了就要用”的内容,尤其是期末大作业、课程设计甚至面试笔试,很大概率都围绕它出题。这篇文章我不打算把公式摆完就完事,而是把这一章的定位、核心公式的来龙去脉、实战中会踩的坑,以及怎么用它去完成一个大作业,全部按从业者的经验讲明白。

1. 第18章在整本教材里的中枢位置

1.1 前面十七章到底为这一章准备了什么

很多人学人工智能数学基础,学到第四章矩阵求导觉得“这辈子用不上了”,学到第九章概率分布觉得“这不就是高中概率换个马甲”,结果到第十八章才发现,前面所有的章节都在为一个目标服务:让机器自己找到一组参数,把输入映射到输出。

我列个对照表你就明白了:

前面章节的核心内容在第18章中的落点
线性代数:矩阵乘法、转置、逆矩阵神经网络层与层之间的线性变换,权重矩阵的存储与更新
微积分:偏导数、链式法则、梯度反向传播的数学内核,损失函数对每个参数的偏导计算
概率论:条件概率、期望、熵交叉熵损失函数的设计,softmax输出层的概率解释
最优化:梯度下降、凸优化参数更新规则,各种优化器(SGD、Adam)的原理
数值分析:浮点误差、稳定性激活函数取值域的设计,softmax与交叉熵的数值稳定技巧

看到没有,第十八章本质上是把数学工具箱全部打开,然后以“训练神经网络”这个实际任务为线索重新组织了一遍。它不引入太多全新数学概念,而是把旧知识组合成一个能直接落地的操作流程。这也是为什么我常说,如果你只能细读一章来应付人工智能大作业或者期末,那一定是第十八章。

1.2 为什么深度学习是最能体现数学价值的场景

有人说深度学习无非是“调库调参”,这句话只对了一半。调库的前提是你得知道库里那些接口背后做了什么数学运算,否则遇到报错和性能瓶颈基本抓瞎。

神经网络训练可以抽象成一句话:给定一大堆样本数据,找一个参数向量,让某个目标函数的值尽可能小。这个描述里的三个要素,分别对应三块数学:

第一,样本数据在电脑里本质是矩阵。一张 32x32 的彩色图片就是一个 32x32x3 的张量,展开成一列向量就是 3072 维。第二,模型本身是一个带参数的复合函数,从输入到输出每一步都是矩阵乘法和非线性映射的交替。第三,目标函数(损失函数)通常写成所有样本损失的平均值,而让它最小化靠的是梯度下降——每一步沿负梯度方向更新参数。

这三个要素单独拿出来都不是第十八章的原创,但组合在一起就需要一个统一的计算框架来承载。第十八章引入的计算图(computational graph)就是这个框架,它让“从损失函数反推每个参数梯度”变成了一件有固定套路的事。你用 TensorFlow、PyTorch 这类框架训练模型时,框架自动帮你做的就是这件事,而这一章就是把你从“只会点运行按钮”变成“知道按钮背后发生了什么”的那扇门。

2. 神经网络前向传播的代数本质

2.1 每一层网络都是一次矩阵变换加非线性激活

如果不看内部细节,一个最简单的全连接神经网络长这样:

输入向量 x,第一层的权重矩阵 W1,偏置 b1,经过一个激活函数 σ,得到中间结果 a1 = σ(W1 x + b1)。第二层再在 a1 的基础上做一次类似的变换,得到输出 a2,最后把 a2 和真实标签 y 比较,算出一个损失值 L。

写成公式就是:

$$ z^{[1]} = W^{[1]} x + b^{[1]} $$

$$ a^{[1]} = \sigma(z^{[1]}) $$

$$ z^{[2]} = W^{[2]} a^{[1]} + b^{[2]} $$

$$ a^{[2]} = \sigma(z^{[2]}) $$

$$ L = \text{loss}(a^{[2]}, y) $$

这组式子看着简单,但里面全是容易懵的细节。咱们逐个拆。

第一,为什么是矩阵乘法?因为如果不用矩阵,你有 n 个输入特征、m 个隐藏神经元,就得写 m 个独立线性函数,每个函数又包含 n 个权重系数,手写下来是 m 行 n 列的系数表,这就是矩阵的来历。矩阵乘法本质上让你用一套符号同时描述所有神经元的加权求和。

第二,为什么要加偏置?因为 W x 这个线性变换必须经过原点,而现实中决策边界基本不会正好过原点,b 的存在让每一层可以自由平移决策边界。没有 b,整个模型的拟合能力会差一大截。

第三,为什么要非线性激活函数?这个很多人理解不到位。如果每一层都只做线性变换,那么无论网络叠多少层,最终效果都等价于一层线性变换。你把两个线性映射套在一起:W2(W1 x + b1) + b2 = (W2 W1)x + (W2 b1 + b2),还是一个线性函数。那还不如一开始就直接用一个矩阵 W = W2 W1 来得省事,为什么要费劲去构造多层结构?正是因为激活函数把非线性引进来了,多层才能逼近任意复杂函数。这是神经网络表达力的关键。

2.2 链式法则:神经网络反向传播的基石

学员问得最多的问题之一是:为什么要单独学链式法则?因为神经网络从输入到输出是一条层层嵌套的复合函数,损失对某一层参数的偏导,必须从损失函数开始,顺着这条链一路往回算。

我拿生活中的例子打个比方。假设你要计算“网购订单总金额”对“商品单价”的敏感程度:总金额 = 商品数 × 单价,而商品数又 = 购物车里的件数。单价变一点,会影响商品数吗?不会,但会影响总金额。这种“间接影响”在数学上就是复合函数求导,链式法则告诉你:一间接地表达这种影响,要把每一步的局部导数连乘起来。

对于三层网络的简单案例,损失对第二层某个权重 W[2]_{ij} 的偏导是:

$$ \frac{\partial L}{\partial W^{[2]}{ij}} = \frac{\partial L}{\partial a^{[2]}} \cdot \frac{\partial a^{[2]}}{\partial z^{[2]}} \cdot \frac{\partial z^{[2]}}{\partial W^{[2]}{ij}} $$

这里有三项相乘,每一项都是局部的导数。哪一项算错了,整个梯度就错了。PyTorch 自动微分帮你算了,但你自己推导时就得保持清醒:谁的导数对谁,先算哪个中间变量。

2.3 维度一致性检查:快速发现公式错误的利器

我做项目时有个习惯,任何矩阵公式写出来都要做维度核对(dimension check)。原理很简单:矩阵能相乘,必须满足左矩阵列数等于右矩阵行数;结果的行数等于左矩阵行数,列数等于右矩阵列数。

假设你有一个 3 维输入 x(形状是 3×1),第一层有 4 个神经元,那么 W1 的形状必须是 4×3,b1 是 4×1,这样 z1 = W1 x + b1 的结果才是 4×1。如果第一层输出是 4 维,第二层要输出 2 维,W2 就是 2×4。

很多同学在推导反向传播公式时写错转置,维度检查几秒就能发现问题。比如梯度更新公式里出现 W 的转置还是原矩阵,你拿维度一看:如果乘出来形状对不上,多半是转置写反了。这个习惯比任何调试工具都管用。

3. 损失函数与反向传播的核心细节

3.1 MSE 与交叉熵到底该怎么选

第十八章通常会介绍两类最常用的损失函数:均方误差(MSE)和交叉熵(Cross Entropy)。

MSE 的公式是:

$$ L_{MSE} = \frac{1}{n} \sum_{i=1}^{n} (y_i - \hat{y}_i)^2 $$

它天然适合回归问题,比如预测房价、温度、销量这类连续数值。为什么选平方而不是绝对值?因为平方函数在误差为 0 附近光滑,可导;绝对值函数在 0 点有一个尖角,导数不存在。而梯度下降需要求导,光滑的损失函数能让优化过程稳定得多。

交叉熵的典型形式是:

$$ L_{CE} = -\frac{1}{n} \sum_{i=1}^{n} \left[ y_i \log \hat{y}_i + (1 - y_i) \log (1 - \hat{y}_i) \right] $$

这是二分类的形式,多分类还要套 softmax。为什么分类任务不推荐用 MSE?因为分类模型输出的是概率,概率必须落在 0 到 1 之间,而 MSE 的梯度在预测值接近 0 或 1 时会变得非常小,导致训练极慢。交叉熵的梯度结构天然缓解了这个问题,尤其是配 softmax 输出时,梯度形式会极度简化。

3.2 反向传播四步走

完整走一遍反向传播是第十八章最重要的实操训练。以二分类单隐层网络为例,操作流程是固定的四步:

第一步,前向传播。按前面写的公式,从 x 开始一直算到损失 L,每一层的 z、a 都要保留下来,因为后面求导要用它们。

第二步,计算输出层误差。定义 δ2 = ∂L/∂z2,它表示损失对第二层线性输出 z2 的敏感度。对 softmax + 交叉熵的组合,经过数学推导,δ2 会化简成一个非常漂亮的结果:预测概率减真实标签,即 (a2 - y)。这个化简不是巧合,而是交叉熵和 softmax 相互配合的结果。

第三步,向隐藏层传播误差。利用链式法则:

$$ \delta^{[1]} = (W^{[2]})^T \delta^{[2]} \odot \sigma'(z^{[1]}) $$

这里 ⊙ 表示逐元素相乘,σ' 是激活函数的导数。这一式子的含义是:第二层的误差信号,通过 W2 的转置“传回”到第一层,再乘以激活函数在 z1 位置的斜率,修正这个信号在当前神经元上的强度。

第四步,求参数梯度并更新。所有权重和偏置的梯度都能用前面算好的中间结果表示:

$$ \frac{\partial L}{\partial W^{[2]}} = \delta^{[2]} (a^{[1]})^T $$

$$ \frac{\partial L}{\partial b^{[2]}} = \delta^{[2]} $$

$$ \frac{\partial L}{\partial W^{[1]}} = \delta^{[1]} x^T $$

$$ \frac{\partial L}{\partial b^{[1]}} = \delta^{[1]} $$

更新规则是 W = W - η * ∂L/∂W,η 是学习率。这个流程看着公式多,但大多数深度学习框架已经自动完成。你在作业里手动实现时,按这四步走,基本不会乱。

3.3 数值稳定性:一个被大多教材轻描淡写但直接决定成败的细节

第十八章如果只讲公式推导,忽略数值稳定性,那实战会让你怀疑人生。最常见的问题出现在 softmax 函数里,它要算 e 的 z 次幂。如果 z 里有个 1000,e^1000 直接溢出成无穷大,整个训练立刻崩溃。

解决办法非常朴素:在计算 softmax 之前,对每个 z 减去该批数据的最大值。因为 softmax 的分子分母同时除以 e^max,结果不变,但指数的输入就变成了 z - max,最大值是 0,其他值全是负数,绝不会溢出。这个技巧叫 max-shift,几乎所有深度学习框架的底层都内置了。你手动实现分类器时,千万别省略这一步。

同理,交叉熵和 sigmoid 组合时也要避免直接计算 log(0)。如果预测概率被算成精确的 0,log(0) 直接报错。工程上是给概率加一个极小量 ε,比如 1e-12,保证对数函数输入永远大于 0。这不是为了假装数据,而是为了让数值计算在计算机的有限精度下也能稳定推进。

4. 优化算法:从梯度下降到 Adam 的数学原理

4.1 梯度下降的几何直觉与数学表达

梯度下降的出发点很简单:你在山上雾天看不见远处,只能靠脚下感知坡度,想下山就往坡度最陡的方向迈一步。数学里,一个多元函数在某点的梯度,就是它“上升最快的方向”,那反方向就是“下降最快的方向”。

参数更新公式:

$$ \theta_{t+1} = \theta_t - \eta \nabla_{\theta} L(\theta_t) $$

这里 θ 是所有参数的集合,∇L 是损失对每个参数的偏导组成的向量,η 是学习率。第十八章通常会告诉你,学习率不能太小也不能太大:太小,训练要几万步才收敛,浪费时间;太大,参数一步越过最优点,loss 可能震荡甚至发散。实践经验往往是先用 0.01 试跑,观察 loss 曲线再调。

4.2 从 SGD 到动量:为什么要考虑历史梯度

最朴素的梯度下降每一步都用全部训练数据算梯度,数据量大时太慢。随机梯度下降(SGD)每次只用一个或一小批数据来估算梯度,速度快但是方向动荡。动量方法就是解决这种动荡的。

动量的更新公式里有历史梯度信息的累积项:

$$ v_t = \beta v_{t-1} + \eta \nabla L(\theta_t) $$

$$ \theta_{t+1} = \theta_t - v_t $$

物理上的类比是“惯性”:球从山坡滚下来不会因为遇到一个突然的方向变化就立刻转向,而是带着原来的势头冲过去。β 通常取 0.9,表示当前方向 90% 参考历史动量,只有 10% 来自最新梯度。这样训练过程的 loss 曲线会平稳很多,不容易在狭长峡谷区域来回震荡。

4.3 Adam:一阶矩与二阶矩的配合

Adam 是现在项目里最常用的优化器,它把动量思想和自适应学习率结合到一起。Adam 维护两个变量:一阶矩估计 m(类似动量,反映平均梯度方向)和二阶矩估计 v(反映梯度平方的平均,用它来自适应缩放每个参数的学习率)。

更新规则大致是:

$$ m_t = \beta_1 m_{t-1} + (1 - \beta_1) g_t $$

$$ v_t = \beta_2 v_{t-1} + (1 - \beta_2) g_t^2 $$

$$ \hat{m}_t = \frac{m_t}{1 - \beta_1^t}, \quad \hat{v}_t = \frac{v_t}{1 - \beta_2^t} $$

$$ \theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{\hat{v}_t} + \epsilon} \hat{m}_t $$

为什么要做偏差修正(除以 1-β^t)?因为初始几步 m_t、v_t 都从 0 开始,估计值偏小,直接使用会让前几步更新幅度失真。偏差修正把早期估计拉回正常尺度。熟悉正则化或移动平均的同学会发现,这本质上是带权重的指数移动平均。

Adam 的默认值几乎可以适配大多数任务:β1 = 0.9,β2 = 0.999,η = 0.001。这也是框架里 torch.optim.Adam 的开箱参数。它不见得一定比精心调过的 SGD 好,但胜在鲁棒,失败概率低。你做大作业时直接上 Adam,省去大量调学习率的痛苦。

4.4 学习率、Batch Size 与梯度噪声的实际关系

第十八章最后如果讲优化,一定会提到一个工程概念:batch size。你用全部数据算梯度叫全批梯度下降,用一小批叫 mini-batch。batch size 影响内存、训练速度和收敛稳定性。

batch size 越大,梯度估计越准,但每步更新成本越高;越小,梯度噪声越大,但相当于给优化过程引入了随机扰动,有时反而能帮助跳出局部极小值。经验法则是先用 32 或 64 起步。这个数字通常不讲究什么神奇原理,更多是性能和精度的折中。

还有一个很实用但文档里不常写的规律:如果增大 batch size,同时把学习率按比例放大,能在不损失收敛效果的情况下显著加速训练。这背后的数学原因是更大批次给出了更准确的梯度方向,足以支持更大步长。这个技巧叫学习率缩放,在分布式训练里尤其常见。

5. 第18章常见问题与实战避坑实录

5.1 梯度消失与梯度爆炸的数学根源

训练深层网络时最经典的报警就是 loss 不降,甚至直接 nan。原因大概率出在两个地方:梯度消失和梯度爆炸。

梯度消失的根源是连乘效应。反向传播要一路乘上每一层激活函数的导数。如果激活函数是 sigmoid,它的导数最大也只有 0.25,多层连乘后就趋近于 0。一个 10 层的网络,0.25 的 10 次方已经小到让前面的层几乎收不到更新信号。

梯度爆炸则是权重矩阵的特征值大于 1 时,连乘后梯度指数级增长。解决办法常见的几种:换用 ReLU 这类导数固定为 0 或 1 的激活函数;做权重初始化时控制方差别太大;加上梯度裁剪(gradient clipping)——当梯度的整体长度超过某个阈值时按比例压缩。深层网络配合残差连接也能大幅缓解梯度消失,因为残差结构相当于给梯度开了一条“高速公路”。

5.2 L1 和 L2 正则化:从导数角度理解为什么它们能防过拟合

正则化是损失函数后面加一个惩罚项,让模型权重不要太大。L2 惩罚是加 λ 乘以权重平方和,L1 是加 λ 乘以绝对值之和。为什么这会有效?你从梯度角度想就特别清楚。

L2 的导数就是 2λw,因此每一步更新除了原来的梯度,还要额外把权重向 0 方向拉一点,等于每个权重被“压缩”一圈。L1 的导数是 λ 乘以 w 的符号,所以它对每个参数施加的是一个恒定大小的推力,让权重无论大小都会被往 0 推。这也解释了为什么 L1 更容易产生稀疏解,很多参数被推成精确 0,相当于自动做特征选择。L2 只会让参数变小,不会变成 0。

在作业里加正则化很容易,直接在 loss 之后加一行:loss += lambda * (W.pow(2).sum())。lambda 一般从 0.0001 到 0.01 之间调。太小没效果,太大模型会欠拟合——loss 只顾着压权重,忽略了数据本身。

5.3 期末、大作业怎么利用第十八章知识拿高分

期末和大作业最常见的项目是从零实现一个手写数字识别或简单的分类器,不借助高级框架,只用 NumPy。这类任务想拿高分,拼的不是代码多花哨,而是你是否把数学环节做到了位。

第一,自己做一次完整的前向和反向传播推导,建议纸笔写一遍,再把公式翻译成代码。很多网上的博客直接给了代码,你复制跑通也行,但一旦面试问你某个维度为什么是转置,你就暴露了。

第二,数据预处理至少要做标准化。原始像素值在 0 到 255 之间,直接送进网络会让初始梯度量级很大。减均值除以标准差,数值上更平滑,训练也更稳。

第三,训练时保存一份 loss 曲线。这一点特别重要,因为老师看作业时会根据 loss 曲线判断你是真的理解了过程,还是仅仅“调包运行成功”。一个正常收敛的曲线是前几百步快速下降,后面平缓。如果曲线乱跳,说明学习率太大,或者数据没有洗牌。

第四,多跑几组对比实验,比如不同学习率、不同隐藏层宽度、加不加正则化。用表格记录最终准确率。这种对照实验是区分高分作业和及格作业的重要标志。

5.4 第十八章高频公式速查

这里把最容易被出成填空或简答题的公式集中列一下,考试前半小时过一遍很管用:

  • 单层前向传播:z = Wx + b,a = σ(z)
  • 均方误差:L = (1/n) Σ(y - a)^2
  • 交叉熵:L = -(1/n) Σ[y log a + (1-y) log(1-a)]
  • sigmoid 导数:σ'(z) = σ(z)(1 - σ(z))
  • tanh 导数:tanh'(z) = 1 - tanh^2(z)
  • ReLU 导数:z > 0 时为 1,z ≤ 0 时为 0
  • softmax 交叉熵输出层误差:δ = a - y
  • 隐藏层误差:δ^[l] = (W^[l+1])^T δ^[l+1] ⊙ σ'(z^[l])
  • 权重梯度:∂L/∂W^[l] = δ^[l] (a^[l-1])^T
  • 偏置梯度:∂L/∂b^[l] = δ^[l]
  • L2 正则化损失:L_total = L_data + λ Σ W^2
  • L1 正则化损失:L_total = L_data + λ Σ |W|

第十八章的内容拆开看,每一块都没有超出前面章节的难度,但组合起来就是一个完整可运行的智能系统雏形。我个人带过不少从零起步的学员,他们的一个共同变化发生在真正自己动手推导一次反向传播后:在此之前,神经网络对他们是一个黑盒;在此之后,见到模型报错或者效果差,第一反应不是去网上盲目改参数,而是从数学上判断问题大概出在哪一层、是梯度的问题还是损失函数的问题。这种判断力,就是第十八章存在的意义。如果你正在学这一章,别急着跳代码,先用纸笔把三层网络的前向和反向推导完整写一遍,再打开编辑器实现,你会发现后面所有内容都顺了。

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

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

立即咨询