深 度 学 习 教 材

从零开始

第二版 · 先建骨架,再谈架构
本版目标:建立一套完整、能自洽的神经网络知识体系

本教材的五条讲法

目 录

按"先能跑通一个完整闭环,再逐个加深"的顺序编排。第 0 章是工具间,随时回来查。
第 零 章

数学工具箱

这一章是工具间,不是必修课。全书用到的数学就这 6 样,先在这里一次讲清,后面用到时我只说"这是第 0 章那个东西",不再打断正文。你可以先跳到正文,卡住了再回来查。
先给一颗定心丸:深度学习听起来数学味很重,但你真正常用的只有三样——加法乘法斜率相似度。其余都是这三样的组合与记法。你学过微积分(哪怕忘了),足够看懂这本书。

0.1函数:一台有进有出的机器

函数就是一个规定:"给我一个数,我还你一个数。"

f(x) = 2x + 1   →   给 3,还  7;给 5,还  11

神经网络本质上就是一个函数,只不过式子特别长、参数特别多:输入一张图片,输出"这是猫"的概率。

0.2导数 = 斜率 = "你动一点,它动多少"

忘掉公式,只记一句话:导数就是放大倍数——输入动 1,输出动几。

f(x) = x2 ,在 x = 3 处,导数 = 6   意思是:x 从 3 变到 3.001,f 大约变 6 × 0.001 = 0.006

不信手算一遍(这就是导数的定义,没有别的):

f(3.001) − f(3)3.001 − 3 = 9.006001 − 90.001 = 6.001 ≈ 6

越取越近,就越接近 6,这就是 f(x) = x² 的导数是 2x(x=3 时 = 6)的来历。导数回答的是"灵敏度":这个函数在这儿有多陡、往哪边陡。

x f(x) f(x) = x² 切线(斜率 = 6) x = 3 f(3) = 9 0
图 0-1 · 导数就是曲线在某一点的"陡峭程度"。红色切线的斜率是 6,意思是:x 每增加 1,f 大约增加 6。

0.3偏导数:把其它旋钮按住,只转一个 (重点)

一句话:偏导数就是"多变量函数的导数",多出来的动作只有一个——先声明我动哪一个,其它全当常数。

类 比 你在调一杯咖啡,甜度受三个旋钮影响:糖、蜂蜜、牛奶。你想知道"多加一勺糖,甜度会涨多少"。怎么办?把蜂蜜和牛奶按住不动,只加糖,看甜度计跳了多少。这个"只动一个旋钮"的操作,就是求偏导。

用数字说清楚。设甜度 = 3 × 糖 + 2 × 蜂蜜(糖、蜂蜜都是勺数):

问题操作答案
多加 1 勺糖,甜度涨多少?蜂蜜当常数不动+3
多加 1 勺蜂蜜,甜度涨多少?糖当常数不动+2
两个都多加 1 勺呢?两个效果相加+5

写成式子就是这样(∂ 这个符号就读作"只动这一个"):

∂甜度∂糖 = 3      ∂甜度∂蜂蜜 = 2

它就是导数,一点都没变难。唯一的区别:单变量函数只有一个旋钮,不用声明;多变量函数有好几个旋钮,所以要先声明"我拧的是哪个",记号上用 ∂ 代替 d 来提醒你"还有别的旋钮被按住了"。

甜度 = 3×糖 + 2×蜂蜜 结果(一个数字) 蜂蜜 ×3(正在拧) ×2(按住不动) ∂甜度/∂糖 = 3 两个旋钮 → 两条偏导数 → 合起来就是"梯度" 梯度 = (3,2),读作"往糖的方向走最划算"
图 0-2 · 偏导数就是"按住其它旋钮,只拧一个"。虚线表示被按住的那个。
为什么神经网络非它不可?一个网络有几千到几十亿个参数(旋钮),而损失(错了多少)是一个数字。我们想知道每一个参数各自该往哪调——这正是"对每个参数各求一次偏导数"。
把所有偏导数排成一列,这个清单就叫梯度梯度 = 每个旋钮各自的灵敏度清单。第 5 章你会亲手算一遍。
常见误解澄清:偏导数里的"偏"不是"不完整、近似"的意思,而是"其它变量固定"的意思。它的计算完全精确,跟普通导数一样,只是多了一句声明。

0.4链式法则:接力赛的变化率相乘

如果 y = 3w,而 L = y²,问"w 动 1,L 动几"?把两段的变化率乘起来就行:

∂L∂w = ∂L∂y × ∂y∂w = 2y × 3 = 18w

验证:L = (3w)² = 9w²,直接对 w 求导 = 18w。完全一致。这条规则是反向传播的全部数学基础(第 5 章)。

0.5向量与点积:相似度

向量就是把几个数字排成一列,比如一个学生的 [身高, 体重] = [175, 65]。

点积是"对应位置相乘再相加",它衡量两个向量有多像

[1, 2, 3] · [1, 2, 3] = 1×1 + 2×2 + 3×3 = 14(方向完全一致,值最大)
[1, 2, 3] · [3, 2, 1] = 3 + 4 + 3 = 10(有点像,但没那么像)

后面讲注意力时会用到它,现在只要记住:点积大 = 两个向量方向接近 = 相似。

0.6Σ(求和符号):10 秒讲完

i=13 wixi   =   w₁x₁ + w₂x₂ + w₃x₃

它只是"把这一串加起来"的简写。神经元的"加权求和"就是它。

0.7数学止损线

全书会用到在哪一章
函数(有进有出的机器)第 1 章
导数 = 斜率 = 放大倍数第 4 章
偏导数 = 只拧一个旋钮第 5 章
链式法则 = 变化率相乘第 5 章
向量点积 = 相似度第 9 章及以后
Σ 求和符号第 1 章
以下这些,你可以放心完全不懂,不影响看懂这本书: 矩阵形式求导 · 凸优化与收敛性证明 · 信息论(熵、KL 散度推导) · 泰勒展开 · 雅可比矩阵 / 海森矩阵 · 概率图模型 · 变分推断
本章小结
  • 导数 = 放大倍数 = 你动一点,它动多少。
  • 偏导数 = 把其它旋钮按住,只拧一个。它就是导数,只是多了一句"我动哪个"的声明。
  • 梯度 = 所有参数的偏导数清单——这是全书最核心的一个词。
  • 链式法则 = 接力时各段变化率相乘,是反向传播的地基。
  • 点积 = 相似度,现在只需留个印象。
第 一 章

神经元:一次加权打分

一句话:神经元做的事就是把多个因素按重要程度打个总分,再看这个分够不够高。全部数学只有加法和乘法。

1.1一个生活场景:周末要不要去爬山

你要决定去不去,脑子里其实在给三个因素打分:

因素今天的情况你内心的看重程度(权重)
天气好不好0.9(很好)+0.6(我很在意)
朋友去不去1.0(去)+0.3(有点在意)
路远不远0.7(有点远)−0.2(越远越不想去)

打分过程(对应位置相乘再相加,就是第 0 章的 Σ):

z = 0.6 × 0.9  +  0.3 × 1.0  +  (−0.2) × 0.7 = 0.54 + 0.30 − 0.14 = 0.70

再定个门槛:总分 > 0.5 就去。0.70 > 0.5,去!

恭喜,你刚刚就是一个人肉神经元。整个过程只有两步:①加权求和(打分)②过门槛(判断)。神经网络里成千上万个神经元,干的都是这件事,只是没人手工设定这些"看重程度"——它们是自己学出来的(第 4–5 章)。

1.2把上面的过程翻译成术语

人话术语记号本例的值
今天的情况输入x₁, x₂, x0.9, 1.0, 0.7
你的看重程度权重w₁, w₂, w0.6, 0.3, −0.2
基础倾向(门槛移项)偏置b−0.5
打分加权求和z = Σw·x + b0.70 − 0.5 = 0.20
过门槛激活函数a = f(z)z > 0 → "去"
最终决定输出a

关于偏置 b,多说两句(这是很多人卡住的地方)。上面我把"总分 > 0.5"改成"总分 − 0.5 > 0",于是门槛 −0.5 就并进了打分式子里,成了 b = −0.5。好处是式子统一成 z = Σwx + b,处理起来方便。

那偏置到底有什么用?没有它,你的判断线被强制穿过原点。比如天气 = 0、朋友 = 0 时,没有偏置的话总分必然是 0,判断必然是"不去"——可现实里你可能就是"今天心情好,随便走走也行"。偏置就是给神经元一个与输入无关的基础倾向。

x₁ 天气 0.9 x₂ 朋友 1.0 x₃ 距离 0.7 w₁ = 0.6 w₂ = 0.3 w₃ = −0.2 加权求和 Σwx + b z = 0.20 激活 f(z) 过门槛 a = 去 输入 → 打分 → 判断:这就是一个神经元的全部
图 1-1 · 一个神经元的结构。中间的"加权求和"是唯一的算术,右侧的"激活"是下一章的主角。

1.3权重到底"学"的是什么

在上面的例子里,权重是我替你写的。真实网络里,这些数字一开始是随机的乱猜,然后通过看成千上万个例子,一点点调整到合适的值。

所以"学习"这个词的实际含义非常朴素:就是给几千个权重找到一组合适的数字。第 4–5 章讲怎么找。

1.4一个神经元能干的事很有限

单个神经元只能画一条"分界线":把打分高于门槛的放一边,低于的放另一边。这类问题叫线性可分

但很多问题不是一条直线能分开的——比如"异或":两个条件相同时为是,不同时为否。这种得画两条线才行,一个神经元做不到。这就是需要"多层"的原因,也是下一章的起点。

本章小结
  • 神经元 = 加权求和(打分)+ 激活(过门槛),只用加法和乘法。
  • 权重 = 每个因素的重要程度(可正可负);偏置 = 与输入无关的基础倾向,让判断线不被强制过原点。
  • "学习"就是给所有权重找到一组合适的数字。
  • 单个神经元只能画一条分界线,复杂问题需要多个、多层——第 2、3 章解决。
第 二 章

激活函数:为什么必须"弯一下"

一句话:如果神经元只做加权求和(加分),那再多层也等于一层;激活函数的作用就是把直线掰弯,让"层数"真正产生价值。ReLU 是最简单的一种掰法。

2.1先看一场灾难:没有激活函数,100 层 = 1 层

假设神经元不做任何"过门槛",只做加权求和。来看两层会发生什么:

第一层: y = w₁x + b₁
第二层: z = w₂y + b₂ = w₂(w₁x + b₁) + b₂ = (w₂w₁)x + (w₂b₁ + b₂)

把 (w₂w₁) 看成一个新数 W,把 (w₂b₁ + b₂) 看成新数 B,那么:

z = W·x + B   ——   还是"一次函数"这个形式,跟一层一模一样

再叠第三层、第四层……结果永远是 Wx + B 这个形式。线性的东西叠多少层,都还是线性的。你辛辛苦苦搭 100 层,等价于 1 层。深度完全白费。

x y 第 1 层:y = w₁x + b₁ 第 100 层:还是一条直线 直线叠直线 = 直线。100 层和 1 层能表达的东西完全一样
图 2-1 · 不加激活函数的灾难。两条线参数不同,但形状一样都是直线——表达能力没有任何增加。

2.2激活函数只干一件事:把直线掰弯

既然"叠直线还是直线",那就必须在每层之间插入一个能改变形状的东西。这个东西就是激活函数。它的全部职责只有一条:引入非线性(把直线掰弯)。

类 比 想象一根直铁丝。你可以把 100 根直铁丝首尾相接,接完还是一根直铁丝。但只要允许你在每个接口处折一下,你就能用它拼出任意形状——一个圆、一个波浪、一只鸟的轮廓。
激活函数就是那个"折"。层数越多 = 折点越多 = 能拼出的形状越复杂。

2.3ReLU:最简单的一种"折"

ReLU 的定义简单到不像话:

ReLU(x) = max(0, x)   →   负数归零,正数原样通过

举例:ReLU(2.5) = 2.5;ReLU(−1.3) = 0;ReLU(0) = 0。

x ReLU(x) 拐点 x = 0 负数全部拍平到 0 正数原样通过(斜率 1) 一条在 0 处折起的折线——世界最简单的"非线性"
图 2-2 · ReLU 的图像。左边被拍平,右边保持 45°。就这一个"折",足以救活整个深度网络。

2.4关键问题:这么简单的东西,凭什么够用?

这是你最该搞懂的一段。答案分两步:

第一步:一个 ReLU 神经元 = 一条折线。因为 max(0, wx + b) 在 wx + b = 0 处拐弯,图像就是一条折线。移动 b 可以左右挪动这个拐点,改变 w 可以改变折线的倾斜程度

第二步:多个折线相加 = 任意形状。把若干条折线加起来,就能拼出各种起伏。下面用真实数字拼一次,你看完就彻底明白了。

动手:用 3 个 ReLU 神经元拼出一个"驼峰"

取三个 ReLU 神经元,各自在不同的位置拐弯,然后按权重 1、−2、1 加起来:

f(x) = 1 × ReLU(x)  −  2 × ReLU(x − 1)  +  1 × ReLU(x − 2)

逐点手算(每一项都很简单,负数就取 0):

xReLU(x)−2·ReLU(x−1)ReLU(x−2)合计 f(x)
−10000
00000
0.50.5000.5
1.01.0001.0 ← 峰顶
1.51.5−1.000.5
2.02.0−2.000
3.03.0−4.01.00

看最后那一列:0 → 0 → 0.5 → 1.0 → 0.5 → 0 → 0,一个标准的三角形驼峰!而它是由 3 个"只会折一下"的神经元拼出来的。

0 1 2 0 1 ReLU(x) −2·ReLU(x−1) ReLU(x−2) 合计 = 驼峰 峰顶 (1, 1) 三条各自只会"折一下"的线,加起来就是一个有峰有谷的形状 这就是"万能逼近"的直觉版:折点足够多,就能逼近任意曲线
图 2-3 · 三个 ReLU 拼出驼峰。这正是"两层网络"的标准结构:隐藏层 3 个神经元各自在不同位置拐弯,输出层把它们按权重加起来。
把这件事说透:图 2-3 里的三条虚线,就是隐藏层 3 个神经元(各自的偏置 b 决定拐点位置:0、1、2);那条蓝色实线,就是输出层按权重 (1, −2, 1) 把它们加起来
神经元越多 → 折点越多 → 能拼的形状越复杂。"深"和"宽"之所以有价值,秘密全在这。

2.5ReLU 的导数:只有两个值

求导(第 0 章的"斜率")之后你会发现,ReLU 的导数简单得离谱:

区间ReLU(x)导数(斜率)含义
x > 0x1信号原样通过,梯度也是 ×1 传回去
x < 000这个神经元"关着",不参与
x = 00无所谓实践中取 0 或 1 都行,几乎不会精确命中

为什么这个 1 特别珍贵?第 5 章你会看到,反向传播时梯度每穿过一层都要乘一次激活函数的导数。ReLU 的导数是 1——乘 1 等于没乘,梯度原封不动地穿过去。这就是"深度网络能训得动"的关键之一(第 6 章详讲梯度消失)。

2.6为什么叫"激活"

因为它的行为像一个开关:输入为正,这个神经元被"激活",有输出;输入为负,它"熄灭",输出 0,等于这一轮不参与计算。所以一个网络在处理不同输入时,参与的神经元组合是不同的——这让它能对不同情况做出不同反应。

ReLU 的一个副作用(知道即可,不用深究):如果一个神经元的输入永远是负数,它就永远输出 0、梯度永远是 0,再也醒不过来,这叫"死神经元"。常见处理是 Leaky ReLU(负数时给一个很小的斜率,如 0.01x),见下节表格。

2.7其他激活函数:只留一句话,不展开

按本教材的原则——平行概念只讲最容易理解的一种。你已经吃透了 ReLU,其余的只需要知道"它们只是换了一种弯法":

名称一句话为什么现在不主讲
ReLU负数归零,正数照过(本书主讲这一种
sigmoid把任何数字压进 0~1 区间,曲线是平滑的 S 形导数最大只有 0.25,是梯度消失的元凶(第 6 章会详细算这笔账)
tanh把数字压进 −1~1 区间,也是 S 形同样有饱和区,问题与 sigmoid 同类
Leaky ReLU负数不归零,给个很小的斜率(0.01x)只为治"死神经元",是 ReLU 的小修小补
GELUReLU 的平滑版,按概率决定通过多少现代大模型常用,但理解门槛高,知道名字即可
记住这张表的一句话总结:激活函数家族里,大家干的都是同一件事——把直线掰弯。区别只是"弯得平不平滑、弯完之后范围多大、导数好不好算"。搞懂 ReLU,其余全是变体。
本章小结
  • 不加激活函数,多少层都等于一层——因为"线性叠线性还是线性"(z = Wx + B)。
  • 激活函数的唯一职责:把直线掰弯(引入非线性),让层数真正产生表达能力。
  • ReLU(x) = max(0, x),一条在 0 处折起的折线。它是"最简单的一次弯折"。
  • 够用的原因:多个折线相加能拼出任意形状——3 个 ReLU 就拼出了一个完整的驼峰(0→1→0)。
  • ReLU 的导数只有 0 和 1,正数区乘 1 相当于梯度原样穿过,这是深层网络能训练的前提之一。
  • sigmoid / tanh / Leaky ReLU / GELU 都是"换一种弯法",本书不展开
第 三 章

从神经元到网络:层、深、参数

一句话:一层 = 一群并列的神经元(各看各的重点);深 = 一层接一层(后一层拿前一层的结论当输入)。"参数量"就是这些连接上所有待定数字的总数。

3.1一层 = 一群并列的神经元

第 1 章只有一个神经元,只能画一条分界线。现在把它们并列摆放,看同一批输入,但各自的权重不同——于是每个神经元关注的东西不一样:

神经元它的权重(天气 / 朋友 / 距离)它在关注什么
第 1 个0.6,0.3,−0.2综合派:整体值不值得去
第 2 个0.9,−0.1,0.1天气派:几乎只看天气
第 3 个−0.1,0.8,−0.5社交派:主要看朋友去不去、远不远

这一层的输出不再是一个数字,而是三个数字——可以理解为"这份输入在三个不同角度上的评分"。层把输入转换成了一组新的描述。

3.2深 = 把上一层的结论当输入

把若干层串起来:第 1 层输出 3 个数字 → 第 2 层拿这 3 个数字当输入 → 输出 2 个数字 → 第 3 层……最后一层给出答案。

输入 x  →  第1层(3个神经元,ReLU) →  第2层(2个神经元,ReLU) →  输出层(1个数字)

按第 2 章的说法,每一层都在增加一批折点。层数越深、每层神经元越多,能拼出的形状就越复杂。

输入层 第 1 层(全连接) 第 2 层 输出 每一根连线就是一个权重——线越多,要学的数字越多
图 3-1 · 一个 3 → 3 → 2 → 1 的网络。每一根线都代表一个待定的权重,数一数线就知道有多少个旋钮要调。

3.3手算:这个网络有多少个参数

规则极简单:每条连线一个权重,每个神经元一个偏置。数图 3-1:

部分连线数(权重)偏置小计
输入 → 第 1 层3 × 3 = 9312
第 1 层 → 第 2 层3 × 2 = 628
第 2 层 → 输出2 × 1 = 213
合计17623

23 个数字要定。手调当然不可能——所以才需要"学习"(第 4–5 章)。

感受一下真实规模:一个很普通的网络——输入 784 个像素(28×28 的小图片),隐藏层 128 个神经元,输出 10 类:
784 × 128 + 128 = 100,480   +   128 × 10 + 10 = 1,290   →   合计 101,770 个参数
一个"入门级"网络就有十万多个旋钮。而今天的大模型是千亿级。所以"训练"必须是全自动的——这就是第 4、5 章要解决的问题。

3.4层在学什么(一句话,不深究)

按第 2 章的结论,每一层就是一批折点。前层负责切分出简单的小片段,后层把这些片段组合成更复杂的形状。做图像识别时常听到的说法是"前层看边缘、中层看部件、后层看物体"——这是一个有用的直觉,但不是严格结论,知道就够了,不必深究。

3.5三大网络家族:只讲一种,其余一句话

网络可以按"神经元怎么连线"分成几大家族。本教材只主讲最容易理解的那一种,其余只告诉你它是什么、解决什么问题——先立骨架,需要时再展开。

家族连线的特别之处擅长本书处理
全连接网络(MLP)每个神经元看全部输入表格数据、通用拟合本书主讲这一种
卷积神经网络(CNN)每个神经元只看局部输入,且同一套权重在整张图上共享图像一句话,不展开
循环神经网络(RNN)顺序逐个处理,并把上一步的状态传给下一步(带记忆)语音、文本等序列一句话,不展开
Transformer不用记忆,改用"注意力"让每个位置直接看所有位置现代大模型的主流一句话,后续专题
为什么先讲全连接?因为损失函数、梯度下降、反向传播这三件事,在所有家族里完全一样——它们跟"怎么连线"无关。所以把这三件事在全连接网络上搞懂,换到 CNN 或 Transformer 上可以直接平移。这也是本版不急着讲 Transformer 的原因。
本章小结
  • 一层 = 一群并列神经元,各自权重不同 → 各自关注不同角度;输出从"一个数"变成"一组描述"。
  • 深 = 串联,后一层以前一层的输出为输入。每层都在增加一批折点。
  • 参数量 = 连线数(权重)+ 神经元数(偏置)。本书的例子是 23 个,真实网络动辄十万、千亿。
  • 网络家族只讲全连接——因为训练的三件核心事(损失、梯度下降、反向传播)在所有家族中完全相同
第 四 章

损失函数与梯度下降

一句话:损失是一个数字,告诉你错得多离谱;梯度下降是照着"哪个方向能让这个数字变小",每次挪一小步。这一章解决"怎么找到一组合适的权重"。

4.1损失函数:用一个数字衡量"错得多离谱"

例子:预测房价。真实成交价 200 万,网络预测 180 万,差 20 万。这个"差"就是误差。

最直观的损失就是差的平方(平方误差):

L = (预测 − 真实)2 = (180 − 200)2 = 400

为什么要平方?两个理由,都用数字说:

理由不平方会怎样平方之后
① 正负不能相消两套房子误差 +20 和 −20,加起来 = 0,看起来完美400 + 400 = 800(真实反映都错了)
② 大错要重罚误差 20 和误差 100,只是 5 倍关系400 vs 10000,是 25 倍(错得离谱,罚得更狠)

实践中有时写成 L = ½(预测 − 真实)²,那个 ½ 纯粹是为了求导时把平方带来的 2 抵消掉,让式子干净——它不影响"谁大谁小"的判断

分类任务常用另一种损失(交叉熵),一句话带过:回归(预测数值)用平方误差,分类(预测类别)用交叉熵。它们的作用完全一样——都是给"错得多离谱"打一个分,只是打分方式不同。本书不展开交叉熵的公式。

4.2损失曲面:把"找最优权重"想成下山

先看只有一个权重的极端情况。假设损失是 L(w) = (w − 3)²,那么 w = 3 时损失最小(=0),这就是最优权重

把 L 随 w 变化的曲线画出来,是一个碗(抛物线)。训练就是:你被随机丢在山坡上某处,蒙着眼睛,要走到碗底。

权重 w 损失 L L = (w − 3)² 起点 w=0 碗底 w=3 每一步都朝"坡度向下"的方向走一小段 —— 这就是梯度下降
图 4-1 · 红点是梯度下降的前 5 步(学习率 0.1)。可以看到越接近碗底,步长自动变得越小——因为坡度变缓了。

4.3手算梯度下降:5 步走到碗底

规则就一条(第 0 章的导数登场):

新 w = 旧 w − η ×(损失对 w 的导数)

其中 η(读 eta)叫学习率,就是"每步走多大"。取 η = 0.1,从 w = 0 出发。L = (w − 3)² 的导数是 2(w − 3)

当前 w导数 2(w−3)移动 −0.1×导数新 w新损失
00.0000−6.000+0.60000.60009.000
10.6000−4.800+0.48001.08005.760
21.0800−3.840+0.38401.46403.686
31.4640−3.072+0.30721.77122.359
41.7712−2.458+0.24582.01701.510
52.0170−1.966+0.19662.21360.966

损失从 9.000 一路降到 0.966,w 从 0 稳步走向最优值 3。调参这件看似不可能的事,就这么被一个乘法和一个减法解决了。继续走下去会无限逼近 3。

注意导数的符号在替你做决定:起点 w=0 时导数是 −6,负号告诉你"往增大 w 的方向走";等你走到 w=4(超过了 3),导数会变成 +2,正号告诉你"往减小 w 的方向走"。所以"减去 η × 导数"这个式子自带方向判断,不需要你操心该往哪边走。

4.4学习率:太大会炸,太小太慢

η 是唯一需要你手动设定的"手感"参数。还是同一个例子,把 η 从 0.1 改成 1.5

当前 w导数移动 −1.5×导数新 w新损失
00−6+9936(变大了!)
19+12−18−9144
2−9−24+3627576

损失 9 → 36 → 144 → 576,越跑越离谱——因为步子太大,每一步都冲过碗底冲到对面更高的山坡上,来回震荡直至发散。

η 的大小表现怎么办
太大损失震荡、变大、最后变成 NaN调小(常见起手:0.01 或 0.001)
太小损失在降,但慢得让人绝望调大,或换更聪明的优化器(第 7 章)
合适损失平稳下降后逐渐走平就这样

4.5多个参数时:梯度就是那张"清单"

真实网络有成千上万个权重 w₁, w₂, …,损失 L 由它们共同决定。这时要做的事完全一样,只是对每个参数各求一次偏导数(第 0 章 0.3 节那个"只拧一个旋钮"):

新 w₁ = 旧 w₁ − η × ∂L∂w₁  ,  新 w₂ = 旧 w₂ − η × ∂L∂w₂  ,  …全部参数同时更新

把所有这些偏导数排成一列,就是梯度所以"梯度下降"的字面意思是:拿着这张"每个旋钮各自的灵敏度清单",让每个旋钮都往让损失变小的方向挪一点。

唯一还没解决的问题是:这些偏导数怎么算?网络有几万层嵌套,手写求导会疯掉——这就是第 5 章反向传播要做的事。

4.6三个常见误区

误区一:"梯度下降保证找到最好的解。" 不保证。它只保证走到附近的谷底。真实网络的损失曲面不是一只干净的碗,而是布满坑洼的山地,你可能落进任意一个坑(局部最优)。实践中这不是大问题,但要知道这个前提。

误区二:"损失越小越好,降到 0 最好。" 训练损失降到 0 往往是坏消息——意味着网络把训练题背下来了,遇到新题就废。这叫过拟合(第 8 章)。

误区三:"学习率是网络自己学的。" 不是。它是你手动设定的(第 7 章的 Adam 等方法能自动调整"有效步长",但基础学习率仍要你给)。

本章小结
  • 损失 = 一个数字,衡量错得多离谱。平方误差的两个作用:防止正负相消、重罚大错。
  • 梯度下降 = 新 w = 旧 w − η × 导数。负号自带的符号判断替你决定往哪边走。
  • 手算验证:L = (w−3)²,η = 0.1,5 步把损失从 9.000 降到 0.966。
  • 学习率 η 太大会震荡发散(η=1.5 时 9→36→144→576),太小会慢得绝望。
  • 多参数时,梯度 = 每个参数的偏导数清单,所有参数按各自的偏导数同时更新。
  • 遗留问题:这些偏导数到底怎么高效算出来?→ 第 5 章反向传播。
第 五 章

反向传播:把责任退回去

一句话:它是"算账"算法——算出网络里每一个参数对最终错误各负多少责任(即各自的偏导数),好让第 4 章的梯度下降能用上。它用的唯一工具是第 0 章的链式法则。
先交代一件事:本章的示例网络用 sigmoid 而不是第 2 章主讲的 ReLU,原因有两个:① sigmoid 的导数有个漂亮性质(算完前向就顺手得到导数),手算特别干净;② 它能自然地暴露出一个严重问题,那正是第 6 章的主题。看完第 6 章你就会明白为什么实践中要换回 ReLU。方法本身与激活函数无关

5.1要解决的问题

第 4 章留下一个尾巴:网络有几万层嵌套,难道要手推每一个参数的偏导数?

答案是不用推,可以算。因为网络的结构是"一层套一层",而第 0 章的链式法则正好告诉我们:嵌套函数的导数 = 各层导数相乘。于是可以从最后一层开始,把"责任"一层层乘回去,顺路就把所有参数的偏导数都算出来了。这就是反向传播。

类 比 一条流水线,3 个工人接力,出厂产品不合格,厂长要罚款。罚单不能只开给最后一个人,错误可能是任何环节埋下的。合理做法是从后往前追溯责任:最后一人离错误最近,先算他的;他会说"我是按上一环节的半成品做的",于是他的责任按上游对他影响的程度成比例往上传;一层层传下去,每人分到一份。
错误 = 损失,责任值 = 偏导数(梯度),往回传 = 链式法则。

5.2搭一个 5 参数的迷你网络

结构:2 个输入 → 隐藏层 2 个神经元 → 1 个输出。它小到能手算,却包含了深网的全部要素:多层、非线性、损失回传

x₁ x₂ 输入层 h₁ h₂ 隐藏层(sigmoid) ŷ 输出层 w₁₁w₂₁ w₁₂w₂₂ v₁v₂ L = ½(ŷ−y)² 误差
图 5-1 · 本章的迷你网络。黑线是前向(数据从左到右),红线是误差的来源。
符号含义初始值
x₁, x输入1.0,2.0
w₁₁, w₁₂(+b₁)h₁ 的两个权重0.1,0.2(b₁ = 0)
w₂₁, w₂₂(+b₂)h₂ 的两个权重−0.2,0.3(b₂ = 0)
v₁, v₂(+b₃)输出神经元的权重0.4,0.3(b₃ = 0.1)
y正确答案1.0

sigmoid 只需知道两条:① σ(z) = 1/(1 + e−z),把任何数压进 (0,1);② σ′(z) = σ(z)(1 − σ(z))——算完前向就顺手得到了导数,不用再求一次。

5.3第一步:前向传播

zh1 = 0.1×1.0 + 0.2×2.0 + 0 = 0.5   →   h₁ = σ(0.5) = 0.6225
zh2 = −0.2×1.0 + 0.3×2.0 + 0 = 0.4   →   h₂ = σ(0.4) = 0.5987
zo = 0.4×0.6225 + 0.3×0.5987 + 0.1 = 0.5286   →   ŷ = σ(0.5286) = 0.6292
L = ½(0.6292 − 1.0)² = 0.0688

预测 0.6292,正确答案是 1.0,差了 0.37。现在的问题是:这 0.0688 的损失,该记在每个参数头上各多少?

5.4第二步:反向传播(四步)

约定记号:δ(读作"责任值")= 某个神经元的输出对最终损失的影响率。算法的核心动作是:先算最后一层的 δ,然后一层层往前递推。

STEP 1 · 算输出层自己的责任 δo

输出离错误最近,它的责任由两件事相乘:

∂L∂ŷ = ŷ − y = 0.6292 − 1.0 = −0.3708   (预测偏低,所以是负的)
∂ŷ∂zo = σ′(zo) = 0.6292 × 0.3708 = 0.2333
δo = (−0.3708) × 0.2333 = −0.0865
STEP 2 · 算输出层三个参数的偏导数

zo = v₁h₁ + v₂h₂ + b₃,对 v₁ 求偏导就是把 h₁"拎出来"(其它当常数,第 0 章 0.3 节):

∂L∂v₁ = δo · h₁ = −0.0865 × 0.6225 = −0.0539
∂L∂v₂ = δo · h₂ = −0.0865 × 0.5987 = −0.0518
∂L∂b₃ = δo = −0.0865

读一读这些数:偏导数为负 = 参数增大一点损失就会变小 = 参数应该增大。这就是"责任"的方向。

STEP 3 · 把责任传给隐藏层(本算法的灵魂一步)

h₁ 影响损失的路径只有一条:h₁ →(乘 v₁)→ zo →(乘 σ′)→ ŷ → 损失。把沿途变化率全部相乘

δh1 = ∂L∂h₁ = δo × v₁ × σ′(zh1)

注意括号里 σ′(zh1) 这一项——责任还要穿过 h₁ 自己的 sigmoid,再打一次折:

σ′(zh1) = 0.6225 × 0.3775 = 0.2350   →   δh1 = (−0.0865) × 0.4 × 0.2350 = −0.0081
σ′(zh2) = 0.5987 × 0.4013 = 0.2403   →   δh2 = (−0.0865) × 0.3 × 0.2403 = −0.0062
请盯住这三个数:δo = −0.0865 → 传到隐藏层只剩 −0.0081 和 −0.0062,缩小了约 10 倍。
缩小的原因有两个:乘了绝对值小于 1 的权重(0.4、0.3),又乘了小于 1 的 σ′(约 0.24)。这个观察就是第 6 章"梯度消失"的全部伏笔。
STEP 4 · 算隐藏层四个权重的偏导数

与 STEP 2 完全同构:拿 δ 乘上"流入这条边的输入值":

∂L∂w₁₁ = δh1·x₁ = −0.0081    ∂L∂w₁₂ = δh1·x₂ = −0.0163
∂L∂w₂₁ = δh2·x₁ = −0.0062    ∂L∂w₂₂ = δh2·x₂ = −0.0125

注意 w₁₂ 的梯度是 w₁₁ 的两倍——因为 x₂ = 2.0 是 x₁ 的两倍,同样一份责任,作用在更大的输入上,权重就该多调一倍。梯度里藏着数据本身。

L = 0.0688 损失(错误的源头) ŷ h₁ h₂ x₁ x₂ ŷ−y = −0.3708 ×v₁×σ′ ×v₂×σ′ δh₁×x₁ δh₂×x₂ δo = −0.0865 δ = −0.0081 δ = −0.0062 责任从损失出发,边往回走边做乘法 每穿一层就乘两回:一次乘权重,一次乘激活函数导数
图 5-2 · 责任的反向流动。红色数值是各处的 δ,可以看到它边往回走边缩小

5.5第三步:用梯度更新参数并验证

按第 4 章的规则 w ← w − η·(∂L/∂w),取 η = 0.5。所有梯度都是负的,所以所有参数都往增大的方向挪(预测偏低,该增大):

参数旧值偏导数新值
v0.4000−0.05390.4269
v0.3000−0.05180.3259
b0.1000−0.08650.1433
w₁₁0.1000−0.00810.1041
w₁₂0.2000−0.01630.2081
w₂₁−0.2000−0.0062−0.1969
w₂₂0.3000−0.01250.3062

用新参数重做一次前向(建议你自己算一遍,这里给出结果):

h₁ = 0.6269 ,h₂ = 0.6024 ,ŷ = 0.6472 ,L = 0.0622

预测从 0.6292 → 0.6472,离目标 1.0 更近;损失 0.0688 → 0.0622一次"前向 + 反向 + 更新"就是训练的一步。重复几千次,预测会逼近 1.0。训练的全部秘密到此为止,没有更多了。

5.6提炼成通用公式(适用任意深度)

回头看,不管网络多深,反复用的只有两条模式:

① 责任回传: δ(l) = δ(l+1) · w(l+1) · f′(z(l))
② 权重归账: ∂L∂w(l) = δ(l) ×(这条边流入的输入值)

① 是"责任穿过一层时打折":乘权重、乘激活函数导数。② 是"责任分配给权重":输入越大,分到的梯度越大。PyTorch 里的 loss.backward() 干的就是这件事——沿着前向时自动记下的计算图,把这两条乘法规则机械化执行一遍。

5.7三个常见误区

误区一:"反向传播是学习算法。" 不准确。反向传播只负责算梯度(算账),真正"学习"的是第 4 章的梯度下降(拿梯度去更新)。分工:反向传播是侦察兵,梯度下降是执行者。

误区二:"梯度是损失的大小。" 梯度是变化率(第 0 章的斜率),不是损失本身。它回答"往哪挪、挪多灵敏",不是"现在错多少"。

误区三:"一次反向传播就训好了。" 一次只走一小步(本例预测只从 0.6292 挪到 0.6472)。训练是把这个循环跑成千上万次。

本章小结
  • 反向传播 = 用链式法则,把总误差的责任从输出层逐层乘回去,顺路算出每个参数的偏导数。
  • 两条乘法贯穿始终:δ 回传(乘权重、乘激活导数)与权重归账(δ × 输入值)。
  • 手算验证:损失 0.0688 → 更新后 0.0622,预测 0.6292 → 0.6472。
  • 它只负责算梯度,更新参数的是梯度下降——两者合起来才叫"训练一步"。
  • 最重要的伏笔:δ 从 −0.0865 传到隐藏层只剩 −0.0081。责任每穿一层都要打折
第 六 章

梯度消失:为什么一深就学不动

一句话:责任是逐层连乘着往回传的,而每一层的乘数通常小于 1——层数一深,梯度指数级缩小,底层参数几乎分不到责任,也就几乎不再学习。这一章兑现第 5 章的伏笔,也解释了第 2 章为什么要用 ReLU。

6.1从上章的数字直接出发

不引入任何新东西,只看第 5 章算出来的两个数:

输出层的责任 δo = −0.0865   →   穿过一层后 δh1 = −0.0081

责任穿一层就缩到原来的 9.4%。这一层里发生了什么?回看 5.4 节的公式:

δh1 = δo × v₁ × σ′(zh1) = δo × (0.4) × (0.235)

两个乘数:一个来自权重(0.4),一个来自激活函数的导数(0.235)。它们都小于 1,而小于 1 的数连乘会指数级坍缩。这就是梯度消失的全部机理,没有更神秘的东西。

6.2头号嫌疑犯:sigmoid 的导数封顶 0.25

权重小于 1 只是"碰巧",而 sigmoid 的导数是必然拖后腿

σ′(z) = σ(z)·(1 − σ(z))   (两个都不超过 1 的正数相乘)

所以 σ′ 在 z = 0 处取到最大值 0.5 × 0.5 = 0.25。也就是说:sigmoid 的导数上限只有 0.25,多数区域仅 0.1 左右,两端更是趋近于 0。

0.25(天花板) σ(z) σ′(z) 峰值 0.25 @ z=0 饱和区 σ′≈0 饱和区 σ′≈0 z = 0
图 6-1 · sigmoid(灰虚线)与它的导数(蓝实线)。导数最高只有 0.25,且输入稍大或稍小(|z| > 4)就趋于 0——两个红色"饱和区"是梯度消失的重灾区。

6.3算一笔指数账

做一个最保守的估计:每层只经过一个 sigmoid(导数取最好情况 0.25),先不看权重。梯度每层缩到 1/4:

回传穿过的层数梯度缩为原来的直观理解
1 层0.25损失 1/4 —— 还能学
3 层0.0156损失 98.4% —— 底层已经吃力
5 层0.000977只剩约千分之一
10 层0.00000095百万分之一
20 层0.0000000000009万亿分之一——实际等于 0

再考虑权重:回传公式里还要乘每层的 |w|。若权重也普遍小于 1(早期常见的初始化方式),衰减更夸张——每层总共乘 0.1 的话,10 层之后是 10−10

对照第 5 章手算:我们的网络只有一个隐藏层,δ 就已经从 0.0865 缩到 0.0081(缩 94%)。同样的折扣连续发生 10 次、50 次会怎样?这就是深层网络在 2010 年前难以训练的直接原因。

回传穿过的 sigmoid 层数(纵轴为对数刻度) 梯度大小(log) 135 101520 0.251.6%0.1% 百万分之一十亿分之一万亿分之一 指数级坍缩
图 6-2 · 即使每层只乘"最好情况"的 0.25,梯度也随深度指数坍缩。20 层网络回传到底层时约 10−13——用 float32(精度约 10−7)存储的话,这个数连计算机都存不住,直接下溢为 0

6.4现在回头看:ReLU 为什么救了场

把第 2 章的结论和这一章接起来。回传公式里的第二个乘数,是激活函数的导数:

激活函数导数(回传时的乘数)穿过 10 层之后
sigmoid最大 0.25梯度 × 10−6(几乎归零)
ReLU(正数区)恒为 1梯度原样穿过,一点不损失

这就是 ReLU 取代 sigmoid 成为默认选择的真正原因——不是因为它算得快(虽然确实快),而是因为它的导数在正数区恒等于 1,把连乘中那个"必然小于 1"的因子直接变成了 1。第 2 章说它"把直线掰弯"解决了表达能力,这一章说它"导数为 1"解决了训练可行性。一个改动,两个问题一起解决。

顺带解释第 2 章留下的疑问:ReLU 的负数区导数是 0,梯度会完全断掉(这正是"死神经元")。所以实践上还有 Leaky ReLU 等改良。但只要有一部分神经元处于正数区,梯度就有路可走——这已经比 sigmoid 的"全区域都打折"好太多了。

6.5症状:它是"沉默地失败"

梯度消失不会报错,它只是学不动。训练时看到以下现象,第一个该怀疑的就是它:

症状机理
损失降得极快然后卡住不动(像直线)底层梯度≈0,只剩最后几层在微调,模型容量被锁死
浅层权重几乎不变,深层的在变把每层梯度大小打出来,从后往前指数递减(诊断金标准)
网络加深反而不如浅的多出来的层学不到东西,只增加噪声和计算量

6.6孪生兄弟:梯度爆炸

连乘是把双刃剑。若每层的乘数不是 0.25 而是 2(权重偏大时完全可能),梯度就指数放大:10 层后是 210 ≈ 1024 倍。症状正好相反——损失剧烈震荡、变成 NaN。两者统称"梯度不稳定",根源相同:连乘结构对乘数的大小极其敏感

每层乘 0.25,10 层后 ×10−6(消失)  ·  每层乘 2,10 层后 ×10+3(爆炸)

6.7其余解法:一句话,不展开

按本教材原则,只给一句话,需要时再展开:

解法一句话原理
ReLU 激活函数导数恒为 1(正数区),把连乘里那个 0.25 变成 1(第 2 章已详讲
He / Xavier 初始化让权重的方差恰好抵消连乘的缩放,使乘积的期望保持 1
BatchNorm / LayerNorm把每层输入拉回"不饱和"的区间,让 σ′ 不再贴着 0
残差连接(ResNet)给梯度修一条高速公路:加法结构让梯度无衰减直达底层
LSTM 门控给 RNN 加"传送带",让长程梯度有旁路可走
本章小结
  • 梯度消失的根源不在 bug,而在反向传播的连乘结构本身(第 5 章公式 ①)。
  • 两个乘数:权重绝对值 + 激活函数导数。sigmoid 导数上限 0.25,是历史罪魁。
  • 指数账:0.25 连乘 10 次 ≈ 百万分之一,20 次 ≈ 万亿分之一——底层参数等于被冻结。
  • 与第 2 章会合:ReLU 正数区导数为 1,让连乘中的那个因子直接变成 1——一个改动同时解决了"表达能力"和"训练可行性"。
  • 症状是"沉默地卡住";诊断靠打印各层梯度大小。
  • 其余解法(初始化、归一化、残差)都在攻同一个目标:让连乘的每一步别再小于 1