使用社交账号登录
引用自 https://github.com/An-Jhon/Hand-Drawn-Transformer



多头注意力的核心作用是:让模型在不同表示子空间里并行地关注不同类型的关系。
单头注意力当然也能工作,但它只能学到一套注意力模式。 多头注意力则可以让不同 head 分别关注不同的信息,比如:
所以,多头注意力并不是“把一个头简单复制很多份”,而是给模型提供了并行的、多视角的关系建模能力。原始 Transformer 论文明确说明,多头注意力可以让模型同时在不同表示子空间中关注信息,而单头注意力会削弱这种能力。
Q 和 K 虽然都来自输入表示,但它们承担的角色不同:
如果 Q 和 K 使用同一个投影矩阵,那 attention score 更接近一种“自相似度”计算,表达能力会受限。 而把 Q 和 K 分开,模型就能学习更灵活的匹配关系,也就是:
原始论文就是分别使用 (W^Q)、(W^K)、(W^V) 三组矩阵。
常见的 attention 打分方式有两类:
Transformer 选择点乘注意力,主要原因是:
原始论文明确比较过这两种方式,并指出两者理论复杂度类似,但点乘注意力在实践中更快、更省计算,因为它能直接利用高效的矩阵乘法实现。
attention 的原始分数来自:
[ QK^T ]
如果 (Q) 和 (K) 的每个分量都满足均值为 0、方差为 1,那么它们点积后的方差大约会随着维度 (dk) 增大而增大。 也就是说,(dk) 越大,attention score 的数值通常越大。
这样会带来一个问题:
所以 Transformer 在 softmax 之前会做:
[ \frac{QK^T}{\sqrt{d_k}} ]
这样做的效果是把 score 的尺度压回更稳定的范围,让 softmax 不容易饱和。原始论文明确给出了这个解释。
padding mask 的作用是:屏蔽掉那些只是补齐长度、并不是真实 token 的位置。
常见做法是在 softmax 之前,把 padding 位置对应的 attention score 设成一个极小值(理论上写作 (-\infty)):
这样经过 softmax 之后,padding 位置的权重就会变成 0。 这里要区分两种 mask:
这两者不是一回事。原始论文在 decoder 里写到,用 mask 把非法连接设为 (-\infty);padding mask 也是同样思想。
多头注意力通常把总 hidden dim 切成多个 head,每个 head 只负责其中一部分维度。
原因有两个:
保持总计算量不过度膨胀 如果 8 个 head 每个都还用完整维度,那参数量和计算量都会暴涨。
让每个 head 在自己的子空间里专门化 每个 head 只看一部分维度,更容易学到不同类型的关系。
原始论文中,每个 head 的维度通常设为 (d_{model}/h),这样多头后的总计算量与单头全维 attention 大致在同一数量级。
原始 Transformer 的一个 Encoder block 结构是:
在进入 Encoder stack 之前,输入还会先经过:
所以更完整的结构是:
其中每个 block 做两件事:
原始论文里的 Encoder 就是这样定义的。
在原始的 encoder-decoder Transformer 中,decoder block 有三部分:
其中第二部分就是 Encoder 和 Decoder 的交互点。
这里一定要记清:
也就是说,decoder 会先根据已经生成的前缀形成自己的查询表示,然后再去“读取”encoder 编码后的输入信息。原始论文明确是这样定义 cross-attention 的。
两者核心形式相同,但有一个关键区别:
为什么 decoder 必须这样做? 因为 decoder 是自回归生成模型,在训练时它的目标是“根据前文预测下一个 token”。如果当前位置能看到未来 token,就会信息泄露。原始论文明确指出,decoder 需要 mask 掉后续位置。
Transformer 相比 RNN 的一个重要优势是:在训练时,整个序列的很多位置可以并行计算。
例如:
这和 RNN 不一样。RNN 必须按时间步一步一步往后算,而 Transformer 在训练时可以把整段序列一起送进去。原始论文也把“更强的并行化能力”列为 Transformer 的重要优点。
要分开看:
训练时可以并行化。 因为训练通常使用 teacher forcing,整个目标序列已知,模型可以一次性处理所有位置,只要用 causal mask 保证每个位置不能偷看未来。
推理时不能跨时间步完全并行。 因为推理是自回归生成:
所以推理时跨 token 仍然是串行的,只能在“单步内部”并行。KV cache 可以减少重复计算,但不能把生成过程变成完全并行。Hugging Face 对 KV cache 的说明也明确强调,生成是 token by token 的。
你之前的理解是反的。 原始 Transformer 论文写的是:embedding 之后要乘以 (\sqrt{d_{model}}),也就是放大,而不是缩小。
直觉上可以这样理解:
这里要注意,原论文并没有像解释 scaled attention 那样给出详细数学推导,所以这个点你知道“做了什么”和“大致为什么这样做”就够了。
self-attention 本身并不天然感知顺序。 如果不给模型提供位置信息,那么“猫追狗”和“狗追猫”只看 token 集合可能非常接近,但它们的语义显然不同。
所以 Transformer 需要显式注入位置信息。 原始 Transformer 的做法是:
也就是把位置编码直接加到输入 embedding 上。它的意义是:
原始论文使用的是固定的 sinusoidal positional encoding。
优点:
缺点:
除了原始 Transformer 的绝对位置编码,还有很多后续方案。
直接学习每个位置的向量表示。
优点:
缺点:
不直接强调“绝对第几个位置”,而强调 token 之间的相对距离。Shaw 等人的工作就是代表。
优点:
缺点:
把位置信息编码进 Q/K 的旋转中。RoFormer 明确把它作为一种能将相对位置信息自然融入 self-attention 的方法。
优点:
缺点:
不把位置编码加到 embedding 上,而是直接给 attention score 加线性距离偏置。ALiBi 的论文强调它在长度外推上表现不错。
优点:
缺点:
Transformer 在每个子层外面都加了残差连接,然后再做 LayerNorm,也就是论文里的 “Add & Norm”。
如果一个子层输出是 (F(x)),那么残差大致可以理解成:
[ x + F(x) ]
它的意义是:
原始论文明确在每个子层外用了 residual connection followed by layer normalization。
这里要纠正一个常见误解:
LayerNorm 论文明确指出,BatchNorm 的效果依赖 mini-batch size,而 LayerNorm 在训练和测试时计算方式一致,更适合 RNN 和序列模型。
Transformer 选择 LayerNorm,主要因为:
在原始 Transformer 中,LayerNorm 位于每个子层的残差相加之后,也就是:
也就是经典的 “Add & Norm”。
BatchNorm 的做法是:
BatchNorm 原论文说明,它能显著加快训练并提高效果。
优点:
缺点:
Transformer block 里除了 attention,还包含一个前馈网络(FFN)。
原始论文给出的形式是:
[ FFN(x) = \max(0, xW1 + b1)W2 + b2 ]
也就是:
而且它是 position-wise 的,也就是说: 对每个 token 位置分别、独立地应用同一套前馈网络。
你可以把 Transformer block 分成两个职责:
优点:
缺点:
原始 Transformer 并不是简单用固定学习率,而是用了非常经典的 Noam learning rate schedule:
[ lrate = d_{model}^{-0.5}\cdot \min(step^{-0.5},\ step \cdot warmup^{-1.5}) ]
也就是:
论文中还明确写了 warmup_steps = 4000。优化器用的是 Adam。
原始 Transformer 使用了 residual dropout:
测试/推理时要特别注意:
model.eval()这点和你前面 PyTorch 学到的 train() / eval() 完全一致。
不会。
因为如果 masked self-attention 本身已经正确应用了 causal mask,那么它的输出里就不会包含未来 token 信息。残差连接只是把“当前位置已有的合法输入表示”加回来,并不会凭空引入未来信息。
所以:
特点是:输入序列里的每个位置通常都可以看到整个输入。 所以它更适合做“理解输入”的任务。
典型任务:
典型模型:
特点是:当前位置只能看见自己和左边的 token。 所以天然适合自回归生成。
典型任务:
典型模型:
特点是:先由 encoder 编码输入,再由 decoder 在生成时通过 cross-attention 读取 encoder 输出。 最适合“输入一个序列,输出另一个序列”的任务。
典型任务:
典型模型:
这个过程最好分成两段看:
先把整段 prompt 输入模型。 模型会:
Hugging Face 对 KV cache 的说明明确指出,缓存历史 K/V 是为了避免自回归生成时重复计算上下文。
在 prompt 前向结束后,模型会得到最后一个位置的 logits。 接着:
从最后一个位置 logits 中选出下一个 token
Hugging Face 的 generation 文档明确说,不同 decoding strategy 决定“下一个 token 怎么选”。
因为有 KV cache。
后续每一步只需要:
所以 decoder-only 的生成本质是:
这就是典型的自回归生成过程。KV cache 只是让这个过程更快,而不是改变它的本质。
因为 attention 已经负责了跨 token 的交互,而 FFN 的职责是对每个 token 的当前表示做统一的非线性变换。原始论文明确说 FFN 是对 each position separately and identically 应用的。
因为两者解决的问题不同:
所以它们不是互相替代,而是组合使用,形成 “Add & Norm”。原始 Transformer 就是这样设计的。
Hugging Face LLM Course — How do Transformers work?
https://huggingface.co/learn/llm-course/en/chapter1/4
Hugging Face LLM Course — Transformer Architectures
https://huggingface.co/learn/llm-course/en/chapter1/6
Hugging Face Course — Chapter 1 Summary
https://huggingface.co/docs/course/chapter1/10
scaled_dot_product_attentionLayer Normalization
https://arxiv.org/abs/1607.06450
Batch Normalization
https://proceedings.mlr.press/v37/ioffe15.html
Self-Attention with Relative Position Representations
https://arxiv.org/abs/1803.02155
RoFormer: Enhanced Transformer with Rotary Position Embedding
https://arxiv.org/abs/2104.09864
Train Short, Test Long: Attention with Linear Biases (ALiBi)
https://arxiv.org/abs/2108.12409
Hugging Face Transformers — KV Cache
https://huggingface.co/docs/transformers/v4.44.2/en/kv_cache
Hugging Face Transformers — Generation Strategies
https://huggingface.co/docs/transformers/generation_strategies
Multi-Head Self-Attention
-> Add & LayerNorm
-> Position-wise Feed-Forward Network
-> Add & LayerNormtoken embedding + positional encodingtoken ids
-> embedding + positional encoding
-> N 层 Encoder blocksinput representation = token embedding + positional encodingSubLayer -> Add -> LayerNorm当前前缀
-> 预测下一个 token
-> 把这个 token 接回前缀
-> 继续预测