大语言模型基础

首先大模型不是凭空出来的,它都是基于Transformer架构的。

Transformer架构不是凭空出现的,它是去掉了RNN的循环结构,仅保留注意力机制,对RNN的改进重构出现的。

RNN也不是凭空出现的,是对全连接神经网络的改进出现的。

神经网络是多个逻辑回归的组合,逻辑回归又基于线性回归。

而要学懂这些,你还需要一些必备的数学基础。

作者:小萌 链接:https://www.zhihu.com/question/621550974/answer/1935263681278608637 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

1.搞懂微小的模型,几个参数再到几十个参数的模型,比如感知机,或者反向传播网络BP。这里重点要搞懂模型参数是什么?搞懂训练过程,搞懂损失函数(L2,交叉熵)搞懂梯度下降法,搞懂推理过程。这里基本搞清楚了机器学习中的“学习”到底是什么,以后不管模型多大,使用的“学习”都是一样。

2.然后接着搞懂RNN,连LSTM都不用搞。就把RNN搞清楚就行。搞懂递归神经网络其实就是把一个bp反复用而已。至于模型的训练策略,损失函数,推理过程是跟bp类似。RNN在拓扑结构上类似两个bp并联然后再串联一个bp.

  1. 接着就看transformer,关注一下:与rnn相比,为啥transformer可以不再递归?不再递归以后,没有顺序信息了,序列长度不固定的问题,解码器训练时的样本组织问题等都是怎样打补丁解决的?你会发现所谓的自注意力其实还是BP的变形,整个transformer虽然分解码器,编码器,再具体的,虽然分自注意力模块,前馈网络模块,但基本计算单元还是类似bp中的隐藏层。transformer在拓扑结构上,就是大概40个bp的复杂串并连。

  2. 再看Bert,gpt1,2,3,4,llama你就会发现网络结构与transformer相比没有创新。Bert的最重要创新点在于更改了输入输出,更改了恢复顺序信息的办法,实现了训练数据自由,所以叫无导师,从而实现更大规模的,更多损失函数的训练,从而训练出了更好的transformer解码器。GPT创新点就是大量的增加解码器,编码器的层数,原理真的就仅仅是大力出奇迹而已,以至于实现了所谓的零学习,单例学习,或者元学习,使人工智能看起来不再像个智障,回答问题差不多靠谱了。当然原理上大力出奇迹,但工程量大,优化需求旺盛。

总之,三层反向传播网络没学好,大模型就理解不了,也学不好。三层反向传播网络里的细节搞清楚了,稍微大点的模型就真的很容易,只要直接看源码,就很容易搞清楚作者论文里提出的各种新名词,就比如自注意力,多头自注意力等,都是三层反向传播网络的串并连而已,只不过不同位置的隐藏层发挥作用不同,所以有的叫词嵌入,有的叫QKV注意力等。总之,不被这些新名词迷惑住,还会觉得作者们真的很会玩概念,起名字。

作者:赵明明 链接:https://www.zhihu.com/question/621550974/answer/3430393581 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

入门资料

Attention 机制

标准 Multi-Head Attention(MHA):Q、K、V 各有 n_heads 个头,每个头独立做 softmax(QK^T / sqrt(d)) · V。你需要理解为什么朴素实现会产生 O(n²) 的中间矩阵,以及为什么 FlashAttention 能通过 tiling 和 online softmax 避免这个中间矩阵的显存开销——这是目前最重要的 Attention kernel 优化技术。

Multi-Query Attention(MQA):所有 Q 头共享一组 K 和一组 V。这意味着 KV Cache 大幅缩小,对推理时的显存和带宽压力影响巨大。

Grouped-Query Attention(GQA):MHA 和 MQA 的折中,比如 Llama 3 用 8 个 KV 头对 32 个 Q 头。Kernel 实现时需要处理 Q 头到 KV 头的映射关系,不再是简单的一对一。

Multi-Latent Attention(MLA):DeepSeek V2/V3 引入的,把 KV 投影到低维潜空间再存储,进一步压缩 KV Cache。这是更前沿的知识。

Claude 学习指南

按照"每个模型引入了什么你必须知道的架构创新"来筛选,只列真正绕不开的:

第一梯队:必须深入理解

Transformer(2017,Google)——一切的起点。原始论文 "Attention Is All You Need" 定义了 Self-Attention、Multi-Head Attention、位置编码、残差连接、LayerNorm 这些基本组件。不需要背公式,但要理解每个组件的计算流和张量形状。

GPT-2(2019,OpenAI)——Decoder-only Transformer 的标准参考实现。架构极其干净:Pre-LayerNorm、标准 MHA、两层 FFN + GELU。Andrej Karpathy 的 nanoGPT 就是基于 GPT-2 写的,很多 kernel 工程师的入门就是从读 nanoGPT 的代码开始。它的价值不在于性能,而在于它是理解所有后续模型的"基准线"——后面的模型都是在它基础上改东西。

Llama 1(2023,Meta)——现代 LLM 的"标准模板"。相比 GPT-2,它引入了三个关键变化:RMSNorm 替代 LayerNorm、RoPE 替代学习型位置编码、SwiGLU 替代 GELU FFN。这三个改动如今已经成为几乎所有主流 LLM 的默认配置。如果你只深入研究一个模型的完整实现,选 Llama 1 就对了。

第二梯队:必须了解架构差异

Llama 2 70B(2023,Meta)——架构上与 Llama 1 高度一致,但 70B 版本首次在 Llama 系列中引入了 GQA(8 个 KV 头对 32 个 Q 头)。注意 Llama 2 的 7B 和 13B 仍然使用标准 MHA,与 Llama 1 架构相同。GQA 的引入意味着 Q 头和 KV 头的数量不再对称,对 kernel 工程师来说,Attention kernel 和 KV Cache 管理的实现都要处理这种不对称性。

Llama 3(2024,Meta)——将 GQA 变成全系列的默认配置(包括 8B),并大幅扩展词表到 128K。从 Llama 3 开始,GQA 成为主流 LLM 的标配,必须会。

Mixtral 8x7B(2023,Mistral)——开源界最有影响力的 MoE 模型。你需要理解 router/gate 怎么把 token 分发给不同 expert,以及由此带来的不规则计算(每个 expert 接收的 token 数不同)和 Expert Parallelism 下的 All-to-All 通信。MoE 的 kernel 优化和 dense 模型差异很大。

第三梯队:了解其创新点即可

GPT-1(2018,OpenAI)——历史意义大于实际参考价值。它确立了"预训练 + 微调"的范式和 decoder-only 架构,但具体实现已经被 GPT-2 完全覆盖。知道它的定位就行,不需要深入代码。

BERT(2018,Google)——Encoder-only 架构,bidirectional attention。做 LLM 推理 kernel 的人不太会直接碰到它,但理解 encoder 和 decoder 的 attention mask 区别(full attention vs causal attention)是基本功。

GPT-3(2020,OpenAI)——架构上和 GPT-2 几乎一样,只是大了 100 倍。它的意义在于验证了 scaling law,但对 kernel 工程师来说没有引入新的计算模式。不过它让人们开始认真思考大规模 GEMM 的效率问题。

DeepSeek V2/V3(2024,深度求索)——引入了 MLA(Multi-Latent Attention),把 KV 投影到低维空间来压缩 KV Cache。如果你做推理优化,这是需要关注的前沿方向。另外 DeepSeek 在 2025 年开源周放出的 FlashMLA、DeepGEMM 等 kernel 实现本身就是非常好的学习材料。

Mistral 7B(2023,Mistral)——引入了 Sliding Window Attention,限制每个 token 只关注局部窗口内的上下文。这改变了 Attention 的计算范围和 KV Cache 的管理策略。


实际学习路径建议:

先把 GPT-2 的完整前向过程搞透(推荐读 nanoGPT 的代码),然后切到 Llama 1 理解现代 LLM 的三件套改动(RMSNorm、RoPE、SwiGLU),再看 Llama 2 70B/ Llama 3 理解 GQA,最后看 Mixtral 理解 MoE。这几个模型吃透了,你遇到市面上任何新模型都能快速定位它改了什么、对 kernel 有什么影响。

Gemini 学习指南

结合这份指南,我为你定制了一条更偏向体系结构和底层工程的落地路径:

Phase 1: 筑基 (理解计算与内存架构)

Phase 2: 现代工程标准 (算子融合与访存优化)

Phase 3: 解决系统级瓶颈 (带宽与通信)

Phase 4: 挑战前沿 (稀疏性与动态调度)

推理框架

训练框架

训练流水线、Pre-train、Post-train

   原始互联网文本
       ↓
       ├── Pre-training      ← 阶段 1:让模型"会说话"
       ↓
   Base 模型                  ← 比如 LLaMA 2 7B(不能聊天)
       ↓
       ├── SFT               ← 阶段 2:让模型"会聊天"
       ↓
   SFT 模型
       ↓
       ├── RLHF / DPO        ← 阶段 3:让模型"对齐人类偏好"
       ↓
   Chat 模型                  ← 比如 LLaMA 2 7B Chat

Post-training = "Pre-training 之后所有的训练阶段" 的总称。包括但不限于:

注意,base 模型和 chat 模型架构和模型参数量完全相同,只要有参数的具体值是不一样的。

Pre-training

Base 模型的特征:

如果你直接给 LLaMA 2 7B base 一个问题:输入: "What is the capital of France?"

它可能续写:

"What is the capital of France? What is the capital of Germany?
What is the capital of Italy? Here are 50 capital city quiz questions..."

它不知道"应该回答你"——它只是把你的问题当作一段文本的开头,继续往下写。这就是 base 模型——像高中生一样能补全任何文本,但不会"聊天"。

SFT (Supervised Fine-Tuning)

RLHF (Reinforcement Learning from Human Feedback)

DPO(Direct Preference Optimization)

LLaMA 2 用的是 RLHF——LLaMA 3 之后很多模型切到了 DPO。

Decoder Only Transformer

class DecoderOnlyTransformer:
    def forward(self, token_ids):
        x = embedding(token_ids)
        for _ in range(N):       # 重复 N 次
            x = x + attention(norm(x))   # masked self-attention + 残差
            x = x + ffn(norm(x))         # FFN + 残差
        x = norm(x)
        return linear(x)          # 输出 logits

Bias 问题

原版 Transformer 论文("Attention Is All You Need")的描述里——WQ,WK,WV,WO 严格说就是矩阵乘法,没显式写 bias。当然 FFN 部分是有两个 bias 的。

但是,实际代码实现中经常带 bias:

为什么会有这种差异?

  1. 工程惯性:

    nn.Linear 的默认值是 bias=True——程序员写代码时如果没特别注意,自然就带上了。没人特意去掉。

  2. bias 通常无害:

    带不带 bias 在小模型上几乎没区别——加上也不会让模型变差。所以早期实现都"宁可加上"。

  3. 论文原意:

    如果你逐字读 Vaswani 等人的论文——WQ 等就是纯矩阵乘——他们大概率没特别考虑 bias 这件事。这是论文表述的简化。

真正去掉 bias 是什么时候开始的?LLaMA 1(2023) 是第一个**明确强调"全部去掉 bias"**的旗舰大模型——

为什么 LLaMA 选择去掉?

LLaMA 之后——Qwen、DeepSeek、Mistral 等几乎所有现代大模型都跟进去掉了 bias——这成了新的"事实标准"。

Chat 模型对话时的具体行为

LLaMA 2 Chat 的对话格式

LLaMA 2 Chat 用的特殊 token:

第 1 轮对话

用户问:"What is the capital of France?"

预先设置的 system prompt:"You are a helpful AI assistant. You must only provide positive feedback."

实际喂给模型的字符串:

<s>[INST] <<SYS>>
You are a helpful AI assistant. You must only provide positive feedback.
<</SYS>>

What is the capital of France? [/INST]

模型生成:

Paris is the wonderful capital of France! </s>

第 2 轮对话

用户继续问:"What's Paris famous for?"

实际喂给模型的字符串(包含完整历史):

<s>[INST] <<SYS>>
You are a helpful AI assistant. You must only provide positive feedback.
<</SYS>>

What is the capital of France? [/INST] Paris is the wonderful capital of France! </s><s>[INST] What's Paris famous for? [/INST]

注意几个细节——

  1. 每一轮 user-assistant 都被一对 <s>...</s> 包裹(多轮对话不只有一对 BOS/EOS)
  2. system prompt 只在第一轮的 [INST] 内——后续轮不重复
  3. assistant 的回复直接接在 [/INST] 后面——没有专门的 "Assistant:" 标签

特殊 token 对应的 id

字符串经过 tokenizer——所有特殊 token 变成对应的 ID:

# 简化示意(实际 ID 因 tokenizer 而异)
"<s>"           → 1
"[INST]"        → 518, 25580, 29962  (拆成几个 token,不是单个特殊 token)
"<<SYS>>"       → ...
"</s>"          → 2

注意——[INST] 这种"标签"在 LLaMA 2 里实际上不是单个 special token——是普通的字符串 token,会被 tokenizer 拆成几个 token。

但 <s> 和 </s> 是真正的 special token——单个 token ID。

ChatML 格式

LLaMA 2 用的 [INST] 风格在 2023 年很流行——但现代模型(GPT-4、Claude、Qwen、LLaMA 3 等)大多切到了 ChatML 格式。

ChatML 长这样——

<|im_start|>system
You are a helpful AI assistant.
<|im_end|>
<|im_start|>user
What is the capital of France?
<|im_end|>
<|im_start|>assistant
Paris.
<|im_end|>
<|im_start|>user
What's Paris famous for?
<|im_end|>
<|im_start|>assistant

注意:

ChatML 比 LLaMA 2 的 [INST] 格式更清晰、更结构化——所以成了新标准。

serving 引擎如何处理多轮对话

1. 第 1 轮:

input_string = format_chat([
    {"role": "system", "content": "You are a helpful AI assistant..."},
    {"role": "user", "content": "What is the capital of France?"}
])
# 套上 ChatML(或其他模型特定格式)

input_ids = tokenizer.encode(input_string)
output_ids = model.generate(input_ids)
response_1 = tokenizer.decode(output_ids)

2. 第 2 轮:

input_string = format_chat([
    {"role": "system", "content": "You are a helpful AI assistant..."},
    {"role": "user", "content": "What is the capital of France?"},
    {"role": "assistant", "content": response_1},   # 上一轮的回答
    {"role": "user", "content": "What's Paris famous for?"}    # 新问题
])

input_ids = tokenizer.encode(input_string)
output_ids = model.generate(input_ids)
response_2 = tokenizer.decode(output_ids)

每轮对话——整个历史重新拼接、重新 tokenize、重新喂进模型。

KV cache 复用

如果每轮都把整个历史重新喂进去——前面的 token 不是要重新 prefill 一遍吗?非常浪费。

这就是为什么实际部署时会用 KV cache 复用:

所以实际上——多轮对话的"prefill"只覆盖每一轮新增的部分,不是每次都从头算。

但 vLLM、HuggingFace 的 generate() 等系统对这个的支持各有不同:

这是 LLM 推理服务的一个核心优化——叫 "prefix caching" 或 "KV cache reuse"。

PagedAttention

你可能想,"如果 KV cache 跨轮复用——那每个对话都要在 GPU 上保留一份 cache?这内存怎么够?"

这就是 vLLM 的 PagedAttention 解决的问题:

这是为什么 vLLM 能高效服务几千个并发对话:靠精细的 KV cache 内存管理。

预测下一个 token 的实现和语义

预测下一个 token 到底是怎么做到的?是 attention + FFN 协同 + 最后的 LM head 共同完成的。

首先,我们需要明确一下 decoder-only transformer 各结构的作用:

总结起来就是: