☰
LLM 从零到一:Transformer 架构、训练与推理部署全解析
2026/10/28 23:50:58 网站建设 项目流程

1. 从零构建 LLM 认知框架:为什么直接啃论文会劝退

很多刚接触大模型的朋友都有类似的经历:打开一篇 Transformer 原论文,看到 Encoder-Decoder 架构图、多头注意力公式、位置编码的三角函数,翻了三页就合上了。这不是你的问题,而是学习路径的问题。LLM 这个领域有一个很反直觉的特点——它的核心思想其实非常简洁,但工程实现和训练细节极其庞杂。如果一上来就试图把每个公式推导清楚,大概率会在第二周放弃。

我自己是从 2022 年底开始系统学习 LLM 的,当时踩过的最大坑就是“先学完 Transformer 再看 GPT”。后来发现这条路效率极低,因为 Transformer 原论文是为机器翻译设计的,而现代 LLM 绝大多数是 Decoder-only 架构,两者在训练目标、推理方式、工程优化上差异巨大。更合理的路径是:先建立“LLM 到底在做什么”的直觉,再回头补架构细节,最后深入训练和对齐阶段。

这篇文章面向的读者是:有基本 Python 和深度学习概念(知道什么是梯度下降、什么是神经网络层),但还没系统接触过 LLM 的开发者。我会按照“直觉→架构→训练→对齐→推理优化”的顺序,把 LLM 的核心技术点串成一条线。每个环节我都会解释“为什么这么设计”,而不只是“是什么”。读完之后,你应该能看懂主流开源模型的配置文件,理解训练日志里的 loss 曲线在说什么,并且知道当你遇到显存不够、推理太慢、输出不符合预期时该往哪个方向排查。

2. 核心架构拆解:Transformer 到底在算什么

2.1 自注意力机制:让每个词看到所有词

Transformer 最核心的创新就是自注意力(Self-Attention)。用一句话概括:对于序列中的每个位置,计算它与其他所有位置的相关性,然后根据相关性加权聚合信息。

具体怎么算?假设输入序列有 n 个 token,每个 token 被映射成一个 d 维向量。我们通过三个线性变换得到 Query(Q)、Key(K)、Value(V)三个矩阵。注意力分数的计算方式是:

Attention(Q, K, V) = softmax(QK^T / sqrt(d_k)) V

这里的sqrt(d_k)是缩放因子,作用是防止点积结果过大导致 softmax 梯度消失。我试过去掉这个缩放,在 d_k=64 时训练 loss 会明显震荡,收敛速度慢很多。

为什么要用 Q、K、V 三组不同的变换?一个生活化的类比:你在图书馆找书,Q 是你脑子里的问题,K 是每本书的标签,V 是书的内容。你先用问题去匹配标签(QK^T),找到最相关的几本书,然后把它们的内容加权组合起来(乘以 V)。如果 Q 和 K 用同一组变换,就相当于用书的内容去匹配书的内容,表达能力会受限。

多头注意力(Multi-Head Attention)则是把上述过程并行做 h 次,每次用不同的线性变换。比如 d_model=512,h=8,那么每个头的维度是 64。这样做的好处是:不同的头可以关注不同的模式——有的头关注语法关系,有的头关注语义相似性,有的头关注位置邻近性。实测下来,8 个头比 1 个头在翻译任务上 BLEU 分数能高 2-3 个点。

2.2 位置编码:没有它,模型分不清“猫追狗”和“狗追猫”

自注意力本身是位置无关的——把输入序列打乱,输出结果不变。但语言是有顺序的,所以需要注入位置信息。原始 Transformer 用的是正弦位置编码:

PE(pos, 2i) = sin(pos / 10000^(2i/d_model)) PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))

这个设计很巧妙:不同频率的正弦波组合,可以让模型通过线性变换推断出相对位置。但后来大家发现,可学习的位置嵌入(Learned Position Embedding)在 LLM 中更常见,比如 GPT 系列用的就是可学习的位置嵌入。再后来,RoPE(旋转位置编码)成为主流,LLaMA、Qwen 等模型都在用。RoPE 的核心思想是把位置信息编码成旋转矩阵,直接作用在 Q 和 K 上,这样注意力分数天然就包含了相对位置信息。

注意:如果你在复现一个 LLM,位置编码的选择会直接影响长文本外推能力。正弦编码和可学习嵌入在训练长度之外的表现会急剧下降,而 RoPE 配合 NTK 插值可以外推到更长的序列。

2.3 前馈网络与残差连接:深度堆叠的关键

每个 Transformer 层除了注意力,还有一个前馈网络(FFN),通常是两个线性层加一个激活函数:

FFN(x) = W2 * activation(W1 * x + b1) + b2

中间层的维度通常是 d_model 的 4 倍。比如 d_model=4096,中间层就是 16384。这个设计让模型有足够的容量去拟合复杂的非线性关系。激活函数从 ReLU 到 GELU 再到 SwiGLU,效果越来越好但计算量也略有增加。LLaMA 用的就是 SwiGLU。

残差连接和层归一化是让深层网络能训练起来的关键。没有残差连接,超过 12 层的 Transformer 就很难收敛。层归一化的位置也有讲究:Post-LN(原始论文)在残差之后做归一化,训练不稳定;Pre-LN 在子层之前做归一化,训练更稳定,现在绝大多数 LLM 都用 Pre-LN 或 RMSNorm。

3. 训练全流程:从随机初始化到能说人话

3.1 预训练:海量文本上的下一词预测

LLM 的预训练目标非常简单:给定前 n 个 token,预测第 n+1 个 token。损失函数就是交叉熵。但就是这么一个简单的目标,在足够大的数据和模型上,涌现出了推理、翻译、代码生成等能力。

预训练的数据量通常是模型参数量的 10-20 倍。比如一个 7B 参数的模型,训练数据大约在 1-2 万亿 token。数据来源包括网页、书籍、代码、论文等。数据清洗和去重是极其关键的环节——重复数据会导致模型记忆而非泛化,低质量数据会拉低整体表现。

训练过程中有几个关键超参数:学习率通常用 warmup + cosine decay,峰值在 1e-4 到 3e-4 之间;batch size 尽可能大,用梯度累积来模拟;优化器用 AdamW,weight decay 设 0.1 左右。我自己的经验是,预训练阶段 loss 降到 2.0 以下时,模型开始能生成连贯的句子;降到 1.5 左右时,基本对话能力就出来了。

3.2 监督微调:让模型学会“回答问题”而不是“续写”

预训练完的模型只会续写,你问它“今天天气怎么样”,它可能会续写成“今天天气怎么样?明天呢?”——因为它见过的网页里经常有这种句式。监督微调(SFT)就是用人工标注的“指令-回答”对来教模型按照人类期望的方式回应。

SFT 的数据量不需要很大,通常几万到几十万条就够。关键是数据质量:回答要准确、有帮助、无害。数据格式一般是:

### Instruction: 解释什么是注意力机制 ### Response: 注意力机制是一种让模型...

训练时只对 Response 部分计算 loss,Instruction 部分 mask 掉。学习率要比预训练小一个数量级,通常 1e-5 到 5e-5,训练 2-3 个 epoch 就够,多了会过拟合。

3.3 RLHF:用人类偏好对齐模型行为

SFT 之后,模型能回答问题了,但回答的质量参差不齐。RLHF(基于人类反馈的强化学习)分三步:

第一步,训练奖励模型。给同一个 prompt 的两个回答,让人标注哪个更好。奖励模型学习预测这个偏好分数。

第二步,用 PPO 算法优化策略模型。策略模型生成回答,奖励模型打分,PPO 根据分数更新策略模型参数。同时加一个 KL 散度惩罚,防止策略模型偏离 SFT 模型太远。

第三步,迭代优化。重复上述过程,不断收集新的偏好数据。

RLHF 的工程实现非常复杂,PPO 训练不稳定是出了名的。后来出现了 DPO(直接偏好优化),跳过奖励模型,直接用偏好数据优化策略模型,简单很多,效果也不错。现在很多开源模型都用 DPO 或它的变体。

实操心得:如果你资源有限,SFT + DPO 的组合性价比最高。RLHF 的 PPO 训练需要至少 4 个模型同时加载(策略、参考、奖励、价值),显存开销是 SFT 的 3-4 倍。

4. 推理与部署:模型训好了,怎么让它跑得快

4.1 KV Cache:用空间换时间

自回归生成时,每生成一个新 token,都需要重新计算所有之前 token 的 Key 和 Value。如果不缓存,计算量会随序列长度平方增长。KV Cache 就是把之前算过的 K 和 V 存下来,新 token 只需要计算自己的 Q、K、V,然后和缓存的 K、V 做注意力。

KV Cache 的大小是:2 * batch_size * num_layers * num_heads * head_dim * seq_len * dtype_size。以一个 7B 模型为例,32 层、32 头、head_dim=128、fp16 精度,序列长度 4096,batch size 1,KV Cache 大约占用 2GB 显存。这就是为什么长对话会越来越吃显存。

4.2 量化:让大模型跑在小显存上

量化是把模型权重从 fp16 降到 int8 或 int4,减少显存占用和计算量。常见方案:

量化方案精度显存节省质量损失适用场景
FP1616位基准无训练和高质量推理
INT88位约50%很小推理部署
INT4 (GPTQ)4位约75%较小消费级显卡
INT4 (AWQ)4位约75%较小消费级显卡
GGUF Q4_K_M4位约75%中等CPU/边缘设备

我实测过 7B 模型在 RTX 3060 12GB 上的表现:FP16 需要 14GB 显存,跑不起来;INT8 需要 8GB,能跑但速度一般;INT4 需要 4GB,流畅运行。质量方面,INT4 在通用对话上几乎感觉不到差异,但在代码生成和数学推理上会有可感知的下降。

4.3 推理框架选型:vLLM vs TGI vs llama.cpp

vLLM 的核心是 PagedAttention,把 KV Cache 分页管理,显存利用率高,吞吐量大。适合服务端部署,支持连续批处理。

TGI(Text Generation Inference)是 HuggingFace 的方案,集成度高,和 Transformers 生态无缝衔接,适合快速上线。

llama.cpp 是纯 C++ 实现,支持 CPU 推理和多种量化格式,适合本地运行和边缘设备。GGUF 格式就是它推广开的。

选型建议:服务端高并发选 vLLM;快速原型选 TGI;本地/离线选 llama.cpp。

5. 常见问题排查与避坑指南

5.1 训练 loss 不下降或震荡

这是最常见的问题。排查顺序:

  1. 检查数据:有没有空样本、标签错位、tokenizer 不匹配。我遇到过一次 loss 完全不降,最后发现是数据里混入了二进制文件,tokenizer 把它们编码成了一堆无意义 token。

  2. 检查学习率:太大导致震荡,太小导致不收敛。用 learning rate finder 找一个合适的初始值。

  3. 检查梯度:打印梯度范数,如果持续为 0 说明梯度消失,如果爆炸说明需要梯度裁剪。

  4. 检查初始化:LLM 通常用正态分布初始化,std 在 0.02 左右。初始化太大会导致输出饱和。

5.2 显存不够用

问题解决方案
模型加载就 OOM用量化(INT8/INT4)或换更小模型
训练时 OOM减小 batch size,用梯度累积;用梯度检查点;用 ZeRO 优化器
推理时 OOM限制 max_seq_len;用 PagedAttention;减小 batch size
多卡训练 OOM用 DeepSpeed ZeRO-3 或 FSDP 分片

避坑技巧:梯度检查点(Gradient Checkpointing)用计算换显存,能省 50-70% 激活显存,代价是训练速度慢 20-30%。如果显存实在紧张,这是最有效的单卡优化手段。

5.3 模型输出重复、胡言乱语

原因可能有:温度设太低导致重复,温度设太高导致胡言乱语。通常 temperature=0.7、top_p=0.9 是比较平衡的设置。另外,重复惩罚(repetition penalty)设 1.1-1.2 能有效缓解重复问题。如果模型在 SFT 后出现灾难性遗忘,说明学习率太大或训练轮次太多。

5.4 长文本外推能力差

RoPE 模型在训练长度之外表现会下降。解决方案:NTK-aware 插值、YaRN、LongRoPE 等。这些方法的核心思想是调整 RoPE 的频率基数,让模型适应更长的位置。实测下来,YaRN 在 4 倍外推时效果最好,但需要少量微调。

6. 思维链与智能体:LLM 的进阶玩法

6.1 思维链提示:让模型“想清楚再回答”

思维链(Chain-of-Thought)的核心思想是让模型在给出最终答案之前,先输出推理步骤。对于数学题、逻辑题、多步推理任务,CoT 能显著提升准确率。

最简单的实现方式是在 prompt 里加一句“Let's think step by step”。更高级的做法是 few-shot CoT,给几个带推理步骤的示例。实测下来,在 GSM8K 数学题上,标准 prompt 准确率约 17%,加一句“Let's think step by step”能到 40% 以上,few-shot CoT 能到 60% 以上。

6.2 智能体:让 LLM 调用工具、自主决策

LLM 智能体的基本循环是:观察→思考→行动→观察。模型根据当前状态决定调用哪个工具(搜索、计算器、代码执行),获取结果后继续推理,直到任务完成。

构建可靠智能体的关键点:

  • 工具描述要清晰:模型需要知道每个工具能做什么、输入输出格式是什么。
  • 错误处理要健壮:工具调用失败时,模型应该能重试或换方案。
  • 记忆管理要合理:短期记忆放对话历史,长期记忆放向量数据库。
  • 安全边界要明确:限制模型能调用的工具范围,防止危险操作。

实操心得:智能体的容错控制是工程难点。我试过让模型自主决定是否重试,结果它经常陷入无限重试循环。后来改成“最多重试 3 次,之后必须换方案或报告失败”,稳定性大幅提升。

6.3 LLM as Judge:用模型评估模型

当你有多个模型或多个 prompt 方案时,人工评估成本太高。LLM as Judge 是用一个强模型(如 GPT-4)来给其他模型的输出打分。关键是评分标准要明确,最好给出评分细则和示例。实测下来,LLM Judge 和人工评估的一致性在 80% 左右,足够做快速迭代。

7. 学习路径与资源推荐

如果你是从零开始,我建议按这个顺序:

第一周:理解自注意力和 Transformer 架构。推荐 Jay Alammar 的 The Illustrated Transformer,图解非常清晰。同时动手写一个单层自注意力,用 PyTorch 实现,输入随机向量,观察输出。

第二周:理解 GPT 架构和预训练目标。找一个开源的小模型(如 GPT-2 124M),加载权重,手动实现生成循环,理解 KV Cache 的作用。

第三周:理解 SFT 和 RLHF。用 LoRA 微调一个小模型,体验数据格式和训练过程。然后尝试 DPO,对比效果。

第四周:推理优化和部署。用 llama.cpp 或 vLLM 部署一个量化模型,测试吞吐量和延迟。

资源方面,除了原论文,我强烈推荐 Andrej Karpathy 的 nanoGPT 项目,代码简洁,适合逐行阅读。HuggingFace 的 Transformers 文档和 TRL 库也是必备参考。中文资源里,李沐的论文精读系列质量很高。

最后分享一个我自己的习惯:每学一个概念,就写一段代码验证它。比如学位置编码,就画出不同位置的编码向量,观察相似度矩阵。学注意力,就可视化注意力权重,看模型在关注哪些 token。这种“手脑并用”的方式,比单纯看论文效率高得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询