神经网络是怎么"学"的:梯度下降与反向传播
AI 系列第 3 篇。上一篇我们看了 AI 圈三十年的派系内战。 这一篇钻进联结主义的引擎舱——神经网络到底是怎么"学"东西的。
0. 还上一篇的"债"
第 1 篇结尾我留了一个钩子:
"1986 年那篇被冷落了 25 年的反向传播论文,为什么 2012 年突然就改变了世界?"
今天就还这个债。但要回答这个问题,得先把"神经网络怎么学"这件事讲清楚。
我们不上数学。我们用一个生活化的比喻——在高维大山上摸黑下山——把所有关键概念串起来。读完你会得到一种"哦原来就这么回事"的感觉。
1. 先把"学习"这件事拆掉
抛开所有术语,机器"学"东西的过程就三步:
$ machine_learn
step 1: 猜一个答案
step 2: 看猜得有多差
step 3: 朝"少差一点"的方向调整自己
loop until 满意
人类小孩学走路也是这套流程:迈步 → 摔倒 → 把腿放对一点 → 再迈步。区别是,人类用感觉、用本能;机器需要把"猜得多差"和"朝哪个方向调"都变成可以计算的数字。
这就是机器学习要解决的两个核心问题:
- 怎么把"差"变成一个数? —— 损失函数(loss function)
- 怎么根据这个数,反推应该怎么调? —— 梯度(gradient)+ 梯度下降(gradient descent)
理解了这两件事,神经网络是怎么训出来的,你就懂了 90%。
2. 损失函数:把"猜得有多差"变成一个数
假设我们要教一个模型识别手写数字。给它看一张 "7",它输出 "1"。这就是猜错了——但错多少?
直觉答案:差了 6。但这种"减一减"的算法对图像、文字都不适用。我们需要一个更通用的方式来量化错误。
一个例子:均方误差(MSE)
最简单的损失函数叫均方误差:
真实答案: y = 7
模型预测: ŷ = 1
误差 = (y - ŷ)² = (7 - 1)² = 36
平方一下,是为了让"差很多"惩罚得比"差一点"重。差 1 就罚 1,差 6 就罚 36,差 10 就罚 100。这正好符合直觉:错得越离谱、越要狠狠调。
真实场景:交叉熵
但识别数字这种"分类问题",更常用的是交叉熵(cross-entropy)。它的核心思想是:
模型输出的应该是"我有多确信这是 7"。 而不是直接输出一个数字 7。
也就是说,模型对每个可能的数字(0–9)都给一个概率:
模型说: 0:1% 1:5% 2:2% ... 7:60% ... 9:3%
真实答案是 7。
loss = -log(0.60) ≈ 0.51
如果模型对 7 的把握是 99%,loss 就是 -log(0.99) ≈ 0.01(几乎没损失)。
如果只有 10%,loss 就是 -log(0.10) ≈ 2.30(损失大)。
这样我们就把"猜得有多差"压缩成了一个标量数字。所有的训练,都是在追求让这个数字尽可能小。
一句你可以拿去吹的话: 训练神经网络的本质,就是在一个超高维空间里,找到能让 loss 函数最小化的那组参数。
3. 梯度:朝哪个方向调,loss 会变小?
OK 现在我们知道 loss 是 0.51。然后呢?怎么调模型?
模型的"参数"是什么?就是神经网络里那些数。一个 GPT-3 有 1750 亿个数。每改一个,loss 都会变一点。问题是:
我们应该把这 1750 亿个数里的哪一个、朝哪个方向、调多少?
这就是梯度要解决的问题。
一维的直觉版
先假设模型只有一个参数 w,loss 是 w 的函数 L(w)。画出来长这样:
L(w)
│ ╱╲
│ ╱ ╲
│ ╱ ╲
│___╱ ╲___
│ ╲___ ╱
│ ╲___╱
│ 当前在这里
└────────────────────────── w
你现在站在某个点上。梯度告诉你的就是:当前点的斜率是多少。
- 斜率为正 → 往左走 loss 会变小
- 斜率为负 → 往右走 loss 会变小
- 斜率接近 0 → 你可能站在山底(也可能是山顶或者鞍点,后面讲)
高维的实际版
现实里参数不止一个。GPT-3 有 1750 亿个参数。这时候 loss 是一个 1750 亿维空间里的"曲面",无法画出来。
但直觉是一样的:每个参数都有自己的偏导数(partial derivative),加起来就是这个 1750 亿维空间里的"下坡方向"。
整个 1750 亿维的偏导数向量,就叫做梯度。
一句你可以拿去吹的话: 梯度就是损失函数曲面在每个参数上的"坡度"。它告诉你:每个参数往哪边动一点,能让 loss 下降最快。
4. 梯度下降:在山上摸黑下山
现在到了关键比喻。
想象你被空投到一座非常陡、非常黑的山上。任务是走到山谷底。但是——
- 你没有地图。
- 你看不见周围(漆黑)。
- 你只能感觉脚下当前位置的坡度。
你会怎么做?
最朴素的策略是:用脚感受一下哪个方向是下坡,迈一小步,然后再感一次,再迈一步。一直循环到坡度变成 0(站在平地了)。
这就是梯度下降(gradient descent)。
$ gradient_descent --loss_fn=L --params=w
while True:
g = compute_gradient(L, w) # 摸一下脚下的坡
w = w - learning_rate * g # 沿下坡方向走一小步
if abs(g) < threshold:
break # 到底了
那个 learning_rate(学习率)是步长。步太大会越过山谷直接撞到对面山坡,步太小会走一辈子也走不到底。这个超参数是炼丹里最重要的一个。
三个"摸黑下山"的真实困境
困境 1:局部最小值(local minimum)
你以为到底了,其实只是个小坑:
╱╲ ╱╲
╱ ╲ 你 ╱ ╲
╱ ╲ ▼ ╱ ╲
╱ ╲___╱ ╲___ ← 真正的山底
在二维空间这是个大问题。但在 1750 亿维空间,局部最小值很少见——更常见的是鞍点(saddle point)。
困境 2:鞍点(saddle point)
在某些方向是上坡,在另一些方向是下坡:
↑ 这个方向上坡
─── ─── ─── ─── ─── 你
↓ 这个方向下坡
鞍点在高维空间里到处都是。好消息是,现代优化器(Adam、AdamW)能比较快地逃出鞍点。
困境 3:梯度消失 / 爆炸
如果网络太深,反向传播时梯度要经过很多层。如果每层都让梯度缩小 0.5 倍,10 层之后就是 0.001 倍——梯度几乎为 0,没法学了。这就是梯度消失(vanishing gradient)。
反过来如果每层放大 2 倍,10 层就是 1024 倍——梯度爆炸(exploding gradient),参数瞬间炸到 NaN。
1986 → 2012 这 25 年深度学习上不去,很大程度上就死在这两件事上。 直到 ReLU、batch normalization、ResNet 出来才解决。
5. 反向传播:怎么高效地算梯度?
到这里我们假设"梯度是能算出来的"。但 1750 亿个参数,每个都要算一下偏导数——怎么算才不慢死?
这就是 1986 那篇论文的贡献——反向传播(backpropagation)。
直觉版:连锁责任追究
想象你是个 CEO,公司年底亏了 1 亿(这就是 loss)。你想知道:
- 销售部该负多少责任?
- 营销部该负多少责任?
- 产品部该负多少责任?
你不会直接问销售部:"你为这 1 亿负多少责任?" 那太抽象。你会顺着责任链反推:
1 亿 loss
▲
│ 来自销售目标没达成
▼
销售部 = -7000 万责任
▲
│ 销售目标 = 营销线索 × 转化率
▼
营销部 = -4000 万(线索少了)
产品部 = -3000 万(功能不行所以转化低)
反向地,从"最终损失"一层一层往回链式法则求导,把责任分配到每个部门、每个员工。
这正是反向传播在做的事。Loss 从输出层算起,往前一层一层算每一层的梯度。每一层的梯度怎么算?用链式法则(chain rule)——就是高中数学里的:
d(f(g(x)))/dx = f'(g(x)) * g'(x)
神经网络是一堆函数嵌套:output = layer_N(... layer_2(layer_1(input)) ...)。链式法则从最外层 layer_N 开始往里推,每一层算一次乘法,最终把每个参数的梯度算出来。
整个过程只需要走两遍网络:
forward pass: input → ... → loss (算出损失)
backward pass: loss → ... → input (算出梯度)
这就是为什么它叫"反向传播"——梯度从输出层往输入层反向传。
为什么这个算法这么重要?
因为它把算梯度的复杂度从"指数级"砍到了"线性级"。
没有反向传播之前,每加一层网络,算梯度的计算量爆炸式增长。有了反向传播,你的网络深 100 倍,算梯度也只贵 100 倍(线性增长)——这就让训练深的网络变得可行了。
一句你可以拿去吹的话: 反向传播不是一个"学习算法",它是一个"求导加速器"。它让训练 100 层的网络从理论上的"几年"变成了实际上的"几小时"。
6. 一个手写数字识别的全流程
把上面所有概念串起来。我们要训一个能识别 0–9 手写数字的模型:
┌─────────────────────────────────────────────────────────┐
│ Step 1: 随机初始化 1750 亿个参数(瞎猜) │
│ │
│ Step 2: 拿一张图(一个 "7"),forward 走一遍 │
│ → 模型输出 "我有 10% 把握是 7" │
│ │
│ Step 3: 算 loss = -log(0.10) = 2.30 │
│ │
│ Step 4: backward 走一遍 │
│ → 算出 1750 亿个参数各自的梯度 │
│ │
│ Step 5: 每个参数 = 当前值 - 0.001 × 它的梯度 │
│ (0.001 是学习率) │
│ │
│ Step 6: 回到 Step 2,换一张图。循环 1000 万次。 │
└─────────────────────────────────────────────────────────┘
经过 1000 万次循环,模型对 "7" 的把握从 10% 变成 99.5%。
这就是"训练"。 整个深度学习圈每天都在做这件事。从 AlexNet 到 GPT-5,本质都是这个循环——只是网络变大了、数据变多了、技巧变巧了。
7. 回答最初的债:1986 vs 2012
现在可以回头看那个钩子了:为什么反向传播 1986 年发表,2012 年才改变世界?
答案不在算法本身。算法 1986 年就是对的。瓶颈在三件事都没到位:
1. 算力不够
1986 年:一台 SUN 工作站每秒能做几百万次浮点运算。 2012 年:一块 NVIDIA GTX 580 GPU 每秒能做 1.5 万亿次浮点运算。
6 个数量级。AlexNet 在两块 GPU 上训了 6 天,1986 年这种规模的训练要花几十年。
2. 数据不够
1986 年:手写数字数据集 MNIST 还要等 1998 年才有,且只有 6 万张图。 2012 年:ImageNet 1400 万张图、22000 个类别。
神经网络是"数据饕餮"。没数据它就发挥不出来。
3. 工程技巧不够
反向传播光是"对"还不够,要让它稳——
- 梯度消失怎么办?2010 年 ReLU 激活函数登场,缓解了大半。
- 训练不稳定怎么办?2015 年 batch normalization。
- 网络太深训不动怎么办?2015 年 ResNet 的残差连接。
这些工程 hack 都是 2010 年代才出现的。没有它们,反向传播在深网络上根本跑不通。
还有一件容易被忽视的事:信仰
1986 → 2006 这 20 年,整个 AI 圈在"嘲笑神经网络"。Hinton 那群人坚持下来了——不是因为他们看到了未来,是因为他们赌对了。
这是科学史上一个常见的剧情:一个算法对,时机不对,等 25 年。
一句你可以拿去吹的话: AI 的历史不是"算法的胜利",是"算法 × 算力 × 数据 × 信仰"的乘积。任何一项为 0,整个乘积就是 0。
8. 给你的小作业
- 用"摸黑下山"的比喻,向一个不懂技术的朋友解释什么叫梯度下降。
- 学习率太大、太小、刚好,分别会出现什么现象?画三张示意图。
- 去 playground.tensorflow.org 玩 10 分钟。试试不同的网络深度、激活函数、学习率,感受一下"调参炼丹"是什么意思。
下一篇钩子:神经网络会学了。但要它"看见"——把一张图理解成"猫"、"狗"、"街景"——还需要专门的架构。 下一篇我们讲从 1958 年 Frank Rosenblatt 的感知机,到 1998 年 LeCun 的 LeNet,再到 2012 年 AlexNet 的地震—— 神经网络是怎么从"识别单个像素",进化成"看懂整张图"的。 顺便回答:今天的 ViT(Vision Transformer)打败 CNN 了吗? 答案比你想的复杂。