在大模型领域,Transformer 是绕不开的骨架。从 GPT 系列到各类开源大模型,几乎都把 Transformer 作为核心网络结构。很多人第一次接触这个概念时,会看到一个名字:Ashish Vaswani。他是 2017 年论文《Attention Is All You Need》的第一作者,这篇论文提出的 Transformer 架构,被普遍认为是现代大模型的重要基础。与其说 Transformer 是哪一个人的成果,不如说它来自一个研究团队的协作,但 Vaswani 作为论文第一作者,确实让这个名字与 Transformer 深度绑定。这篇文章不准备写人物故事,而是从工程视角拆解 Transformer 的来龙去脉、核心原理、最小实现,以及从 Transformer 走向大模型时的学习与落地路径。读完你可以理解自注意力机制、多头注意力和位置编码为什么存在,也能用 PyTorch 跑通一个最小注意力模块,并知道调用、微调和本地部署大模型时应该关注哪些问题。
这篇内容适合三类读者:刚接触大模型、想弄清楚底层结构的后端或算法工程师;已经会用 Hugging Face 调用模型,但面对源码和论文时感到吃力的开发者;以及准备做本地大模型部署或微调,需要先补齐 Transformer 基础知识的工程人员。
1. 先理解 Transformer 为什么能成为大模型的奠基架构
1.1 从 RNN、LSTM 到注意力机制
在 Transformer 出现之前,序列建模的主流工具是循环神经网络 RNN 及其改进版本 LSTM、GRU。RNN 的核心思路是“按时间步处理”:每个时刻把当前输入和上一时刻的隐状态组合起来,生成新的隐状态。这种结构天然适合文本这类序列数据,但也带来两个明显问题。
第一是并行困难。下一个时间步必须等前一个时间步计算完,GPU 的优势很难发挥。第二是长距离依赖较弱。当输入序列很长时,早期信息经过多步传递会被逐步冲淡,LSTM 通过门控机制缓解了这个问题,但没有彻底解决。机器翻译任务中还出现了一个补充方案:注意力机制,让生成某个词时重点关注输入中的相关词。
Transformer 的贡献,是彻底抛弃了循环结构,只用注意力机制完成序列到序列的建模。用一句话概括:它不再逐词排队处理,而是让输入序列中的所有 token 同时参与计算,并动态学习它们之间的关系。这个设计同时解决了并行性和长距离依赖问题,也为后续扩大模型规模铺平了道路。
1.2 Transformer 到底改进了什么
Transformer 相比 RNN/LSTM 的改进,不只是换了一种网络结构,而是改变了整个序列建模的效率上限。
从训练角度看,Transformer 对一句话中的所有位置可以一次性完成前向计算,计算过程可以高度向量化。从建模能力看,任意两个 token 之间的依赖路径长度都是 1,信息从句子开头传到结尾只需要一次注意力计算。这样的设计让模型更容易捕捉长距离语义关系,比如一段前文中的指代对象。
下表归纳了 RNN/LSTM 与 Transformer 的主要差异。
| 特性 | RNN / LSTM | Transformer |
|---|---|---|
| 序列处理方式 | 按时间步逐步处理 | 所有位置并行计算 |
| 长距离依赖能力 | 较弱,靠门控缓解 | 强,任意位置依赖路径短 |
| 训练并行效率 | 低 | 高 |
| 位置信息 | 天然由时间步提供 | 需要额外引入位置编码 |
| 向超大模型扩展 | 困难 | 结构稳定,相对容易 |
| 计算复杂度 | 随序列长度线性增长 | 自注意力随序列长度平方增长 |
这里的代价也很明确:自注意力的计算复杂度是 O(n²),n 是序列长度。因此输入变长时,GPU 显存和时间开销增长很快。这也是后来各种稀疏注意力、线性注意力、KV Cache 等优化技术出现的原因。
1.3 为什么大模型普遍采用 Transformer
目前看到的大模型,绝大多数采用 decoder-only 的 Transformer 作为主干。原因并不神秘:这类结构可以按照“预测下一个 token”的方式做自回归语言建模,训练数据只需要“上文-下一个词”这种自然文本,数据获取和组织都相对容易。
随着参数规模和数据量同步扩大,模型能力会表现出比较稳定的增长趋势。这种规律常被称为 Scaling Laws,也就是缩放定律。Transformer 的优势在于它的结构可以稳定地扩展到千亿参数级别,配合分布式训练、混合精度和流水线并行等工程手段,形成了现代大模型的标准路线。
需要说明的是,Transformer 不是“完美结构”,但随着训练数据和算力的增长,它被证明是一条容易扩展、效果稳定的技术路径。理解了这个背景,再去看注意力公式和代码,就不会只觉得它们是一堆矩阵运算,而是能明白每一步都在解决什么问题。
2. 拆解 Transformer 核心模块,别停留在结构图上
2.1 从 Query、Key、Value 理解自注意力
自注意力机制是整个 Transformer 的核心。它借鉴了“检索”的概念:有一组内容,每个内容有编号;当你想找东西时,会拿自己的需求去和所有编号做匹配,再按匹配程度取出对应内容。
在 Transformer 中,每个 token 经过线性变换得到三个向量:Query、Key、Value。Query 表示“当前 token 想找什么”,Key 表示“每个候选 token 提供什么索引”,Value 表示“候选 token 真正携带的信息”。当前 token 与所有 token 的 Key 计算相似度,得到权重后对 Value 做加权求和,就是自注意力的输出。
具体公式可以写成:
Attention(Q, K, V) = softmax(Q * K^T / sqrt(d_k)) * V其中,d_k 是每个头的维度。除以sqrt(d_k)是为了避免点乘结果过大,导致 softmax 进入梯度很小的区域。学习时先记住这个缩放步骤,后面调试训练不收敛时经常要用到。
2.2 多头注意力:让模型从多个角度观察序列
单个注意力机制只能生成一种“关联模式”,但句子里的关系是多样的:有的关注词性搭配,有的关注指代关系,有的关注局部短语。多头注意力把这个单一过程复制成多份,每一份使用不同的线性变换,因此可以从不同子空间学习关系。
实现时,模型会先通过多个线性层把 Q、K、V 投影到不同维度,然后拆分出n_head个头,每个头独立计算注意力,最后把所有头的输出拼接起来,再经过一个输出线性层。常见设置为d_model = 512,n_head = 8,这样每个头的维度d_k = 64。
多头注意力的价值在于:模型不必用一套权重去平衡所有关系,而是自然地分工学习。这也是 Transformer 表达能力强的一个重要来源。
2.3 位置编码:模型如何知道词的顺序
Transformer 因为没有循环结构,输入 token 之间天然没有顺序概念。如果不加额外处理,“我打你”和“你打我”在模型看来可能是同样的输入。位置编码就是用来把顺序信息注入模型的结构。
原始论文使用了正弦位置编码,利用正弦和余弦函数生成不同频率的位置向量,然后加到 token embedding 上。这样不同位置的向量就会带有位置信息,并且可以通过线性关系表达相对位置。
后来的模型也发展出不同做法:一部分模型使用可学习位置编码,直接让网络在训练中学习位置向量;近些年大模型更常使用旋转位置编码 RoPE,它通过旋转矩阵对 Q 和 K 施加位置信息,在 LLaMA 等模型中应用很广泛。学习时不必死记每种编码的数学细节,但要理解一个原则:位置信息不是可有可无的,没有它,Transformer 就无法区分词序。
2.4 残差、LayerNorm 与前馈网络
Transformer 的每个子层并不只有注意力。标准结构会在自注意力模块之后接一个 LayerNorm,做残差叠加,再接一个前馈网络 FFN,再一次残差加 LayerNorm。这样做的原因很实际:模型加深之后,梯度传播容易不稳定,残差连接让梯度有一条直达通道,LayerNorm 则让每一层输入保持合适尺度。
前馈网络通常包含两层线性变换和一个激活函数,例如FFN(x) = ReLU(x * W1 + b1) * W2 + b2。它作用于每个 token 位置,是模型参数的重要组成部分。实际计算中,FFN 的参数往往比注意力模块还多,因为中间层维度通常会放大到d_model的 4 倍左右。
2.5 Encoder-Decoder、Encoder-Only 与 Decoder-Only
原始 Transformer 论文为了解决机器翻译,使用了 Encoder-Decoder 结构:Encoder 读取完整输入,Decoder 一边看输入一边自回归生成目标。
后来这套结构分化出三条路线:
| 结构类型 | 代表模型 | 典型任务 |
|---|---|---|
| Encoder | BERT 系列 | 文本分类、语义理解、检索 |
| Decoder | GPT 系列 | 文本生成、对话、代码生成 |
| Encoder-Decoder | T5、BART | 翻译、摘要、条件生成 |
理解这个分类,有助于看懂各类模型的设计差异。比如做语义向量时优先考虑 Encoder 模型,做生成任务时优先考虑 Decoder 模型。但并不是绝对的,当前很多大模型用 Decoder 也能做分类,只是设计初衷不同。
3. 用 PyTorch 实现最小自注意力模块并验证输出
3.1 环境准备与依赖版本
动手写代码前,先准备一个干净的 Python 环境。建议使用 Python 3.9 或 3.10,PyTorch 使用 2.x 版本。如果你只是验证注意力模块,安装 CPU 版即可,不需要 GPU。
python -m venv .venv source .venv/bin/activate pip install torch如果你已经安装了 PyTorch 但版本不确定,可以先查看版本:
python -c "import torch; print(torch.__version__)"实际项目中的版本以你自己环境为准,示例代码只依赖 PyTorch 最基础的张量计算和nn.Module,版本差异不会影响核心逻辑。下面代码用于学习原理,不是完整 Transformer 复现。
3.2 实现 Scaled Dot-Product Attention
先实现最核心的注意力计算模块。输入是 Q、K、V,形状为batch_size, n_head, seq_len, d_k,输出是注意力结果和注意力权重。
import torch import torch.nn as nn import torch.nn.functional as F class ScaledDotProductAttention(nn.Module): def __init__(self, dropout=0.0): super().__init__() self.dropout = nn.Dropout(dropout) def forward(self, query, key, value, mask=None): d_k = query.size(-1) # scores shape: [batch_size, n_head, seq_len, seq_len] scores = torch.matmul(query, key.transpose(-2, -1)) / (d_k ** 0.5) if mask is not None: scores = scores.masked_fill(mask == 0, float("-inf")) attn_weights = F.softmax(scores, dim=-1) attn_weights = self.dropout(attn_weights) output = torch.matmul(attn_weights, value) return output, attn_weights这段代码里有几个关键点:
key.transpose(-2, -1)交换最后两个维度,使 Q 和 K 能进行矩阵乘法。- 除以
d_k ** 0.5是公式中的缩放步骤。 mask == 0时填充负无穷,这样 softmax 后的权重会变成 0,避免看到不该看的位置。- 返回的
attn_weights可以用来可视化模型关注位置。
3.3 实现 Multi-Head Attention
多头注意力只是把 Q、K、V 分别做线性变换,拆成多个头,独立计算后再拼接。
class MultiHeadAttention(nn.Module): def __init__(self, d_model, n_head, dropout=0.0): super().__init__() assert d_model % n_head == 0, "d_model must be divisible by n_head" self.d_model = d_model self.n_head = n_head self.d_k = d_model // n_head self.w_q = nn.Linear(d_model, d_model) self.w_k = nn.Linear(d_model, d_model) self.w_v = nn.Linear(d_model, d_model) self.w_o = nn.Linear(d_model, d_model) self.attention = ScaledDotProductAttention(dropout) def forward(self, query, key, value, mask=None): batch_size, seq_len, _ = query.size() # [batch_size, seq_len, d_model] -> [batch_size, n_head, seq_len, d_k] Q = self.w_q(query).view(batch_size, seq_len, self.n_head, self.d_k).transpose(1, 2) K = self.w_k(key).view(batch_size, -1, self.n_head, self.d_k).transpose(1, 2) V = self.w_v(value).view(batch_size, -1, self.n_head, self.d_k).transpose(1, 2) attn_output, attn_weights = self.attention(Q, K, V, mask) # [batch_size, n_head, seq_len, d_k] -> [batch_size, seq_len, d_model] attn_output = attn_output.transpose(1, 2).contiguous().view( batch_size, seq_len, self.d_model ) output = self.w_o(attn_output) return output, attn_weights注意view与transpose的组合方式。先view成四维张量,再用transpose把n_head换到第二维。最后要contiguous()再view,因为transpose之后张量内存布局不连续,直接view会报错。
3.4 运行验证与预期输出
用一个很小的随机输入验证模块能否正确运行。
if __name__ == "__main__": torch.manual_seed(42) batch_size = 2 seq_len = 6 d_model = 128 n_head = 8 query = torch.randn(batch_size, seq_len, d_model) key = torch.randn(batch_size, seq_len, d_model) value = torch.randn(batch_size, seq_len, d_model) mha = MultiHeadAttention(d_model, n_head) output, attn_weights = mha(query, key, value) print("output shape:", output.shape) print("attn shape:", attn_weights.shape)预期输出:
output shape: torch.Size([2, 6, 128]) attn shape: torch.Size([2, 8, 6, 6])output的形状与输入一致,说明信息经过多头注意力后维度没有丢失。attn_weights的形状是[2, 8, 6, 6],含义是:batch 内每个样本、每个注意力头、每个目标位置对所有源位置的注意力权重。
3.5 常见的形状与调试问题
手写注意力时最常见的错误都集中在张量形状上。
| 问题现象 | 常见原因 | 检查方式 |
|---|---|---|
报错shape mismatch | Q、K、V 维度没有对齐 | 分别打印 Q、K、V 的 shape |
d_model % n_head != 0 | 头数不能整除模型维度 | 确认模型维度和头数设计 |
view报错invalid size | transpose后直接view | 先调用contiguous() |
| 注意力权重全是 0 或 NaN | mask 形状不对或 softmax 输入有 NaN | 检查 mask 尺寸,检查是否缩放 |
| 训练不收敛 | 忘记除以sqrt(d_k) | 回到公式核对缩放逻辑 |
注意:实际训练时,mask 的逻辑要仔细确认。比如 decoder 中的因果掩码需要让当前位置只能看到过去,不能直接让 mask 全为 1。
4. 从 Transformer 到大模型:调用、微调和本地部署
4.1 用 Hugging Face 快速运行一个模型
理解了注意力模块后,可以找一个现成的小模型体验真实的大模型推理流程。Hugging Face Transformers 库对社区很友好,适合学习。
pip install transformers然后加载一个很小的 GPT-2 模型做生成测试。GPT-2 虽然远小于今天的千亿参数模型,但结构上已经具备 decoder-only Transformer 的核心特性。
from transformers import AutoTokenizer, AutoModelForCausalLM model_name = "gpt2" tokenizer = AutoTokenizer.from_pretrained(model_name) model = AutoModelForCausalLM.from_pretrained(model_name) input_text = "Transformer is a" inputs = tokenizer(input_text, return_tensors="pt") outputs = model.generate(**inputs, max_length=20, do_sample=True) print(tokenizer.decode(outputs[0], skip_special_tokens=True))第一次运行会下载模型和词表文件,需要确认本机网络和磁盘空间。gpt2很小,适合学习;如果你的机器配置有限,也可以选择更小的distilgpt2。
4.2 预训练与微调的差异
大模型并不是直接训练来完成某个特定任务的。常规流程是先在海量通用文本上预训练,学习语言规律和知识;再用下游数据微调,让模型适配具体业务。
微调方式也有不同取舍:
| 微调方式 | 修改范围 | 显存占用 | 适用场景 |
|---|---|---|---|
| 全参微调 | 所有参数 | 高 | 数据充分、计算资源充足 |
| LoRA | 额外注入低秩矩阵 | 中低 | 常见业务任务,性价比高 |
| QLoRA | 量化基础模型 + LoRA | 低 | 单卡场景、消费级 GPU |
学习阶段建议先了解 LoRA 思路:冻结原始权重,只训练注入的少量低秩矩阵。这样显存占用低,并且可以快速试试不同数据集的效果。不要一开始就做全参微调,容易把资源耗在调试上。
4.3 本地部署大模型的基本姿势
本地部署常见目标有两种:用 GPU 提供高吞吐服务,或在普通机器上用较小量化模型跑推理。这里给出 vLLM 的部署思路,它主要用于 GPU 推理,能够管理 KV Cache 和连续批处理,吞吐较好。
pip install vllm python -m vllm.entrypoints.openai.api_server \ --model /path/to/model \ --port 8000启动后,可以通过 OpenAI 兼容接口做一次请求:
curl http://localhost:8000/v1/completions \ -H "Content-Type: application/json" \ -d '{ "model": "/path/to/model", "prompt": "Transformer 是", "max_tokens": 64 }'这里的/path/to/model要替换成你本地模型目录或 Hugging Face 模型名称。实际使用时要根据 GPU 显存选择模型大小和量化方式。如果显存有限,可以先把模型转成 AWQ、GPTQ 等量化格式,或者使用 llama.cpp 在 CPU 上运行。
注意:本地部署不是把模型文件放到服务器上就结束了。生产环境必须考虑 license 合规、输入输出安全过滤、日志监控、版本回滚和压测。
4.4 大模型工程落地检查清单
真正给业务使用大模型时,不要只看“模型能不能生成”,要按清单逐项确认。
- 模型来源和 license 是否允许商用。
- 模型文件校验和备份是否完整。
- GPU 显存、磁盘空间、内存和推理吞吐是否满足需求。
- 是否需要量化,量化后效果是否能接受。
- 推理框架与 CUDA、PyTorch 版本是否匹配。
- 输入长度限制和最大输出长度怎么设置。
- 生成接口是否做了超时、重试和异常捕获。
- 是否记录请求日志、token 用量和耗时。
- 是否有敏感内容过滤和内容无害化策略。
- 是否有压测数据和回滚方案。
这份清单在学习环境可能不完全需要,但一旦进入生产,每一项都可能成为故障点。
5. 学习 Transformer 与大模型的推荐路线和常见坑
5.1 推荐学习路线
如果从零开始学,不建议一上来就追着几十个模型跑。下面这条路线更稳。
- 读原论文《Attention Is All You Need》,先看图,再逐段理解公式。
- 用 PyTorch 手写自注意力和多头注意力,也就是本文第三节的内容。
- 借助 Hugging Face 源码,看一下真实大模型中注意力块的实现。
- 用一个小模型做文本生成,观察输入输出和 KV Cache 的作用。
- 在小型开源模型上尝试 LoRA 微调,任务可以选一个简单分类或对话。
- 再逐步扩展到量化、分布式推理、模型部署等内容。
每一步都要有可运行结果。只收藏资料而不敲代码,很难真正建立对张量形状和训练流程的感觉。
5.2 常见概念混淆与避坑
很多人学了 Transformer 结构图,仍然会在实际项目中踩坑。
自注意力不是“查找记忆表”。它是在当前上下文条件下动态计算的一组权重,同样一个 token,在不同句子里的注意力分布完全不同。
位置编码不是 token embedding。token embedding 表达“这个词是什么”,位置编码表达“这个词在句子哪里”。两者相加后输入模型,不能混为一谈。
训练和推理并不完全一样。训练时 Decoder 通常使用真实标签作为输入,称为 teacher forcing;推理时只能一个 token 一个 token 地生成。KV Cache 主要优化推理阶段,训练阶段不会直接使用缓存去更新梯度。
大模型部署不等于微调。很多人以为部署一个模型后,效果不好就继续训练。实际上要先确认数据、prompt、解码参数和量化方式。尤其是温度参数和重复惩罚,对生成质量影响很大,这些都可以在不改模型的情况下调节。
5.3 从现象到根因的排查清单
| 目标或现象 | 排查方向 |
|---|---|
| 模型加载很慢 | 检查网络速度、本地缓存、磁盘读取速度 |
| 显存溢出 OOM | 降低 batch、缩短max_length、使用量化、开启梯度检查点 |
| 生成质量差 | 检查数据质量、prompt、解码参数、量化损失 |
| 推理吞吐低 | 确认是否启用 KV Cache、是否使用连续批处理 |
| 接口响应超时 | 查看排队时间、单次推理耗时、GPU 利用率 |
| 结果出现乱码 | 检查 tokenizer 与模型是否匹配,序列是否被截断 |
| 微调后效果反而变差 | 检查数据标签质量、学习率、是否忘记冻结原模型 |
排查问题时,先确认输入和路径,再确认版本和配置,最后才怀疑模型本身。这条顺序可以节省大量时间。
5.4 进一步走向真实大模型工程
Transformer 从论文走向大模型,技术上已经非常成熟。Ashish Vaswani 作为论文第一作者,让“注意力”成为这一轮 AI 发展的关键词。但更重要的是,这个架构是团队合作和工程规模共同推进的结果。
下一步可以朝几个方向深入:一是研究具体模型的实现,如 LLaMA、Mistral、Qwen 等开源模型的注意力细节;二是学习 RAG 和 Agent,把大模型接入业务系统;三是研究分布式训练和推理优化,理解张量并行、流水线并行与 KV Cache 的内部机制;四是在小模型上反复做微调和评估,积累真实经验。
对新手的建议很直接:先把最小注意力模块跑通,再去看真实模型的源码。结构图背得再熟,都不如动手验证一次张量形状变化来得可靠。理解 Transformer 之后,你会发现大模型领域的大多数新概念,都是在这套骨架上的扩展和优化。