第2章 大语言模型原理与工程化基石

“所有大模型幻觉的本质,不是工程缺陷,而是概率拟合的宿命;理解这一点,是 Agent 架构师区别于调包侠的第一道分水岭。”


2.0 章首导读

本章是全书的地基。无论你是一名在 AI Agent 开发平台深耕多年的资深技术专家,还是一名在企业前线扛着交付压力的 AI Agent 应用架构师,抑或是一位需要在董事会里回答"我们到底用谁家模型、是否自主可控"的 CTO,你都绕不开一个根本命题:你必须真正理解大语言模型(LLM)的原理,才能在工程化层面做出正确决策。

很多人对 LLM 的理解停留在"调 API"的层次:传入一段 prompt,拿到一段回复,觉得这就是全部。这种理解深度,在 2023 年或许还够用,但在 2026 年的今天,当 Agent 系统开始承载政务审批、金融风控、医疗辅助决策等高风险任务时,停留在调 API 层次的工程师,注定会被淘汰。因为:

  • 不懂得 Token 与计费模型,你无法压降 70% 的推理成本;
  • 不懂得 Self-Attention 的本质,你无法理解为什么长上下文会出现"中间遗忘"现象;
  • 不懂得 RLHF 与 DPO 的区别,你无法在模型选型时判断哪个基座更"听指令";
  • 不懂得幻觉的概率本质,你无法设计出靠谱的 RAG + 工具调用架构来兜底;
  • 不懂得 MoE 架构,你无法理解为什么 DeepSeek-V3 能用 1/5 的激活参数达到稠密模型的效果;
  • 不懂得流式输出原理,你无法在 Agent 多轮编排中控制首字时延。

本章将带你从统计语言模型的源头出发,一路走到 Transformer、MoE、预训练三阶段、上下文窗口经济学、幻觉的概率推导、后训练对齐技术,最后落到面试题、选型对比和工程实践案例上。这是一章需要反复读三遍的章节——第一遍建立直觉,第二遍建立体系,第三遍建立判断力。

本章适合以下读者:

读者画像 核心关注点 本章对应章节
开发平台资深技术专家 后训练 SFT/RLHF/DPO、ASR/TTS 与 LLM 结合、流式输出 2.1-2.5, 2.8, 2.10
AI Agent 应用架构师 模型选型、上下文管理、成本与时延权衡 2.4, 2.6, 2.7, 2.9, 2.11
CTO / 技术负责人 模型供应链、自主可控、成本全局视角 2.6, 2.9, 2.11, 2.12

2.1 从统计语言模型到 Transformer:一段 40 年的技术长征

2.1.1 语言模型的本质:一句话的概率

语言模型(Language Model, LM)的核心定义极其简单:给定一段文字,计算这段文字出现的概率。 用数学语言表达:

P(w1, w2, ..., wn) = P(w1) × P(w2|w1) × P(w3|w1,w2) × ... × P(wn|w1,...,wn-1)

即:一段话的概率,等于每个词在前面所有词出现的条件下出现的概率的连乘。这个看似朴素到可笑的公式,却是过去 40 年自然语言处理领域所有技术演进的北极星——从 n-gram 到神经网络语言模型,从 RNN 到 Transformer,从 BERT 到 GPT,所有进步都是在更好地近似这个条件概率 P(wt|w1,…,wt-1)。

为什么这个公式如此重要? 因为一旦你能准确估计"给定前文,下一个词是什么"的条件概率分布,你就拥有了生成语言的能力——你只需从分布中采样或取最大概率的词作为输出,然后把它拼到上下文里,再预测下一个词,如此反复,就是"自回归生成"。今天所有 GPT 类大模型的生成机制,本质上就是这个循环。

金句:大语言模型不是"理解语言"的机器,而是"预测下一个 Token"的机器;但当你把"预测下一个 Token"这件事做到极致,涌现出的能力看起来就像理解——这就是涌现的魔法,也是幻觉的根源。

2.1.2 n-gram 时代:数词频的笨办法

1990 年代,语言模型的主流方法是 n-gram。其核心思想是:不要考虑太长的上下文,只看前 n-1 个词。比如 bi-gram(二元模型)只看前 1 个词:

P(wt|w1,...,wt-1) ≈ P(wt|wt-1) = count(wt-1, wt) / count(wt-1)

即在训练语料中数一数,"wt-1"后面跟"wt"出现了多少次,除以"wt-1"出现的总次数,就得到转移概率。这方法简单到可以用 Excel 实现,但问题也很明显:

  1. 数据稀疏:如果"wt-1, wt"这个组合在训练语料中没出现过,概率就是 0,整个句子的概率就坍缩为 0。解决方法是各种平滑技术(Good-Turing、Kneser-Ney 等),但终究是补丁。
  2. 上下文太短:bi-gram 只看 1 个词的上下文,tri-gram 看 2 个词,4-gram 看 3 个词……上下文越长,组合空间指数增长,语料再大也覆盖不了。
  3. 没有语义理解:n-gram 完全基于词频统计,它不知道"猫吃鱼"和"鱼吃猫"在语义上的区别——它只知道哪个在语料中出现更多。

2003 年,Bengio 等人提出了第一个神经网络语言模型(NNLM),用三层前馈神经网络学习词的分布式表示(Word Embedding),解决了 n-gram 的维度灾难问题。这是语言模型从"数词频"到"学语义"的关键转折。但 NNLM 用的是前馈网络,依然无法处理长序列——序列一长,输入维度就爆炸。

2.1.3 RNN/LSTM 时代:记住前文的尝试

1997 年,Hochreiter 和 Schmidhuber 提出了长短期记忆网络(LSTM),解决了普通 RNN 的梯度消失问题。LSTM 通过门控机制(输入门、遗忘门、输出门)来控制信息的保留与遗忘,能够处理较长的序列(几百个 Token)。

LSTM 语言模型的结构是:逐个 Token 输入,每一步更新一个隐状态 h_t,然后用 h_t 预测下一个 Token 的分布:

h_t = LSTM(x_t, h_{t-1})
P(w_{t+1} | w_1,...,w_t) = softmax(W × h_t + b)

LSTM 在 2015-2017 年间统治了 NLP 领域,机器翻译、文本分类、情感分析等任务都在用 LSTM 及其变体(Bi-LSTM、GRU 等)。但 LSTM 有一个致命缺陷:无法并行训练。因为 h_t 依赖 h_{t-1},必须按顺序逐个计算,无法像图像领域的 CNN 那样在 GPU 上大规模并行。这意味着训练一个大规模 LSTM 语言模型需要极长的时间。

此外,LSTM 的长程记忆能力仍然有限。虽然有门控机制,但在几百个 Token 之后,早期的信息仍然会被逐渐稀释。这就是所谓的"长程依赖"问题。

2.1.4 Transformer 的诞生:一次革命性的范式跃迁

2017 年 6 月,Google Brain 的 Ashish Vaswani 等人在 NeurIPS 上发表论文《Attention is All You Need》,提出了 Transformer 架构。这篇论文改变了 NLP 的一切——也改变了 AI 的一切。

Transformer 的核心创新只有一点:用 Self-Attention 替代 RNN 的循环结构,从而实现完全并行计算。 在 RNN 中,要计算第 100 个 Token 的隐状态,你需要先计算前 99 个 Token 的隐状态;而在 Transformer 中,所有 Token 的表示可以同时计算,因为每个 Token 都直接"看到"所有其他 Token(通过 Attention 机制)。

这听起来只是工程上的改进(并行 vs 串行),但其意义远超于此:

  1. 并行训练使得模型可以做得极大:GPT-3 有 1750 亿参数,BERT-base 只有 1.1 亿参数——如果没有并行训练,训练 1750 亿参数的 LSTM 在工程上几乎不可能(按当时的 GPU 算力,可能需要几十年)。
  2. 全局视野:RNN 只能通过隐状态间接"记住"前面的信息,而 Transformer 的每个 Token 可以直接attend到任意位置的 Token,信息通路是直接的,不存在"信息逐步稀释"的问题。
  3. 可扩展性:Transformer 的结构高度规整(堆叠相同的层、相同的 Attention Head),极其适合 GPU/TPU 的矩阵运算。这种规整性使得 Scaling Law 可以发挥作用——模型越大、数据越多、算力越多,效果就越好,且没有明显的饱和点。

金句:Transformer 之所以伟大,不是因为它"更聪明",而是因为它把语言建模从"串行记忆"变成了"并行全局视野"——这一个小小的改变,释放了 Scaling Law 的全部能量。

下面是一张从 n-gram 到 Transformer 的技术演进对比图:

╔══════════════════════════════════════════════════════════════════════════╗
║                  语言模型技术演进 40 年路线图                                ║
╠══════════════════╦═══════════╦═══════════════╦══════════════════════════╣
║ 技术代际          ║ 年代       ║ 核心思想       ║ 致命局限                  ║
╠══════════════════╬═══════════╬═══════════════╬══════════════════════════╣
║ n-gram           ║ 1990s     ║ 数词频统计     ║ 数据稀疏,无语义,上下文短  ║
║ NNLM(Bengio)    ║ 2003      ║ 词向量+神经网络 ║ 前馈网络,无法处理长序列    ║
║ RNN/LSTM         ║ 1997-2015 ║ 门控循环记忆   ║ 无法并行,长程依赖弱       ║
║ Seq2Seq+Attention║ 2014     ║ 编解码+注意力   ║ 编码器仍是RNN,瓶颈仍在    ║
║ Transformer      ║ 2017      ║ 纯Self-Attn    ║ (目前最优)算力消耗O(n²)   ║
║ MoE Transformer  ║ 2023+     ║ 稀疏专家路由   ║ 路由不稳定,训练复杂度高   ║
╚══════════════════╩═══════════╩═══════════════╩══════════════════════════╝

2.1.5 从 Encoder-Only 到 Decoder-Only:GPT 路线的胜利

Transformer 论文提出的是 Encoder-Decoder 架构(用于机器翻译),但后续的发展分化为三条路线:

  1. Encoder-Only(BERT 路线):只用 Transformer Encoder,适合"理解"类任务(分类、NER、相似度)。BERT 是典型代表,2018 年问世后横扫各种 NLP 基准。但 BERT 不是自回归的,不能直接生成文本。
  2. Encoder-Decoder(T5/BART 路线):保留完整 Encoder-Decoder 结构,用 text-to-text 框架统一所有 NLP 任务。T5 是典型代表,效果优秀但模型参数效率不如 Decoder-Only。
  3. Decoder-Only(GPT 路线):只用 Transformer Decoder,纯自回归生成。GPT 系列是典型代表。早期(2018-2020)被认为是"只会生成、理解不如 BERT"的路线,但随着模型规模增大,GPT-3 展现出了惊人的 few-shot 能力,证明了 Decoder-Only + 大规模 + 自回归预训练 是通向 AGI 的最优路径。

到 2024 年,Decoder-Only 已经成为绝对主流。GPT-4、Claude、Llama、Qwen、DeepSeek 等几乎所有主流大模型都采用 Decoder-Only 架构。BERT 路线的开创者 Google 也在 Gemini 系列中转向了 Decoder-Only(虽然官方说法是"原生多模态",但语言建模部分确实是 Decoder-Only)。

金句:BERT 教会了模型"理解",GPT 教会了模型"生成";但当模型足够大时,生成即理解——这就是为什么 Decoder-Only 最终赢了。不是因为 Decoder-Only 架构更优,而是因为自回归生成是语言建模最自然的任务形式,它天然支持 Scaling Law 的无限扩展。

2.1.6 Scaling Law:大模型世界的"物理定律"

2020 年,OpenAI 的 Jared Kaplan 等人发表了论文《Scaling Laws for Neural Language Models》,提出了大模型领域的"物理定律":

Loss(N, D, C) ≈ A / N^α + B / D^β + C / C^γ + L_inf

其中:
  N = 模型参数量
  D = 训练数据量(Tokens)
  C = 计算量(FLOPs)
  α, β, γ ≈ 0.5 左右(幂律指数)
  L_inf = 不可约损失(数据本身的熵)

这个公式的含义是:Loss 随参数量、数据量、计算量的增加,呈幂律下降,且没有明显的饱和点。 也就是说,只要持续投入更多参数、更多数据、更多算力,模型就会持续变好——这就是"大力出奇迹"的理论基础。

DeepMind 的 Hoffmann 等人在 2022 年进一步提出了 Chinchilla 定律,指出当时大多数模型"过度参数化、训练不足":在给定计算预算下,最优策略是让参数量和数据量同步增长,比例约为 1:20(即 1 个参数对应 20 个 Token)。这个发现直接导致了 Llama-2 等模型采用"小模型+大数据"的训练策略,大幅降低了训练和推理成本。

金句:Scaling Law 不是一条建议,而是一条物理定律——在它被打破之前(即出现明确的饱和点之前),你做任何模型决策,都要先问自己:我的选择是否顺应了 Scaling Law?


2.2 Token、Embedding 与位置编码:大模型的"三原色"

2.2.1 Token:大模型的"原子"

大模型不直接处理文字,它处理的是 Token。Token 是文本的最小切分单元,介于"字符"和"词"之间。英文中,一个 Token 通常对应 3-4 个字符(约 0.75 个单词);中文中,一个汉字通常对应 1-2 个 Token(取决于分词器)。

Token 化(Tokenization)的过程可以用下图表示:

╔══════════════════════════════════════════════════════════════╗
║                    Token 化处理流程                            ║
╠══════════════════════════════════════════════════════════════╣
║                                                                ║
║  原始文本: "大语言模型很强大"                                   ║
║      │                                                         ║
║      ▼                                                         ║
║  分词器(BPE/SentencePiece/Tiktoken)                           ║
║      │                                                         ║
║      ├──→ "大" → Token ID: 162                                 ║
║      ├──→ "语言" → Token ID: 5237                              ║
║      ├──→ "模型" → Token ID: 8291                              ║
║      ├──→ "很" → Token ID: 99                                  ║
║      └──→ "强大" → Token ID: 11203                             ║
║      │                                                         ║
║      ▼                                                         ║
║  Token ID 序列: [162, 5237, 8291, 99, 11203]                  ║
║      │                                                         ║
║      ▼                                                         ║
║  Embedding 查表: 每个 Token ID → 一个 d维向量                  ║
║  [162]    → [0.12, -0.34, 0.56, ..., 0.78]  (d=4096)          ║
║  [5237]   → [0.45, 0.23, -0.67, ..., 0.11]  (d=4096)          ║
║  ...                                                           ║
║                                                                ║
╚══════════════════════════════════════════════════════════════╝

目前主流的分词算法有三种:

分词算法 提出者 代表模型 特点
BPE (Byte Pair Encoding) Sennrich 2016 GPT 系列、Llama 基于字节频率合并,通用性强,子词切分
WordPiece Schuster 2012 BERT 类似 BPE 但基于似然,概率最大化
SentencePiece Google 2018 T5、Qwen、ChatGLM 直接在 Unicode 字符层面操作,语言无关
Tiktoken OpenAI 2023 GPT-4/Claude BPE 的高效实现,100MB/s 级别

Token 与成本的关系极其重要。 在 API 计费模型中,你输入的每一个 Token 都要付费,输出的每一个 Token 都要付费(且输出通常更贵)。举个例子:假设你用 GPT-4 调用一次,输入 1000 Token 的 prompt,输出 500 Token 的回复:

成本 = 输入Token × 输入单价 + 输出Token × 输出单价
     = 1000 × $0.03/1K + 500 × $0.06/1K
     = $0.03 + $0.03
     = $0.06

看起来不多,但如果你的 Agent 每天调用 10 万次,一个月就是:

月成本 = $0.06 × 100000 × 30 = $180,000/月

这就是为什么 Token 经济学 是 Agent 架构师必须精通的领域(详见 2.6 节)。

2.2.2 Embedding:从离散到连续的魔法

Token ID 是一个离散整数(比如 5237),它本身没有任何语义信息——5237 和 5238 之间在语义上没有任何关系。Embedding 的作用是将这些离散整数映射到连续的高维向量空间,使得语义相近的 Token 在向量空间中也相近。

Embedding 矩阵 E 的形状是 [V, d],其中 V 是词表大小(通常 3-10 万),d 是隐藏维度(通常 2048-12288)。每个 Token ID 查表得到一个 d 维向量:

embedding(token_id) = E[token_id]  # 形状 [d]

这个 Embedding 矩阵是模型在预训练中学到的。训练过程中,模型会发现"猫"和"狗"的 Embedding 向量很近(因为它们在相似上下文中出现),"猫"和"汽车"的 Embedding 向量很远。

一个有趣的事实:Embedding 向量的维度 d 随模型规模线性增长:

GPT-2 Small:   d = 768    (124M 参数)
GPT-2 Medium:  d = 1024   (355M 参数)
GPT-2 XL:      d = 1600   (1.5B 参数)
GPT-3:         d = 12288  (175B 参数)
Llama-2 70B:   d = 8192   (70B 参数)
Llama-3 405B:  d = 16384  (405B 参数)

为什么维度越大越好? 因为高维空间能容纳更丰富的语义信息。在 768 维空间中,"猫"和"老虎"可能被迫挤在一起;但在 16384 维空间中,它们可以在某些维度上分离(比如"家养/野生"维度、"体型"维度等)。当然,维度越大,计算和存储成本也越高,这是工程权衡。

2.2.3 位置编码:让模型知道"顺序"

Transformer 的 Self-Attention 机制本身是置换不变的(permutation-invariant)——也就是说,打乱输入 Token 的顺序,Attention 的输出(集合意义上)不变。这显然不行:"猫吃鱼"和"鱼吃猫"的 Token 集合相同,但语义完全相反。

为了让模型感知 Token 的位置信息,需要引入位置编码(Positional Encoding, PE)。位置编码的主流方案有:

  1. 绝对正弦位置编码(原版 Transformer):用不同频率的正弦/余弦函数生成位置向量,加到 Token Embedding 上。

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

    优点:无需学习,可以外推到训练时未见过的更长序列。缺点:绝对位置信息对长序列效果有限。

  2. 可学习位置编码(BERT/GPT-2 使用):为每个位置学习一个位置 Embedding,直接加到 Token Embedding 上。优点:灵活。缺点:不能外推——训练时最大 1024,推理时就不能超过 1024。

  3. RoPE 旋转位置编码(Llama/GPT-NeoX 使用):通过对 Query 和 Key 向量施加旋转矩阵来注入位置信息。核心思想是:两个 Token 的相对位置关系通过它们 Query-Key 内积的变化来编码。RoPE 的最大优点是天然支持相对位置编码,且通过 NTK-aware 插值可以扩展到更长的上下文。

╔══════════════════════════════════════════════════════════════════╗
║                    三种位置编码方案对比                              ║
╠════════════════╦═══════════╦════════════╦═══════════╦════════════╣
║ 方案            ║ 是否可学习 ║ 相对/绝对    ║ 外推能力   ║ 代表模型    ║
╠════════════════╬═══════════╬════════════╬═══════════╬════════════╣
║ 正弦编码        ║ 否        ║ 绝对        ║ 较弱       ║ 原版Transformer║
║ 可学习编码      ║ 是        ║ 绝对        ║ 无         ║ BERT, GPT-2  ║
║ ALiBi          ║ 否        ║ 相对(线性偏置)║ 强        ║ BLOOM, MPT   ║
║ RoPE           ║ 否        ║ 相对(旋转)   ║ 强(需插值) ║ Llama, Qwen  ║
╚════════════════╩═══════════╩════════════╩═══════════╩════════════╝

金句:位置编码是 Transformer 的"空间锚点"——没有它,Self-Attention 就像一个失忆的审稿人,能看到所有稿件却分不清哪段在先哪段在后。RoPE 的旋转之美在于:它不是给每个位置贴一个标签,而是让每个向量在空间中旋转一个角度——相对位置就编码在两个向量的夹角差里。


2.3 Self-Attention 与 Multi-Head Attention 的本质

2.3.1 Self-Attention:会议室里的"全局偷听"

Self-Attention 是 Transformer 的心脏。理解它,就理解了 Transformer 的一半。

类比:想象一个会议室里有 n 个人,每个人代表一个 Token。在 Self-Attention 机制下,每个人同时"偷听"所有人(包括自己)的发言,然后根据相关性加权整合信息,更新自己的"认知"。 相关性高的人说的话权重高,相关性低的人说的话权重低,最终每个人都得到了一个"融合了全局信息"的新表示。

用数学语言表达,Self-Attention 的计算过程如下:

给定输入序列 X ∈ R^{n×d}(n个Token,每个d维)

1. 线性变换生成 Q, K, V:
   Q = X × W_Q    # Query  (查询)  形状 [n, d_k]
   K = X × W_K    # Key    (键)    形状 [n, d_k]
   V = X × W_V    # Value  (值)    形状 [n, d_v]

2. 计算注意力分数:
   S = Q × K^T / sqrt(d_k)   # 形状 [n, n] — 每对Token的相关性

3. 归一化:
   A = softmax(S, dim=-1)    # 形状 [n, n] — 每行加和为1

4. 加权聚合:
   Output = A × V             # 形状 [n, d_v] — 融合后的新表示

这里的 Query/Key/Value 命名来自信息检索领域:

  • Query(查询):当前 Token 想要找什么信息。
  • Key(键):每个 Token 能提供什么信息(用于匹配)。
  • Value(值):每个 Token 实际提供的信息内容。

通俗比喻:你在图书馆找书。你的需求是 Query,每本书的标签是 Key,书的内容是 Value。你用 Query 去匹配所有书的 Key,匹配度高的书权重高,然后把所有书的内容按权重加权求和,得到你需要的"综合知识"。

在 Self-Attention 中,Query、Key、Value 都来自同一个输入序列 X(这就是"Self"的含义——自己 attend 自己)。在 Cross-Attention(交叉注意力,用于 Encoder-Decoder 架构)中,Query 来自 Decoder,Key 和 Value 来自 Encoder。

╔══════════════════════════════════════════════════════════════════════╗
║                    Self-Attention 计算流程图                            ║
╠══════════════════════════════════════════════════════════════════════╣
║                                                                        ║
║   输入 X [n×d]                                                          ║
║     │                                                                  ║
║     ├──→ ×W_Q ──→ Q [n×d_k]                                            ║
║     ├──→ ×W_K ──→ K [n×d_k]                                            ║
║     └──→ ×W_V ──→ V [n×d_v]                                            ║
║                                                                        ║
║              Q × K^T                                                    ║
║           ┌────────────────┐                                           ║
║           │  S [n×n]       │ ← 注意力分数矩阵                            ║
║           │  S_ij = Q_i·K_j│   (Token i 对 Token j 的关注程度)          ║
║           └───────┬────────┘                                           ║
║                   │ ÷sqrt(d_k)                                         ║
║                   ▼                                                    ║
║           ┌────────────────┐                                           ║
║           │  softmax(dim=-1)│ ← 归一化,每行加和为1                       ║
║           └───────┬────────┘                                           ║
║                   │                                                    ║
║                   ▼                                                    ║
║           ┌────────────────┐                                           ║
║           │  A [n×n]       │ ← 注意力权重矩阵                            ║
║           └───────┬────────┘                                           ║
║                   │                                                    ║
║                   ▼  × V [n×d_v]                                       ║
║                                                                        ║
║           ┌────────────────┐                                           ║
║           │ Output [n×d_v] │ ← 每个Token融合全局信息后的新表示           ║
║           └────────────────┘                                           ║
║                                                                        ║
╚══════════════════════════════════════════════════════════════════════╝

为什么除以 sqrt(d_k)? 这是为了控制注意力分数的方差。当 d_k 较大时,Q·K 的内积值会很大,导致 softmax 梯度趋近于 0(进入饱和区),训练不稳定。除以 sqrt(d_k) 将方差控制回 1,使梯度保持健康。

2.3.2 Multi-Head Attention:并行多视角审稿

Single-Head Self-Attention 只有一组 Q/K/V 变换,这意味着每个 Token 只能从"一个视角"来attend其他 Token。但语言的关系是多维的——"猫吃鱼"中,"猫"和"吃"有主谓关系,"吃"和"鱼"有动宾关系,"猫"和"鱼"有语义关系。一个 Head 可能只能捕捉一种关系。

Multi-Head Attention(MHA)的解决方案:同时运行多组 Self-Attention,每组用不同的参数,最后拼接结果。 类比:一间会议室里有多位审稿人,每人从不同角度(语法、语义、逻辑、风格)审稿,最后汇总各自的审稿意见。

数学表达:

MultiHead(X) = Concat(head_1, head_2, ..., head_h) × W_O

其中每个 head:
head_i = SelfAttention(Q = X×W_Q^i, K = X×W_K^i, V = X×W_V^i)

参数:
  W_Q^i ∈ R^{d×d_k}, W_K^i ∈ R^{d×d_k}, W_V^i ∈ R^{d×d_v}, W_O ∈ R^{h×d_v × d}
  通常 d_k = d_v = d / h

以 GPT-3(175B)为例,有 96 个 Attention Head,d = 12288,每个 Head 的维度 d_k = 12288/96 = 128。96 个 Head 各自计算 Self-Attention,然后拼接成 [n, 96×128] = [n, 12288] 的矩阵,再经过 W_O 线性变换回到 [n, 12288]。

╔══════════════════════════════════════════════════════════════════════╗
║               Multi-Head Attention 结构示意图                           ║
╠══════════════════════════════════════════════════════════════════════╣
║                                                                        ║
║                        输入 X [n×d]                                     ║
║                           │                                            ║
║           ┌───────────────┼───────────────────┐                        ║
║           │               │                   │                        ║
║      ┌────┴────┐    ┌────┴────┐         ┌────┴────┐                   ║
║      │ Head 1  │    │ Head 2  │  ···   │ Head h  │                   ║
║      │ Q,K,V  │    │ Q,K,V  │         │ Q,K,V  │                   ║
║      │ Attn   │    │ Attn   │         │ Attn   │                   ║
║      │[n,d_v] │    │[n,d_v] │         │[n,d_v] │                   ║
║      └────┬────┘    └────┬────┘         └────┬────┘                   ║
║           │              │                   │                        ║
║           └──────────────┼───────────────────┘                        ║
║                          │ Concat                                      ║
║                          ▼                                             ║
║                   [n, h×d_v]                                           ║
║                          │                                             ║
║                          × W_O [h×d_v, d]                              ║
║                          │                                             ║
║                          ▼                                             ║
║                   Output [n×d]                                         ║
║                                                                        ║
║   类比: h 个审稿人从不同视角并行审稿,最后汇总意见                       ║
║   Head 1: 语法关系    Head 2: 语义相似    Head 3: 指代消解 ...          ║
║                                                                        ║
╚══════════════════════════════════════════════════════════════════════╝

一个关键洞察:Multi-Head Attention 的总计算量与 Single-Head(相同 d)相同,但表达能力更强。 因为 h 个 Head 各有 d_k = d/h 维,总维度 h × d_k = d,所以参数量和计算量不变,但多个 Head 可以学习不同的注意力模式,从多个视角建模序列内关系。

金句:Multi-Head Attention 的本质不是"更多注意力",而是"多视角注意力"——就像一个团队里需要法务、财务、技术三个视角的审稿人,单靠一个全能选手反而会漏掉细节。

2.3.3 Causal Mask:自回归的"防火墙"

在 Decoder-Only 模型(GPT 系列)中,Self-Attention 有一个关键约束:每个 Token 只能 attend 到它之前的 Token,不能 attend 到未来的 Token。 这是因为 Decoder 是自回归的——生成时从左到右逐个预测,如果训练时允许看到未来 Token,就是"作弊"。

实现方式是在注意力分数矩阵 S 上施加一个下三角掩码(Causal Mask):

Mask 矩阵(下三角为0,上三角为-∞):
  ┌                                    ┐
  │  0    -∞   -∞   -∞  ...  -∞   -∞  │
  │  0     0   -∞   -∞  ...  -∞   -∞  │
  │  0     0    0   -∞  ...  -∞   -∞  │
  │  ...                               │
  │  0     0    0    0  ...   0   -∞  │
  │  0     0    0    0  ...   0    0  │
  └                                    ┘

S + Mask 后再 softmax:
  -∞ 的位置 → softmax 后权重为 0
  → 未来 Token 的注意力权重为 0 → "看不到未来"

这个 Causal Mask 是 Decoder-Only 模型和 Encoder-Only 模型(如 BERT)的核心区别之一。BERT 用的是双向 Attention(所有 Token 互相可见),适合理解任务;GPT 用的是 Causal Attention(只看过去),适合生成任务。

2.3.4 Attention 的计算复杂度:O(n²) 的诅咒

Self-Attention 的计算复杂度是 O(n² × d),其中 n 是序列长度,d 是隐藏维度。这是因为注意力分数矩阵 S 的形状是 [n, n],需要计算 n² 个点积。

这个 O(n²) 复杂度是大模型长上下文的"阿喀琉斯之踵":

序列长度 n    注意力矩阵大小(n²)    显存占用(d=4096, fp16)
1,000        1M                    ~8 MB
4,000        16M                   ~128 MB
16,000       256M                  ~2 GB
65,000       4.2B                  ~33 GB
128,000      16.4B                 ~131 GB
1,000,000    1 Trillion            ~8 TB !!!

当上下文从 4K 扩展到 128K,注意力矩阵的显存从 128MB 暴涨到 131GB——这就是为什么长上下文模型的训练和推理都极其昂贵,也是为什么各种"高效 Attention"方案层出不穷:

方案 复杂度 核心思想 代表实现
标准 Attention O(n²) 全局精确注意力 原版 Transformer
Sliding Window O(n×w) 局部窗口注意力 Mistral (w=4096)
FlashAttention O(n²) IO 优化(不减少FLOPs) FlashAttention-2/3
Linear Attention O(n) 核函数近似 Performer, Linformer
Sparse Attention O(n×√n) 稀疏注意力模式 Longformer, BigBird
Ring Attention O(n²) 多卡环形分块 Llama-3 1M 上下文

金句:Attention 的 O(n²) 复杂度,是大模型世界的"光速限制"——在突破它之前(或绕过它),长上下文永远是一个奢侈品,而非标配。FlashAttention 没有降低 FLOPs,它降低的是 HBM 读写次数——有时候,工程的突破不在于算得更快,而在于少去读写。


2.4 MoE 架构:稀疏激活的工程奇迹

2.4.1 为什么需要 MoE

Scaling Law 告诉我们:模型越大,效果越好。但"大"带来了一个致命问题:推理成本随参数量线性增长。 一个 700B 的稠密模型,每次前向传播都要激活全部 700B 参数,推理成本极高。

MoE(Mixture of Experts,混合专家)架构的核心思想是:用一个大模型的总参数量来获得更好的表达能力,但每次推理只激活其中一部分参数(稀疏激活),从而在不增加推理成本的情况下提升模型质量。

打个比方:稠密模型像一个"全科医生",什么病都自己看;MoE 模型像一家"综合医院",有眼科、内科、外科、骨科等多个科室(专家),每次来了病人,先由"分诊台"(Router/Gating)判断该去哪个科室,然后只有对应科室的医生出动,其他医生休息。

╔══════════════════════════════════════════════════════════════════════╗
║                       MoE 架构原理图                                    ║
╠══════════════════════════════════════════════════════════════════════╣
║                                                                        ║
║                    输入 Token Embedding                                 ║
║                              │                                         ║
║                              ▼                                         ║
║                    ┌─────────────────┐                                 ║
║                    │  Router (门控网络) │  ← 一个小的线性层              ║
║                    │  W_gate [d, E]   │     E = 专家数量                ║
║                    └────────┬────────┘                                 ║
║                             │                                         ║
║                    gate_scores = softmax(X × W_gate)                   ║
║                    [score_1, score_2, ..., score_E]                    ║
║                             │                                         ║
║                    选出 Top-K 个得分最高的专家(通常 K=2)               ║
║                             │                                         ║
║              ┌──────────────┼──────────────┐                           ║
║              │              │              │                           ║
║         ┌────┴────┐   ┌────┴────┐   ┌────┴────┐  ...  ┌────────┐      ║
║         │Expert 1 │   │Expert 2 │   │Expert 3 │      │Expert E│      ║
║         │(FFN层)  │   │(FFN层)  │   │(FFN层)  │      │(FFN层) │      ║
║         │ 激活 ✓  │   │ 休眠 ✗  │   │ 激活 ✓  │      │ 休眠 ✗ │      ║
║         └────┬────┘   └────────┘   └────┬────┘      └────────┘      ║
║              │                        │                                ║
║              └───────────┬────────────┘                                ║
║                          │                                             ║
║                    加权融合输出                                         ║
║                    out = Σ(gate_i × expert_i(x))                       ║
║                          │                                             ║
║                          ▼                                             ║
║                    MoE 层输出                                          ║
║                                                                        ║
║   关键:总参数量 = E × 单专家参数,但每次只激活 K 个专家               ║
║   → 表达力≈大模型,推理成本≈小模型                                     ║
║                                                                        ║
╚══════════════════════════════════════════════════════════════════════╝

2.4.2 MoE 的关键参数与代表模型

MoE 架构有几个关键参数:

  • 总专家数 E:整个模型有多少个专家。DeepSeek-V3 有 256 个,Qwen2-MoE 有 60 个。
  • 激活专家数 K:每次推理激活几个专家。通常 K=2 或 K=8。
  • 稀疏比例 E/K:总专家/激活专家。比例越高,理论上的"性价比"越高。
  • 共享专家(Shared Expert):有些 MoE 设计(如 DeepSeek-V3)保留少量"共享专家",每次都激活,用于处理通用知识,减少路由噪声。
模型 总参数 激活参数 专家数(E) 激活数(K) 稀疏比
Mixtral 8×7B 46.7B 12.9B 8 2
DeepSeek-V2 236B 21B 160 6 26.7×
DeepSeek-V3 671B 37B 256 8 32×
Qwen2-57B-A14B 57B 14B 64 8
GPT-4(推测) ~1.8T ~220B ~16 ~2 ~8×

金句:DeepSeek-V3 用 671B 总参数、37B 激活参数,达到了接近 GPT-4 的效果——这就是 MoE 的魔法:用"人口基数"换"上场人数",用大水库的容量换小水管的流量。

2.4.3 MoE 的工程挑战

MoE 在理论上很美,但工程实现有一堆难题:

  1. 负载均衡问题:如果 Router 总是把 Token 送到少数几个专家,其他专家就"饿死"了,训练效率低下。解决方案是加辅助损失(auxiliary loss)来惩罚不均匀的分配,或用 Expert Choice 路由让专家反向选择 Token。
  2. 通信开销:在多卡训练中,不同专家分布在不同 GPU 上,Token 需要跨卡路由(All-to-All 通信),通信开销很大。
  3. 推理批效应:MoE 推理时,每个 Token 只激活 K 个专家,如果 batch size 小,每个专家的计算量不饱和,GPU 利用率低。需要通过 continuous batching 等技术来提高吞吐。
  4. 路由坍缩:训练过程中,Router 可能陷入局部最优,总是选择同样的几个专家。这是 MoE 训练不稳定的常见原因。

金句:MoE 不是免费的午餐——你用稀疏激活换来了低成本推理,但也换来了路由不稳定、通信开销和训练复杂度的三重诅咒。工程能力不够的团队,训 MoE 比训稠密模型难十倍。


2.5 预训练-指令微调-RLHF 三阶段:从"读万卷书"到"听指挥"

2.5.1 三阶段总览

大模型的生命周期分为三个阶段,就像一个人的成长:

╔══════════════════════════════════════════════════════════════════════╗
║              大模型训练三阶段:从"读万卷书"到"听指挥"                    ║
╠══════════════════════════════════════════════════════════════════════╣
║                                                                        ║
║  阶段1: 预训练 (Pre-training)                                          ║
║  ─────────────────────────────                                         ║
║  类比: 一个人从小到大读了万亿字的书籍                                    ║
║  目标: 学习语言规律和世界知识                                           ║
║  数据: 海量无标注文本(网页、书籍、论文、代码)                           ║
║  规模: 数万亿 Tokens                                                    ║
║  成本: 数百万到数千万美元(GPU 集群运行数月)                             ║
║  产出: 基座模型(Base Model),会"续写"但不会"对话"                      ║
║                                                                        ║
║         ──────→ Base Model 可以做"续写"但不懂"听指令" ──────→          ║
║                                                                        ║
║  阶段2: 指令微调 (SFT - Supervised Fine-Tuning)                       ║
║  ─────────────────────────────                                         ║
║  类比: 给这个人上课,教他"问什么答什么"                                   ║
║  目标: 学会遵循指令,以问答形式输出                                      ║
║  数据: 数万到数十万条(指令, 回答)对                                     ║
║  规模: 数万到数十万条样本                                               ║
║  成本: 数百到数千美元(单卡到几十卡,几小时到几天)                       ║
║  产出: 指令模型(Instruct Model),会对话但可能不够安全/好用             ║
║                                                                        ║
║         ──────→ Instruct Model 会对话但风格/安全性未优化 ──────→       ║
║                                                                        ║
║  阶段3: 人类偏好对齐 (RLHF / DPO)                                      ║
║  ─────────────────────────────                                         ║
║  类比: 让这个人实习,有师傅打分反馈,逐步改进行为                         ║
║  目标: 使输出更安全、更有用、更符合人类偏好                              ║
║  数据: 人类标注的偏好对(A好于B)                                        ║
║  规模: 数万到数十万条偏好数据                                           ║
║  成本: 数千到数万美元(训练+标注)                                       ║
║  产出: 对齐模型(Aligned Model),可以安全上线                           ║
║                                                                        ║
║         ──────→ Aligned Model = 可上线的 ChatGPT/Claude/Qwen          ║
║                                                                        ║
╚══════════════════════════════════════════════════════════════════════╝

2.5.2 阶段一:预训练——读万卷书

预训练是大模型成本最高、耗时最长的阶段。以 GPT-3 为例:

  • 数据: 3000 亿 Token(WebText2、Common Crawl、书籍、Wikipedia)
  • 参数: 1750 亿
  • 算力: 约 3.14 × 10²³ FLOPs
  • GPU: V100 × 1024 张,训练约 34 天
  • 成本: 估算约 1200 万美元(按 2020 年云 GPU 价格)

预训练的目标函数极其简单:下一个 Token 预测(Next Token Prediction, NTP):

L = -Σ log P(w_t | w_1, ..., w_{t-1})

即:给定前 t-1 个 Token,预测第 t 个 Token 的对数似然的负值。在全部训练语料上求和(或分 batch 求平均),就是预训练的损失函数。

这个损失函数的精妙之处在于:它不需要任何人工标注——互联网上现成的文本就是训练数据,每个 Token 的"正确答案"就是它在语料中的下一个 Token。这就是为什么预训练可以用"万亿级"数据——因为数据是免费且无限的。

预训练出来的 Base Model 有一个特点:它会"续写"但不会"对话"。比如你给它输入"中国的首都是",它会续写"北京,人口约 2100 万,位于华北平原……“——它在续写一篇关于中国的文章,而不是在回答你的问题。如果你给它输入"请帮我写一首关于春天的诗”,它可能会续写"请帮我写一首关于夏天的诗 请帮我写一首关于秋天的诗……"——因为它没学过"问答"的格式。

金句:Base Model 是一个读过万亿字书籍的"书呆子"——知识渊博但不会对话。SFT 是教他学会"一问一答"的礼仪,RLHF 是教他学会"说人爱听的话"。三个阶段,缺一不可。

2.5.3 阶段二:指令微调(SFT)——学会听指挥

SFT(Supervised Fine-Tuning)的目标是让 Base Model 学会以"问答"形式输出。方法很简单:收集一批高质量的(指令, 回答)对,用监督学习的方式继续训练模型。

SFT 的数据格式通常是 ChatML 格式:

<|im_start|>system
你是一个有帮助的AI助手。
<|im_end|>
<|im_start|>user
请解释什么是量子计算。
<|im_end|>
<|im_start|>assistant
量子计算是一种利用量子力学原理...
<|im_end|>

SFT 的损失函数仍然是 Next Token Prediction,但只在 assistant 部分计算 loss(user 和 system 部分不参与 loss 计算,因为模型不需要学习"生成用户的问题"):

L_SFT = -Σ_{t ∈ assistant_tokens} log P(w_t | w_1, ..., w_{t-1})

SFT 的关键不在算法,而在数据质量。OpenAI 在 InstructGPT 论文中发现:少量高质量的 SFT 数据(几千条)效果优于大量低质量数据(几万条)。这就是"数据质量 > 数据数量"的原则。

SFT 数据的来源通常包括:

  1. 人工编写:雇佣专业人员编写高质量的指令-回答对。成本高但质量最高。
  2. 开源数据集:如 OpenOrca、Alpaca、ShareGPT 等。免费但质量参差不齐。
  3. 合成数据:用更强的模型(如 GPT-4)生成问答对,用于训练小模型。如 Alpaca 就是用 GPT-4 生成的 5.2 万条数据微调 Llama-7B。
  4. 领域数据:针对特定领域(医疗、法律、金融)收集或构造的问答对。

2.5.4 阶段三:人类偏好对齐(RLHF)——说人爱听的话

SFT 之后的模型已经能对话了,但它的回答可能:

  • 不够安全:用户问"怎么制造炸弹",它可能直接回答。
  • 不够有用:用户问"帮我写代码",它可能写了一段不能运行的代码。
  • 风格不佳:回答冗长、跑题、或者语气不当。

RLHF(Reinforcement Learning from Human Feedback,人类反馈强化学习)的目标是:通过人类偏好反馈,让模型的输出更"好"——更安全、更有用、更符合人类偏好。

RLHF 的经典流程(InstructGPT/ChatGPT 方案)分为三步:

╔══════════════════════════════════════════════════════════════════════╗
║                    RLHF 三阶段流程图                                    ║
╠══════════════════════════════════════════════════════════════════════╣
║                                                                        ║
║  Step 1: 训练奖励模型 (Reward Model, RM)                               ║
║  ───────────────────────────────────                                   ║
║                                                                        ║
║  SFT模型 ──→ 对同一个prompt生成多个回答(K个)                          ║
║                    │                                                  ║
║                    ▼                                                  ║
║  人类标注员对K个回答排序: A > B > C > D                                ║
║                    │                                                  ║
║                    ▼                                                  ║
║  训练奖励模型: 给定(prompt, response),输出一个标量奖励分              ║
║  损失: L_RM = -log[exp(r(x,y_w)) / (exp(r(x,y_w)) + exp(r(x,y_l)))]  ║
║  (y_w = 更好的回答, y_l = 更差的回答)                                 ║
║                                                                        ║
║  ┌────────────────────────────────────────────────────┐                ║
║  │  Reward Model: 输入(prompt, response) → 输出分数   │                ║
║  │  通常用SFT模型初始化,把最后一步的LM head换成标量输出│                ║
║  └────────────────────────────────────────────────────┘                ║
║                                                                        ║
║  Step 2: 用 PPO 强化学习优化策略模型                                   ║
║  ───────────────────────────────────                                   ║
║                                                                        ║
║  ┌──────────────┐     ┌──────────────┐    ┌──────────────┐            ║
║  │  Policy Model│────→│   生成回答    │───→│ Reward Model │            ║
║  │  (待优化的LLM)│     │              │    │   打分        │            ║
║  └──────────────┘     └──────────────┘    └──────┬───────┘            ║
║       ↑                                          │                    ║
║       │           ┌──────────────┐               │                    ║
║       │←──更新策略 │  PPO 算法    │←──────────────┘                    ║
║       │           │  最大化奖励   │                                    ║
║       │           └──────────────┘                                    ║
║       │                    │                                          ║
║       │           ┌──────────────┐                                    ║
║       │           │ Reference Model│ ← 冻结的SFT模型                   ║
║       │           │ KL散度约束     │ → 防止策略偏离太远                ║
║       │           └──────────────┘                                    ║
║       │                    │                                          ║
║       │           ┌──────────────┐                                    ║
║       └────────── │ KL Penalty   │ ← β × KL(π || π_ref)              ║
║                   └──────────────┘                                    ║
║                                                                        ║
║  Step 3: 迭代 Step 2 直到收敛或奖励不再增长                            ║
║                                                                        ║
║  目标函数:                                                              ║
║  max E[r(x,y)] - β × KL(π_θ || π_ref)                                ║
║  第一项: 最大化奖励(让回答更好)                                       ║
║  第二项: KL约束(不要偏离SFT模型太远,防止reward hacking)              ║
║                                                                        ║
╚══════════════════════════════════════════════════════════════════════╝

RLHF 的核心难点:

  1. Reward Hacking(奖励黑客):模型可能学会"骗"奖励模型,输出一些 Reward Model 给高分但实际不好的回答。比如 Reward Model 可能偏好长回答,模型就拼命写长文,不管对不对。
  2. KL 散度约束的作用:为了防止 Reward Hacking,RLHF 引入了一个 KL 惩罚项,限制策略模型不能偏离 SFT 模型太远。这就像给实习生一个规矩:“你可以改进,但不能偏离基本功太远。”
  3. 训练不稳定:PPO 是一种 on-policy 强化学习算法,训练过程容易震荡。需要精细的超参调节(学习率、KL 系数 β、clip 范围等)。
  4. 标注成本高:人类偏好标注需要专业人员,成本约 $5-20/条,标注 10 万条就是 50-200 万美元。

金句:RLHF 的本质不是"教模型什么是对的",而是"教模型什么是人类偏好的"——偏好不等于正确,这也是为什么 RLHF 后的模型有时会"讨好"用户而非给出最准确的答案。

2.5.5 DPO:RLHF 的简化革命

2023 年,斯坦福大学的 Rafailov 等人提出了 DPO(Direct Preference Optimization),直接用偏好数据训练模型,不需要训练 Reward Model,也不需要 PPO 强化学习。DPO 的核心洞察是:最优的奖励函数可以解析地从策略模型中推导出来,因此可以跳过 Reward Model,直接用偏好数据优化策略模型。

DPO 的损失函数:

L_DPO = -log [ σ( β × (log π_θ(y_w|x)/π_ref(y_w|x) - log π_θ(y_l|x)/π_ref(y_l|x)) ) ]

其中:
  y_w = 偏好回答(preferred)
  y_l = 不偏好回答(rejected)
  π_θ = 当前策略模型
  π_ref = 参考模型(冻结的SFT模型)
  β = 温度系数(控制偏离程度)
  σ = sigmoid 函数

DPO 与 RLHF 的对比如下:

维度 RLHF (PPO) DPO
是否需要 Reward Model 是,需要单独训练 否,直接从策略模型推导
是否需要强化学习 是,PPO 算法 否,普通的分类损失
训练稳定性 差,PPO 容易震荡 好,类似于 SFT 的训练体验
训练成本 高(4个模型同时在显存中) 低(2个模型:策略+参考)
效果 略好(理论上限更高) 接近RLHF(实际差距很小)
工程复杂度
适合场景 大厂有充足资源 中小团队、快速迭代

到 2024-2025 年,DPO 及其变体(IPO、KTO、ORPO、SimPO)已经成为大多数开源模型对齐的首选方案。Llama-3、Qwen-2.5、DeepSeek-V3 等模型都采用了 DPO 或其变体进行后训练对齐。DeepSeek 还提出了 GRPO(Group Relative Policy Optimization),是 PPO 的简化版,去掉了 Critic 模型,用组内相对奖励来估计 baseline,在大模型推理能力训练(如 DeepSeek-R1)中表现优异。

金句:DPO 的伟大之处在于:它发现 RLHF 中的 Reward Model 和 PPO 都是"中间商",而中间商可以省掉——直接从偏好数据到策略优化,一步到位。这就像从"先翻译再润色"变成"直接用目标语言写作",少了一层转换,少了一层信息损失。


2.6 上下文窗口与长上下文:大模型的"工作记忆"

2.6.1 上下文窗口是什么

上下文窗口(Context Window)是指大模型一次推理能处理的最大 Token 数。它就像人的"工作记忆"——你能在脑子里同时记住多少信息,决定了你能处理多复杂的任务。

╔══════════════════════════════════════════════════════════════════════╗
║                   上下文窗口的演进历程                                   ║
╠══════════════╦═══════╦═══════════════════════════════════════════════╣
║ 模型          ║ 年代   ║ 上下文窗口                    ║ 大约可容纳   ║
╠══════════════╬═══════╬═══════════════════════════════╬═════════════╣
║ GPT-2        ║ 2019  ║ 1,024 Token    ║ 约 3页A4纸    ║
║ GPT-3        ║ 2020  ║ 2,048 Token    ║ 约 6页        ║
║ GPT-3.5      ║ 2022  ║ 4,096-8,192    ║ 约 12-24页    ║
║ GPT-4        ║ 2023  ║ 8,192-32,768   ║ 约 24-96页    ║
║ Claude-2     ║ 2023  ║ 100,000        ║ 约 300页 ≈ 1本书║
║ GPT-4-Turbo  ║ 2023  ║ 128,000        ║ 约 375页      ║
║ Gemini-1.5-Pro║ 2024 ║ 1,000,000      ║ 约 3000页 ≈ 10本书║
║ Llama-3.1    ║ 2024  ║ 131,072        ║ 约 390页      ║
║ Claude-3.5   ║ 2024  ║ 200,000        ║ 约 600页      ║
║ Gemini-2.0   ║ 2025  ║ 2,000,000+     ║ 约 6000页 ≈ 20本书║
╚══════════════╩═══════╩═══════════════════════════════╩═════════════╝

2.6.2 长上下文的工程挑战

扩展上下文窗口不是简单地把 max_seq_len 参数调大就行,它涉及一系列工程挑战:

  1. Attention 的 O(n²) 复杂度:如前所述,标准 Attention 的计算和显存随序列长度平方增长。128K 上下文的注意力矩阵是 4K 上下文的 1024 倍。
  2. 位置编码外推:如果模型训练时最长只见过 4K 序列,推理时给 128K 序列,位置编码会进入"未见过的区域",效果急剧下降。解决方案包括:
    • NTK-aware 插值:对 RoPE 的频率做非线性插值,让高频部分保持原样(精细位置),低频部分压缩(粗略位置)。
    • YaRN(Yet another RoPE extensioN):NTK 的改进版,Llama-3 用此方案扩展到 128K。
    • 渐进式训练(Progressive Training):先在 4K 上训练,再逐步扩展到 32K、128K,让模型逐步适应长序列。
  3. 中间遗忘(Lost in the Middle):即使模型支持 128K 上下文,研究表明模型在长文本中,对开头和结尾的信息记忆较好,对中间的信息容易遗忘。这是 Attention 机制的一个已知偏置。
  4. 推理显存(KV Cache):长上下文推理时,KV Cache 会占用大量显存。128K 上下文、70B 模型的 KV Cache 约需 32GB 显存(详见 2.7 节)。

2.6.3 长上下文 vs RAG:永远的路线之争

一个永恒的架构问题是:当需要处理大量外部知识时,应该用长上下文(把所有知识塞进 prompt)还是用 RAG(检索增强生成,只塞相关片段)?

╔══════════════════════════════════════════════════════════════════════╗
║              长上下文 vs RAG:架构路线对比                                ║
╠═════════════════╦══════════════════════╦═════════════════════════════╣
║ 维度             ║ 长上下文(直接塞入)   ║ RAG(检索增强)              ║
╠═════════════════╬══════════════════════╬═════════════════════════════╣
║ 实现复杂度       ║ 低(直接拼prompt)     ║ 高(需要向量库+检索+重排)   ║
║ 知识更新成本     ║ 高(每次都要更新prompt)║ 低(只更新向量库)           ║
║ 推理成本         ║ 高(每次付全部Token费) ║ 低(只付检索片段的Token费)  ║
║ 知识规模上限     ║ 受上下文窗口限制      ║ 理论无限                    ║
║ 信息精度         ║ 低(模型需自己"找")   ║ 高(检索器精准定位)         ║
║ 复杂推理         ║ 强(全局视野)         ║ 弱(片段化,可能缺上下文)   ║
║ 幻觉风险         ║ 中(有信息但可能记错) ║ 低(有明确出处)             ║
║ 适合场景         ║ 少量文档深度分析      ║ 大规模知识库问答            ║
╚═════════════════╩══════════════════════╩═════════════════════════════╝

金句:长上下文和 RAG 不是二选一的关系,而是互补的——长上下文负责"深度理解少量文档",RAG 负责"广度覆盖大量知识",成熟的 Agent 系统永远是两者的融合:先 RAG 检索,再把检索结果放入长上下文中做深度推理。


2.7 幻觉的概率本质:LLM 是概率机器,必然幻觉

2.7.1 什么是幻觉

幻觉(Hallucination)是指大模型生成了看似合理但事实错误的内容。比如你问"林黛玉倒拔垂杨柳的故事出自哪里?“,模型可能回答"出自《红楼梦》第五回”——看似合理(林黛玉确实是《红楼梦》角色),但事实上"林黛玉倒拔垂杨柳"是网络恶搞梗,原作中根本没有这个情节。

幻觉是 LLM 在企业级应用中最大的信任障碍。一个会幻觉的模型,不能用于医疗诊断、法律咨询、政务审批等高风险场景。理解幻觉的本质,是 Agent 架构师设计兜底方案的前提。

2.7.2 从概率拟合演绎推导:为什么 LLM 必然幻觉

核心论证:LLM 的训练目标是 Next Token Prediction,即最大化 P(w_t | w_1, …, w_{t-1})。这个目标函数本身不包含"事实正确性"约束,只包含"统计似然性"约束。

详细推导:

  1. LLM 学的是分布,不是事实:训练语料中,“巴黎是法国的首都"出现了 1000 次,“巴黎是中国的首都"出现了 0 次。模型学到的分布 P(巴黎|首都) 中,“法国"的概率远高于"中国”。但这只是统计规律,不是逻辑事实。如果训练语料中有足够的错误信息(比如网上有人说"巴黎是英国的首都”),模型也会学到一定概率的"英国”。

  2. 概率高 ≠ 事实对:即使"巴黎是法国的首都"的概率是 99.99%,模型在生成时仍有 0.01% 的概率输出错误答案——特别是在高温采样(temperature > 0)时。而且,对于训练语料中覆盖不足的事实,概率分布会更加平坦,错误概率更高。

  3. 长尾知识必然幻觉:训练语料是有限的,世界上的事实是无限的。对于训练语料中很少出现的事实(比如"某县某镇的某条路叫什么名字"),模型没有足够的统计证据,只能靠关联推理猜测——而猜测就可能错。

  4. 组合泛化必然幻觉:即使模型分别知道"A是B"和"B是C",也不保证它能正确推理出"A和C的关系"——因为 Next Token Prediction 不训练逻辑推理,只训练模式匹配。当需要多步推理时,每一步都有一个概率误差,误差累积导致最终结果出错。

  5. 指令与知识冲突时必然幻觉:当用户的问题前提是错误的(比如"林黛玉倒拔垂杨柳出自哪里"),模型可能不纠正前提,而是顺着错误前提"编造"一个看起来合理的答案——因为它学到的模式是"回答问题",而不是"质疑前提"。

金句:幻觉不是 bug,是 feature——LLM 本质上是一台"概率生成机",它的目标函数是"最可能的续写",不是"最正确的回答"。要求一台概率机器永远说真话,就像要求一个画家画出照片级精确的画作——他可以努力逼近,但本质上,画笔不是相机。

2.7.3 幻觉的分类与对策

幻觉类型 定义 典型案例 工程对策
事实性幻觉 生成与客观事实不符的内容 “爱因斯坦生于1900年”(实为1879) RAG + 事实核查 + 知识库约束
逻辑性幻觉 推理过程有逻辑错误 “A>B, B>C, 所以 C>A” CoT + 自我验证 + 多步推理验证
指令幻觉 不遵循用户指令 要求JSON格式但输出纯文本 结构化输出约束(JSON Mode)
来源幻觉 编造不存在的引用/出处 “根据Smith et al. (2023)…” 引用验证 + RAG
上下文幻觉 无视或曲解上下文信息 给了文档但回答与文档矛盾 上下文注入 + 引用标注
自我矛盾幻觉 同一回答中前后矛盾 前面说"是"后面说"不是" 多轮自洽检查

2.7.4 Agent 架构师的防幻觉工具箱

作为 Agent 架构师,你不能指望模型自己不幻觉,你必须设计工程方案来兜底:

  1. RAG(检索增强生成):先从知识库检索相关事实,注入 prompt,让模型"看着资料回答"。
  2. 工具调用(Tool Use / Function Calling):让模型调用外部 API 获取实时数据,而非靠记忆。
  3. 结构化输出约束:用 JSON Mode / Constrained Decoding 强制模型输出结构化格式,减少自由发挥空间。
  4. 多模型交叉验证:用两个不同模型生成答案,比对一致性,不一致时触发人工审核或额外验证。
  5. 置信度估计:通过 logprob 或自洽性检查估计模型对答案的置信度,低置信度时拒绝回答或补充检索。
  6. 事实后验核查:用专门的 fact-checking 模型或规则引擎,对生成内容做事后核查。
  7. 人机协同(Human-in-the-Loop):高风险场景(如政务审批)设置人工审核环节,模型只做初筛。

金句:防幻觉不是消灭幻觉(那是不可能的),而是把幻觉控制在一个可接受的误差率内——就像自动驾驶不需要零事故才允许上路,而是要比人类驾驶员更安全。工程的意义,在于用不完美的零件构建足够可靠的系统。


2.8 Token 计费与成本模型:Agent 架构师的"财务报表"

2.8.1 Token 经济学基础

在 LLM API 计费模型中,成本由两部分组成:

总成本 = 输入Token成本 + 输出Token成本
       = N_in × P_in + N_out × P_out

其中:

  • N_in = 输入 Token 数(prompt + context + history)
  • N_out = 输出 Token 数(模型生成的回复)
  • P_in = 输入单价($/1K Token 或 $/1M Token)
  • P_out = 输出单价(通常是输入的 2-3 倍)

为什么输出比输入贵?因为输入是一次性的矩阵乘法(可以高度并行),而输出是自回归的逐 Token 生成(每步依赖上一步,并行度低)。更关键的是,输出阶段需要维护 KV Cache,显存占用随序列长度增长,导致 GPU 利用率更低,单位成本更高。

2.8.2 主流模型价格对比(2025-2026 年)

以下价格仅供参考,实际价格以厂商官网为准:

模型 输入价格($/1M Token) 输出价格($/1M Token) 上下文窗口 备注
GPT-4o 2.50 10.00 128K OpenAI 旗舰
GPT-4o-mini 0.15 0.60 128K 高性价比
GPT-4-Turbo 10.00 30.00 128K 旧旗舰
Claude-3.5-Sonnet 3.00 15.00 200K Anthropic
Claude-3.5-Haiku 0.80 4.00 200K 轻量级
Claude-3-Opus 15.00 75.00 200K Anthropic 旗舰
Gemini-2.0-Pro 1.25 5.00 2M Google
Gemini-2.0-Flash 0.075 0.30 1M 极致性价比
DeepSeek-V3 0.27 1.10 128K 国产之光
Qwen2.5-72B 0.35 1.40 128K 通义千问
Llama-3.1-405B(自部署) ~0.50(算力折算) ~2.00 128K 开源自部署

从上表可以看出:

  1. 价格差异巨大:最贵的 Claude-3-Opus 输出价格是最便宜的 Gemini-2.0-Flash 的 250 倍。
  2. 国产模型性价比突出:DeepSeek-V3 的价格只有 GPT-4o 的约 1/10,效果接近。
  3. 小模型/Flash 系列适合大规模调用:当你的 Agent 每天调用百万次时,$0.075/1M 和 $2.50/1M 的差距就是 $2400/天 vs $75/天。

2.8.3 Agent 系统的成本估算模型

一个 Agent 系统的单次任务成本,不只是"一次 API 调用"的成本,而是"整个任务链路所有 LLM 调用的成本之和":

C_task = Σ_i (N_in_i × P_in + N_out_i × P_out)

其中 i 遍历 Agent 执行任务过程中的所有 LLM 调用:
  - 初始意图理解
  - 工具选择
  - 每一步推理/规划
  - 工具调用结果的解读
  - 多轮对话历史
  - 最终回复生成
  - 可能的自我反思/验证

一个典型的 Agent 任务可能涉及 5-20 次 LLM 调用,每次调用都包含不断增长的对话历史(因为 Agent 需要记住之前的步骤)。这意味着:

实际成本 ≈ 简单估算 × (5~20) × 历史膨胀因子(1.5~3)

这就是为什么很多团队在 POC 阶段觉得"LLM 成本可控",但上线后发现成本爆炸——因为他们只算了"一次调用"的成本,没算"一个完整任务链路"的成本。

金句:Agent 的成本不是一次 API 调用的成本,而是一条任务链路上所有调用的成本之和——这才是 CTO 视角下的真实账单。一个优秀的 Agent 架构师,必须在设计阶段就建好成本模型,否则上线即破产。

2.8.4 成本压降七板斧

基于以上分析,降成本的策略可以归纳为"七板斧":

  1. 模型分级路由(Model Routing):简单任务用小模型(GPT-4o-mini/Haiku),复杂任务才用大模型(GPT-4o/Sonnet)。用一个轻量分类器(甚至规则引擎)判断任务复杂度,路由到不同模型。可降成本 60-80%。
  2. Prompt 精简与缓存:删除 prompt 中的冗余信息,用 system prompt 缓存(OpenAI/Anthropic 都支持 prompt caching),减少重复输入成本。可降成本 30-50%。
  3. 上下文窗口管理:不要把完整对话历史每次都传入,用摘要 + 最近 N 轮的策略压缩历史。可降成本 40-60%。
  4. 输出长度控制:设置 max_tokens 限制输出长度,用 system prompt 指示"简洁回答"。输出 Token 更贵,控制输出比控制输入更有效。
  5. 批处理(Batch API):OpenAI/Anthropic 都提供 Batch API,50% 折扣,适合非实时场景。
  6. 自部署开源模型:对高频低复杂度任务,自部署 Llama/Qwen/DeepSeek 等开源模型,长期成本可降至 API 的 1/5-1/10。
  7. 结果缓存:对相同/相似 query 的结果做缓存(Semantic Cache),命中率 10-30% 可显著降本。

2.9 流式输出原理:首字时延与体验优化

2.9.1 为什么需要流式输出

大模型的生成是自回归的——逐个 Token 生成,每个 Token 依赖前序所有 Token。如果一个回复有 500 个 Token,非流式模式下用户需要等全部 500 个 Token 生成完才能看到回复,可能等 10-20 秒,体验极差。

流式输出(Streaming)的核心思想是:每生成一个(或几个)Token 就立即返回给前端,不等全部生成完。 这样用户几乎可以即时看到回复开始出现,然后像打字一样逐步显示完整内容。

2.9.2 流式输出的技术实现

流式输出通常基于 SSE(Server-Sent Events)协议实现:

╔══════════════════════════════════════════════════════════════════════╗
║                    流式输出技术架构                                      ║
╠══════════════════════════════════════════════════════════════════════╣
║                                                                        ║
║  客户端(前端)          API Gateway           LLM 推理服务              ║
║                                                                        ║
║  ┌──────────┐         ┌──────────┐         ┌──────────┐               ║
║  │ EventSource│←─SSE──│ 反向代理  │←─SSE──│ vLLM/TGI  │               ║
║  │ (浏览器)  │  连接   │(Nginx等) │  连接   │(推理引擎) │               ║
║  └──────────┘         └──────────┘         └──────────┘               ║
║       │                     │                    │                    ║
║       │ 1.发起POST请求      │                    │                    ║
║       │────stream:true─────→│ 2.转发请求         │                    ║
║       │                     │───────────────────→│                    ║
║       │                     │                    │ 3.逐Token生成       ║
║       │                     │                    │                    ║
║       │                     │ 4. SSE: data: tok1 │←───────────────────│
║       │ 5. SSE: data: tok1  │←───────────────────│                    ║
║       │←───────────────────│                    │                    ║
║       │                     │                    │ 6. 继续生成         ║
║       │ 7. SSE: data: tok2  │                    │                    ║
║       │←───────────────────│←───────────────────│                    ║
║       │   ...               │   ...              │   ...              ║
║       │                     │                    │ 8. 生成完毕         ║
║       │ 9. SSE: data: [DONE]│                    │                    ║
║       │←───────────────────│←───────────────────│                    ║
║       │                     │                    │                    ║
║       │ 10.关闭连接         │                    │                    ║
║                                                                        ║
║   SSE 数据格式:                                                        ║
║   data: {"choices":[{"delta":{"content":"你"}}]}                      ║
║   data: {"choices":[{"delta":{"content":"好"}}]}                      ║
║   data: {"choices":[{"delta":{"content":",世界"}}]}                  ║
║   data: [DONE]                                                         ║
║                                                                        ║
╚══════════════════════════════════════════════════════════════════════╝

2.9.3 首字时延(TTFT)与每 Token 时延(TPOT)

流式输出的体验质量由两个关键指标决定:

  1. TTFT(Time To First Token,首字时延):从用户发送请求到看到第一个 Token 的时间。理想 < 1 秒,可接受 < 3 秒。
  2. TPOT(Time Per Output Token,每 Token 时延):生成阶段,每两个 Token 之间的间隔。人类阅读速度约 5-10 Token/秒,所以 TPOT < 200ms 时体验流畅。

影响 TTFT 的主要因素:

  • Prompt 处理(Prefill)时间:输入 prompt 越长,prefill 越慢。因为 prefill 阶段需要对所有输入 Token 计算一次前向传播。
  • 网络延迟:客户端到 API Gateway 到推理服务的网络往返。
  • 队列等待:如果推理服务繁忙,请求需要排队。
  • 模型大小:大模型的前向传播更慢。

影响 TPOT 的主要因素:

  • 模型大小与解码效率:大模型每个 Token 的生成时间更长。
  • KV Cache 大小:上下文越长,KV Cache 越大,每步 Attention 计算越慢。
  • 批处理大小:并发请求多时,GPU 可以批量并行 decode,但单请求 TPOT 可能略增。

金句:流式输出的本质是"用体验换容忍度"——用户愿意等 15 秒看完整回复,但如果 15 秒内一个字都没出现,用户会觉得系统卡死了。流式输出让用户"看到 AI 在思考",这种心理上的"在场感"比实际速度更重要。

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐