从「它凭什么学会」讲起:零基础读懂深度学习
深度学习没有魔法。一个神经网络就是一堆初始随机的数字,每个神经元只做三件事:把输入乘上权重、加起来再加一个偏置、最后过一次非线性函数。所谓「训练」,就是反复测量这些数字该往哪边挪一点,再挪一点。
一个神经元只做三件事,网络要学的全部是权重和偏置
一个神经元的工作只有三步。第一步,每个输入乘上一个权重,权重表示「这个输入对结果有多重要」。第二步,把乘完的结果加起来,再加一个偏置。第三步,把这个和送进激活函数,输出它的值。Goodfellow 等人在《Deep Learning》第 6 章把前向计算写成可数的三步:$h^{(0)} = x$,每层 $a^{(k)} = b^{(k)} + W^{(k)}h^{(k-1)}$,然后 $h^{(k)} = f(a^{(k)})$;同一章还给出隐藏单元的通式:接受输入向量 $x$,先做仿射变换 $z = Wx + b$,再过逐元素非线性函数 $g(z)$。Goodfellow
权重 $W$ 和偏置 $b$ 就是网络要学的全部参数。一开始它们是随机数,「学会」的过程就是这些数字被反复微调到合适的值。至于「凭什么能从数据里学」,Stanford 的 CS231n 讲义给的是最干净的回答:走 data-driven 路线,流程固定为 Input、Learning、Evaluation 三步,我们不在代码里描述「猫长什么样」,而是喂给算法大量带标注的样本,让学习算法从样本里自己学出每个类的样貌。CS231n
同一张网络有两种画法,画的是同一件事。下图左边把每个单元画成一个节点,右边把每层画成一个向量、用矩阵箭头连接。

图注:前馈网络的两种画法。左侧每个单元一个节点,右侧每层一个向量。节点是结构,边上标的 $W$ 与 $w$ 才是参数,而且这种画法省略了截距项。Goodfellow
「层按顺序串起来」这件事在官方文档里有现成的画面。下面这张图是 PyTorch 教程里的经典卷积网络结构,从 32×32 的手写字母输入一路经过卷积、下采样、全连接,最后给出 10 个输出。它是官方另一张网络示例,不是本专题后面要搭的最小全连接网络,用它只看一件事:尺寸如何一层层变化。

图注:层与层按顺序串联、每层尺寸逐级变化的一个真实网络例子。图层之间标注了卷积、下采样与全连接三类操作,右端是 10 个输出。PyTorch
后面会反复用到这些词,先把它们的日常含义放在这里:
| 术语 | 一句话白话 |
|---|---|
| 权重 (weight) | 决定「这个输入有多重要」的可调数字 |
| 偏置 (bias) | 贴在每个神经元上的可调常数,让单元不必从零点起步 |
| 激活函数 (activation) | 加在加权求和之后的非线性函数,决定这个单元能不能「弯」 |
| 梯度 (gradient) | 损失对参数的偏导数,告诉你往哪个方向调能让损失变小 |
| 学习率 (learning rate) | 每一步沿梯度方向走多远,太大会震荡,太小会爬不动 |
| epoch | 把整个训练集完整过一遍算一个 epoch |
| batch | 一次拿多少个样本算梯度再更新参数 |
| embedding | 把离散的对象(词、类别)表示成一串连续数字 |
到这里画面立住了:网络是一堆会被反复微调的权重,每个神经元做三件事。但缺口也出现了:只会加权求和,凭什么学得会?
不加激活函数,多层网络会塌缩成一层线性模型
多层网络一旦没有非线性,就等价于一层线性模型,层数再多也换不来新的表达力。这不是修辞,是一条可以手算的论证。
先看最坏的情况。假设第一层是线性的 $f^{(1)}(x) = Wx$,第二层是 $f^{(2)}(h) = hw$,那么两层接起来是 $f^{(2)}(f^{(1)}(x)) = xWw$。这个函数还能写成 $f(x) = xw'$,其中 $w' = Ww$,也就是两层被压回了「一个矩阵乘输入」。Goodfellow 等人给出的正是这个例子,并写下结论:必须用非线性函数来刻画特征。Goodfellow
把偏置也算进去结论不变:两个仿射变换复合之后仍然是仿射变换,$W_2(W_1x + b_1) + b_2 = (W_2W_1)x + (W_2b_1 + b_2)$,形式还是「一次矩阵乘法加一次加法」。CS231n 讲义把同一件事讲成了新手版:不加非线性,两个矩阵可以塌缩成一个矩阵,预测的类别分数又变回输入的线性函数,非线性才是我们获得「弯」的地方。讲义还提醒另一类问题:sigmoid 两端饱和会「杀死梯度」,tanh 是零中心的缩放版 sigmoid,实践中更常被优先采用,而 ReLU 在学习率过高时可能出现大批「死掉」的神经元。CS231n
下图是按上面这条论证自绘的对照,两条链分别对应「没有激活」和「加了激活」两种网络:
下面这段代码把两种网络放在同一个任务上:拟合一条正弦曲线。第一个网络一层激活都没有,第二个在中间插了一个 ReLU。
import torch
import torch.nn as nn
x = torch.linspace(-3, 3, 200)
y = torch.sin(3 * x)
def fit(net, steps=2000):
opt = torch.optim.SGD(net.parameters(), lr=0.05)
for _ in range(steps):
loss = ((net(x[:, None]).squeeze(-1) - y) ** 2).mean()
opt.zero_grad()
loss.backward()
opt.step()
return loss.item()
plain = fit(nn.Sequential(nn.Linear(1, 32), nn.Linear(32, 1)))
curved = fit(nn.Sequential(nn.Linear(1, 32), nn.ReLU(), nn.Linear(32, 1)))
print(plain, curved)
跑完对比两行损失:没有激活的那个明显降不下去,因为它只能画出一条直线去追正弦曲线;加了 ReLU 的那个能贴着曲线走。
讲义里有一个同类案例给出过具体数字:在 2D 螺旋数据上,线性分类器的训练准确率是 49%,换成带 ReLU 隐藏层的网络后升到 98%,代码改动只是多写一行前向、反向多回传一轮。CS231n
激活函数本身长什么样,2026 年有一篇工作给出了一个统一说法:ReLU、GELU、SiLU/Swish、hard swish 都可以写成同一个带阈值的门 $a_F(z) = z \cdot F(z)$,区别只在门的阈值分布不同。GELU26
| 激活函数 | 门的形式 | 阈值分布 |
|---|---|---|
| ReLU | $\max(0, z)$ | 零点上的确定性硬门 |
| GELU | $z\Phi(z)$ | 高斯阈值 |
| SiLU / Swish | $z\sigma(z)$ | logistic 阈值 |
| hard swish | $z \cdot F(z)$ | 均匀阈值,宽度 $\beta = 3$ |
下面的图就是这篇工作里的 GELU 曲线。

图注:三条曲线同框,绿色是 GELU(z) = zΦ(z),它穿过原点,在负半轴先略微下探形成一个浅凹坑再回到 0,说明「门」并不是硬开关。图中没有 ReLU、SiLU、hard swish 的曲线,不能当成四类激活的同框对比。GELU26
非线性这一步补上之后,网络终于有了弯的能力。可还剩一个更根本的问题:这些权重没有人教,是谁在推着它们动?
误差靠链式法则退回每一层,梯度下降照着它改参数
参数是被「损失往回传的方向 + 一个小步长」推着走的,除此之外没有任何神秘力量。
这套流程在 1986 年有了名字。Rumelhart、Hinton 与 Williams 在 Nature 上发表的那篇 Letter 把方法称作 back-propagation,摘要写得很直白:反复调整网络连接的权重,使实际输出向量与期望输出向量之间的差距度量尽可能小。同一篇论文还给出两句至今仍然站得住的判断:作为权重调整的结果,那些不属于输入也不属于输出的「隐藏」单元会自发表示任务域的重要特征;而创造有用新特征的能力,正是反向传播区别于更早的感知机收敛过程之处。Rumelhart86
具体怎么算,靠的是链式法则。把网络写成计算图:节点是变量,边是「这个变量由那个运算作用于它得到」。下面这张图就是一张真实的计算图,节点是 $X$、$W^{(1)}$、$U^{(1)}$、$H$ 这些变量,边上标着 matmul、relu、cross_entropy 这些运算。

图注:一张有向计算图。上半是前向链 matmul → relu → matmul → cross_entropy 得到主损失;下半支路把权重的平方求和、乘上 λ,并入总损失。图中只有前向箭头,没有画出反向那一趟。Goodfellow
链式法则说的是:复合函数的导数等于各层导数之积。对一个两层的网络 $z_1 = W_1x$、$h = f(z_1)$、$z_2 = W_2h$,损失 $L$ 对两层权重的偏导可以逐层拆开:$\frac{\partial L}{\partial W_2} = \frac{\partial L}{\partial z_2} \cdot h^{\mathsf T}$,$\frac{\partial L}{\partial W_1} = \frac{\partial L}{\partial z_2} \cdot W_2^{\mathsf T} \cdot f'(z_1) \cdot x^{\mathsf T}$。第一个式子里的每一项都是下一步要传下去的东西,第二个式子把它乘上中间那一步的局部导数继续传。Goodfellow 等人把这件事总结成两句:backprop 是让损失的信息沿网络反向流动以计算梯度的算法,它是「以高度高效的运算顺序实现链式法则」的算法,而且它只负责算梯度,真正把参数改掉的是 SGD 这类优化器。Goodfellow
今天不用手写这条链,框架会算。但梯度从哪来、值是多少,你必须亲眼看一次。PyTorch 官方的做法是给张量打上 requires_grad=True,前向算完调 loss.backward(),梯度就存进了参数的 .grad 里:
import torch
w = torch.randn(5, 3, requires_grad=True)
b = torch.randn(3, requires_grad=True)
x = torch.ones(5) # input tensor
y = torch.zeros(3) # expected output
z = torch.matmul(x, w) + b
loss = torch.nn.functional.binary_cross_entropy_with_logits(z, y)
loss.backward()
print(w.grad)
print(b.grad)
官方页面记录的输出形如 tensor([[0.0099, 0.0290, 0.1397], ...]),这是那一次运行的结果。换句话说,梯度确实是从 w.grad 里冒出来的真实数字,而不是某种玄学。PyTorch
官方文档把这张计算图画了出来。注意图里只有前向箭头,没有反向箭头,也没有 grad_fn 的名字。

图注:x 与参数框里的 w 相乘,加上 b 得到 z,再与 y 一起过交叉熵得到 loss。官方正文紧接着这张图写道 This code defines the following computational graph。PyTorch
这里有两个必踩的坑。梯度默认是累加的,同一张图上第二次 backward() 会让梯度翻倍,官方用 inp.grad.zero_() 复原。另外同一张计算图只能 backward() 一次,要多次就得传 retain_graph=True。梯度算出来之后,优化器按学习率把它挪一小步(步长 = 学习率 × 梯度);学习率是一个超参数,实践中常随训练推进逐步调小,这就是梯度下降的日常版本。
现在「训练」这件事变得完全透明了:前向算出预测、算出损失、反向算出每层参数的梯度、优化器按梯度把参数挪一小步。可还有最后一个问题:「训练误差降下来了」和「学会了」是一回事吗?
训练集全对证明不了学会,泛化才作数
判断学会与否要看泛化误差,也就是模型在没见过的数据上的表现。训练误差小这件事本身不含任何关于学会的信息。
这个判断可以被证伪,而且证伪它的实验干净得出奇。Zhang 等人在 2016 年的工作里做了一次「随机化测试」:只把训练标签换成随机标签,模型、模型规模、超参、优化器全部不动。论文的结论是,深度网络「轻松拟合随机标签」,训练误差照样归零,而测试误差退回随机水平。在 CIFAR10 上,小 Inception 在随机标签下训练准确率仍然是 100.0%,测试准确率掉到 9.78%,也就是纯随机水平。Zhang2017

图注:三个并排折线图。(a) 是训练损失随训练步数下降,真实标签与随机标签两条曲线最后都走到 0,随机标签那条只是降得更晚;(c) 是测试误差随标签噪声上升,最后停在 0.9 附近,那是 CIFAR10 的随机猜测水平。(a) 画的是损失不是准确率。Zhang2017
理论侧给出了对应的构造:只要参数量超过样本量,哪怕只是两层带 ReLU 的网络,也足以在有限样本上完美插值任意函数,论文的定理给的是 $2n + d$ 个权重就能表示 $n$ 个 $d$ 维样本上的任意函数。「背题」这件事在原理上根本没有难度。
但这份结论不能被读成「网络学不会」。同一批架构在真实标签下的测试准确率仍然有 76% 到 89%(这是论文自身设定下的数字),作者自己的结论也不是网络没用,而是优化容易的原因和泛化的原因并不相同。换句话说,训练误差小只是「学会」的必要条件,不是充分条件。
那怎么救。Goodfellow 等人在第 7 章把四种手段的针对性讲得很清楚,它们各治一种病,也可以按需叠加使用:
| 手段 | 治什么 | 本质 |
|---|---|---|
| L2 正则 / weight decay | 参数被推得过大、模型太灵活 | 在目标函数里加权重范数惩罚,把权重压向原点 |
| Dropout | 神经元之间过度依赖、互相搭便车 | 训练时随机丢掉一部分单元,等价于训练一个子网络集合 |
| 早停 | 训练继续下去开始记住细节 | 训练误差还在降但验证误差已经回升,回到验证误差最低处 |
| 数据增强 | 训练样本不够多样 | 人造「假数据」扩训练集,但变换不能改变类别标签 |
其中数据量那一条最朴素也最有效,讲义里的原话是:让模型泛化得更好的最好办法就是用更多数据训练它。这正好接回「训练集全对、新数据全错」时最容易想到的那一层原因:数据太少、模型见的不够多,是真的原因之一,但前提是先分清模型到底是在学规律还是在背细节。Goodfellow
到这里,「机器凭什么能从数据里学会」这个问题已经有了完整答案:它靠加权求和非线性搭出表达力,靠链式法则和梯度下降把参数推到合适的位置,而判断它是否真的学会,要看它没见过的数据。现在把同一套机制搬到今天的主干架构上,就是注意力机制。
注意力按查询—键—值给词分配权重,但热力图不是因果解释
注意力确实在给每个词分配权重,Transformer 也因此成为今天的主干,但热力图说明的是「看了哪里」,不是「为什么这么判断」。
机制本身只有四步。给定查询 $Q$、键 $K$、值 $V$,先算查询和每个键的匹配程度,再把它按维度缩放,取 softmax 变成权重,最后用这组权重对值做加权求和:
$$\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^{\mathsf T}}{\sqrt{d_k}}\right) V$$
除以 $\sqrt{d_k}$ 有两重理由。正文的说法是:当 $d_k$ 较大时点积的幅度会变大,把 softmax 推进梯度极小的区域,于是要用 $1/\sqrt{d_k}$ 抵消;附录 A 给出算术理由,若 $q$、$k$ 各分量独立、均值 0 方差 1,则 $q \cdot k$ 的方差是 $d_k$,除以 $\sqrt{d_k}$ 就把方差拉回 1。Vaswani17
下面这张图上半正是这四步,下半是「多头」到底是什么。

图注:上半是缩放点积注意力,Q 与 K 过 MatMul 后依次经过 Scale、Mask(可选)、SoftMax,再与 V 相乘得到输出;Scale 方框只写了 Scale,1/√d_k 来自正文公式,掩码只在解码器自注意力里需要。下半是多头注意力:V、K、Q 各经 Linear 投影后并行做注意力,再 Concat 后过一层 Linear。Vaswani17
手算一遍最省事的方式是压到 2 维、用手写向量。下面这个玩具例子把「猫 很 可爱」三个词各给一组 key、query、value,query 取「可爱」那一行,得到的权重是:
| query / key | 猫 | 很 | 可爱 |
|---|---|---|---|
| 可爱 | 0.248 | 0.248 | 0.504 |
算式很短:$q \cdot k$ 三个值分别是 1、1、2,除以 $\sqrt{2}$ 后是 0.707、0.707、1.414,取指数得 2.03、2.03、4.11,除以和 8.17 就是表里那一行。用这组权重去加权 value,得到输出 $[0.752, 0.752]$。换成真正的模型,向量和维度都是学出来的,但算式分毫不差:
import torch, math
tokens = ["猫", "很", "可爱"]
K = torch.tensor([[1., 0.], [0., 1.], [1., 1.]]) # 每个词的 key
Q = torch.tensor([[1., 0.], [0., 1.], [1., 1.]]) # 每个词的 query
V = torch.tensor([[1., 0.], [0., 1.], [1., 1.]]) # 每个词的 value
d_k = K.shape[-1]
W = torch.softmax(Q @ K.T / math.sqrt(d_k), dim=-1)
print(W.round(decimals=3))
注意力不是只做一次。多头注意力的做法是把 Q、K、V 各自用不同的、经过学习的线性投影投影 $h$ 次,在这 $h$ 份投影上并行做注意力,输出拼接后再过一次线性投影。论文给的作用只有一句:多头注意力让模型能够同时关注来自不同表示子空间、位于不同位置的信息,单头情况下的平均化会抑制这种能力。论文用的是 $h = 8$、$d_k = d_v = d_{model}/8 = 64$,并说明由于每个头维度被压小,总计算量与全维度的单头注意力相当。

图注:Transformer 的整体骨架。编码器与解码器都是若干层堆叠,每个子层外包一条残差连接并接 Add & Norm;解码器中间那个多头注意力吃的是编码器输出,也就是交叉注意力。底部输入嵌入旁有加号圆圈,表示位置编码是加上去的。Vaswani17
注意力换掉循环结构,理由是结构性的:自注意力层用常数个顺序操作连接所有位置,而循环层需要 $O(n)$ 次;循环模型固有的顺序性还排除了训练样本内部的并行化。Transformer 因此能在少得多的时间里训完更大的模型,也就成了今天大语言模型的主干。
今天这条路上有哪些分支,可以用一个三维框架看:效率、表达力、可解释性。2026 年的一篇综述把注意力方法族全部放进这三个轴上,并给出一条 2015 到 2026 的演化时间线。

图注:一条 2015 至 2026 的时间轴,下方三段色带把这条线分成基础期、稀疏与线性期、混合与系统期。轴上 2026 年的三个节点在该综述写作时都还是 arXiv 预印本;综述另注明 Mamba-3 后来已在 ICLR 2026 发表,Focus 与 DashAttention 至今无独立复现。轴上的位置是示意性的,不是穷尽清单。EEI26
这份综述自己声明检索窗口止于 2026 年 5 月,所以它只能用来讲「今天有哪些路」,不能代表此后半年的进展。它给出的分数也是单一评分者的序数判断,只支持粗粒度分层,不支持「A 比 B 好」这种细排名。EEI26
而热力图这件事需要单独降级期待。Jain 与 Wallace 2019 年的工作指出,如果注意力权重真的提供了忠实解释,那么换一套注意力分布就该带来相应的预测变化。实际结果是:可以构造出一套与原分布差异极大的注意力权重,模型给出的预测几乎不变。Jain2019

图注:同一段英文影评的两栏词级注意力着色。左栏高亮 asking 与 waste,右栏高亮 myself 与 was,两栏给出的预测值都是 0.01。两套注意力明显不同,输出却相同。Jain2019
这张图的意思不是「注意力没用」,而是「热力图不足以唯一归因」。论文自己的告诫是:不要用注意力权重去标出那些「对输出负责」的词,再据此编一个自圆其说的故事。
连「把某个注意力头置零再看性能掉多少」这种干预也要审计。2026 年的一篇预印本发现,GPT-2 的注意力块先把各头输出拼接、再过一个输出投影 $W_O$ 混进残差流。如果置零动作落在投影之后,被置零的就不是「一个头」,而是所有头在若干输出通道上的某个线性组合。同一个模型、同一个输入、同样的名义头编号,两种置零方式得到的每头效应排序几乎不相关,top-5 名单一个都不重合。Huang2026

图注:散点图把两种置零方式的每头效应并排对照,横轴是投影前置、纵轴是投影后置。点并没有落在这条 y=x 参考线上,而是密集聚在原点附近。r = 0.057 是 GPT-2 small 在特定受控复制任务下的一个数,不是普适常数。Huang2026
这篇工作给出的三条可操作要点是:核对干预落在投影的哪一侧;用没有被饱和的连续指标而不是二元准确率;在没有参与选头的数据上做匹配对照。
自己跑通一遍:同一套骨架在 PyTorch、飞桨、TensorFlow 上长什么样
这套体系在三个框架里是同一套骨架,学会一个就等于学会三个,剩下的是查文档和改数据的活。
最小可运行流程固定就是这几步,写法以 PyTorch 官方教程为准。下面这段把模型定义、训练与评估放在一起,train_loader 与 test_loader 由 DataLoader 包住官方教程提供的 FashionMNIST 数据集得到:
import torch
from torch import nn
class NeuralNetwork(nn.Module):
def __init__(self):
super().__init__()
self.flatten = nn.Flatten()
self.linear_relu_stack = nn.Sequential(
nn.Linear(28*28, 512), nn.ReLU(),
nn.Linear(512, 512), nn.ReLU(),
nn.Linear(512, 10),
)
def forward(self, x):
return self.linear_relu_stack(self.flatten(x))
model = NeuralNetwork()
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=1e-3)
for epoch in range(5):
model.train()
for X, y in train_loader:
pred = model(X) # 前向
loss = loss_fn(pred, y) # 算损失
loss.backward() # 回传梯度
optimizer.step() # 更新参数
optimizer.zero_grad() # 清梯度
model.eval()
test_loss, correct = 0.0, 0
with torch.no_grad():
for X, y in test_loader:
logits = model(X)
test_loss += loss_fn(logits, y).item()
correct += (logits.argmax(1) == y).sum().item()
print(f"epoch {epoch}: loss {test_loss/len(test_loader):.4f}, acc {correct/len(test_loader.dataset):.4f}")
跑完之后打印 model.named_parameters(),你会看到六行,每一行对应一层的一个参数:
linear_relu_stack.0.weight [512, 784]
linear_relu_stack.0.bias [512]
linear_relu_stack.2.weight [512, 512]
linear_relu_stack.2.bias [512]
linear_relu_stack.4.weight [10, 512]
linear_relu_stack.4.bias [10]
参数名里带着层号,「哪一层的参数被改了」这个问题有了确定答案。继承 nn.Module 会自动注册模型里定义的字段,所有参数都能通过 parameters() 或 named_parameters() 取到。另外注意最后一层线性层输出的是 logits(可以是无穷大的原始分数),要过 nn.Softmax(dim=1) 才是概率。PyTorch
把同一套骨架换到另两个框架,差别几乎只在命名:
| 环节 | PyTorch | 飞桨 PaddlePaddle | TensorFlow / Keras 3 |
|---|---|---|---|
| 建层 | nn.Module 子类 | paddle.nn.Layer 子类 | tf.keras.Model / Sequential |
| 前向 | model(X) | model(x) | model(x) |
| 算损失 | nn.CrossEntropyLoss() | paddle.nn.CrossEntropyLoss() | tf.keras.losses.SparseCategoricalCrossentropy() |
| 求梯度 | loss.backward() | loss.backward() | tape.gradient(loss, vars) |
| 更新参数 | optimizer.step() | optimizer.step() | optimizer.apply_gradients() |
| 清梯度 | optimizer.zero_grad() | optimizer.clear_grad() | 用完自行把 grads 置零 |
飞桨官方文档还做了一件对读代码的人很友好的事:它把高层 API 一层层拆回基础 API,明确写出 Model.prepare、Model.fit 这些封装背后就是下面这段循环,所以两个框架的骨架确实是一回事。Paddle
下面这张图是飞桨官方给出的项目阶段地图,左边是阶段,右边是对应的 API 命名空间。

图注:左右分栏的模型开发流程。左侧自上而下是数据集定义与加载、数据预处理、模型组网、模型训练与评估、模型保存与载入、模型推理六个阶段;右栏灰底列出每一阶段对应的 API 名称。图右下角自注这里只呈现部分核心 API,它画的是用户侧阶段,不是底层调度。Paddle
TensorFlow 这一侧的自动求导换了外形但机制相同:用上下文管理器 tf.GradientTape() 记录前向操作,出作用域后用 tape.gradient(loss, variables) 取梯度,再交给优化器更新。Keras 3 已经变成框架无关层,可以在 jax、tensorflow、torch 三个后端之间切换。TensorFlow
版本现状(核验日期 2026-11-04):截至 2026 年 11 月的稳定版,PyTorch 是 2.15,2026-10-28 发布,并且不再支持 Python 3.10,需要 3.11 以上。飞桨上游最新发布是 v3.4.0,2026-09-11,但 PyPI 上可安装的最新 wheel 仍是 3.3.1。TensorFlow 的稳定版是 2.21.0,2026-03-06 发布,2.22.0 目前仍是 rc 预发布,不能当成稳定版能力;Keras 稳定版是 3.15.1。这些是动态事实,装之前应当以官方页面再核一次。
最后用三条常见误解收口。深度学习并没有取代机器学习,它是机器学习的一个分支,区别只是把特征工程换成了让网络自己学特征。网络也不是越深越好,讲义给出的经验边界是三层通常优于两层,再深帮助不大;而且不要因为怕过拟合就缩小模型,该做的是用正则化去控制。至于注意力权重能不能当解释,前面已经讲清了:它说明的是看了哪里,不是为什么这么判断。
CS231n 讲义把卷积网络与循环网络的分工讲得比较清楚:卷积网络处理有空间层次结构的数据,循环网络处理长度可变的序列。CS231n 而 Transformer 是 2017 年之后文本与大模型的主干,这一条的依据是 Vaswani 等人的 Transformer 论文,不是本课程讲义正文。Vaswani17 这三者不是替代关系,而是各自数据形态上的分工。
参考文献
本专题引用的核心资料如下,建议深入学习时阅读原文:
- Deep Learning(Goodfellow, Bengio, Courville),MIT Press 官方免费在线版 — https://www.deeplearningbook.org/
- Learning representations by back-propagating errors(Rumelhart, Hinton, Williams, Nature vol 323, 1986) — https://www.nature.com/articles/323533a0
- Understanding deep learning requires rethinking generalization(Zhang et al., ICLR 2017) — arXiv:1611.03530
- Attention Is All You Need(Vaswani et al., 2017) — arXiv:1706.03762
- A Structural Interpretation of GELU and Threshold-Transmission Activations via the First-Order Loss Function(Rossi, 2026) — arXiv:2607.03664
- Not All Attention Is Equal: A Quantitative Survey of the EEI Trade-off(Singh, 2026) — arXiv:2608.15459
- Attention is not Explanation(Jain & Wallace, NAACL 2019) — arXiv:1902.10186
- When Do Attention-Head Ablations Support Causal Claims?(Huang, 2026) — arXiv:2610.00373
- CS231n: Deep Learning for Computer Vision(Stanford 课程讲义) — https://cs231n.github.io/
- PyTorch 官方入门教程(Quickstart / Build Model / Autograd / Optimization Loop) — https://docs.pytorch.org/tutorials/beginner/basics/quickstart_tutorial.html
- 飞桨 PaddlePaddle 官方中文文档(快速开始 / 模型组网) — https://www.paddlepaddle.org.cn/documentation/docs/zh/index.html
- TensorFlow 官方教程与 Keras 指南 — https://www.tensorflow.org/tutorials