📚 学习资源致谢
本学习笔记主要参考自 DataWhale Base-LLM 教程——一份由datawhale社区精心打磨的开源教材,涵盖大模型基础理论与实践应用。特此鸣谢!

📖 第一节 Seq2Seq 架构

1. 为什么需要 Seq2Seq?

传统的 RNN/LSTM 处理序列有局限性:

  • 对齐限制:Many-to-Many(等长)要求输入输出一一对应。
  • 现实痛点:翻译、摘要任务中,输入 XX 和输出 YY 的长度通常不相等(Unaligned)。
  • 核心逻辑:Seq2Seq 引入了“中间人”机制,将信息理解(Encoder)与信息生成(Decoder)彻底解耦。

2. 架构拆解:双螺旋结构

A. Encoder (压缩信息)

  • 任务:将变长序列 XX 映射为定长向量 CC(Context Vector)。
  • 关键点CC 通常是 RNN 最后一个时间步的隐状态(Hidden State)。
  • 思考:为什么常用双向 RNN?因为编码时可以“开天眼”看到全文,双向捕捉到的语义比单向更扎实。

B. Decoder (条件生成)

  • 本质:一个条件语言模型P(yty<t,C)P(y_t | y_{<t}, C)

  • 运行模式

  • 训练时 (Teacher Forcing):像有老师在旁边纠错,直接把“标准答案”喂给下一步。目的是加速收敛。

  • 推理时 (Autoregressive):自己教自己,把上一步生成的 当作下一步输入。

  • 结束逻辑:生成 <EOS> 停止,或者达到硬性设定的 max_len


3. 两种“递交”上下文 的方式

方式 做法 优缺点
初始化 CC 仅作为 Decoder 第 0 步的状态 逻辑清爽,但长序列容易“忘词”,信息在后续步骤中被稀释。
步步注入 每一个时间步都把 CC 拼接到输入向量中 “反复提醒”,缓解遗忘,但增加了计算开销且 CC 依然是静态的。

4. 落地细节(避坑指南)

  • Offset(偏移)训练

  • Decoder Input: [<SOS>, A, B, C]

  • Decoder Target: [A, B, C, <EOS>]

  • 这样模型才能学会在看到 A 的时候预测 B。

  • 推理策略

  • Greedy Search:每步选概率最大的。快,但容易陷入局部最优(目光短浅)。

  • Beam Search:每步保留 KK 个最优路径。慢,但全局效果更好。


💡 个人思考

1. 关于“信息瓶颈”的哲学

Seq2Seq 最大的痛点是 CC 的维度是固定的。
想象一下,要把一本《西游记》的内容压缩成一个 512 维的向量,再让另一方把它还原出来,这几乎是不可能的。这就是所谓的信息瓶颈。

  • 进化方向:为了打破瓶颈,我们不再强求把所有信息压进“一个向量”,而是允许 Decoder 在生成时去“翻看” Encoder 所有的历史状态——这就是 Attention(注意力机制) 的由来。

2. 与大模型 (LLM) 的关联

  • 目前的 GPT 系列是 Decoder-only 架构。
  • 它们取消了显式的 Encoder,把“输入”和“输出”都放在一个序列里。
  • 反思:其实 Seq2Seq 的 Decoder 本身就是一个 LLM 的雏形,只是那时候我们受限于 RNN 的串行效率和长程建模能力。

📖 第二节 注意力机制 (Attention)

1. 核心动机:打破“一言以蔽之”

  • 瓶颈回顾:标准 Seq2Seq 强迫 Encoder 将所有信息压入一个固定维度的向量 CC,导致长序列信息丢失。
  • 注意力逻辑:不再寻求“完美的压缩”,而是保留 Encoder 的所有中间状态,让 Decoder 在生成每个词时,根据需要“按需索取”。
  • 直观理解:从“背诵全文再默写”转变为“开卷考试,边看边写”。

2. 数学本质:三部曲

注意力机制本质上是一个加权求和的过程,其上下文向量 CtC_t 的计算分为三步:

  1. 打分 (Score):计算当前需求 ht1h'_{t-1}(查询)与每个输入 (键)的相关性。
  • 点积方案:$$e_{tj} = {h’_{t-1}}^T h_j$$
  • 加法/参数方案:$$e_{tj} = v^T \tanh(W[h’_{t-1}; h_j])$$
  1. 归一化 (Softmax):将分数转化为总和为 1 的权重。

αtj=exp(etj)exp(eti)\alpha_{tj} = \frac{\exp(e_{tj})}{\sum \exp(e_{ti})}

  1. 加权求和:用权重对输入状态 (值)进行求和。

Ct=αtjhjC_t = \sum \alpha_{tj} h_j


3. QKV 范式:万物皆可 Attention

这是目前最通用的抽象表达:

  • Query (Q)查询。我当前需要什么?(解码器当前状态)
  • Key (K)。我有这些索引可供匹配。(编码器所有状态)
  • Value (V)。我实际包含的信息内容。(编码器所有状态)

Attention(Q,K,V)=softmax(QKTdk)V\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V

思考点:为什么在计算点积后要除以 dk\sqrt{d_k}

这是缩放(Scaling)。当维度 dkd_k 很大时,点积结果会极大,导致 Softmax 进入梯度饱和区(非常平坦),产生梯度消失。缩放能让训练更稳。


4. 关键架构变体

类型 特点 备注
Cross-Attention Q 来自序列 A,K/V 来自序列 B Seq2Seq 翻译任务的标准做法。
Soft Attention 对所有输入计算权重,全序列加权 主流做法,端到端可微,易于训练。
Hard Attention 强行只选一个最相关的点(非0即1) 不可微,需强化学习,现在较少直接使用。
Local Attention 只在一个预定义的窗口内看信息 介于 Soft 和 Hard 之间,处理超长序列的折中方案。

💡 个人思考

1. 对齐(Alignment)的进化

在没有 Attention 之前,对齐是隐含在隐藏状态传递中的;有了 Attention,对齐变得显性化且可解释。你可以通过可视化注意力矩阵,清晰地看到模型在生成“Apple”时,到底盯着中文里的哪个词。

2. 关于“状态适配”的小细节

在写 PyTorch 代码时,如果 Encoder 是双向的(Bidirectional),它的输出维度是 hidden * 2,而 Decoder 通常是单向的。

  • 做法 A:用线性层 Linear(hidden * 2, hidden) 把 Encoder 的输出压回来。
  • 做法 B:把 Decoder 的状态维度也翻倍。
  • 经验:通常选择 A,因为这样参数量更受控,逻辑也更统一。

3. 迈向 Transformer 的一步

注意力的出现,让 RNN 变得不再那么“不可或缺”。既然我们可以通过 Attention 获取全局信息,那我们还需要 RNN 这种低效的、必须一个词一个词串行计算的结构吗?

  • 这就是 2017 年那篇著名的论文 《Attention Is All You Need》 的核心反思,也是接下来我们要学习的 Transformer 的开端。

📖 第三节 深入解析 Transformer

1. 核心思想:从“串行”到“全连接”

  • RNN 的缺陷:信息必须像接力棒一样从左往右传,导致长距离依赖丢失,且无法并行计算。
  • 自注意力的逻辑:抛弃接力,采用“海选”。序列中的每一个词元(Token)直接与序列中所有其他词元进行对比。
  • 本质:通过捕捉序列内部的依赖关系,为每个词元重构一个包含全局上下文的表示。

2. QKV 角色扮演:同一来源,不同功能

在自注意力中, Q,K,VQ, K, V 全部来自同一个输入矩阵 XX。它们通过三个不同的权重矩阵 WQ,WK,WVW^Q, W^K, W^V 进行线性变换:

  • Query (Q):代表“我要找什么”。
  • Key (K):代表“我这里有什么标签”。
  • Value (V):代表“我实际承载的语义信息”。

思考:为什么要三个矩阵?为了让模型能够学习到:在匹配(Q-K)时关注一种特征,而在信息提取(V)时保留另一种特征,从而增加表达的维度。


3. 矩阵化计算:并行的艺术

自注意力之所以比 RNN 快,是因为它将序列处理变成了矩阵运算:

  • 相似度矩阵: QKTQK^T。得到一个 [seq_len, seq_len] 的分数矩阵,第 ii 行第 jj 列代表词 ii 对词 jj 的关注度。
  • 缩放 (Scaling):除以 dk\sqrt{d_k}。防止 dkd_k 过大导致点积过大,使 Softmax 进入梯度消失区。
  • 权重分配: softmax()\text{softmax}(\cdot)。将分数归一化为概率分布。
  • 信息聚合: 乘 VV。根据权重把所有词的信息“吸过来”。

公式总结:

Z=Attention(Q,K,V)=softmax(QKTdk)VZ = \text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V


4. 自注意力 vs. 交叉注意力

这是很多面试会考到的细节:

比较维度 自注意力 (Self-Attention) 交叉注意力 (Cross-Attention)
Q 的来源 序列 A (当前序列) 序列 B (通常是解码器状态)
K/V 的来源 序列 A (当前序列) 序列 A (通常是编码器输出)
功能目标 内部理解:理解词与词的依赖 外部对齐:寻找源文与译文的对应
典型位置 Transformer 的 Encoder/Decoder 内部 Encoder 和 Decoder 连接处

💡 个人思考与深度总结

1. 时间复杂度之殇

  • 自注意力的时间复杂度是 O(L2d)O(L^2 \cdot d),其中 LL 是序列长度。
  • 这意味着序列长度翻倍,计算量翻四倍。这就是为什么目前的 LLM 很难直接处理极其庞大的上下文(如整本书),也解释了为什么会有各种“稀疏注意力”或“线性注意力”的改进。

2. 局部性 vs 全局性

  • RNN 和 CNN 都有某种“局部性”偏见(倾向于看旁边的词)。
  • 自注意力是完全没有位置偏见的。它认为第 1 个词和第 100 个词的距离是一样的。为了让模型知道词的顺序,我们必须额外引入 位置编码 (Positional Encoding) ——这也是下一节你会学到的重要内容。