结论先行
5 集按"结构 → 学习 → 分析 → 反向传播直觉 → 反向传播数学"递进,约 1.5 小时看完:
| 集 | 主题 | 建立的核心心智模型 | 在 AI/工程里的落点 |
|---|---|---|---|
| 1 | 什么是神经网络 | 神经元=存 0~1 数字的"东西";层=从像素到边缘到模式到数字的抽象阶梯;权重网格=该神经元检测的像素模式;整个网络=一个函数 a^(n+1) = σ(Wa^(n) + b) |
任何 Wx+b 前向传播;为什么必须有非线性激活(否则多层=单层线性);ReLU vs sigmoid |
| 2 | 梯度下降 | 学习=求函数最小值;负梯度=13,002 维空间里的"下坡方向",每个分量=对应权重的调整量与敏感度 | 学习率 η 的权衡;局部最小值;为什么激活值要连续可导 |
| 3 | 分析训练好的网络 | 训练完≠学会了"人设想的特征":二层的权重是"乱糟糟的团块"而非干净边缘;对噪声也会自信地乱猜 | 泛化能力、训练分布约束(居中/固定尺寸)、为什么需要 CNN(权重共享让模式迁移到任意位置) |
| 4 | 反向传播(直觉) | 从输出层倒推:想让哪个神经元变亮 → 调偏置/调权重(按激活值比例)/改上层激活 → 各神经元诉求叠加 → 递归回传;SGD 小批量加速 | "fire together, wire together"(Hebb 理论);batch size 选择;SGD 像"醉汉快速下山" |
| 5 | 反向传播(数学) | 链式法则分解依赖树:∂C/∂w = a^(L-1)·σ'(z)·2(a^(L)-y);多层=长链相乘,分叉=多路径求和 |
框架自动求导(autograd)的本质;读懂框架报错时的梯度张量形状(层号上标 + 神经元下标) |
工程背景读者的验收标准:能口述"学习=代价函数求最小值"和"反向传播=链式法则沿着依赖树倒着乘、分叉处求和"这两个故事,并知道每集对应的公式长什么样,不要求手推。
第 1 集:什么是神经网络?(But what is a Neural Network?)
网络结构
- 神经元 :先当"存一个 0.0~1.0 数字的东西"理解,这个数字叫激活值(activation)。输入层 784 个神经元对应 28×28 像素灰度值,输出层 10 个神经元对应 0-9 的置信度。
- 隐藏层:本例 2 层 × 16 神经元(数量是拍脑袋选的,实践中有自由)。
- 层间连接的权重(weight):正权重=该输入激活时希望此神经元也激活;负权重=相反。把 784 个权重摆回 28×28 网格,就是"这个神经元在检测什么像素模式"------例如"中心区域亮 + 上下边缘暗"=检测一条短横边。
- 偏置(bias):加在加权和上的常数,控制"加权和要多大神经元才有意义地激活"(阈值作用)。
- 本例网络共 13,002 个权重和偏置------"学习"就是给这 13,002 个旋钮找到最优设置。
为什么用层(分层抽象)
- 认数字时人是"像素 → 边缘 → 环/直线等模式 → 数字"逐层组合的;希望网络第二层神经元检测小边缘、第三层检测环和长线条、最后一层组合成数字。
- 层化结构的价值:把大问题拆成每层一小步,同样的抽象阶梯可用于语音(声波→音节→词→短语)。
单神经元计算与紧凑记号
- 一个神经元的激活 = 上一层所有激活的加权和 + 偏置,再过 sigmoid 压到 0~1:
a_0^(1) = σ(w_00·a_0^(0) + w_01·a_1^(0) + ... + w_0n·a_n^(0) + b_0) - 矩阵紧凑记号(整层同时算):
a^(n+1) = σ(W·a^(n) + b)------每行 W 对应一个神经元的连接,向量 b 一次加上,sigmoid 逐元素应用。这也是为什么 ML 硬件(TPU 等)拼命优化矩阵乘法。 - 整个网络就是一个函数:784 个输入数字 → 10 个输出数字,13,000+ 参数,由多层"矩阵乘 + 非线性挤压"迭代而成。
附注:sigmoid 的毛病(ReLU 登场)
- sigmoid 在两端太平坦:权重再怎么微调输出几乎不动 → 训练慢("13,000 维的你热我冷游戏"每步反馈都弱)。
- ReLU(负输入输出 0,正输入原样)永不平坦,微调总有反馈,深网络训练快得多。
- 为什么必须有一层非线性?若每层只有缩放+偏移,整个网络退化为一次矩阵乘法(线性),表达能力骤降。(非线性为何重要:Chris Olah 的 linear separability 可视化文章。)
第 2 集:梯度下降,神经网络如何学习(Gradient descent, how neural networks learn)
代价函数(Cost Function)
- 权重偏置随机初始化 → 输出是垃圾。代价 = 各输出神经元"实际值 − 期望值"的平方和(单样本);总代价 = 对几万条训练样本取平均。
- 关键视角:网络是函数(像素→数字),代价函数是盖在网络之上的一层函数------输入是 13,002 个权重偏置,输出一个"这批参数有多烂"的数。
- 训练集/测试集分离:训练完用没见过的数据测泛化。
梯度下降
- 13,002 维的函数没法直接解最小值 → 从随机点出发,反复朝下坡方向迈步。
- 单变量看斜率;多维用梯度 ∇C (最陡上升方向向量)→ 负梯度 −∇C 即下坡方向,其模长=坡度。
- 每步更新:
参数 ← 参数 − η·∇C,η 是学习率:大→快但可能过冲震荡,小→稳但慢。步长与坡度成正比,接近谷底自然收步,不易过头。 - 落到 13,002 维:负梯度也是 13,002 分量向量,每个分量=对应权重该往哪挪、挪多少才最划算------梯度编码了每个参数的重要性(性价比)。
- 计算这个梯度向量的高效算法=反向传播(下一集)。
- 注意:只能收敛到局部最小值,起点不同结果不同;且代价函数必须光滑 → 这正是人工神经元激活值连续取值(而非生物神经元 0/1)的原因。
第 3 集:分析我们的神经网络(Analyzing our neural network)
- 链接:图文(2017-10-16,短小精悍)
- 讲什么:先"剧透"看训练结果,建立对这类网络的合理预期。
训练效果
- 这个 2×16 的网络对没见过的新图准确率约 96%,调调结构可到 98%;现代网络(更花哨的变体)MNIST 上可达 99.75%。
- 从没被告知任何模式,却能在未见过的图上做对------这就是"学习"的可信之处。
层里实际学到了什么(反直觉!)
- 第二层权重网格并不是期望的小边缘检测器,而是近乎随机的松垮团块------网络在 13,002 维空间里找到了一个能分类好但不可解释的局部最小值。
- 喂一张随机噪声图,它会自信地给出某个数字(比如笃定是 5),而不是表达不确定------说明它"会认不会画",不理解数字长什么样。
- 根源是训练约束太死:所有样本都是居中、定尺寸的清晰数字,代价函数从不奖励"不确定"。把图放大/缩小/移一移,它就懵了。
承上启下
- 这个"80-90 年代的老技术"是理解现代变体的起点。想清楚"怎么改结构让学到的模式能从一个位置迁移到任意位置",就自然引出**卷积神经网络(CNN)**的权重共享。
- 动手:用第 1 集的手画 Demo,画 3 慢慢延伸成 8,观察二层的哪些神经元在预测翻转点改变、为什么改变。
第 4 集:反向传播到底在做什么?(What is backpropagation really doing?)
核心机制(以某样本、某输出神经元为例)
目标是提升"数字 2"这个输出神经元的激活,有三条路可走:
- 改偏置:最直接、效果恒定可预测------想让它更亮就加大它的偏置。
- 改权重 :权重乘的是上一层激活值,所以与"上层激活大"的那些连接调权重性价比最高------按激活值比例调。这就是 Hebb 理论"一起放电的神经元连得更紧"的数学版镜像:看到 2 时活跃的连接,与"应该被激活为 2"更紧地绑定(区别是"想成 2"由标签硬编码,不是网络真在"想")。
- 改上一层激活 :与正权重相连的想更亮、负权重的想更暗,按权重大小比例改性价比最高。
回传(propagate backward)
- 我们不能直接改激活,只能改权重偏置。于是把"希望此神经元如何变"的记录传给上一层 :每个输出神经元都对第二层各有一份按比例的建议,10 份诉求相加得到该层总体的期望调整。
- 然后对这一层重复同样过程,一层层递归到输入端------这就是"反向"传播。
- 对所有训练样本各算一份诉求再平均 → 就是(正比于)代价函数的负梯度。
随机梯度下降(SGD)
- 每个下降步都遍历全部几万样本太慢 → 打乱数据切成 mini-batch(如每批 100 条),按批次近似算梯度:单步不准但每步只要 1/100 时间,100 步后每条样本都发过声。
- 形象说法:从"精算者慢步下山"变成"醉汉快步踉跄下山"。
第 5 集:反向传播的数学(Backpropagation calculus)
记号约定
- 上标
^(L)=所在层(L 是最后一层,不是指数);下标=层内第几个神经元;权重w_jk^(L)= 第 (L−1) 层第 k 个神经元 → 第 L 层第 j 个神经元。 z_j^(L) = Σ_k w_jk^(L)·a_k^(L-1) + b_j^(L)(加权和),a_j^(L) = σ(z_j^(L))。- 单样本代价
C_0 = Σ_j (a_j^(L) − y_j)²;总代价C = (1/n)Σ C_k。
单神经元单层的链式法则(本集公式核心)
把 ∂C_0/∂w^(L) 沿依赖链 w → z → a → C_0 拆成三段相乘:
∂C_0/∂w^(L) = ∂z/∂w · ∂a/∂z · ∂C_0/∂a
= a^(L-1) · σ′(z^(L)) · 2(a^(L) − y)
∂z/∂w = a^(L-1):上一层激活越大,调该权重对代价影响越大("fire together, wire together"的数学体现)。∂C_0/∂a = 2(a−y):离期望越远,微调激活越值钱。- 偏置的导数更简单 :
∂z/∂b = 1,其余完全相同 →∂C_0/∂b^(L) = σ′(z)·2(a−y)。 - 对全部样本求平均即得该权重对应的梯度分量:
∂C/∂w = (1/n)Σ_k ∂C_k/∂w。
向前面层回传
- 上一层神经元的激活由它自己的权重偏置决定 → 继续链式法则:
∂z^(L)/∂a^(L-1) = w^(L),于是∂C_0/∂a^(L-1) = w^(L)·σ′(z^(L))·2(a^(L)−y)。 - 多神经元时唯一新东西:一个 (L−1) 层神经元通过多条路径影响代价(连到 L 层每个神经元) ,所以敏感度要把所有路径的链式乘积求和 :
∂C_0/∂a_k^(L-1) = Σ_j [ w_jk^(L) · σ′(z_j^(L)) · ∂C_0/∂a_j^(L) ] - 之后对再前一层重复同一操作------长链相乘、分叉处求和,即可算出整个梯度向量 ∇C。
全系列的落点(作者结语,值得记住)
- 矩阵乘法优雅地承载层间信息传播;代价函数把"智能"这个模糊概念变成可微积分的对象(求最小值);梯度/导数给出找最小值的具体手段;链式法则把疯狂复杂的依赖网干净地拆成对每个权重的敏感度分析。
- 实现层面这些公式最终会被封装成漂亮的向量表达式(框架自动求干的事);真正可迁移的理解是沿依赖链拆解敏感度的思维方式。
系列外的补充与去向
- 本系列 5 集是 2017 年的"经典 MLP + MNIST",文中自述是"旧技术但必须理解的起点"。后续 3b1b 出了 交叉熵(Cross-Entropy) 等补充集,与输出层损失函数直接相关,可作第 6 集看。
- 文中提到的进阶方向:CNN(权重共享/模式迁移,见第 3 集)、Transformer/RNN(第 1 集提到但全系列不展开)。
- 动手路线:看完 1-2 集后自己写一个 784→16→16→10 的网络(NumPy 即可,约百行代码),亲手实现前向 + 反向传播对照第 5 集公式,比只看不练收益大一个量级。