From Seq2Seq to Attention

提醒补图

从Seq2Seq 讲起

1. 模型结构

Seq2Seq 由 Encoder-Decoder 结构 组成:

  • Encoder(编码器)
  • 处理输入序列(如 “we are eating bread”)。
  • 通过循环神经网络(RNN/LSTM/GRU)生成一系列隐藏状态
  • 处理完最后一个单词后,输出 最终隐藏状态
  • 这个最终隐藏状态 作为上下文向量 c 传递给解码器。
  • Decoder(解码器)
  • 接收 初始隐藏状态 (可以通过 c 计算得到)。
  • 依次生成目标语言的单词(如 “estamos comiendo pan”)。
  • 生成方式:
  1. 先输入特殊起始标记 [START](即 )。
  2. 结合上一个隐藏状态 、上下文向量 c、前一步输出单词 ,计算当前时间步的隐藏状态 s_t。
  3. 生成当前时间步的单词 ,并输入下一个时间步。
  4. 持续执行,直到遇到 [STOP]。

2. 关键数学公式

  • Encoder 计算隐藏状态:

其中 是 RNN 计算公式(如 LSTM/GRU)。

  • Decoder 计算隐藏状态:

  • 这里的 也是一个 RNN(或者 LSTM/GRU)。
  • 需要输入:
  • 之前的单词 (即上一步的输出)。
  • 之前的隐藏状态
  • 上下文向量 c,这个可以理解为编码器传递的全局信息。

3. 具体流程

  • Step 1: Encoder 处理输入
  • “we are eating bread” → 变成一系列隐藏状态。
  • 最后一个隐藏状态 h_4 作为 上下文向量 c 传递给解码器。
  • Step 2: Decoder 生成翻译
  1. 初始输入: 先喂入 [START],然后:
  2. 生成 “estamos”(y1),作为下一步的输入。
  3. 生成 “comiendo”(y2),继续往下传。
  4. 生成 “pan”(y3)。
  5. 生成 [STOP](y4),结束。

初始解码器隐藏状态 s0和上下文向量 c 的计算

Seq2Seq 结构 中, (初始解码器隐藏状态)和 c(上下文向量)是由编码器的最终隐藏状态 计算得到的。它们的计算方式如下:

1. 上下文向量 c

上下文向量 c 是 编码器的最终隐藏状态,即:

其中:

  • 是编码器在处理完 最后一个输入单词 后的隐藏状态。
  • 它总结了整个输入序列的信息,作为解码器的全局信息传递给每个时间步。

解释:

  • 普通 Seq2Seq(不含 Attention)的实现中,直接取 作为上下文向量。
  • 带 Attention 的 Seq2Seq 中,c 会是一个加权求和的隐状态组合,而不是单独的

2. 初始解码器隐藏状态

的计算方式 可以有不同的实现方式,但常见做法是:

其中:

  • 是一个 可学习的权重矩阵
  • 偏置项
  • 是一个激活函数(防止值爆炸)。

解释:

  • 由于编码器和解码器可能使用 不同的 RNN 结构(比如 Encoder 用 LSTM,Decoder 用 GRU),直接用 h_T 可能不合适,所以通常会经过一个**前馈层(Fully Connected Layer)**转换成解码器合适的隐藏状态。
  • 有时候可以直接取 s_0 = h_T(如果编码器和解码器使用相同的 RNN 结构)。

总结

  • (编码器的最终隐藏状态)。
  • (通常需要一个前馈层)。
  • 这两者都来源于编码器的最终隐藏状态 ,但 可能会有额外的变换。

如果是 带 Attention 机制的 Seq2Seq,那么 c 就不只是 了,而是由所有隐藏状态的加权和计算得到。

Seq2Seq 模型的核心缺陷:信息瓶颈

即:

在经典的 Seq2Seq 模型中,上下文向量 c 是从整个输入句子提取出来的一个固定长度的向量,它在解码阶段每个时间步都会被使用。但当输入序列很长时,仅靠一个向量来表示全部信息是不够的

Attention 的引入(为了解决这个瓶颈)

Attention 的核心思想:

与其把整句话“压缩成一个向量”,不如让解码器在每个时间步都自己决定要关注输入句子的哪些部分

  • 不再依赖唯一的 c
  • Decoder 每生成一个词,就动态地对 Encoder 的每个隐藏状态 打分,计算加权和(也叫 attention vector)
  • 这样 Decoder 每一步都“看着”不同位置的信息 —— 信息流动不再被限制。

Attention

Cross-Attention

对齐方式

  1. **Dot-product Attention: 模版匹配角度理解
  2. General Attention:

:General attention = dot-product attention + learnable 权重变换在 dot-product 之前,先用矩阵 映射到和 更匹配的空间。给每个候选向量 做了一个“语义增强”或“预对齐”,再去匹配 。• 只是我们把候选模板( )先“调整形状”再比较学习到的 可以强调某些特征、压制无关信息 ⇒ 更精细匹配
3. Additive Attention(Bahdanau Attention)

  • 用两个不同的权重矩阵把

映射到同一个空间

  • 把两个向量相加,再通过 tanh 非线性压缩(增加表达力)
  • 最后用一个可学习向量 v_a 去对这个融合结果做投影 ⇒ 输出一个标量
  • Bahdanau Attention 更像是一个“小型神经网络”,它不只做相似度匹配,而是学习一个复杂的“匹配函数”。不再是简单的“你像我就打高分”,而是“你哪些特征和我哪些部分组合起来后,我就认为你重要”。是一种更深层次的语义耦合计算

soft-attention vs hard-attention

可以通过抽象出一层 attention layer, 插入到 cnn, rnn 里 “注意力”从一种启发式机制变成了一个可泛化的、高效的、端到端可训练的神经网络计算模块,它的抽象化(Query-Key-Value)和优化(用矩阵乘法加速)为 Transformer 的诞生打下了基础。

缩放因子

防止 softmax 输出过于极端,保证反向传播中梯度不会消失,训练过程更加稳定

Query-Key-Value 框架

分离 Key 和 Value 给模型带来了表达能力的解耦

  • Key 用于比对,用更适合匹配的特征空间
  • Value 用于内容提取,用更丰富的语义表示
  • 二者不再共享参数或表达空间,不互相限制

Self-Attention

Masked self attention

仅仅根据前面的的信息

Multi-Headed

多头注意力就是在同一个输入上,并行运行多个注意力头(多个独立 Q-K-V 计算),让模型能从多个“子空间”学习不同的注意力模式,最后把所有结果拼接(concat)起来,再线性投影得到最终输出。

 头的数量(number of heads):h
每个头内部的维度: (或者 = =

这是每个 attention 子空间的维度

  • 也就是每个 head 中 Query/Key/Value 的维度
  • 假设总输入维度是

用了 8 个头

  • 那每个 head 的维度通常设置为

注意:你可以自己设置 ,但通常默认:

这样拼接后维度刚好对上。

把 Self-Attention 当作 CNN 里的新型操作层

  • 之前的局限:传统 CNN 里的每个位置只看局部邻域(感受野小),但:

Self-Attention 层允许每个位置看整张图的所有位置(long-range dependency)

输入结构(来自 CNN)

原始输入图像 → CNN 提取特征 → 得到一个 shape 为:

C 是通道数,H × W 是空间尺寸。

Self-Attention 模块的 3 个主要分支:

名称 维度(每个位置) 用途
Query 表示“我要问什么”
Key 表示“我是谁,是否值得被关注”
Value 表示“我能贡献什么内容”

通过三个不同的 卷积,把特征变换成:

Attention 权重计算流程:

  1. 将 Query 和 Key 展平为矩阵

  • 同样 Key 也 reshape 为

  1. 点积计算相似度矩阵(注意力权重)

  • 得到 shape 为

的注意力矩阵

  • 表示:图中每个位置如何关注所有其他位置
  1. 加权 Value 向量求和

  • 将所有位置的 value 向量做加权求和,得到新特征图

输出重建 + 残差连接:

  • 得到的新特征图再通过一个

卷积恢复维度到 C

  • 再加上残差连接(Residual Connection):

  • 保留原始特征,防止退化,训练稳定
元素 作用
1×1 Conv 将输入特征线性变换成 Q/K/V
softmax(Q·Kᵀ) 计算所有位置之间的注意力权重
A·V 加权求和,整合信息
Residual 保留输入,防止特征破坏
输出 和输入同尺寸的新特征图

这种结构就是 SAGAN(Self-Attention GAN) 中首次被用在图像生成的注意力层,后来被广泛用于图像识别、分割、检测,甚至是 ViT(Vision Transformer)的启发来源。
总结一句话:

这是一个“视觉自注意力层”,它允许图像中每个位置和所有其他位置进行全局交互,非常适合处理图像中远距离依赖(比如一只猫头和尾巴很远但有关联)。

图像生成字幕

Step 1: 用 CNN 处理图片,得到一个 feature map

  • 比如输出是 3×3 的网格,每个位置有一个特征向量
  • 这些向量对应图像的不同区域(视觉 patch)

Step 2: 解码器 RNN 开始生成描述(例如生成单词 “cat”)

  1. 初始状态为 ,通常由平均池化所有 或者其他方式得到。
  2. 利用打分函数 对图像各个位置计算对齐分数
  3. 用 softmax 将对齐分数归一化,得到注意力权重
  4. 用这些权重加权图像特征:

  1. 、前一个词(START token) 、和隐藏状态 送入 RNN,得到
  2. 预测当前单词 (比如 “cat”)

Step 3: 继续生成下一个单词

  1. 进入下一轮:用 与每个 再次计算对齐分数
  2. 得到新的注意力分布 ,新的上下文向量
  3. , , 计算 ,并预测
  4. 重复这个过程直到生成句子结束(比如输出 [STOP])

直觉解释:

你可以把这个注意力机制想象成“看图写话”时的眼动过程:

我正在生成描述的第一个词,比如“cat”,此时我看向图片中有猫的那一块区域,对那部分关注度就高。

接着,当我要说“sitting on a tree”时,我又把注意力转移到图中树的位置,依此类推。

RNN vs CNN(一维卷积) vs Self-Attention

背景

假设你有一个输入序列:

我们要从这组输入中提取出有用的特征(比如做翻译、分类、总结等)。

第一种方式:RNN / LSTM

  • 优点:
  • 擅长建模长距离依赖(long-term dependency)
  • 每个输出状态 h_t 都能从所有过去输入中学习:

缺点:

  • 串行执行(计算 必须等
  • 很难并行 → 训练速度慢,GPU 加速能力差
  • 可扩展性差(scalability issue)

第二种方式:1D ConvolutionConvolutional Neural Networks for Sentence Classification

优点:

  • 可以并行计算所有位置的输出
  • 适合 GPU、大模型、深层堆叠

缺点:

  • 每个输出只看固定感受野(例如 3 个 token)
  • 要想获得全局信息,必须堆叠很多层(层数多、难训练)
  • 长距离信息传播效率差

第三种方式:Self-Attention(自注意力)

优点:

  • 每个位置直接看整个序列

  • 没有时间顺序限制 → 可以并行计算所有位置
  • 高效利用 GPU:主要是矩阵乘法 + Softmax

缺点:

  • 内存占用大:attention matrix 是 ,输入越长越吃显存
属性 RNN 1D CNN Self-Attention
是否支持长依赖 不行 非常好
并行性(GPU友好) 串行 高并行 高并行
可扩展性 很好
参数共享 时序共享 滑动卷积共享 查询共享
缺点 慢,难训练 感受野小 显存大(T×T attention)

作者的推荐和看法(总结性一句):

自注意力机制结合了 RNN 的长依赖捕捉能力 和 CNN 的高度并行性,是当前最优的序列处理机制。

虽然它显存占得多,但随着 GPU 越来越强,这不是大问题

小技巧记忆三者:

  • RNN:一层一层传话,很认真,但慢
  • CNN:短距离小组讨论,快但不懂全局
  • Attention:群聊广播,人人互相可见,信息同步一流

抽象出 Attention Layer

任何时候你有两组数据 —— 一组是想去“检索”的(Query),一组是你可以“提取信息”的(Key/Value)

Query来源

任务 Q 的来源 含义
Transformer 编码器 当前 token 的 embedding + position embedding “这个词需要聚合哪些上下文”
Transformer 解码器 当前 decoder 的隐藏状态 “我现在准备输出一个词,我要去查和它相关的输入”
图像描述生成 当前 LSTM 状态 “我正在生成词,我要看看图像中哪些区域重要”
文本问答系统 问题 embedding “问题是 Q,文档是输入,我要找到答案”
ChatGPT 对话模型 当前对话 token 的 embedding “当前词该从历史中提取哪些信息”

在 Transformer 中的标准操作:

在 Self-Attention 中,Q、K、V 都是从输入向量 X(比如 embedding)映射出来的:

也就是说:

  • Q 来自输入序列本身,表示“每个位置想获取的信息”
  • 它和 K 比较,找到“我要从哪里获取信息”
  • 然后从 V 中提取信息做融合

结果 y 的含义

这些 y_i 是聚合后的信息结果,是每个 query 想要“从输入中收集到”的上下文表示。它们会被传给神经网络的下一层,作为新表示、用于预测、分类、生成等任务。


Attention Layer 本质上做了什么?

以图中结构为例,每个 (比如 , , , ):

  • 都会从 Key-Value 对中提取出自己想要的信息
  • 得到输出向量
  • 这些 就是 attention 之后的结果 —— 也就是整个 Attention Layer 的 输出 Y

Y 的作用取决于你把 Attention Layer 放在什么位置

1:放在 Transformer 编码器中

  • 输入是 token 的 embedding
  • 每个 是 token i 在当前层聚合完上下文后的表示
  • 这些 会被送到下一层 Transformer block,继续做多头注意力 + FFN

2:放在 Transformer 解码器中

  • 输入是当前 decoder 的隐藏状态(比如想生成某个词)
  • 是该状态从 encoder 输出中聚合出的上下文(cross-attention)
  • 被用于下一步词的生成(比如预测 “seagull”)

3:放在图像描述任务中

  • Query 是 decoder 的语言状态
  • 输出 是图像区域特征的加权组合
  • 被用来生成下一个词的输入

4:用作一般信息融合层

  • Query 是用户行为序列的表示,Input 是候选商品
  • 输出的 是聚合完商品特征后的推荐意图向量
  • 送入 MLP 做点击率预测

CNN & RNN & Self-Attention

属性 RNN 1D CNN Self-Attention
是否支持长依赖 不行 非常好
并行性(GPU友好) 串行 高并行 高并行
可扩展性 很好
参数共享 时序共享 滑动卷积共享 查询共享
缺点 慢,难训练 感受野小 显存大(T×T attention)