跳到主要内容

从一个神经元到一层网络 — 一条直线分不开的,加一层就分得开

这一章讲三件事: 一个神经元里到底发生了什么;「一层」是怎么用一次矩阵乘就算完的; 以及把手写零件的反向公式补齐之后,同一份数据上的成绩变成多少。

它在全书链条里的位置: 第 05 章那条直线在弯月上停在 0.8450,原因是它只能画直线。 这一章给它加一层,让它能画弯的。

需要的基础: 第 05 章那条 S 形曲线和交叉熵;第 02 章的「乘到另一侧」。

1. 先看现象:两条弯月,一条直线怎么放都分不开

回到第 05 章那份数据:两弯交扣的月牙,各一类。

一条直线只能把平面切成两半。 而这两个月牙是彼此环抱的—— 无论那条线怎么放、怎么转,总有一大块被切错。 0.8450 就是这么来的。

书里给的出路是:先把输入变换一下,再画直线。 上一章第 3 节讲过这一招(把 x 升成 [x, x², …] 再做线性组合), 这一章把它做成一个可复用的零件,而且这个零件的变换方式也是学出来的。

全章主走查:一个神经元 → 一层 → 两层 → 在弯月上训一遍
═══════════════════════════════════════════════════════════════════
§2 ① 两条 5 维样本过一个神经元 → 加权求和得 1.1149 与 1.2194
§2 ② 过一道非线性 → 挤到 0 和 1 之间
§4 ③ 一层 = 把 ① 并排做很多次 → 一次矩阵乘算完
§5 ④ 两层串起来:5 → 10 → 1 → 一条随机样本吐出 0.6587
§7 ⑤ 在 1 000 条弯月上训 1 000 轮 → 测试 0.8150 / 损失 0.4548
§7 ⑥ 换成框架内置零件重跑 → 测试 0.8200 / 损失 0.5561
═══════════════════════════════════════════════════════════════════

2. 一个神经元里发生了什么

结论先行:两步,加权求和,再过一道非线性。

书里的定义是:神经元把生物神经元的工作机制抽象为—— 接收一组输入信号,按权重线性叠加,再经非线性激活后输出1

第一步:加权求和
────────────────────────────────────────────
z = w₁x₁ + w₂x₂ + … + w_D x_D + b
└ 每个输入各乘一个数,全部加起来,再加一个常数

这个 z 在书里有个名字:净活性值

那个「加起来之后再加的常数」就叫偏置。 它的作用是让这条直线不必穿过原点——没有它,模型被强制要求「输入全为 0 时输出也为 0」。

主走查第 ① 步,书里的实际输出: 两条 5 维样本、一组 5 个权重、一个偏置, 算出来的净活性值是2:

输入(2 条样本,每条 5 个数)
[0.4963, 0.7682, 0.0885, 0.1320, 0.3074]
[0.6341, 0.4901, 0.8964, 0.4556, 0.6323]

权重 [0.3489, 0.4017, 0.0223, 0.1689, 0.2939] 偏置 0.5185

第一条:0.4963×0.3489 + 0.7682×0.4017 + 0.0885×0.0223
+ 0.1320×0.1689 + 0.3074×0.2939 + 0.5185 = 1.1149
第二条:同样算一遍 = 1.2194

注意这一步用的就是第 01 章那个矩阵乘法: 形状 [2, 5] 的输入乘上形状 [5, 1] 的权重, 得到形状 [2, 1] 的输出。「一次算两条样本」是免费的——广播和批维替你做完了。

第二步:让净活性值过一道非线性,得到这个神经元真正的输出。 这道非线性有个统称,叫激活函数——它决定这个神经元「兴奋到什么程度」。

书里对它的要求写得很硬:激活函数必须是非线性的, 否则多层网络等价于单层线性变换,无从表达复杂的映射(从输入到输出的对应关系)3

这句话是这一章的地基:如果没有那道非线性,「加一层」这件事就毫无意义 ——两个矩阵相乘还是一个矩阵。

3. 四条曲线:两条 S 形,两条折线

书里画了四条,分成两族。

第一族是 S 形的,第 05 章已经见过其中一条。 另一条叫 Tanh, 形状一样、只是输出区间从 0 到 1 变成 −1 到 14

Logistic Tanh
1 ┤ ╭──── 1 ┤ ╭────
│ ╭──╯ │ ╭──╯
0.5 ┤ ╭─╯ 0 ┤───╭─╯
│ ╭─╯ │ ╭─╯
0 ┤─╯ −1┤─╯
└──┬──┬──┬── └──┬──┬──┬──
−5 0 5 −5 0 5

图说:两条都是「中间近似直线、两端压平」;差别只在输出落在哪个区间。
Tanh 关于原点对称,所以它的输出有正有负。

第二族是折线,长得完全不同。 一条叫 ReLU:负半轴恒为 0,正半轴就是它自己; 另一条叫带泄露的 ReLU,负半轴保留一小段斜率(常取 0.01 或 0.1)5:

ReLU 带泄露的 ReLU
┤ ╱ ┤ ╱
┤ ╱ ┤ ╱
0┤──╱ 0┤──╱
┤ ╱ ┤╱
└──┬──┬── ╱───┴──┬──┬──
0 0
负半轴恒为 0 负半轴留一点斜率

书里对四条曲线的总结是: 两条 S 形在 0 附近近似线性、两端迅速饱和, 输出区间分别是 0 到 1 和 −1 到 1;ReLU 在负半轴恒为 0, 带泄露的那一版仍保留一段小斜率,避免梯度完全消失6

「梯度完全消失」这半句就是第 07 章的伏笔——那一章会把这四条曲线的后果量出来。 为什么带泄露的那一版能把卡死的神经元救回来,第 07 章第 6 节讲。

这里顺带解释「带泄露」这个怪名字: 负半轴本来应该完全关死, 现在留了一条 0.01 的缝让信号漏一点过去——所以叫「泄露」。

4. 一层,就是把同一个动作并排做很多次

结论先行:「层」不是新东西,只是把第 2 节那件事复制很多份,再用一次矩阵乘算完。

一个神经元: 5 个输入 ──→ 1 个输出
权重形状 [5, 1]

一层(10 个神经元):
5 个输入 ──→ 10 个输出
权重形状 [5, 10] ← 10 列,每列是一个神经元的那组权重
偏置形状 [1, 10]

一次算 N 条样本:
[N, 5] × [5, 10] + [1, 10] ──→ [N, 10]
└ 靠广播加到每一行上

书里把这一步写成两个公式:先做仿射变换得到这一层的净活性值, 再过激活函数得到这一层的输出7

这里有一条全书通用的约定,书里用提醒框标了出来: 本书一律使用「样本数 × 特征数」的张量布局, 也就是每一行是一个样本;这与教科书全本里把样本写成列向量的约定刚好转置, 因此权重和偏置的形状也对应转置8

读源码时这一点很要紧: 你看到 [输入维, 输出维] 还是 [输出维, 输入维], 取决于作者用的是哪套约定。两套都对,但混着用会全乱。

层与层之间怎么连? 书里的图很直白:信号从输入层一层层向输出层推进, 每一层把上一层的输出乘权重、加偏置、再过激活函数;整条链路是单向的、无反馈回路9

中间那些既不是输入也不是输出的层,叫隐藏层。 「隐藏」的意思很朴素:你既看不到它的输入是什么含义,也看不到它的输出是什么含义 ——它夹在中间,只对下一层负责。

这种「只往前走、不绕回来」的结构叫前馈神经网络10「前馈」这个词的全部含义就是这个:信息只沿一个方向流。 第 10 章会打破这条规矩,让信息绕回来。

5. 主走查续:两层串起来,吐出 0.6587

书里搭了一个最小的两层网络:输入 5 维、隐藏层 10 个神经元、输出 1 维, 两处激活都用那条 S 形曲线11

主走查第 ④ 步
─────────────────────────────────────────────────────
输入 一条随机样本,形状 [1, 5]
第一层 [1, 5] × [5, 10] + [1, 10] → [1, 10] 净活性值
激活 过 S 形曲线 → [1, 10] 全部落在 0 和 1 之间
第二层 [1, 10] × [10, 1] + [1, 1] → [1, 1] 净活性值
激活 过 S 形曲线 → [1, 1]
─────────────────────────────────────────────────────
书里实际打印:tensor([[0.6587]])

0.6587 这个数本身没有含义——权重是随机初始化的,还没训练。 它的作用是验证形状对不对:输入 5 个数进去,出来 1 个数,中间那 10 个神经元各干各的。

为什么隐藏层取 10? 书里没说理由。这类数就是第 04 章那个「只能人来定」的超参数 ——取多少合适没有公式,只能试。

6. 反向传播落到代码上:三步,两个公式

结论先行:第 02 章那套「每个零件配一个反向」,在这里第一次用到真模型上。

书里把整个训练过程拆成三步12:

  1. 前向算每层的净活性值和输出,直到最后一层——由每个零件的前向实现;
  2. 反向算每层的误差项——由每个零件的反向实现;
  3. 顺带在反向里把每层参数的梯度也算好,交给优化器统一更新。

S 形激活的反向很短,它没有可学习的参数,所以只需要把梯度往上游传13:

δZ = A ⊙ (1 − A) ⊙ δA
└ A 是这一层前向时的输出,⊙ 是逐元素相乘

直觉:A 接近 0 或接近 1 时,A(1−A) 接近 0
→ 上游传来的梯度会被乘上一个很小的数
⚠ 这就是第 07 章那个故障的成因,先记住这个形状

线性层的反向要算三样东西,书里都给了14:

往上游传的 δX = δY · Wᵀ ← 乘到「另一侧」
权重的梯度 δW = Xᵀ · δY ← 也是乘到另一侧
偏置的梯度 δb = 把 δY 按样本维加起来

第 02 章那条「乘到另一侧」在这里出现了两次。 线性层的前向是「输入乘权重」,所以求输入的梯度要乘权重、求权重的梯度要乘输入。 认出这一点,后面所有层的反向公式都不用死记。

偏置那一条为什么是「加起来」? 因为前向时同一个偏置被广播加到了每一行上—— 广播的反向就是求和。 这是一条通用规律,第 01 章那套广播规则在这里有了对应物。

书里还有一个提醒值得记: 手写的零件不会自动构建计算图, 所以模型的反向里要手动按顺序调用每一层的反向,优化器也要显式遍历各层参数15这正是下一节要换掉的东西。

7. 在弯月上跑两次:0.8150 与 0.8200

第一次,用手写零件。 输入 2 维、隐藏层 5 个神经元、输出 1 维, 每步挪 0.2,训 1 000 轮16。结果17:

[Test] score/loss: 0.8150 / 0.4548

书里的评语是:测试集准确率约 0.81,模型已大致拟合出弯月数据的分界线17

第二次,换成框架内置零件重跑。 书里说明:手动按层写反向虽然概念清晰, 但实战中极易在细节上出错;现代框架早已把这一步交给自动求导—— 开发者只描述前向,框架顺着计算图自动推导反向18

同样的结构、同样的每步挪 0.2、同样 1 000 轮,结果19:

[Test] score/loss: 0.8200 / 0.5561

书里的结论是:测试集准确率与手写版本接近, 说明预定义算子在效果上与手写实现相当,工程上还更简洁19

这里要泼一盆冷水:0.8150 → 0.8200,只涨了 0.5 个百分点。 而第 05 章那条直线是 0.8450——加了一层之后,成绩反而比直线低。

这不是加层没用,是这个例子的三个设置压住了它: 隐藏层只有 5 个神经元、激活用的是那条会把梯度压小的 S 形曲线、 而且两处激活里有一处叠在了输出层上。 第 07 章那两组故障实验会把这三件事逐一量出来。

判断(我们的,不是书里的):书里在这里错过了一个很好的教学机会。 它把两个版本的成绩并排给出、说了「效果相当」, 但没有把这两个数和上一章那条直线的 0.8450 放在一起比。 读者很容易带着「加层就更好」的印象往下读,而这份数据恰恰不支持这个印象。 如果错,会错在: 这两组实验的目的本来就是「验证手写与框架实现等价」, 不是「验证加层有用」;拿它去比直线是我们的加戏。 判据是:去看书里 4.2 与 4.3 两节的小标题,它们说的是「模型优化」和「自动梯度计算」, 确实都不是在比效果。

8. 边界与局限

这一章没覆盖的:

没讲什么依据 / 为什么值得知道
隐藏层该取多宽、网络该有几层全书没有系统讨论,都是直接给一个数
通用近似定理书里的知识点图里列了这个名字,正文没有展开
权重初始化的讲究这一章一律用标准正态随机,第 13 章才讲清楚这里面有坑
除四条之外的其他激活函数书里把它们全放进了动手练习20

出门会撞见的名字(这一节的作用是:你在别人的代码和论文里见到它们,能认出是这里讲的东西):

  • nn.Linear 就是第 4 节那个「乘权重、加偏置」的层;它的两个参数是输入维和输出维;

  • torch.tanh / F.relu / F.leaky_relu 是第 3 节那三条曲线在框架里的名字21;

  • nn.Module 是所有层和整网的公共基类;书里的说法是「模型在结构上本就只是 由若干小层组合而成的大层」,继承它就自动接管了自动求导22;

  • ELU、GELU、Swish 是书里在第 07 章末尾点到、但没有实现的几种更复杂的激活函数; 今天的大模型里最常见的是其中的 GELU,以及一种给它加了开关的变体,第 17 章会讲到;

  • 多层感知器(MLP) 是前馈网络的另一个常见叫法,你在论文里会经常撞见它。

补充(不在书里,来自通用知识): 「感知器」这个词有两种用法—— 指 1950 年代那台只有一层、激活是硬阈值的老机器,也指今天说的「多层感知器」。 读老文献时要看上下文,这两者的能力差得很远。

9. 可带走的

  1. 一个神经元 = 加权求和 + 一道非线性;加权求和的结果叫净活性值;
  2. 那道非线性必须是非线性的,否则多层等价于单层,加层毫无意义;
  3. 偏置的作用是让直线不必穿过原点;
  4. 一层 = 把同一个动作并排做很多份,一次矩阵乘算完,形状是 [N, 输入维] × [输入维, 输出维];
  5. 这本书的布局是「样本数 × 特征数」,和教科书全本刚好转置,读源码要认清是哪一套;
  6. S 形激活的反向里有一项 A(1−A),输出接近 0 或 1 时它接近 0——记住这个形状;
  7. 线性层的反向要算三样:传给上游的、权重的、偏置的;前两样都是「乘到另一侧」;
  8. 广播的反向就是求和,所以偏置的梯度是把上游梯度按样本维加起来;
  9. 手写零件与框架内置零件在这份数据上效果相当(0.8150 对 0.8200);
  10. 但这两个数都低于上一章那条直线的 0.8450——加层本身不保证更好,原因第 07 章讲。

10. 原文地图

主题原书章原文位置
神经元与净活性值第4章 前馈神经网络text/05-ch04.txt:30(搜「神经网络的最小构件」) · text/05-ch04.txt:59(搜「input X」)
激活函数必须非线性第4章 前馈神经网络text/05-ch04.txt:77(搜「激活函数必须是非线性的」)
四条曲线第4章 前馈神经网络text/05-ch04.txt:83(搜「泛指曲线形如」) · text/05-ch04.txt:110(搜「带泄露的」) · text/05-ch04.txt:131(搜「区间内取一系列输入值」)
层的公式与张量布局第4章 前馈神经网络text/05-ch04.txt:193(搜「为了充分利用矩阵运算」) · text/05-ch04.txt:203(搜「本书一律使用」)
前馈网络结构第4章 前馈神经网络text/05-ch04.txt:3(搜「由若干神经元按一定连接结构」) · text/05-ch04.txt:164(搜「前馈网络的连接方式」)
两层网络的输出第4章 前馈神经网络text/05-ch04.txt:292(搜「实例化一个输入维度」) · text/05-ch04.txt:306(搜「0.6587」)
反向传播三步第4章 前馈神经网络text/05-ch04.txt:349(搜「整个训练过程可拆为三步」)
两个反向公式第4章 前馈神经网络text/05-ch04.txt:448(搜「就是反向算子要返回的梯度」) · text/05-ch04.txt:496(搜「还要算」)
两次弯月实验第4章 前馈神经网络text/05-ch04.txt:723(搜「0.8150」) · text/05-ch04.txt:913(搜「0.8200」)
交给自动求导第4章 前馈神经网络text/05-ch04.txt:729(搜「极易在细节上出错」)

Footnotes

  1. 出处:「第4章 前馈神经网络」第 30 段(text/05-ch04.txt:30,搜「神经网络的最小构件」)。原话:神经网络的最小构件是带非线性激活函数的神经元,它把生物神经元的工作机制抽象为接收一组输入信号、按权重线性叠加、再经非线性激活后输出;先把输入做加权求和,得到净活性值。

  2. 出处:「第4章 前馈神经网络」第 59 段(text/05-ch04.txt:59,搜「input X」)。这是书里对两条 5 维样本、一组 5 个权重、一个偏置计算净活性值的实际打印输出,结果为 1.1149 与 1.2194。

  3. 出处:「第4章 前馈神经网络」第 77 段(text/05-ch04.txt:77,搜「激活函数必须是非线性的」)。原话:否则多层网络等价于单层线性变换,无从表达复杂映射。

  4. 出处:「第4章 前馈神经网络」第 83 段(text/05-ch04.txt:83,搜「泛指曲线形如」)。原文:Sigmoid 型函数泛指曲线形如「S」、两端饱和的一类函数,最常用的两个代表是 Logistic 函数和 Tanh 函数。

  5. 出处:「第4章 前馈神经网络」第 110 段(text/05-ch04.txt:110,搜「带泄露的」)。原文写明 λ 是带泄露 ReLU 的超参数,常取 0.01 或 0.1,用于在负半轴保留一小段斜率以避免「死亡神经元」。

  6. 出处:「第4章 前馈神经网络」第 131 段(text/05-ch04.txt:131,搜「区间内取一系列输入值」)。原文接着描述四条曲线:Logistic 和 Tanh 在 0 附近近似线性、两端迅速饱和,输出区间分别为 (0, 1) 和 (−1, 1);ReLU 在负半轴恒为 0,Leaky ReLU 仍保留一段小斜率,避免梯度完全消失。

  7. 出处:「第4章 前馈神经网络」第 193 段(text/05-ch04.txt:193,搜「为了充分利用矩阵运算」)。原文给出两个公式:先算这一层 N 个样本的净活性值,再经激活函数得到活性值。

  8. 出处:「第4章 前馈神经网络」第 203 段(text/05-ch04.txt:203,搜「本书一律使用」)。原话:本书一律使用「样本数 × 特征数」的张量布局,X 由 N 个 x 的行向量堆叠而成;这与《神经网络与深度学习》主书中 x 为列向量的写法刚好转置,因此 W 和 b 的形状也对应转置。

  9. 出处:「第4章 前馈神经网络」第 164 段(text/05-ch04.txt:164,搜「前馈网络的连接方式」)。原文:信号从输入层一层层向输出层推进,每一层把上一层的活性值乘权重、加偏置、再过激活函数;整条链路是单向的、无反馈回路。

  10. 出处:「第4章 前馈神经网络」第 3 段(text/05-ch04.txt:3,搜「由若干神经元按一定连接结构」)。原文:前馈神经网络是最早出现、也是结构最简单的一类人工神经网络;它的信息只沿单一方向逐层向前传递,对应一张有向无环图。

  11. 出处:「第4章 前馈神经网络」第 292 段(text/05-ch04.txt:292,搜「实例化一个输入维度」)。实际输出见第 306 段(text/05-ch04.txt:306,搜「0.6587」)。

  12. 出处:「第4章 前馈神经网络」第 349 段(text/05-ch04.txt:349,搜「整个训练过程可拆为三步」)。原文三步:前向计算每层净活性值和活性值;反向计算每层的误差项;在反向里顺带把每层参数的梯度也算好,再交给优化器统一更新。

  13. 出处:「第4章 前馈神经网络」第 448 段(text/05-ch04.txt:448,搜「就是反向算子要返回的梯度」)。批量形式的公式为 δZ = A ⊙ (1 − A) ⊙ δA;书里同时说明这个激活函数本身没有可学习参数,所以反向只需把梯度反传出去。

  14. 出处:「第4章 前馈神经网络」第 496 段(text/05-ch04.txt:496,搜「还要算」)。原文写明线性层带有可学习参数,因此反向除了传梯度还要算参数的梯度,并给出批量形式的三个公式。

  15. 出处:「第4章 前馈神经网络」第 579 段(text/05-ch04.txt:579,搜「手写的算子目前还不会自动构建计算图」)。原文:所以模型的反向里要手动按顺序调用每一层的反向;同样地,优化器也需要显式遍历各层参数。

  16. 出处:「第4章 前馈神经网络」第 674 段(text/05-ch04.txt:674,搜「共训练 1 000 个回合」)。网络结构与学习率见同段之后的代码:输入 2、隐藏层 5、输出 1,学习率 0.2。

  17. 出处:「第4章 前馈神经网络」第 723 段(text/05-ch04.txt:723,搜「0.8150」)。评语见第 725 段(text/05-ch04.txt:725,搜「已大致拟合出弯月数据的分界线」)。 2

  18. 出处:「第4章 前馈神经网络」第 729 段(text/05-ch04.txt:729,搜「极易在细节上出错」)。原话:手动按层写反向虽然概念清晰,但实战中极易在细节上出错;现代框架早已把这一步交给自动梯度计算——开发者只描述前向,框架顺着计算图自动推导反向。

  19. 出处:「第4章 前馈神经网络」第 913 段(text/05-ch04.txt:913,搜「0.8200」)。评语见第 915 段(text/05-ch04.txt:915,搜「预定义算子在效果上与手写实现相当」)。 2

  20. 出处:「第4章 前馈神经网络」第 158 段(text/05-ch04.txt:158,搜「本节只展开了」)。这是动手练习 4.1,列出的其他激活函数包括 Hard-Logistic、Hard-Tanh、ELU、Softplus、Swish 等。

  21. 出处:「第4章 前馈神经网络」第 153 段(text/05-ch04.txt:153,搜「里 的 对 应 实 现」)与第 100 段(text/05-ch04.txt:100,搜「工程上可直接使用」)。后者同时提醒:两者内部按输入正负做了分段稳定实现,手写版本只用于讲清公式,正式代码不要直接复用。

  22. 出处:「第4章 前馈神经网络」第 731 段(text/05-ch04.txt:731,搜「承担这一抽象的是基类」)。原话:本质上模型就是「由若干小算子组合而成的大算子」,只要内部嵌套的子算子也继承同一个基类,框架就会自动跟踪它们的参数、组建计算图、推导梯度并配合优化器更新所有可学习参数。