文章

从一条直线到大模型输出一个token(八):残差连接与前馈网络

从一条直线到大模型输出一个token(八):残差连接与前馈网络 建议先看:从一条直线到大模型输出一个token(七):注意力权重与多头机制 上一篇算出了注意力的输出 O。这一篇走完 Block 的剩余三步:残差相加、归一化、FFN。本篇的主角其实是激活函数——网络里唯一的"掰弯"零件。 1. 分工:注意力管交流,FFN 管消化 先摆清楚 Block 两员大将的分工,如图 8-1 所示。 图8-1

从一条直线到大模型输出一个token(八):残差连接与前馈网络

文章信息

  • 原文链接:https://jiayq.blog.csdn.net/article/details/163999130
  • 发布时间:2026-09-07 11:49:43
  • 标签:#智能体, #ai, #agent, ##SwiGLU, ##deepseek

从一条直线到大模型输出一个token(八):残差连接与前馈网络

建议先看:从一条直线到大模型输出一个token(七):注意力权重与多头机制

上一篇算出了注意力的输出 O。这一篇走完 Block 的剩余三步:残差相加、归一化、FFN。本篇的主角其实是激活函数 ——网络里唯一的”掰弯”零件。

1. 分工:注意力管交流,FFN 管消化

先摆清楚 Block 两员大将的分工,如图 8-1 所示。

分工总览

图8-1 注意力 vs FFN:一个管词间交换信息,一个管词内深度加工

  • 多头自注意力(②):词和词之间交换信息 。「西安」吸收 25.9% 的「怎么样」、22.3% 的「天气」……上一篇的 A V \mathbf{A}\mathbf{V} AV 干的就是跨行混合——跨行操作,行与行互相影响
  • 前馈网络 FFN(④):每个词自己做深度加工 。「西安」的 4096 维向量独立过一遍”升维→激活→降维”,深挖自己的特征——逐行操作,行与行互不相干

类比:开会先互相交换信息(注意力:知道周围有什么),会后各自消化整理成自己的东西(FFN:把吸收的信息深加工)。

2. 残差连接:原始信息的高速通道

2.1 定义:加工结果 + 原始输入

残差连接的公式只有一行:

H = X + Attention ( X ) \mathbf{H} = \mathbf{X} + \text{Attention}(\mathbf{X}) H=X+Attention(X)

注意力算完的加工结果,逐元素加上未经加工的原始输入。贯穿案例的手算如图 8-2 所示(红色 = 每行最大值,下同)。

残差相加

图8-2 残差连接:加工结果 + 原始输入(西安行手算,红色 = 每行最大值)

「西安」行逐维相加: ( − 1.446 ) + 0.264 = − 1.182 (-1.446) + 0.264 = -1.182 (−1.446)+0.264=−1.182, 0.463 + ( − 0.506 ) = − 0.043 0.463 + (-0.506) = -0.043 0.463+(−0.506)=−0.043, 1.257 + 0.214 = 1.471 1.257 + 0.214 = 1.471 1.257+0.214=1.471, ( − 0.485 ) + 0.407 = − 0.078 (-0.485) + 0.407 = -0.078 (−0.485)+0.407=−0.078,得到 H 西安 = [ − 1.182 , − 0.043 , 1.471 , − 0.078 ] \mathbf{H}_{西安} = [-1.182, -0.043, 1.471, -0.078] H西安​=[−1.182,−0.043,1.471,−0.078]。

2.2 举例:全部 6 个 token 的残差连接

残差不是只加一行——矩阵的每一行都做同样的事。全部 6 个 token 的残差连接对比(逐行手算验证),如图 8-3 所示。

全部残差

图8-3 全部 6 个 token 的残差连接:逐行解读(红色 = 每行最大值)

几个值得注意的细节:

  • 「西安」的 d1 从 -1.446 回到 -1.182:原向量的负偏被注意力的正向贡献(+0.264)拉回来一点 ——残差是”微调”而不是”重写”
  • 「天气」的 d4 从 1.433 降到 1.070:招牌特征反而被稀释 了——注意力的角色是”调配”,不是单纯放大
  • 「怎么样」的 d4 从 1.955 涨到 2.053:全场最大值继续登顶
  • 「?」的 d4 几乎没动(-1.643 → -1.598):问号的语气特征很稳定,加工幅度最小

残差的语义 :每个词的新向量 = 自己原来的样子 + 从上下文吸收的信息。变化幅度因词而异——这正是”加工”应有的样子。

2.3 作用:梯度高速路

残差看似只是加了一下,为什么所有深网络都离不开它?关键在训练时的梯度回传,如图 8-4 所示。

梯度高速路

图8-4 残差的真正价值:梯度高速路

没有残差时,梯度要穿过每层的加工函数回传,每层都被乘上一个小于 1 的系数(举例:假设每层保留一半,0.5):

  • 举例 1:10 层网络 → 0.5 10 ≈ 0.001 0.5^{10} \approx 0.001 0.510≈0.001,底层只收到千分之一的信号
  • 举例 2:61 层(DeepSeek-V3 的深度)→ 0.5 61 ≈ 4 × 10 − 19 0.5^{61} \approx 4 \times 10^{-19} 0.561≈4×10−19——梯度完全消失,底层几亿个参数集体”失聪”,训练无从谈起

有了残差, H = X + f ( X ) \mathbf{H} = \mathbf{X} + f(\mathbf{X}) H=X+f(X) 里的 X \mathbf{X} X 是直通项——对它求导恒等于 1,梯度沿这条加法主干一路高速直达底层 ,不管多少层都不衰减。

出处 :这个思想来自 ResNet(Residual Network)——He Kaiming(何恺明)等人 2015 年的论文《Deep Residual Learning for Image Recognition》(CVPR 2016),为解决”网络越深反而越难训”的问题提出。它是计算机视觉史乃至整个深度学习史引用量最高的论文之一(超过 20 万次),Transformers 的残差设计直接继承于此。2024 年何恺明因这一工作获得 IEEE 计算机学会先驱奖。

附带的好处:即使某一层 f f f 学废了(输出全 0),残差保证 H = X \mathbf{H} = \mathbf{X} H=X——信息至少不比原来差,深层网络不会因为个别层拉胯而全盘崩溃。

3. FFN 三段式:升维 → 激活 → 降维

3.1 流程与为什么要升维

FFN 的结构是个”先膨胀再收缩”的三段式,如图 8-5 所示。

FFN三段式

图8-5 FFN 三段式:升维 → 激活 → 降维(大字号版)

FFN ( x ) = SiLU ( x ⋅ W u p ) ⋅ W d o w n \text{FFN}(\mathbf{x}) = \text{SiLU}(\mathbf{x} \cdot \mathbf{W}{up}) \cdot \mathbf{W}{down} FFN(x)=SiLU(x⋅Wup​)⋅Wdown​

  1. 升维 :乘 W u p \mathbf{W}_{up} Wup​(4096×14336),一行 4096 个数展开成 14336 个——为什么要升 :低维挤在一起的模式,高维摊开看得清。就像把一团揉皱的纸展开——”西安=地名”和”西安=朝代”这两种特征,在 14336 维的大空间里可以摊到完全不同的维度组合上分别加工
  2. 激活 :逐格过 SiLU 函数(第 4 节专门讲,本篇真正的重头戏)
  3. 降维 :乘 W d o w n \mathbf{W}_{down} Wdown​(14336×4096),压回 4096 个——为什么再降回来 :升维是为了加工,降维是为了交付;下游的 Block 入口只收 4096 维

W u p \mathbf{W}{up} Wup​ 和 W d o w n \mathbf{W}{down} Wdown​ 是什么、怎么来的:和篇6 的 W Q \mathbf{W}_Q WQ​/ W K \mathbf{W}_K WK​/ W V \mathbf{W}_V WV​ 完全同理——都是训练学出来的权重矩阵(训练前随机初始化,训练中靠梯度下降一点点调整),不是人手工设计的。

3.2 贯穿案例:「西安」行手算

「西安」行完整走一遍三段式(每行最大值红色标注,升维后的 8 个数、激活后的 8 个数都遵守这个约定),如图 8-6 所示。

FFN手算

图8-6 FFN 手算全程:升维-激活-降维-残差(红色 = 每行最大值)

升维手算 ( H 西安 \mathbf{H}{西安} H西安​ 乘 W u p \mathbf{W}{up} Wup​ 第 1 列 [ 0.3 , − 0.1 , 0.5 , − 0.2 ] [0.3, -0.1, 0.5, -0.2] [0.3,−0.1,0.5,−0.2],还是一行乘一列):

( − 1.182 ) × 0.3 + ( − 0.043 ) × ( − 0.1 ) + 1.471 × 0.5 + ( − 0.078 ) × ( − 0.2 ) = 0.401 (-1.182) \times 0.3 + (-0.043) \times (-0.1) + 1.471 \times 0.5 + (-0.078) \times (-0.2) = 0.401 (−1.182)×0.3+(−0.043)×(−0.1)+1.471×0.5+(−0.078)×(−0.2)=0.401

升维后 8 个数: [ 0.401 , 0.343 , − 0.315 , − 1.038 , 0.945 , 0.366 , − 1.031 , 0.872 ] [\textcolor{red}{0.401}, 0.343, -0.315, -1.038, \textcolor{red}{0.945}, 0.366, -1.031, 0.872] [0.401,0.343,−0.315,−1.038,0.945,0.366,−1.031,0.872](红标最大 0.945,演示图里统一处理)。

降维手算 (激活后的 8 个数乘 W d o w n \mathbf{W}_{down} Wdown​ 第 1 行):

0.240 × 0.4 + 0.201 × ( − 0.3 ) + ⋯ + 0.615 × ( − 0.1 ) = 0.136 0.240 \times 0.4 + 0.201 \times (-0.3) + \cdots + 0.615 \times (-0.1) = 0.136 0.240×0.4+0.201×(−0.3)+⋯+0.615×(−0.1)=0.136

残差 2 : ( − 1.182 ) + 0.136 = − 1.046 (-1.182) + 0.136 = -1.046 (−1.182)+0.136=−1.046,最终「西安」的 Block 出口 [ − 1.046 , 0.577 , 1.667 , − 0.655 ] [-1.046, \textcolor{red}{0.577}, \textcolor{red}{1.667}, -0.655] [−1.046,0.577,1.667,−0.655]。

3.3 全部 6 个 token 的 Block 出口

每个 token 独立走完三段式再加残差,Block 完整出口如图 8-7 所示。

Block出口

图8-7 Block 的完整出口:H + FFN(H)(红色 = 每行最大值)

到这一步,Block 六步串行全部走完:①归一化 → ②注意力 → +残差 → ③归一化 → ④FFN → +残差 。出口形状还是 6×4096,但每个词都已混入上下文信息(注意力的功劳),又各自完成深度加工(FFN 的功劳)。

4. 激活函数:网络里唯一的”掰弯”零件

前面铺垫了这么久,现在正式讲本篇真正的重点。很多读者到这里可能还是云里雾里:“激活函数到底是个啥?为什么非要它不可?”

4.1 一个直观的例子:为什么没有它不行

先看一个具体问题,如图 8-8 所示。

为什么需要激活函数

图8-8 激活函数为什么不可或缺:左图无解,右图有解

图上有两类点:绿色圆点是”同号”(x 和 y 正负号相同),红色方块是”异号”。要把绿点和红点分开——这就是著名的 XOR(异或)问题

  • 左图(没有激活函数) :模型能做的只有线性变换(旋转、拉伸、平移),画出来的分界线永远是直线 。而你看图:不存在任何一条直线能把这两类点分开——无论怎么摆,直线两侧总是绿红混杂。数学上可以严格证明: W 2 ( W 1 x ) = ( W 2 W 1 ) x \mathbf{W}_2(\mathbf{W}_1\mathbf{x}) = (\mathbf{W}_2\mathbf{W}_1)\mathbf{x} W2​(W1​x)=(W2​W1​)x——两层线性叠加等价于一层,一百层也一样 。线性模型连 XOR 都解决不了,遑论理解语言
  • 右图(有激活函数) :每层之间插入一个非线性函数,直线可以被”掰弯”——带激活函数的深度网络理论上可以贴合任意复杂的弯曲边界(这叫万能逼近定理,1989 年 Cybenko、Hornik 等人分别证明)

一句话定义 :激活函数是对每个数字独立做的一次非线性变换——输入一个数,按固定公式输出另一个数。它不训练、不学习(公式是死的),但它引入的”弯”让层层叠加有了意义。

4.2 SiLU:当前主流的激活函数

现代大模型(LLaMA/Qwen/GLM/DeepSeek 全系)用的是 SiLU (Sigmoid Linear Unit,也叫 Swish):

SiLU ( x ) = x × sigmoid ( x ) = x 1 + e − x \text{SiLU}(x) = x \times \text{sigmoid}(x) = \frac{x}{1 + e^{-x}} SiLU(x)=x×sigmoid(x)=1+e−xx​

逐格计算:输入一个数 x,先算 sigmoid(x)(把任意数压到 0~1 之间),再乘回 x。举个具体例子建立直觉:

输入 xsigmoid(x)SiLU = x×sigmoid(x)发生了什么
4.00.9823.93大正数:几乎原样放行
1.00.7310.731中等正数:打七折放行
0.40.5990.240小正数:打六折放行
-0.30.426-0.128小负数:保留一点(不全杀)
-1.00.269-0.269负数:缩小到约四分之一
-4.00.018-0.072大负数:几乎清零(静音)

表8-1 SiLU 逐点计算表:像门卫的三段处置

三代激活函数的演进曲线如图 8-9 所示。

三代激活函数

图8-9 三代激活函数:ReLU → GELU → SiLU(右:SiLU 的三段性格)

4.3 激活前后产生了什么变化(贯穿案例解读)

回看「西安」行升维后的 8 个数过 SiLU 的前后对比:

升维后SiLU 后变化解读
0.4010.240正数打六折——门卫放行但收”过路费”
0.9450.680最大正数打七二折——仍保持最大,但与其他值的差距缩小
-0.315-0.133小负数保留 42%——不是所有负信号都该消灭
-1.038-0.271大负数砍到 26%——深负区静音
-1.031-0.271同上——两个深负值被压到几乎相同

表8-2 激活前后对比:SiLU 改变了什么(红色 = 每列最大值)

三个关键变化:

  1. 数值范围收窄 :最大值从 0.945 缩到 0.680,最小值从 -1.038 回到 -0.271——极端值被压向中间,数值更稳
  2. 大小关系保留 :激活前 0.945 > 0.401 > -0.315,激活后 0.680 > 0.240 > -0.133——正区的排序不变,特征的主次保留
  3. 负区非线性压缩 :-0.315 压缩到 42%,-1.038 压缩到 26%——压缩比例随负值深度变化,这是纯线性做不到的(线性只能等比例)

为什么要”门卫”行为 :神经网络要学的是”哪些特征重要、哪些是噪声”。SiLU 让重要特征(大正数)近乎无损通过、噪声(深负数)静音、中间地带平滑过渡——下一层的降维矩阵拿到的是”提纯后”的信号。

4.4 怎么训练出来 + 理论支撑

准确地说,激活函数本身不是训练出来的 ——SiLU 的公式 x 1 + e − x \frac{x}{1+e^{-x}} 1+e−xx​ 是固定的(自门控设计),训练的是它前后的 W u p \mathbf{W}{up} Wup​、 W d o w n \mathbf{W}{down} Wdown​。但有两点值得展开:

理论支撑一:万能逼近定理 (Universal Approximation Theorem,Cybenko 1989 / Hornik 1991)——只要激活函数是非线性的,单隐层网络理论上就能逼近任意连续函数。它保证了”非线性 + 足够宽”的必要性;深度网络的角色是让”逼近”更高效(同样的函数,深而窄比浅而宽需要的参数少得多)。

理论支撑二:梯度流视角 ——激活函数处处光滑(SiLU 可导),梯度能顺利流过;ReLU 在 0 点不可导且负区梯度为 0(死区问题),SiLU 的负区小凹陷恰好修复了这一点(少量负梯度通过,死神经元可以被”救活”)。

演进史一句话 :sigmoid/tanh(1980s,梯度饱和)→ ReLU(2010,AlexNet 引爆深度学习但死区)→ GELU(2016,BERT/GPT-2 标配,概率软化)→ SiLU/SwiGLU(LLaMA 之后全系标配) ——每一代都在”梯度友好”和”表达力”之间找更好的平衡。

4.5 当前主流:SwiGLU

光有 SiLU 还不够。现代大模型实际用的是 SwiGLU 变体:不是”一个矩阵升维 + SiLU”,而是”两个矩阵升维,其中一个过 SiLU 后与另一个逐元素相乘”:

FFN S w i G L U ( x ) = ( SiLU ( x W g a t e ) ⊙ x W u p ) W d o w n \text{FFN}{SwiGLU}(\mathbf{x}) = \left(\text{SiLU}(\mathbf{x}\mathbf{W}{gate}) \odot \mathbf{x}\mathbf{W}{up}\right) \mathbf{W}{down} FFNSwiGLU​(x)=(SiLU(xWgate​)⊙xWup​)Wdown​

⊙ \odot ⊙ 表示逐元素相乘。直觉: W u p \mathbf{W}{up} Wup​ 负责产出”内容”, W g a t e \mathbf{W}{gate} Wgate​ 过 SiLU 后产出”门控信号”(0~1 的系数)——每个特征自己决定自己通过多少 。2020 年 Google 论文《GLU Variants Improve Transformer》实测 SwiGLU 比经典 FFN 略好,从此 LLaMA/Qwen/GLM/DeepSeek 全系采用。代价是多一个矩阵(参数 ×1.5),所以各家把中间维从 4 倍微调到 3.5 倍找补。

5. 各家 FFN 现状

各家 FFN 中间维对比如图 8-10 所示。

各家FFN

图8-10 各家 FFN 中间维:主流 ×3.5 倍 + SwiGLU 三矩阵

表8-3 各家 FFN 配置

模型d_modelFFN 中间维倍数激活
LLaMA-2 7B409611,0082.7xSwiGLU
LLaMA-3 8B409614,3363.5xSwiGLU
Qwen2.5 7B358418,9445.3xSwiGLU
GLM-4 9B409614,3363.5xSwiGLU
Mistral 7B409614,3363.5xSwiGLU
DeepSeek-V371682,048/专家MoE(每 token 激活 8 个专家)SwiGLU

DeepSeek-V3 的 MoE 是另一种思路:256 个小专家 FFN,每个 token 只激活 8 个——总参数巨大但单次计算量小(呼应篇1 的 Seed-1.6:230B 总参数、23B 激活,正是这种架构)。

6. 参数加总账:从 1 加回到 75 亿

呼应篇1 的思想实验——当时把 230B 砍到 1 个参数,现在逐篇加回来,看看到了篇8 攒了多少,如图 8-11 所示。

参数账

图8-11 参数加总账:从 1 个参数加回到 75 亿(对数坐标)

新增组件参数量累计
篇11 个参数(过原点直线)11
篇1 末+ 截距 b12
篇3嵌入表(129,280×4096)5.30 亿5.30 亿
篇6/7注意力四矩阵(GQA:W_Q、W_K、W_V 各 4096×1024、W_O)0.42 亿/层5.72 亿
篇5LayerNorm(γ、β × 2 处)1.6 万/层
篇8FFN 三矩阵(SwiGLU 3×4096×14336)1.76 亿/层2.18 亿/层
篇8 末× 32 层(嵌入 + 32 个完整 Block)75.1 亿

表8-4 参数账明细(按 LLaMA-3-8B 口径,红色为篇8 时点)

75.1 亿 ——这就是”从 1 个参数一路加回来”到本篇为止的家底。再加篇9 的输出层 lm_head(5.3 亿),正好是 80.4 亿 ≈ LLaMA-3-8B 的 8B 。对照篇1 的 230B:LLaMA-3-8B 约是它的 1/28,而 MoE 架构(如 Seed-1.6)用”稀疏激活”让总参数可以堆得更大、单次计算量却和小模型相当——这正是篇1 那个”230B vs 23B 激活”的悬念在参数层面的完整闭环。

顺带一笔对比账:单个 Block 里 FFN(1.76 亿)是注意力(0.42 亿)的 8 倍 ——大模型的知识大部分存在 FFN 权重里(Geva et al. 2021《Transformer Feed-Forward Layers Are Key-Value Memories》论证 FFN 像键值记忆库),注意力主要负责”查询调用”。

小结

这一篇,Block 六步全部走通,激活函数补成完整知识块:

  • 分工:注意力管词间交换(跨行),FFN 管词内加工(逐行)
  • 残差连接: H = X + f ( X ) \mathbf{H} = \mathbf{X} + f(\mathbf{X}) H=X+f(X),6 个 token 逐行手算;真正价值是梯度高速路(无残差 61 层梯度 4 × 10 − 19 4\times10^{-19} 4×10−19);出处 ResNet(何恺明 2015)
  • FFN 三段式:升维 ×3.5(高维摊开模式)→ SiLU → 降维;W_up/W_down 与 W_QKV 同理,训练学出
  • 激活函数完整闭环:XOR 例子(无它线性永远画不出弯)→ 万能逼近定理 → SiLU 门卫逐点表 → 激活前后三变化(收窄/保序/非线性压缩)→ 不是训练出来但不可导的 ReLU 死区被修复 → SwiGLU 门控是当前全系标配
  • 参数账:从 1 加回到 75.1 亿(+篇9 输出层 = 80.4 亿 ≈ LLaMA-3-8B);FFN 是注意力的 8 倍

下一篇预告

单个 Block 出口拿到了。真实模型要把 Block 堆几十层 ——LLaMA-3 堆 32 层,DeepSeek-V3 堆 61 层。为什么要堆这么多?浅层、中层、深层各学什么?

下一篇讲多层堆叠:语义怎么一级级从”词法”升到”任务级”,以及大模型宽度和深度的配比逻辑。

系列目录:

  1. 从一条直线到高维空间
  2. 分词与 token 表
  3. 隐藏层与嵌入
  4. 位置编码 RoPE
  5. Transformer Block 全景与层归一化
  6. QKV 三剑客
  7. 注意力权重与多头机制
  8. 残差连接与前馈网络(当前篇)
  9. 输出矩阵与多层堆叠
  10. 首 token 诞生与 KV-Cache

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文由作者按照 CC BY 4.0 进行授权