土法炼钢兴趣小组的算法知识备份

【Transformer 与注意力机制】04. 函数与神经网络:从 y=f(x) 到一台可学习的拟合机器

文章导航

分类入口
transformer
标签入口
#transformer#神经网络#函数#万能逼近#深度学习

目录

一、从一句最朴素的话开始

如果你问我「神经网络到底是什么」,我会先把所有教材合上,然后给你一句朴素得近乎敷衍的话——神经网络就是一个函数

仅此而已。它接收一些输入,吐出一些输出。中间有什么呢?有一些可以调节的旋钮——我们叫它们参数 \(W\)\(b\)。所谓「训练」,就是不断地拧这些旋钮,让函数的输出越来越像我们想要的样子。所谓「推理」,就是把旋钮固定下来,让函数对新的输入做出预测。

这个视角看似平淡,却是我自己学习深度学习这么多年来最受用的一句话。它能帮你在被各种花哨的术语——Transformer、注意力、残差连接、LayerNorm、Mixture of Experts——淹没的时候,迅速拉回到一个简单的问题:「这一坨东西,作为一个函数,输入是什么、输出是什么、参数在哪里?」一旦回答清楚这三件事,你对它的理解就立住了一半。

所以本篇我们不急着讲注意力,也不急着讲 Transformer。我们退回到中学数学课,从 \(y = f(x)\) 开始,一砖一瓦地把「神经网络」这座房子搭起来。前三篇我们已经备好了砖头:向量、点积、矩阵乘法。这一篇我们用这些砖头砌墙。

二、\(y = f(x)\):你早就认识它了

中学数学课上,老师写下 \(y = f(x)\) 的时候,可能配了一句轻飘飘的解释:「\(y\)\(x\) 的函数」。当时我没觉得这有什么了不起。直到很多年后,我才意识到,这个等号是整个现代科学的支点之一

它说的是什么?它说,对每一个输入 \(x\),都有一个唯一确定的输出 \(y\)。函数 \(f\) 是这种映射关系的「打包」。你可以把它想象成一个黑盒:把 \(x\) 投进去,它吐 \(y\) 出来。

最简单的函数是常数函数,\(f(x) = 3\),无论你输入什么,它都吐出 3。再复杂一点是一次函数,\(f(x) = ax + b\),它有两个旋钮:斜率 \(a\) 和截距 \(b\)。再复杂一点是二次函数 \(f(x) = ax^2 + bx + c\),它有三个旋钮。再复杂一点是多项式、三角函数、指数函数……我们整个高中和大学低年级,都在学习各种各样的「具体的」函数。

但深度学习问的问题是反过来的——给我一堆 \((x, y)\) 数据点,请你猜出那个未知的 \(f\) 是什么样子

这个问题叫做「函数拟合」或「回归」。它有一些经典做法:最小二乘、样条、核回归、支持向量回归……每一种做法都有它的假设和限制。深度学习的做法是:我不假设 \(f\) 是某个特定形式,我用一个非常灵活的「函数空间」去逼近它。这个函数空间,就是神经网络。

三、向量函数:从一维到多维

中学的 \(y = f(x)\) 里,\(x\)\(y\) 都是一个数。但现实世界很少有这么简单的关系。

预测房价:输入是面积、卧室数、楼层、地段、装修……几十上百个数,输出是价格。 图像分类:输入是 \(224 \times 224 \times 3 \approx 15\) 万个像素值,输出是 1000 个类别上的概率分布。 机器翻译:输入是一句英文(变长向量序列),输出是一句中文(也是变长向量序列)。

这些任务的输入输出都是向量,甚至是矩阵、张量。所以现代的「函数」长这样:

\[\mathbf{y} = f(\mathbf{x}), \quad \mathbf{x} \in \mathbb{R}^n, \mathbf{y} \in \mathbb{R}^m\]

它把 \(n\) 维向量映射到 \(m\) 维向量。\(n\)\(m\) 可以非常大——在大语言模型里动辄几万、几十万维。

我之所以在这里多花一段笔墨,是因为很多初学者一开始没有把「函数」从「一维」推广到「多维」的意识。他们脑子里 \(y = f(x)\) 永远是一条曲线。但在神经网络的世界里,一条曲线只是退化的特例。绝大多数时候,你要想象的是一个高维空间里的「映射」:左边是一个云团(输入分布),右边是一个云团(输出分布),函数 \(f\) 把左边的每一个点对应到右边的某个点。这个画面比「曲线」更接近真相。

四、最简单的可学习函数:线性变换

回到最简单的情形:\(y = ax + b\)。这是一条直线,有两个旋钮 \(a, b\)。给我一堆 \((x_i, y_i)\) 数据点,最小二乘可以告诉我最佳的 \(a, b\)。这就是一次函数的「学习」。

把它推广到向量。输入是 \(n\) 维向量 \(\mathbf{x}\),输出是 \(m\) 维向量 \(\mathbf{y}\)。最自然的「直线推广」是:

\[\mathbf{y} = W \mathbf{x} + \mathbf{b}\]

这里 \(W\) 是一个 \(m \times n\) 的矩阵,\(\mathbf{b}\) 是一个 \(m\) 维向量。它的旋钮个数是 \(m \times n + m\)

这个公式看起来朴素,但它就是神经网络中最基本的积木——线性层(Linear Layer / Dense Layer / Fully Connected Layer,名字一堆,本质都一样)。PyTorch 里的 nn.Linear(n, m)、TensorFlow 里的 Dense(m)、Transformer 里 Q/K/V 的投影、FFN 的两层、输出 logits 的投影……全是这个公式。

我学神经网络的时候,被各种「层」迷惑了好久——卷积层、池化层、Dropout 层、BatchNorm 层、LayerNorm 层、Embedding 层、Attention 层……后来才意识到,真正具有可学习参数、真正在做「线性映射」的,绝大多数都是同一个公式 \(W\mathbf{x} + \mathbf{b}\)。其他「层」要么是固定的非线性变换(激活、归一化),要么是参数极少的特殊形式。

所以记住这个公式。它是接下来一切的起点。

五、为什么仅有线性是不够的

一个朴素的问题:既然线性变换 \(W\mathbf{x} + \mathbf{b}\) 这么强,我们叠它一千层,是不是就有一个超强的函数了?

来算一下。两层线性层串起来:

\[\mathbf{y} = W_2 (W_1 \mathbf{x} + \mathbf{b}_1) + \mathbf{b}_2 = (W_2 W_1) \mathbf{x} + (W_2 \mathbf{b}_1 + \mathbf{b}_2)\]

\(W_2 W_1\) 记作 \(W'\)\(W_2 \mathbf{b}_1 + \mathbf{b}_2\) 记作 \(\mathbf{b}'\),结果是:

\[\mathbf{y} = W' \mathbf{x} + \mathbf{b}'\]

——还是一个线性变换。

无论你叠多少层线性变换,它的总效果都等价于一层线性变换。这是线性代数的基本结论:线性变换的复合还是线性变换

这是一个让人哭笑不得的事实。它意味着:如果你只用 \(W\mathbf{x} + \mathbf{b}\),你的整个网络再深,也只能拟合「直线」(在多维空间里是「超平面」)。它无法拟合任何弯曲的、复杂的关系。

而现实世界几乎所有有意思的关系都是非线性的。房价不是面积的线性函数(小户型每平米贵)。图像中的猫不是像素的线性函数(差一个像素就可能从猫变成狗)。语言不是字符位置的线性函数(一个 not 就能把整个意思反过来)。

所以神经网络必须引入非线性。而非线性的引入方式,就是在两个线性层之间插入一个激活函数。下一篇我们专门讲激活函数;这一篇我们先用最简单的 ReLU 来占个位置:

\[\text{ReLU}(z) = \max(0, z)\]

它把负数砍成零,正数留着。这个看起来傻乎乎的函数,恰恰是非线性的——它在 \(z = 0\) 处「拐了一下」。一拐之后,整个性质就变了。

六、两层网络:第一台真正的拟合机器

把线性 + 非线性 + 线性串起来,我们得到一个两层神经网络(也叫单隐藏层神经网络):

\[\mathbf{y} = W_2 \cdot \text{ReLU}(W_1 \mathbf{x} + \mathbf{b}_1) + \mathbf{b}_2\]

我们来一层一层看它做了什么。

输入 \(\mathbf{x}\)\(n\) 维向量。\(W_1\)\(h \times n\) 矩阵(\(h\) 是隐藏层维度),\(\mathbf{b}_1\)\(h\) 维向量,所以 \(W_1 \mathbf{x} + \mathbf{b}_1\)\(h\) 维向量。ReLU 逐元素作用,结果还是 \(h\) 维。\(W_2\)\(m \times h\) 矩阵,\(\mathbf{b}_2\)\(m\) 维向量,最终 \(\mathbf{y}\)\(m\) 维向量。

参数总数:\(h \times n + h + m \times h + m = h(n + m + 1) + m\)

这个网络长得朴素,但它是第一个能拟合任意复杂函数的结构——只要 \(h\) 足够大。这就是著名的「万能逼近定理」(Universal Approximation Theorem)。我们等会再正式讲它。

我喜欢把两层网络比作「乐高拼装机」:

合起来:网络在每个小区域里都是一个线性函数,但不同区域的线性函数不同——它整体上是一个分段线性函数。当区域足够多、足够细,分段线性可以逼近任何连续函数。

这个画面对我帮助极大。深度学习并不神秘——它在做的事情,是用大量的「分段线性面片」去拼一个曲面

七、深度:层次化的特征

两层就够了,为什么还要深度?理论上 \(h\) 足够大的两层网络可以逼近任何函数,但实际上「足够大」可能意味着 \(10^{20}\) 个神经元——根本训不动也存不下。

深度的好处是层次化(hierarchical)的特征复用。把多层网络写出来:

\[\mathbf{h}_1 = \text{ReLU}(W_1 \mathbf{x} + \mathbf{b}_1)\] \[\mathbf{h}_2 = \text{ReLU}(W_2 \mathbf{h}_1 + \mathbf{b}_2)\] \[\mathbf{h}_3 = \text{ReLU}(W_3 \mathbf{h}_2 + \mathbf{b}_3)\] \[\cdots\] \[\mathbf{y} = W_L \mathbf{h}_{L-1} + \mathbf{b}_L\]

每一层的输入是前一层的输出。早期层学到「低级特征」(边缘、纹理、字符),中间层学到「中级特征」(眼睛、鼻子、词组),后期层学到「高级特征」(人脸、句子的意图)。这是 Hubel & Wiesel 在猫的视觉皮层里发现的层次结构,被深度学习借走了。

我用过的最直观比喻:浅层网络像一个「单步推理者」,深层网络像一个「多步推理者」。前者一步把输入变成输出,后者把推理拆成几十步,每一步只做一点点变换,最后累积出复杂的能力。Transformer 的 12 层、24 层、96 层堆叠,背后的逻辑就是这个。

八、宽度 vs 深度:一个有意思的辩题

宽度(每层的神经元数 \(h\))和深度(层数 \(L\))哪个更重要?这在深度学习史上吵过很久。

直观地讲,深度提供了「指数级的表达能力」。每一层可以把上一层的「概念」组合成更高级的概念。每多一层,组合数翻倍。所以同样的参数预算下,深更省。

但深也有代价:梯度消失/爆炸、训练不稳、长程信息丢失……ResNet(残差连接,2015)和 LayerNorm 是深度学习能突破到几十层、上百层的关键技术。Transformer 把这些技术全用上了,所以可以堆到 96 层、175 层、甚至更深。

实际经验: - 经典 CNN 时代:宽 → 深,VGG(19 层)→ ResNet(50/101/152 层)→ DenseNet。 - Transformer 时代:深 + 宽并重。GPT-3 有 96 层、隐藏维度 12288;GPT-4 据传更深更宽。 - 大模型时代:参数量主要堆在 FFN(宽)和层数(深)上。

我的经验:如果不知道选什么,先把宽度调到能放进显存的最大值,再加深。但具体任务里,深和宽的最佳比例需要靠实验。

九、参数与超参数:两类完全不同的旋钮

「神经网络是一个函数,旋钮可以调」这句话其实有歧义——到底哪些是旋钮?

仔细分一下,神经网络里有两类「数」:

参数(parameters)\(W\)\(\mathbf{b}\)通过训练自动学到。一个 70B 大模型有 700 亿个参数。这些是「自动调」的旋钮。

超参数(hyperparameters):层数、每层维度、激活函数选哪个、学习率、batch size、Dropout 率、优化器选什么……人为设定,训练前确定。这些是「手动调」的旋钮。

新手常犯的错是把这两个混为一谈。「我把模型调好了」——你调的是哪个?参数(写代码的人不直接动它,是优化器在动它)还是超参数(写代码的人直接选)?这两件事混淆起来,讨论问题就会鸡同鸭讲。

我自己的总结:写代码 = 选超参数 + 写训练循环;训练 = 让优化器找到好参数;调试 = 在两个层面都可能出问题

十、「学习」到底是什么

到这里,我们终于可以回答最核心的问题:所谓「神经网络的学习」,到底在学什么?

答:在找一组让损失函数最小化的参数 \(W, \mathbf{b}\)

展开讲:

  1. 我们手上有数据集 \(\{(\mathbf{x}_i, \mathbf{y}_i)\}_{i=1}^N\)
  2. 我们定义网络 \(f_\theta\),其中 \(\theta = (W_1, \mathbf{b}_1, W_2, \mathbf{b}_2, \cdots)\) 是所有参数的总和。
  3. 我们定义损失函数 \(L(\theta) = \frac{1}{N} \sum_i \ell(f_\theta(\mathbf{x}_i), \mathbf{y}_i)\),衡量当前 \(\theta\) 下网络输出与真实值的差距。
  4. 我们用梯度下降(或其变种 Adam、SGD with momentum 等)去找一个让 \(L(\theta)\) 尽可能小的 \(\theta\)

\[\theta \leftarrow \theta - \eta \nabla_\theta L\]

其中 \(\eta\) 是学习率。

「学习」就是这四步。它不是什么神秘的事情,就是一个高维空间里的优化问题

我反复强调这一点,是因为很多初学者会把「神经网络学到了语言的本质」想象成某种近乎魔法的过程。其实没有魔法。模型只是在 700 亿维空间里下山,找到了一个让损失低的位置。它学到的「知识」,全都编码在这 700 亿个数字里。

十一、损失函数:告诉网络「错在哪里」

损失函数 \(\ell(\hat{\mathbf{y}}, \mathbf{y})\) 是「真实输出」和「网络输出」之间差距的度量。不同任务用不同损失:

回归任务(输出是连续值,比如房价):均方误差(MSE)。

\[\ell_{\text{MSE}} = \frac{1}{2}(\hat{y} - y)^2\]

分类任务(输出是概率分布):交叉熵(Cross Entropy)。

\[\ell_{\text{CE}} = -\sum_k y_k \log \hat{y}_k\]

语言模型(下一个 token 预测):本质也是分类,用交叉熵。

损失函数是网络的「老师的红笔」。它告诉网络「你这次预测错了多少、错在哪个方向」。梯度下降根据这个红笔涂下来的方向调整参数。

我曾经被「为什么要用交叉熵不用 MSE 做分类」纠结过。简单的答案:用 MSE 做分类训练会很慢(梯度小),交叉熵 + softmax 的组合让梯度长得漂亮。深入的答案在「最大似然」和「指数族分布」里,咱们以后再讲。

十二、梯度下降:朝着最低点走一步

梯度 \(\nabla_\theta L\) 是损失函数相对于参数的导数向量。它指向「损失增加最快的方向」。所以「负梯度」指向「损失减小最快的方向」——也就是下山最快的方向。

梯度下降一步:

\[\theta \leftarrow \theta - \eta \nabla_\theta L(\theta)\]

\(\eta\) 是学习率,控制步长。太大会越过山底,太小会走得太慢。

这是最基础的优化算法。深度学习里实际用的优化器(SGD、Momentum、Adam、AdamW、Lion)都是它的变体——加上动量、自适应学习率、权重衰减等等。但内核还是「沿负梯度方向走一步」。

我习惯把损失函数想象成一个「沟壑纵横的山地」,参数 \(\theta\) 是一个游荡在这片山地上的小人。训练就是这个小人不停地下山。山地有:

深度学习训练的所有「黑魔法」(warmup、cosine schedule、warmup restart、SAM、SWA),都在和这片山地的奇形怪状作斗争。

十三、反向传播:链式法则的工程实现

怎么算梯度 \(\nabla_\theta L\)?参数有上百亿个,损失是一个数。这是一个「多变量函数的复合求导」问题。

数学上的工具是链式法则

\[\frac{\partial L}{\partial \theta_l} = \frac{\partial L}{\partial \mathbf{h}_L} \cdot \frac{\partial \mathbf{h}_L}{\partial \mathbf{h}_{L-1}} \cdot \cdots \cdot \frac{\partial \mathbf{h}_{l+1}}{\partial \theta_l}\]

工程上的实现是反向传播(Backpropagation):从输出层往输入层,逐层反向计算梯度,每层利用前向时的中间结果。

反向传播之所以高效,是因为它避免了重复计算——它用动态规划的思想,把链式法则里的中间项一次算完,然后传递。

PyTorch 和 TensorFlow 之所以叫「自动微分框架」,就是因为它们自动帮你做了反向传播。你只需写出前向计算,调用 loss.backward(),它就会自动算出每一个参数的梯度。这是深度学习能爆炸式发展的工程基础。

我自己手写过几次反向传播——为了真正搞懂它。强烈推荐每一个深度学习从业者都手写一次。Karpathy 的 micrograd(150 行 Python 实现自动微分)是绝佳的入门材料。

十四、万能逼近定理:理论的保证

「两层网络可以逼近任何连续函数」——这个说法不是吹牛,是有严格数学证明的,叫做万能逼近定理(Universal Approximation Theorem,UAT)。

Cybenko (1989) 证明:对任何连续函数 \(f: [0,1]^n \to \mathbb{R}\) 和任何 \(\epsilon > 0\),存在一个用 sigmoid 激活的两层神经网络 \(\hat{f}\),使得 \(|\hat{f}(\mathbf{x}) - f(\mathbf{x})| < \epsilon\) 对所有 \(\mathbf{x}\) 成立。

Hornik (1991) 把它推广到了任何「非多项式」的激活函数。

Leshno et al. (1993) 给出了完整的「激活函数应当满足的条件」。

这些定理告诉我们一件好事:神经网络作为「函数空间」是足够大的。原则上,任何你想拟合的连续函数,都有一个神经网络可以任意逼近它。

但它们也藏着一件坏事——「存在」不等于「找得到」

定理保证了「存在一个 \(\theta^*\) 使得网络逼近你想要的函数」,但它没说你能用梯度下降找到这个 \(\theta^*\)。事实上,梯度下降是一个局部搜索算法,它只能找到局部最优。在高维非凸的损失曲面上,找到全局最优是没有保证的。

这就是深度学习的尴尬——理论说能做到,实践能不能做到要靠运气和工程。但奇妙的是,过去十年的实践告诉我们,用足够大的模型 + 足够好的数据 + 现代优化器,梯度下降几乎总能找到一个「足够好」的 \(\theta\)。这是一个让理论家也难以完全解释的奇迹。

我的看法:UAT 让我们对深度学习有了基本的「许可证」——它证明了「方向是对的」。但具体怎么训得动、训得好,还是另外一门学问。

十五、归纳偏置:函数空间不是越大越好

UAT 说函数空间足够大。但实际上,函数空间太大也不好——会过拟合。

什么是过拟合?模型不仅学到了数据中的「真实规律」,还学到了「噪声」。在训练集上表现极好,在测试集上一塌糊涂。

怎么对抗过拟合?给模型加归纳偏置(inductive bias)——人为限制函数空间,让它只能在「合理的」子集中找。

不同的网络结构有不同的归纳偏置:

这是深度学习里一个让人兴奋的趋势——模型越大、数据越多,归纳偏置可以越弱。GPT 系列就是这个趋势的极致:用 Transformer 的最弱偏置,加上海量数据,硬学出语言的全部规律。

我以前觉得「归纳偏置弱 = 模型差」。后来想通了:归纳偏置弱 = 模型不预设任何结构 = 数据怎么说我怎么学。这恰恰是大模型时代追求的。

十六、为什么是「神经」?一段历史

为什么这个东西叫「神经网络」?因为最早的灵感来自生物神经元。

1943 年,McCulloch 和 Pitts 写了一篇论文《A Logical Calculus of the Ideas Immanent in Nervous Activity》。他们试图用数学描述生物神经元的工作方式:神经元接收多个输入信号,按权重相加,超过阈值就「发放」(输出 1),否则不发放(输出 0)

这个模型用公式写就是:

\[y = \mathbb{1}\left(\sum_i w_i x_i > \theta\right)\]

是不是和现代的 \(\text{ReLU}(W\mathbf{x} + \mathbf{b})\) 神似?

1958 年,Rosenblatt 提出感知机(Perceptron),实现了这个模型并在硬件上跑了起来。当时的媒体兴奋地宣称「机器要能思考了」。

1969 年,Minsky 和 Papert 出版《Perceptrons》一书,证明了单层感知机连 XOR 这种简单逻辑都做不了。打击巨大,AI 进入第一次寒冬。

实际上,他们的证明只针对单层。多层网络 + 隐藏层 + 非线性激活,可以解 XOR——但当时没人知道怎么训练多层网络。

1986 年,Rumelhart、Hinton、Williams 发表了关于反向传播的著名论文,让多层网络的训练成为可能。神经网络第二次复兴。

但很快又遇到瓶颈——多层网络训练不稳定、SVM 横空出世(1995)、计算资源不足。整个 1990 年代到 2000 年代初,神经网络再次冷下去。

直到 2006 年 Hinton 的「深度信念网络」、2012 年 Krizhevsky 的 AlexNet 在 ImageNet 上一鸣惊人,深度学习才真正崛起。然后是 2017 年的 Transformer,2020 年的 GPT-3,2022 年的 ChatGPT……一发不可收拾。

我喜欢把这段历史讲给学生听,因为它告诉我们:深度学习不是凭空冒出来的,它是 80 年的积累、两次寒冬、无数人坚持的结果。如果你今天觉得它很「热」,请记住,热度是几代研究者用冷板凳换来的。

十七、生物神经元真的是这样吗?

老实讲,人工神经元和生物神经元只是名字像

生物神经元的复杂性远远超过 \(y = \text{ReLU}(W\mathbf{x} + b)\)。它有树突、轴突、突触、神经递质、动作电位、各种离子通道、时间常数、可塑性……一个真正的神经元的计算复杂度,可能相当于几千个甚至几万个人工神经元。

所以「神经网络」这个名字其实有点误导。它更像是「受神经元启发的可微分函数复合」。今天的从业者大多直接把它当成数学对象在用,已经很少回头去和生物对应。

我的态度:你不用真的相信「这是脑子的工作方式」。这个名字是历史遗留,至于它现在做的事情——更接近于「高维统计」和「函数逼近」。

十八、神经网络与传统机器学习的关系

学过传统机器学习(线性回归、SVM、决策树、随机森林、XGBoost)的人,可能会问:神经网络和它们是什么关系?

我的总结:

更重要的视角:神经网络是把「特征工程」和「模型」打包学习的方法。传统机器学习里,特征工程占了 80% 的时间。神经网络让网络自己学特征——你只管把原始数据喂进去。

这也是为什么深度学习在「原始数据复杂、特征工程难做」的领域(图像、文本、音频)压倒了传统方法,而在「特征已经被人精心做出来」的领域(一些表格数据),传统方法(XGBoost)依然不输。

十九、计算图:函数复合的可视化

每一个神经网络都对应一个计算图(computational graph)——一个有向无环图(DAG),节点是「张量」或「操作」,边是「数据流」。

举例:\(\mathbf{y} = W_2 \cdot \text{ReLU}(W_1 \mathbf{x} + \mathbf{b}_1) + \mathbf{b}_2\) 的计算图:

x ──→ MatMul(W1) ──→ Add(b1) ──→ ReLU ──→ MatMul(W2) ──→ Add(b2) ──→ y

每一步都是一个函数。整个网络是它们的复合。

计算图不仅是可视化工具,它是反向传播的基础。PyTorch 在前向时记录计算图,反向时沿图反向走一遍,用链式法则计算每个节点的梯度。这就是 autograd 的工作原理。

我学计算图的时候顿悟了一件事:「网络结构」其实只是「计算图的形状」。Transformer、ResNet、UNet、GAN,所有「架构创新」本质上都是在画不同的计算图。一旦你能画出图,剩下的就是工程实现。

二十、可微性:为什么我们到处插小心思

梯度下降需要梯度,所以网络的所有操作必须可微——至少是「几乎处处可微」。

这个要求带来很多约束:

这就是为什么深度学习里到处充满「软化」(softening)的小心思:

让一切都可微」是深度学习的工程基本功。你看到的每一个 softmax、sigmoid、tanh,背后都是这个动机。

二十一、神经网络的通用结构

把前面所有内容总结一下,任何一个神经网络都可以拆成三件事

  1. 结构:网络长什么样?多少层?每层做什么操作?计算图怎么连?
  2. 参数:哪些权重需要学?初始化怎么设?
  3. 训练:损失函数是什么?优化器选哪个?数据怎么喂?

这三件事独立又耦合。结构决定了参数数量和计算图;参数决定了函数的具体值;训练决定了参数最终落在哪里。

这是我看任何一篇深度学习论文时的「三联问」——它的结构是什么?它的参数怎么用?它怎么训的?回答了这三个问题,论文 80% 的内容就懂了。

二十二、「函数」的边界:神经网络做不到什么

神经网络是函数,但不是所有问题都能被「函数化」。

它做不到的事情:

所以现代 AI 系统越来越倾向于「神经网络 + 符号系统 + 工具调用」的混合架构。神经网络负责模糊的、统计的部分;符号和工具负责精确的、确定的部分。这是 LLM Agent、检索增强(RAG)、代码解释器等技术的共同思路。

二十三、一段离题的牢骚:为什么深度学习「丑」却有效?

学纯数学出身的人,第一次接触深度学习可能会皱眉头。它太「丑」了——一堆奇怪的网络结构、一堆调得人哭的超参数、一堆没有数学保证的 trick、训练曲线神秘地起起伏伏。

我有几年也很反感。那时我觉得 SVM 才是「优雅」的——凸优化、唯一最优、理论完备。神经网络?不就是堆参数堆数据嘛,毫无美感。

后来我转变了。深度学习的丑,是来自现实世界的丑。语言、图像、声音这些高维数据的内在结构,本来就乱七八糟,没有那么多漂亮的几何。强行用「优雅的数学」去描述它们,结果就是模型表达力不够。

深度学习放弃了「优雅」,换来「通用」。它愿意接受任何一个奇怪的形状、任何一个奇怪的损失、任何一个奇怪的优化路径——只要最后能把任务做好。这种「不偏不倚的表达力」是它的核心竞争力。

后来我明白了:通用性比优雅性更值钱。这是过去十年深度学习给我的最大思想冲击之一。

二十四、从函数视角看 Transformer

绕了一大圈,我们终于可以回到 Transformer。Transformer 也是一个函数。它的输入是一个 token 序列,输出是另一个 token 序列(或下一个 token 的概率分布)。

Transformer 的内部结构展开看,每一个 Block 是这样的:

\[\mathbf{x}' = \mathbf{x} + \text{Attention}(\text{LN}(\mathbf{x}))\] \[\mathbf{x}'' = \mathbf{x}' + \text{FFN}(\text{LN}(\mathbf{x}'))\]

其中:

Transformer 的 FFN 部分,就是本篇讲的两层神经网络的应用。它在每个 token 上独立作用,把每个 token 的表示变得更丰富。

研究表明:Transformer 的「事实记忆」主要存在 FFN 里。论文《Transformer Feed-Forward Layers Are Key-Value Memories》(2020)就在做这件事。如果你把 Transformer 看成一个推理机器,那 FFN 就是它的「知识库」。

所以本篇看似在讲基础,实则讲的是 Transformer 的一半(另一半是注意力)。FFN 占了 Transformer 大约 2/3 的参数量。理解了 FFN,你就理解了 Transformer 的「记忆」部分。

二十五、从函数视角看大语言模型

继续往上看。大语言模型(LLM)也是一个函数

它的输入是一段文字 prompt,把它分词成 token 序列 \([t_1, t_2, \cdots, t_n]\)。它的输出是「下一个 token」的概率分布——一个长度为词表大小 \(V\) 的向量,每个元素是一个概率。

形式化:

\[P(t_{n+1} | t_1, \cdots, t_n) = \text{LLM}_\theta([t_1, \cdots, t_n])\]

参数 \(\theta\) 包含整个模型的所有权重——70B 模型就有 700 亿个参数。

自回归生成」就是反复调用这个函数:

  1. 输入 prompt \([t_1, \cdots, t_n]\),得到 \(P(t_{n+1})\)
  2. \(P(t_{n+1})\) 采样(或 argmax)得到 \(t_{n+1}\)
  3. \(t_{n+1}\) 接到序列后面,输入 \([t_1, \cdots, t_{n+1}]\),得到 \(P(t_{n+2})\)
  4. 反复,直到生成结束 token。

整个过程,LLM 始终是同一个函数——只是反复调用而已。所谓「思考」「推理」「记忆」,全都发生在 700 亿个参数和反复的前向传播中。

这是我对 LLM 的「祛魅」。它不是一个会思考的智能体,它是一个很大的函数 + 一个采样循环。函数本身是死的,但函数足够大、训得足够好,循环采样出来的输出就能让人类感到「智能」。

二十六、概念回顾:术语小词典

写到这里我们已经覆盖了很多概念。整理一个小词典,方便后面的篇章引用。

术语 中文 含义
Function 函数 输入到输出的映射
Linear Layer / Dense 线性层 / 全连接层 \(\mathbf{y} = W\mathbf{x} + \mathbf{b}\)
Activation 激活函数 非线性变换,让网络能拟合复杂关系
MLP 多层感知机 多个线性层 + 激活的堆叠
Hidden Layer 隐藏层 输入和输出之间的层
Width 宽度 每层的神经元数(向量维度)
Depth 深度 层数
Parameters 参数 通过训练学到的 \(W, \mathbf{b}\)
Hyperparameters 超参数 人为设定的(层数、学习率等)
Loss 损失 衡量预测与真值差距
Gradient 梯度 损失对参数的偏导
Backprop 反向传播 链式法则的工程实现
UAT 万能逼近定理 两层网络可以逼近任意连续函数
Inductive Bias 归纳偏置 网络结构带来的先验假设
Computational Graph 计算图 神经网络的有向无环图表示
FFN 前馈网络 Transformer 中的两层 MLP

二十七、和前几篇的连接

回头看:

三、四两篇是「计算的两面」:第三篇讲计算「怎么算」,第四篇讲计算「为什么这么算」。从信息流的角度看,神经网络就是「输入向量 → 一连串矩阵乘法 + 非线性 → 输出向量」。

下一篇(第五篇)讲激活函数——非线性的具体形式。Sigmoid、Tanh、ReLU、GELU、SwiGLU,每一个都背着一段故事。

二十八、关键概念回顾

回到开头那句朴素的话——神经网络就是一个函数

这句话可以从几个层次理解。

第一层,它是一个数学对象:从 \(\mathbb{R}^n\)\(\mathbb{R}^m\) 的映射,由参数 \(\theta\) 决定具体形式。我们不预设它具体长什么样,而是让数据来告诉我们。

第二层,它是一组结构化的复合函数:一系列线性变换 \(W\mathbf{x} + \mathbf{b}\) 和非线性激活的交替。每一层都是一个简单的「积木」,整体堆出复杂的能力。线性提供了「方向」,非线性提供了「弯曲」,深度提供了「层次」。

第三层,它是一个可学习的系统:通过损失函数 + 梯度下降 + 反向传播,让参数自动找到一个让任务表现好的位置。「学习」不是魔法,是高维空间里的优化。

第四层,它有理论保证(UAT),但理论不等于实践。能逼近 ≠ 能找到。深度学习能 work,是理论 + 数据 + 算力 + 工程的合力。

第五层,它是 Transformer 和 LLM 的「半边天」。Transformer 的 FFN、LLM 的整体框架,都建立在「神经网络作为函数」这一根基上。

把这五层叠起来,你对「神经网络是什么」的理解就完整了。

二十九、常见误解

误解一:神经网络模拟了大脑。

人工神经元和生物神经元只是名字像。生物神经元的复杂性远超 \(\text{ReLU}(W\mathbf{x} + \mathbf{b})\)。神经网络更像「受启发的可微分函数」,不是大脑模型。研究神经网络不能告诉你大脑怎么工作,研究大脑也不能直接告诉你神经网络怎么改进。

误解二:神经网络越深越好。

深度有边际效应。早期增加深度收益巨大,到一定层数后收益急剧下降。深度还会带来梯度问题、训练不稳。深度学习的进展不是「无脑加深」,而是「加深 + 残差 + 归一化 + 优化器改进」综合的结果。

误解三:神经网络越大越好。

参数量大确实能力强,但需要更多数据、更多算力。如果数据少,大模型会严重过拟合;如果算力少,大模型训不动。「大」是手段不是目的。Chinchilla 缩放定律告诉我们,参数量和数据量需要匹配。

误解四:训练好了就万事大吉。

训练只是第一步。还有部署、量化、加速、监控、迭代……一个能用的 AI 系统里,模型本身只是其中一环。你看到的 ChatGPT 背后是模型 + 推理引擎 + 安全过滤 + 用户界面 + RLHF 反馈闭环 + 缓存 + 负载均衡,缺一不可。

误解五:神经网络可以拟合任何函数(万能逼近)。

理论上,两层网络 + 足够多神经元 + 合适激活 + 全局最优解 = 可以逼近任何连续函数。但「足够多」可能是天文数字,「全局最优」可能根本找不到。UAT 是「可能性证明」,不是「方法保证」。

误解六:损失越低越好。

不是。训练损失低 + 测试损失高 = 过拟合,是失败。我们追求的是「测试损失低」,等价于「泛化好」。所以训练时要看 train/val 两条曲线,光看训练损失会被骗。

误解七:参数都在学有用的东西。

恰恰相反,神经网络的参数大量是「冗余」的。剪枝(pruning)研究告诉我们,一个训练好的网络通常可以削减 50%-90% 的参数而几乎不损失性能。但训练时这些「冗余」参数有用——它们提供了优化的「松弛空间」。这是一个吊诡的事实。

误解八:只要有数据,模型自动就能学好。

数据需要质量。脏数据、错标、不平衡,都会让模型学偏。LLM 时代有句话:garbage in, garbage out。OpenAI 等大公司花在数据清洗上的人力,远超你想象。

误解九:深度学习是黑盒,所以不可信。

「黑盒」不等于「不可信」。我们用药、坐飞机、过马路,都信任了我们不完全理解的复杂系统。重要的是「可验证」——能否在测试上验证它的表现。可解释性是研究方向,但不是使用前提。

误解十:神经网络就是「大数据 + 算力」的暴力解。

这是最常见的、最浅薄的误解。深度学习背后有大量的算法创新——ReLU、Dropout、BatchNorm、Adam、残差连接、Transformer……每一个都是巨大的洞察。「暴力」只是表象,「巧思」才是内核。

三十、下一步

下一篇(第五篇)我们讲激活函数

我们将详细对比 Sigmoid、Tanh、ReLU 这三大经典激活函数:它们的形状、它们的导数、它们的优缺点,以及为什么 ReLU 在 2012 年颠覆了深度学习的训练。然后我们讲现代激活函数 GELU、SwiGLU——它们是 Transformer 时代的标配。我们还会回答一个看似简单但很多人答不好的问题:为什么 ReLU 这么简单的函数,反而比 Sigmoid 更好?

之后的篇章里,我们还会用本篇的视角不断审视 Transformer 的每一个组件:

每一篇我们都会回到「它是一个函数」的视角:输入是什么、输出是什么、参数在哪、怎么训。坚持这个朴素视角,复杂的东西就不会太可怕。

三十一、参考文献

三十二、再谈「学习」:从一个最小的例子说起

如果上面那么多概念你一时消化不下来,那我们用一个最小的例子重新走一遍——让神经网络学会 XOR

XOR 是一个简单的逻辑运算:

\(x_1\) \(x_2\) \(y\)
0 0 0
0 1 1
1 0 1
1 1 0

这是个看起来很无聊的任务。但它在神经网络史上意义非凡——单层感知机做不了 XOR,这一点曾经把神经网络打入冷宫。

为什么单层做不到?因为 XOR 不是线性可分的。想象在 \((x_1, x_2)\) 平面上画四个点:\((0,0)\)\((1,1)\) 标 0,\((0,1)\)\((1,0)\) 标 1。你画不出一条直线把两类点分开——它们呈对角线分布。

两层网络 + 非线性激活,可以做到。我们来看看怎么做的。

设隐藏层有两个神经元,参数为:

\[W_1 = \begin{pmatrix} 1 & 1 \\ 1 & 1 \end{pmatrix}, \quad \mathbf{b}_1 = \begin{pmatrix} 0 \\ -1 \end{pmatrix}\]

\[W_2 = \begin{pmatrix} 1 & -2 \end{pmatrix}, \quad b_2 = 0\]

激活函数用 ReLU。前向计算:

\[\mathbf{h} = \text{ReLU}(W_1 \mathbf{x} + \mathbf{b}_1) = \text{ReLU}\begin{pmatrix} x_1 + x_2 \\ x_1 + x_2 - 1 \end{pmatrix}\]

\[y = W_2 \mathbf{h} = h_1 - 2h_2\]

代入四个点:

漂亮!两层网络解决了 XOR。

这个例子的意义在于:它告诉你两层网络不是「比一层多一点」,而是「质变」。一层只能划直线;两层可以「先映射再划直线」——而第一次映射后,原本不可分的点变得可分了。

这个想法是 SVM 核函数的本质,也是深度学习「逐层抽象」的本质。神经网络的每一层都在重新组织输入,让后面的层更容易处理。

三十三、从 XOR 到大模型:本质没变

XOR 这个 toy example 看起来跟 70B 大语言模型没什么关系。但本质是一样的。

70B 模型在做的事情,也是「逐层重组输入,让后面的层好处理」。最初的 token embedding 只是「这个词的初始向量」;经过几十层 Transformer Block 之后,最后一层的向量已经是「整个上下文里这个位置应该输出什么」的浓缩信息。

中间这几十层每一层都做着类似 XOR 的工作——把信息重组到一个对下一层更友好的空间里。只不过 XOR 是 2 维 → 2 维 → 1 维,70B 模型是 12288 维 → 12288 维 →…→ 12288 维 → 词表大小维。

我喜欢这种「连续性」——从最简单的 XOR 到最大的 LLM,背后是同一种思想。这让深度学习既有数学的优雅,又有工程的实用。

三十四、训练动力学:一个让我着迷的话题

我个人最着迷的一个研究方向是「神经网络训练的动力学」——参数在训练过程中是怎么变化的?损失曲面长什么样?为什么 SGD 总能找到「好的」解?

这里有几个有意思的现象:

「双重下降」(Double Descent):传统统计学告诉我们,模型越大越容易过拟合。但深度学习里发现:模型继续变大,过拟合反而消失。损失先降、再升、再降——形成「双重下降」曲线。这违反了经典理论。

「彩票假设」(Lottery Ticket Hypothesis):Frankle & Carbin (2018) 发现,一个大网络里似乎隐藏着一个小的「中奖子网络」,它单独训练就能达到大网络的效果。这是对「神经网络冗余性」的经典证据。

「神经切线核」(NTK):当网络非常宽时,训练动力学可以用一个固定的核函数描述。这给了无限宽神经网络一个数学性质,但有限宽网络的训练仍然是个谜。

「损失曲面的几何」:研究表明,深度神经网络的损失曲面有大量的「平坦盆地」,SGD 偏向于走进这些盆地,而盆地里的解通常泛化好。这是个连接「优化」和「泛化」的关键观察。

「特征学习」(Feature Learning)vs「核回归」(Kernel Regime):训练时网络是在「学新特征」还是「在固定核上做回归」?这是现在前沿研究的争论。

我提这些不是要在本篇详细展开(这都得专著),而是想告诉你:「神经网络是函数」这句话简单,但「这个函数怎么训成的」可以写出几十年的研究。深度学习是一个广阔的领域,有非常多有意思的问题。

三十五、神经网络与「智能」:一个哲学问题

最后聊点哲学。

「神经网络只是函数,它能算作智能吗?」

这是个哲学问题,没有标准答案。但我有个倾向性的看法。

反方观点:神经网络只是模式匹配,没有理解、没有意识、没有真正的推理。它再大也只是一个 stochastic parrot(随机鹦鹉)——重复训练数据里的模式而已。

正方观点:「智能」本身就是个模糊概念。如果一个系统能完成需要智能的任务(写文章、写代码、解题),我们没有理由不叫它「智能」。「真正的理解」如果不可定义不可测量,就是个空概念。

我自己倾向后者,但保留一点谨慎。当前的 LLM 已经在很多任务上展现了「类智能」的行为,但它们也表现出明显的局限——长程推理薄弱、易被欺骗、缺乏自我修正。

更有意思的问题是:现在的局限,是「函数视角的根本局限」吗?还是「目前规模/训练方法的局限」?

如果是前者,那我们需要在「函数」之外加点别的东西(比如符号系统、世界模型、强化学习反馈)。如果是后者,那继续 scaling 就行。

我个人猜测:两者都有。所以未来的 AI 一定是「神经网络 + 其他」的混合体,而不是纯 LLM。

三十六、给写代码的人的提示

如果你是写代码的人(程序员/工程师),本篇知识怎么对你的工作有帮助?

第一写训练代码时永远问「我的输入输出是什么形状」。99% 的训练 bug 是 shape 错配。把每一层的输入输出形状都写在注释里,bug 数量会断崖式下降。

第二调超参数时先调最敏感的几个。学习率 > batch size > 模型大小 > 优化器选择 > 正则化。前期不要一次调多个。

第三画 loss 曲线。train loss + val loss 都要画。不画曲线就训模型,等于闭眼开车

第四梯度爆炸了用 grad clipping,梯度消失了换激活/初始化/归一化。这两个问题有标准对策,不要硬扛。

第五先在小数据上跑通,再上大数据。别一开始就拉满 batch、拉满 epoch。先用 100 个样本能不能让模型 overfit?如果不能,模型/代码有问题。这是 Karpathy 的「neural networks recipe」第一条。

这些不是论文里的内容,是写代码人的「肌肉记忆」。希望对你有用。

三十七、给做研究的人的提示

如果你是做研究的人(研究生/博士),本篇内容怎么帮你?

第一理解「函数视角」是论文创新的脚手架。你想改进 Transformer?请先回答:你在改它的哪部分函数?输入空间?输出空间?中间映射?参数化方式?训练方式?没有清晰回答,论文就缺骨架。

第二理论与实验要互相校准。深度学习里很多「新现象」是实验先发现、理论后追上的(比如双重下降、grokking、emergent ability)。但也有理论指导实验的反例(比如 NTK、信息瓶颈)。两者都得跟。

第三写作要把「直觉」放在「公式」前面。读者先要理解你为什么这么想,再才能消化你的公式。本系列的写作就是这种风格。

第四复现比新研究更重要的时候。一个声称「我有新方法」的论文,如果你不能复现别人的 baseline,新方法的提升数字毫无意义。

第五读经典。Cybenko 1989、Hornik 1991、Rumelhart 1986、LeCun 1998 ……这些老论文今天读起来仍然清晰、深刻。你越往新的读,越要回去读老的。

三十八、一些常见问题答疑

Q1:神经网络的「神经元」是什么?

A1:是「线性变换的一个输出维度 + 激活函数」的合称。\(\mathbf{h} = \text{ReLU}(W\mathbf{x} + \mathbf{b})\)\(\mathbf{h}\)\(h\) 维向量,每一维就是一个「神经元」。每个神经元有自己的权重行 \(\mathbf{w}_i^\top\) 和偏置 \(b_i\)

Q2:偏置 \(\mathbf{b}\) 真的有用吗?

A2:有用。它让 \(W\mathbf{x} + \mathbf{b}\) 这条「超平面」可以不过原点,覆盖更广的函数空间。但有些现代架构里(比如某些 LLM),为了简化和数值稳定,会把 bias 去掉,由 LayerNorm 替代它的作用。

Q3:为什么 ReLU 在 0 不可导也能用?

A3:0 是一个测度为零的点,正常训练几乎不会精确踩到。即使踩到,框架里默认 ReLU 在 0 处的梯度是 0(或 1,看实现)。这叫「次梯度」(subgradient),数学上是合法的扩展。

Q4:为什么不用所有数据一次性算梯度?

A4:理论上是可以(叫 batch gradient descent)。但 1)数据装不进显存;2)每一步太慢;3)随机性其实有助于跳出局部最优。所以实际用 mini-batch SGD——每次用一小批样本估计梯度。

Q5:训练时 loss 不降怎么办?

A5:先排查「代码错」(没接梯度、没把 .train() 切回去、loss 算错)。然后排查「学习率」(太大太小都不行)。再排查「初始化」(爆炸或饱和)。再排查「数据」(脏数据、标签错)。最后才是「换模型/换损失」。

Q6:训练完 loss 很低,但用起来效果差?

A6:典型过拟合。检查 train loss vs val loss——如果 train 远低于 val,肯定过拟合。对策:加数据、加正则、减小模型、早停。

Q7:模型预测时为什么要 model.eval()?

A7:因为 Dropout 和 BatchNorm 在训练和推理时行为不同。Dropout 训练时随机丢,推理时关掉;BatchNorm 训练时用当前 batch 统计,推理时用累积统计。.eval() 切到推理模式。忘了切是新手最常踩的坑

Q8:为什么有的模型用 Tanh 有的用 ReLU 有的用 GELU?

A8:历史 + 任务 + 经验三方面的结果。Tanh 在 RNN 时代是标配(输出有界,方便处理梯度),CNN 时代换成了 ReLU(训练快),Transformer 时代用 GELU(在大模型上略好)。下一篇详细讲。

Q9:参数初始化有那么重要吗?

A9:非常重要。初始化太大 → 梯度爆炸;太小 → 梯度消失。深度网络对初始化敏感得多。常用的有 Xavier 初始化、He 初始化、LSUV 等。Transformer 里还有「残差缩放」等技巧。

Q10:神经网络能保证一定收敛吗?

A10:理论上不能。损失曲面是非凸的,SGD 是局部搜索。但实践中,足够大的网络 + 合适的学习率 + 现代优化器(Adam)几乎总能收敛到一个「足够好」的解。这是工程奇迹,不是数学定理。

三十九、相关概念辨析

梯度(gradient)vs 导数(derivative):导数是单变量函数的「斜率」;梯度是多变量函数对每个变量的导数组成的向量。梯度指向「函数增加最快的方向」。

反向传播(backprop)vs 自动微分(autodiff):反向传播是自动微分的一个特例(reverse mode)。还有 forward mode,适合输入维度小输出维度大的情况。深度学习里几乎全用 reverse mode,因为输入维度(参数)远大于输出维度(loss 是一个数)。

SGD vs Adam:SGD 是基础梯度下降,每个参数同样的学习率。Adam 是「自适应学习率」,每个参数根据自己的历史梯度调整自己的学习率。Adam 对超参数不敏感,初学者首选;SGD 调好了泛化更好,老司机最爱。

Loss vs Cost:在大多数语境里,loss 指「单个样本的误差」,cost 指「整个数据集的平均误差」。但混用的情况很多,不必太纠结。

Epoch vs Iteration vs Step:epoch 是「遍历一次完整数据集」,iteration/step 是「一次梯度更新」。如果数据集 1000 样本,batch size 100,那 1 个 epoch 等于 10 个 step。

Train / Val / Test:训练集用来更新参数,验证集用来选超参/早停,测试集用来最终报告性能。测试集严禁用来调任何东西——一旦用了就不再是「测试集」了。

四十、本篇的局限与延伸

我必须承认本篇有几个没讲到的重要话题

这些都是「让神经网络真正能训起来」的工程技术。我把它们留到「LLM 训练实战」的篇章再讲,因为它们涉及大量代码和经验。

如果你等不及,我推荐:

四十一、收尾

好了,关于「神经网络是函数」这件事,我话很多。现在做个收束。

如果你只能从本篇带走一句话,我希望是:

神经网络 = 一个由参数 \(\theta\) 决定的函数族 + 一个用数据找好 \(\theta\) 的优化过程。

这一句把「结构」「参数」「训练」三件事打包了。带着它去看下一篇的激活函数、再下一篇的注意力、之后的 Transformer、最终的 LLM——你会发现所有的「新东西」都只是这个朴素结构的具体实例。

我们下一篇见。


上一篇03. 矩阵乘法的几何与代数:从批量点积到 QKᵀ  下一篇05. 激活函数:让网络「弯下来」的非线性魔法


同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-04-15 · transformer

【Transformer 与注意力机制】01|为什么要从这里开始

这是【Transformer 与注意力机制】系列的第一篇,承担两件事:一是把这套五十多篇文章为谁写、解决什么问题、彼此之间是什么关系交代清楚;二是为完全没基础的读者画出一条从向量、点积、矩阵乘法走到自注意力、再走到大语言模型的爬升路径,让你在投入时间之前先知道终点在哪、路上要经过哪些坎、读完之后你会、还不会做什么事。


By .