函数就是一个规定:"给我一个数,我还你一个数。"
神经网络本质上就是一个函数,只不过式子特别长、参数特别多:输入一张图片,输出"这是猫"的概率。
忘掉公式,只记一句话:导数就是放大倍数——输入动 1,输出动几。
不信手算一遍(这就是导数的定义,没有别的):
越取越近,就越接近 6,这就是 f(x) = x² 的导数是 2x(x=3 时 = 6)的来历。导数回答的是"灵敏度":这个函数在这儿有多陡、往哪边陡。
一句话:偏导数就是"多变量函数的导数",多出来的动作只有一个——先声明我动哪一个,其它全当常数。
用数字说清楚。设甜度 = 3 × 糖 + 2 × 蜂蜜(糖、蜂蜜都是勺数):
| 问题 | 操作 | 答案 |
|---|---|---|
| 多加 1 勺糖,甜度涨多少? | 蜂蜜当常数不动 | +3 |
| 多加 1 勺蜂蜜,甜度涨多少? | 糖当常数不动 | +2 |
| 两个都多加 1 勺呢? | 两个效果相加 | +5 |
写成式子就是这样(∂ 这个符号就读作"只动这一个"):
它就是导数,一点都没变难。唯一的区别:单变量函数只有一个旋钮,不用声明;多变量函数有好几个旋钮,所以要先声明"我拧的是哪个",记号上用 ∂ 代替 d 来提醒你"还有别的旋钮被按住了"。
如果 y = 3w,而 L = y²,问"w 动 1,L 动几"?把两段的变化率乘起来就行:
验证:L = (3w)² = 9w²,直接对 w 求导 = 18w。完全一致。这条规则是反向传播的全部数学基础(第 5 章)。
向量就是把几个数字排成一列,比如一个学生的 [身高, 体重] = [175, 65]。
点积是"对应位置相乘再相加",它衡量两个向量有多像:
后面讲注意力时会用到它,现在只要记住:点积大 = 两个向量方向接近 = 相似。
它只是"把这一串加起来"的简写。神经元的"加权求和"就是它。
| 全书会用到 | 在哪一章 |
|---|---|
| 函数(有进有出的机器) | 第 1 章 |
| 导数 = 斜率 = 放大倍数 | 第 4 章 |
| 偏导数 = 只拧一个旋钮 | 第 5 章 |
| 链式法则 = 变化率相乘 | 第 5 章 |
| 向量点积 = 相似度 | 第 9 章及以后 |
| Σ 求和符号 | 第 1 章 |
你要决定去不去,脑子里其实在给三个因素打分:
| 因素 | 今天的情况 | 你内心的看重程度(权重) |
|---|---|---|
| 天气好不好 | 0.9(很好) | +0.6(我很在意) |
| 朋友去不去 | 1.0(去) | +0.3(有点在意) |
| 路远不远 | 0.7(有点远) | −0.2(越远越不想去) |
打分过程(对应位置相乘再相加,就是第 0 章的 Σ):
再定个门槛:总分 > 0.5 就去。0.70 > 0.5,去!
| 人话 | 术语 | 记号 | 本例的值 |
|---|---|---|---|
| 今天的情况 | 输入 | x₁, x₂, x₃ | 0.9, 1.0, 0.7 |
| 你的看重程度 | 权重 | w₁, w₂, w₃ | 0.6, 0.3, −0.2 |
| 基础倾向(门槛移项) | 偏置 | b | −0.5 |
| 打分 | 加权求和 | z = Σw·x + b | 0.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,判断必然是"不去"——可现实里你可能就是"今天心情好,随便走走也行"。偏置就是给神经元一个与输入无关的基础倾向。
在上面的例子里,权重是我替你写的。真实网络里,这些数字一开始是随机的乱猜,然后通过看成千上万个例子,一点点调整到合适的值。
所以"学习"这个词的实际含义非常朴素:就是给几千个权重找到一组合适的数字。第 4–5 章讲怎么找。
单个神经元只能画一条"分界线":把打分高于门槛的放一边,低于的放另一边。这类问题叫线性可分。
但很多问题不是一条直线能分开的——比如"异或":两个条件相同时为是,不同时为否。这种得画两条线才行,一个神经元做不到。这就是需要"多层"的原因,也是下一章的起点。
假设神经元不做任何"过门槛",只做加权求和。来看两层会发生什么:
把 (w₂w₁) 看成一个新数 W,把 (w₂b₁ + b₂) 看成新数 B,那么:
再叠第三层、第四层……结果永远是 Wx + B 这个形式。线性的东西叠多少层,都还是线性的。你辛辛苦苦搭 100 层,等价于 1 层。深度完全白费。
既然"叠直线还是直线",那就必须在每层之间插入一个能改变形状的东西。这个东西就是激活函数。它的全部职责只有一条:引入非线性(把直线掰弯)。
ReLU 的定义简单到不像话:
举例:ReLU(2.5) = 2.5;ReLU(−1.3) = 0;ReLU(0) = 0。
这是你最该搞懂的一段。答案分两步:
第一步:一个 ReLU 神经元 = 一条折线。因为 max(0, wx + b) 在 wx + b = 0 处拐弯,图像就是一条折线。移动 b 可以左右挪动这个拐点,改变 w 可以改变折线的倾斜程度。
第二步:多个折线相加 = 任意形状。把若干条折线加起来,就能拼出各种起伏。下面用真实数字拼一次,你看完就彻底明白了。
取三个 ReLU 神经元,各自在不同的位置拐弯,然后按权重 1、−2、1 加起来:
逐点手算(每一项都很简单,负数就取 0):
| x | ReLU(x) | −2·ReLU(x−1) | ReLU(x−2) | 合计 f(x) |
|---|---|---|---|---|
| −1 | 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 | 0 |
| 0.5 | 0.5 | 0 | 0 | 0.5 |
| 1.0 | 1.0 | 0 | 0 | 1.0 ← 峰顶 |
| 1.5 | 1.5 | −1.0 | 0 | 0.5 |
| 2.0 | 2.0 | −2.0 | 0 | 0 |
| 3.0 | 3.0 | −4.0 | 1.0 | 0 |
看最后那一列:0 → 0 → 0.5 → 1.0 → 0.5 → 0 → 0,一个标准的三角形驼峰!而它是由 3 个"只会折一下"的神经元拼出来的。
求导(第 0 章的"斜率")之后你会发现,ReLU 的导数简单得离谱:
| 区间 | ReLU(x) | 导数(斜率) | 含义 |
|---|---|---|---|
| x > 0 | x | 1 | 信号原样通过,梯度也是 ×1 传回去 |
| x < 0 | 0 | 0 | 这个神经元"关着",不参与 |
| x = 0 | 0 | 无所谓 | 实践中取 0 或 1 都行,几乎不会精确命中 |
为什么这个 1 特别珍贵?第 5 章你会看到,反向传播时梯度每穿过一层都要乘一次激活函数的导数。ReLU 的导数是 1——乘 1 等于没乘,梯度原封不动地穿过去。这就是"深度网络能训得动"的关键之一(第 6 章详讲梯度消失)。
因为它的行为像一个开关:输入为正,这个神经元被"激活",有输出;输入为负,它"熄灭",输出 0,等于这一轮不参与计算。所以一个网络在处理不同输入时,参与的神经元组合是不同的——这让它能对不同情况做出不同反应。
按本教材的原则——平行概念只讲最容易理解的一种。你已经吃透了 ReLU,其余的只需要知道"它们只是换了一种弯法":
| 名称 | 一句话 | 为什么现在不主讲 |
|---|---|---|
| ReLU | 负数归零,正数照过(本书主讲这一种) | — |
| sigmoid | 把任何数字压进 0~1 区间,曲线是平滑的 S 形 | 导数最大只有 0.25,是梯度消失的元凶(第 6 章会详细算这笔账) |
| tanh | 把数字压进 −1~1 区间,也是 S 形 | 同样有饱和区,问题与 sigmoid 同类 |
| Leaky ReLU | 负数不归零,给个很小的斜率(0.01x) | 只为治"死神经元",是 ReLU 的小修小补 |
| GELU | ReLU 的平滑版,按概率决定通过多少 | 现代大模型常用,但理解门槛高,知道名字即可 |
第 1 章只有一个神经元,只能画一条分界线。现在把它们并列摆放,看同一批输入,但各自的权重不同——于是每个神经元关注的东西不一样:
| 神经元 | 它的权重(天气 / 朋友 / 距离) | 它在关注什么 |
|---|---|---|
| 第 1 个 | 0.6,0.3,−0.2 | 综合派:整体值不值得去 |
| 第 2 个 | 0.9,−0.1,0.1 | 天气派:几乎只看天气 |
| 第 3 个 | −0.1,0.8,−0.5 | 社交派:主要看朋友去不去、远不远 |
这一层的输出不再是一个数字,而是三个数字——可以理解为"这份输入在三个不同角度上的评分"。层把输入转换成了一组新的描述。
把若干层串起来:第 1 层输出 3 个数字 → 第 2 层拿这 3 个数字当输入 → 输出 2 个数字 → 第 3 层……最后一层给出答案。
按第 2 章的说法,每一层都在增加一批折点。层数越深、每层神经元越多,能拼出的形状就越复杂。
规则极简单:每条连线一个权重,每个神经元一个偏置。数图 3-1:
| 部分 | 连线数(权重) | 偏置 | 小计 |
|---|---|---|---|
| 输入 → 第 1 层 | 3 × 3 = 9 | 3 | 12 |
| 第 1 层 → 第 2 层 | 3 × 2 = 6 | 2 | 8 |
| 第 2 层 → 输出 | 2 × 1 = 2 | 1 | 3 |
| 合计 | 17 | 6 | 23 |
23 个数字要定。手调当然不可能——所以才需要"学习"(第 4–5 章)。
按第 2 章的结论,每一层就是一批折点。前层负责切分出简单的小片段,后层把这些片段组合成更复杂的形状。做图像识别时常听到的说法是"前层看边缘、中层看部件、后层看物体"——这是一个有用的直觉,但不是严格结论,知道就够了,不必深究。
网络可以按"神经元怎么连线"分成几大家族。本教材只主讲最容易理解的那一种,其余只告诉你它是什么、解决什么问题——先立骨架,需要时再展开。
| 家族 | 连线的特别之处 | 擅长 | 本书处理 |
|---|---|---|---|
| 全连接网络(MLP) | 每个神经元看全部输入 | 表格数据、通用拟合 | 本书主讲这一种 |
| 卷积神经网络(CNN) | 每个神经元只看局部输入,且同一套权重在整张图上共享 | 图像 | 一句话,不展开 |
| 循环神经网络(RNN) | 按顺序逐个处理,并把上一步的状态传给下一步(带记忆) | 语音、文本等序列 | 一句话,不展开 |
| Transformer | 不用记忆,改用"注意力"让每个位置直接看所有位置 | 现代大模型的主流 | 一句话,后续专题 |
例子:预测房价。真实成交价 200 万,网络预测 180 万,差 20 万。这个"差"就是误差。
最直观的损失就是差的平方(平方误差):
为什么要平方?两个理由,都用数字说:
| 理由 | 不平方会怎样 | 平方之后 |
|---|---|---|
| ① 正负不能相消 | 两套房子误差 +20 和 −20,加起来 = 0,看起来完美 | 400 + 400 = 800(真实反映都错了) |
| ② 大错要重罚 | 误差 20 和误差 100,只是 5 倍关系 | 400 vs 10000,是 25 倍(错得离谱,罚得更狠) |
实践中有时写成 L = ½(预测 − 真实)²,那个 ½ 纯粹是为了求导时把平方带来的 2 抵消掉,让式子干净——它不影响"谁大谁小"的判断。
先看只有一个权重的极端情况。假设损失是 L(w) = (w − 3)²,那么 w = 3 时损失最小(=0),这就是最优权重。
把 L 随 w 变化的曲线画出来,是一个碗(抛物线)。训练就是:你被随机丢在山坡上某处,蒙着眼睛,要走到碗底。
规则就一条(第 0 章的导数登场):
其中 η(读 eta)叫学习率,就是"每步走多大"。取 η = 0.1,从 w = 0 出发。L = (w − 3)² 的导数是 2(w − 3):
| 步 | 当前 w | 导数 2(w−3) | 移动 −0.1×导数 | 新 w | 新损失 |
|---|---|---|---|---|---|
| 0 | 0.0000 | −6.000 | +0.6000 | 0.6000 | 9.000 |
| 1 | 0.6000 | −4.800 | +0.4800 | 1.0800 | 5.760 |
| 2 | 1.0800 | −3.840 | +0.3840 | 1.4640 | 3.686 |
| 3 | 1.4640 | −3.072 | +0.3072 | 1.7712 | 2.359 |
| 4 | 1.7712 | −2.458 | +0.2458 | 2.0170 | 1.510 |
| 5 | 2.0170 | −1.966 | +0.1966 | 2.2136 | 0.966 |
损失从 9.000 一路降到 0.966,w 从 0 稳步走向最优值 3。调参这件看似不可能的事,就这么被一个乘法和一个减法解决了。继续走下去会无限逼近 3。
η 是唯一需要你手动设定的"手感"参数。还是同一个例子,把 η 从 0.1 改成 1.5:
| 步 | 当前 w | 导数 | 移动 −1.5×导数 | 新 w | 新损失 |
|---|---|---|---|---|---|
| 0 | 0 | −6 | +9 | 9 | 36(变大了!) |
| 1 | 9 | +12 | −18 | −9 | 144 |
| 2 | −9 | −24 | +36 | 27 | 576 |
损失 9 → 36 → 144 → 576,越跑越离谱——因为步子太大,每一步都冲过碗底冲到对面更高的山坡上,来回震荡直至发散。
| η 的大小 | 表现 | 怎么办 |
|---|---|---|
| 太大 | 损失震荡、变大、最后变成 NaN | 调小(常见起手:0.01 或 0.001) |
| 太小 | 损失在降,但慢得让人绝望 | 调大,或换更聪明的优化器(第 7 章) |
| 合适 | 损失平稳下降后逐渐走平 | 就这样 |
真实网络有成千上万个权重 w₁, w₂, …,损失 L 由它们共同决定。这时要做的事完全一样,只是对每个参数各求一次偏导数(第 0 章 0.3 节那个"只拧一个旋钮"):
把所有这些偏导数排成一列,就是梯度。所以"梯度下降"的字面意思是:拿着这张"每个旋钮各自的灵敏度清单",让每个旋钮都往让损失变小的方向挪一点。
唯一还没解决的问题是:这些偏导数怎么算?网络有几万层嵌套,手写求导会疯掉——这就是第 5 章反向传播要做的事。
误区一:"梯度下降保证找到最好的解。" 不保证。它只保证走到附近的谷底。真实网络的损失曲面不是一只干净的碗,而是布满坑洼的山地,你可能落进任意一个坑(局部最优)。实践中这不是大问题,但要知道这个前提。
误区二:"损失越小越好,降到 0 最好。" 训练损失降到 0 往往是坏消息——意味着网络把训练题背下来了,遇到新题就废。这叫过拟合(第 8 章)。
误区三:"学习率是网络自己学的。" 不是。它是你手动设定的(第 7 章的 Adam 等方法能自动调整"有效步长",但基础学习率仍要你给)。
第 4 章留下一个尾巴:网络有几万层嵌套,难道要手推每一个参数的偏导数?
答案是不用推,可以算。因为网络的结构是"一层套一层",而第 0 章的链式法则正好告诉我们:嵌套函数的导数 = 各层导数相乘。于是可以从最后一层开始,把"责任"一层层乘回去,顺路就把所有参数的偏导数都算出来了。这就是反向传播。
结构:2 个输入 → 隐藏层 2 个神经元 → 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))——算完前向就顺手得到了导数,不用再求一次。
预测 0.6292,正确答案是 1.0,差了 0.37。现在的问题是:这 0.0688 的损失,该记在每个参数头上各多少?
约定记号:δ(读作"责任值")= 某个神经元的输出对最终损失的影响率。算法的核心动作是:先算最后一层的 δ,然后一层层往前递推。
输出离错误最近,它的责任由两件事相乘:
zo = v₁h₁ + v₂h₂ + b₃,对 v₁ 求偏导就是把 h₁"拎出来"(其它当常数,第 0 章 0.3 节):
读一读这些数:偏导数为负 = 参数增大一点损失就会变小 = 参数应该增大。这就是"责任"的方向。
h₁ 影响损失的路径只有一条:h₁ →(乘 v₁)→ zo →(乘 σ′)→ ŷ → 损失。把沿途变化率全部相乘:
注意括号里 σ′(zh1) 这一项——责任还要穿过 h₁ 自己的 sigmoid,再打一次折:
与 STEP 2 完全同构:拿 δ 乘上"流入这条边的输入值":
注意 w₁₂ 的梯度是 w₁₁ 的两倍——因为 x₂ = 2.0 是 x₁ 的两倍,同样一份责任,作用在更大的输入上,权重就该多调一倍。梯度里藏着数据本身。
按第 4 章的规则 w ← w − η·(∂L/∂w),取 η = 0.5。所有梯度都是负的,所以所有参数都往增大的方向挪(预测偏低,该增大):
| 参数 | 旧值 | 偏导数 | 新值 |
|---|---|---|---|
| v₁ | 0.4000 | −0.0539 | 0.4269 |
| v₂ | 0.3000 | −0.0518 | 0.3259 |
| b₃ | 0.1000 | −0.0865 | 0.1433 |
| w₁₁ | 0.1000 | −0.0081 | 0.1041 |
| w₁₂ | 0.2000 | −0.0163 | 0.2081 |
| w₂₁ | −0.2000 | −0.0062 | −0.1969 |
| w₂₂ | 0.3000 | −0.0125 | 0.3062 |
用新参数重做一次前向(建议你自己算一遍,这里给出结果):
预测从 0.6292 → 0.6472,离目标 1.0 更近;损失 0.0688 → 0.0622。一次"前向 + 反向 + 更新"就是训练的一步。重复几千次,预测会逼近 1.0。训练的全部秘密到此为止,没有更多了。
回头看,不管网络多深,反复用的只有两条模式:
① 是"责任穿过一层时打折":乘权重、乘激活函数导数。② 是"责任分配给权重":输入越大,分到的梯度越大。PyTorch 里的 loss.backward() 干的就是这件事——沿着前向时自动记下的计算图,把这两条乘法规则机械化执行一遍。
误区一:"反向传播是学习算法。" 不准确。反向传播只负责算梯度(算账),真正"学习"的是第 4 章的梯度下降(拿梯度去更新)。分工:反向传播是侦察兵,梯度下降是执行者。
误区二:"梯度是损失的大小。" 梯度是变化率(第 0 章的斜率),不是损失本身。它回答"往哪挪、挪多灵敏",不是"现在错多少"。
误区三:"一次反向传播就训好了。" 一次只走一小步(本例预测只从 0.6292 挪到 0.6472)。训练是把这个循环跑成千上万次。
不引入任何新东西,只看第 5 章算出来的两个数:
责任穿一层就缩到原来的 9.4%。这一层里发生了什么?回看 5.4 节的公式:
两个乘数:一个来自权重(0.4),一个来自激活函数的导数(0.235)。它们都小于 1,而小于 1 的数连乘会指数级坍缩。这就是梯度消失的全部机理,没有更神秘的东西。
权重小于 1 只是"碰巧",而 sigmoid 的导数是必然拖后腿:
所以 σ′ 在 z = 0 处取到最大值 0.5 × 0.5 = 0.25。也就是说:sigmoid 的导数上限只有 0.25,多数区域仅 0.1 左右,两端更是趋近于 0。
做一个最保守的估计:每层只经过一个 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 年前难以训练的直接原因。
把第 2 章的结论和这一章接起来。回传公式里的第二个乘数,是激活函数的导数:
| 激活函数 | 导数(回传时的乘数) | 穿过 10 层之后 |
|---|---|---|
| sigmoid | 最大 0.25 | 梯度 × 10−6(几乎归零) |
| ReLU(正数区) | 恒为 1 | 梯度原样穿过,一点不损失 |
这就是 ReLU 取代 sigmoid 成为默认选择的真正原因——不是因为它算得快(虽然确实快),而是因为它的导数在正数区恒等于 1,把连乘中那个"必然小于 1"的因子直接变成了 1。第 2 章说它"把直线掰弯"解决了表达能力,这一章说它"导数为 1"解决了训练可行性。一个改动,两个问题一起解决。
梯度消失不会报错,它只是学不动。训练时看到以下现象,第一个该怀疑的就是它:
| 症状 | 机理 |
|---|---|
| 损失降得极快然后卡住不动(像直线) | 底层梯度≈0,只剩最后几层在微调,模型容量被锁死 |
| 浅层权重几乎不变,深层的在变 | 把每层梯度大小打出来,从后往前指数递减(诊断金标准) |
| 网络加深反而不如浅的 | 多出来的层学不到东西,只增加噪声和计算量 |
连乘是把双刃剑。若每层的乘数不是 0.25 而是 2(权重偏大时完全可能),梯度就指数放大:10 层后是 210 ≈ 1024 倍。症状正好相反——损失剧烈震荡、变成 NaN。两者统称"梯度不稳定",根源相同:连乘结构对乘数的大小极其敏感。
按本教材原则,只给一句话,需要时再展开:
| 解法 | 一句话原理 |
|---|---|
| ReLU 激活函数 | 导数恒为 1(正数区),把连乘里那个 0.25 变成 1(第 2 章已详讲) |
| He / Xavier 初始化 | 让权重的方差恰好抵消连乘的缩放,使乘积的期望保持 1 |
| BatchNorm / LayerNorm | 把每层输入拉回"不饱和"的区间,让 σ′ 不再贴着 0 |
| 残差连接(ResNet) | 给梯度修一条高速公路:加法结构让梯度无衰减直达底层 |
| LSTM 门控 | 给 RNN 加"传送带",让长程梯度有旁路可走 |