RNN 的痛点

序列任务里,第 100 步要看第 1 步的信息,必须经过 99 个中间步。信息会衰减。

Transformer 的核心想法:让任意两个位置直接互相"看"

自注意力(Self-Attention)的核心

每个位置算三个向量:

  • Q(Query 查询):我想找什么
  • K(Key 键):我能被什么找到
  • V(Value 值):我提供什么内容

注意力分数:

score(i, j) = Q_i · K_j / sqrt(d)
weights = softmax(scores)              # 归一化成概率
output_i = sum(weights_ij × V_j)        # 加权求和

每个位置的输出 = 所有位置 V 的加权平均——权重由 Q 和 K 的相似度决定。

直观理解

句子 "猫坐在垫子上",处理"坐"这个词时:

  • "坐" 的 Q 和 "猫" 的 K 相似度高 → 多看猫
  • "坐" 的 Q 和 "垫子" 的 K 也高 → 也看垫子
  • "在" 的 K 不相关 → 几乎不看

Self-attention 学会哪些词之间该互相关注

多头注意力(Multi-Head)

把 Q/K/V 拆成多组,并行算多个 attention,最后拼起来:

import torch.nn as nn

attn = nn.MultiheadAttention(embed_dim=512, num_heads=8, batch_first=True)
x = torch.randn(2, 10, 512)         # batch=2, seq_len=10, dim=512
out, weights = attn(x, x, x)         # Q, K, V 都用 x(自注意力)

不同 head 学不同方面的关系(一个学语法、一个学语义)。

完整 Transformer Block

Input
  ↓
Multi-Head Self-Attention
  ↓
Add & LayerNorm     ← 残差连接(和 ResNet 一样的思路)
  ↓
Feed-Forward (MLP)
  ↓
Add & LayerNorm
  ↓
Output

位置编码

self-attention 没有顺序概念——所以要把"我是第几个"的信息加进 embedding:

import torch
import math

def pos_encoding(seq_len, dim):
    pe = torch.zeros(seq_len, dim)
    pos = torch.arange(0, seq_len).unsqueeze(1)
    div = torch.exp(torch.arange(0, dim, 2) * -(math.log(10000.0) / dim))
    pe[:, 0::2] = torch.sin(pos * div)
    pe[:, 1::2] = torch.cos(pos * div)
    return pe

GPT/Claude 用 RoPE(旋转位置编码),原理类似。

Encoder vs Decoder

类型 任务 例子
Encoder Only 分类、抽取信息 BERT
Decoder Only 生成 GPT / Claude / Llama
Encoder-Decoder 翻译、摘要 T5、BART

2026 主流是 Decoder Only——GPT 系所有。

自己写一个迷你 Transformer 块

import torch.nn as nn

class TransformerBlock(nn.Module):
    def __init__(self, dim, heads):
        super().__init__()
        self.attn = nn.MultiheadAttention(dim, heads, batch_first=True)
        self.norm1 = nn.LayerNorm(dim)
        self.ff = nn.Sequential(
            nn.Linear(dim, dim * 4),
            nn.GELU(),
            nn.Linear(dim * 4, dim),
        )
        self.norm2 = nn.LayerNorm(dim)

    def forward(self, x):
        # 自注意力 + 残差
        out, _ = self.attn(x, x, x)
        x = self.norm1(x + out)
        # MLP + 残差
        x = self.norm2(x + self.ff(x))
        return x

GPT-4 / Claude 内部就是堆几十层这种块。

为什么 Transformer 取代 RNN

维度 RNN Transformer
训练并行 串行 全并行
长依赖 衰减 O(1) 距离
上下文长度 几百 几十万
大模型扩展 自然

Transformer 让"大模型"成为可能——这是 GPT 革命的根。

实践中:直接用 Hugging Face

不需要从头实现——下一篇起进入实用环节。