一、大模型推理prefill/decode流程
在大模型推理中,是的,输入确实需要先经过prefill阶段再经过decode阶段,并且在两个阶段都需要完整地通过模型的所有层(假设您提到的61层)。但两个阶段的处理方式有很大不同:
Prefill阶段的层处理
- 完整的层处理:所有输入token都需要完整地通过模型的全部61层(包括embedding层、transformer层和最后的LM head层)。
- 并行处理:所有输入token可以并行地通过这些层。简单来说,所有输入token可以同时进入模型并被处理。
- 生成KV缓存:当这些token通过每一层时,会生成对应的Key-Value缓存,这些缓存会被保存并用于后续的decode阶段。
- 计算特点:
- 计算密集型
- 内存带宽受限
- 可以有高度的并行化
Decode阶段的层处理
- 完整的层处理:每个新生成的token也需要完整地通过模型的全部61层。
- 顺序处理:与prefill不同,decode阶段是一次只处理一个新token,且必须等待前一个token完全处理完毕。
- 利用KV缓存:处理新token时,会利用prefill阶段生成的KV缓存,减少重复计算。
- 计算特点:
- 内存访问密集型
- 延迟敏感
- 并行度有限(主要是层内部的并行)
关键区别总结
虽然两个阶段都需要通过模型的所有层(61层),但它们的处理方式有显著差异:
- 数据流向:
- Prefill:所有输入token并行通过所有层
- Decode:一次只有一个新token通过所有层
- 缓存使用:
- Prefill:创建KV缓存
- Decode:利用已有KV缓存
- 计算模式:
- Prefill:批量处理
- Decode:自回归处理
二、 Prefill阶段的目标与工作原理
Prefill阶段的核心目标是将理解用户输入token的含义,生成输出第一个token,是后续decode阶段的首个输入。
三、举例说明prefill、decode阶段在单台服务器上的物理推理模型
假设在一台单机部署ds r1 671b上问“巴黎市法国的"问题,模型会如何处理这个问题,具体见下面的分析模型。
硬件与部署配置
服务器配置
- GPU资源: 8×H20 GPU,每卡141GB显存
- 网络互联: 400Gb Infiniband NDR连接,确保高速GPU间通信
VLLM部署策略
- 张量并行度: 8(模型各层权重分布在8个GPU上)
- 专家并行: 256个路由专家分布在8个GPU上,每卡约32个专家
- 模型参数分布: 完整的DeepSeek R1 MoE(约671B参数)分片存储在8张GPU上
Prefill阶段物理过程
当用户输入"巴黎是法国的"进入系统后:
1. 输入处理与分词
- 输入被分词为4个token: [巴黎, 是, 法国, 的]
- 这些token被发送至所有8张GPU
2. 分布式计算过程
- GPU 0-3和GPU 4-7分工:
- 不同GPU负责不同层参数:GPU0和GPU4共同负责层1-15,GPU1和GPU5负责层16-30,以此类推
- 每卡存储约32个专家模型(共同构成完整的256专家网络)
- 对于每一层的计算,都需要跨GPU协同完成
- 张量并行执行:
- 每一层的注意力计算被分割到8个GPU上并行执行
- 例如,注意力头的计算(128个头)分布到8个GPU,每个GPU处理16个头
- 专家选择与计算:
- 路由网络在每张GPU上计算路由分数
- 每个token位置选择8个专家(从256个中)
- 根据专家所在的GPU位置,将计算任务分发到对应GPU
3. 跨GPU通信
- 在每一层计算完成后,所有GPU执行All-reduce操作同步计算结果
- 专家计算时,执行All-to-all通信聚合各专家的输出
- 高带宽Infiniband互联提供高效率数据交换(400Gb/s)
4. KV缓存创建
- 对于4个输入token,创建KV缓存
- 缓存大小:61层 × 2(K和V) × 128头 × 64维/头 × 4token × 1字节 ≈ 0.25MB/卡
- 每个GPU存储部分KV缓存,通过张量并行分割
5. 物理资源使用
- 计算负载: 使用H20的Transformer Engine和Tensor Cores加速矩阵计算
- 内存带宽: 充分利用H20的3TB/s内存带宽并行处理所有输入token
- 显存占用: 模型参数+ KV缓存(初始很小)+ 激活值
Decode阶段物理过程
预填充完成后,模型开始生成响应:
1. 第一个Token生成
- 基于prefill处理结果,模型预测下一个token是"首都"
- 单个token的生成仍需通过所有8个GPU协同完成:
- GPU0处理embedding和部分层1-15
- GPU1处理部分层16-30
- 以此类推
2. 专家路由的物理执行
- 对新token位置计算路由分数(分布在8个GPU上)
- 选择8个最高得分的专家(从256个专家中)
- 专家可能分布在不同GPU上,例如:
- 专家12可能位于GPU0
- 专家87可能位于GPU2
- 专家145可能位于GPU4
- 等等...
- 跨GPU通信协调专家计算并同步结果
3. KV缓存更新
- 将新生成token的K和V向量添加到各GPU的KV缓存
- 缓存增长:从4个token扩展到5个token(约0.6MB/卡)
- 虽然缓存小,但随着回答长度增加会逐渐增长
4. 持续生成过程
- 重复上述物理过程,生成第二个token、第三个token...
- 最终生成完整回复:"巴黎是法国的首都。"
- 每个新token生成都需要全部8个GPU参与计算
性能特征
在这种硬件配置下,对于这个简短查询:
- Prefill阶段时间: 约50-100毫秒(4个token的并行处理)
- Decode阶段时间: 每个token约20-40毫秒
- MoE架构增加了一定延迟(选择专家的额外计算开销)
- 关键延迟因素:
- 专家选择的计算开销
- 跨GPU通信(虽然通过高速Infiniband降低)
- 对于更长上下文,KV缓存访问也会成为瓶颈
所有基于Transformer架构的大语言模型(不仅仅是MoE模型)在推理时都遵循prefill和decode两个阶段。这是Transformer自回归解码的基本工作方式,包括:
- 普通Transformer模型(如GPT系列早期模型)
- MoE模型(如DeepSeek R1 MoE、Mixtral等)
- 各种变体架构(如LLaMA、Falcon等)
MoE模型的特殊之处在于它在FFN层使用了专家混合机制,但prefill和decode的基本流程是所有Transformer模型的共同特征。
三、prefill、decode阶段本质理解
- Prefill阶段:
- 处理全部输入token(如"巴黎是法国的")
- 通过全部61层的计算
- 在每一层建立KV缓存
- 最终计算出第一个输出token的概率分布
- 选择最可能的token(如"首都")作为第一个输出
- Decode阶段:
- 开始"接龙"过程
- 将已选择的token("首都")经过embedding层
- 让它完整地通过全部61层
- 在每一层利用已有的KV缓存
- 计算第二个输出token的概率分布
- 选择下一个token(如"。")
- 然后重复这个过程,继续生成更多的token
这种机制非常像一个接力比赛:Prefill阶段跑完第一棒,然后Decode阶段接过接力棒,一个接一个地完成余下的路程。
每个新token都是基于之前所有内容(原始输入+已生成内容)预测出来的,这就是为什么大语言模型能够产生连贯、有逻辑的文本。
四、持续研究decode阶段循环输出与停止机制
Decode阶段的循环输出是一个精心设计的迭代过程,让我以"介绍一下爱因斯坦"这个查询为例,详细说明整个循环流程:
基本循环过程
- 初始化:
- Prefill阶段处理完整的输入"介绍一下爱因斯坦"
- 生成第一个输出token: "爱"
- KV缓存已包含输入的信息
- 循环步骤:
- 第1次循环:
- 输入上下文: "介绍一下爱因斯坦爱"
- 新token"爱"通过完整的61层处理
- 输出下一个token: "因"
- 第2次循环:
- 输入上下文: "介绍一下爱因斯坦爱因"
- 新token"因"通过61层处理
- 输出下一个token: "斯"
- 第3次循环:
- 输入上下文: "介绍一下爱因斯坦爱因斯"
- 新token"斯"通过61层处理
- 输出下一个token: "坦"
- 持续循环:
- 这个过程不断重复,每次处理一个新token
- 模型可能生成完整的介绍,如"爱因斯坦是20世纪最伟大的物理学家之一,出生于1879年..."
- 第1次循环:
停止输出的机制
大模型有几种方式决定何时停止生成:
1. 生成结束符号
最常见的停止方式是模型生成特殊的结束符号(EOS token):
- DeepSeek R1 MoE中,结束符通常是token ID为1的特殊token
- 当模型认为回答已经完整时,会自然生成这个结束符
- 例如,完成爱因斯坦介绍后:"...在物理学领域做出了革命性贡献。[EOS]"
2. 达到最大长度限制
为防止无限生成,通常会设置一个最大长度限制:
- 常见设置为512、1024或2048个token
- 当生成的token数量达到这个限制时,即使模型想继续生成也会强制停止
3. 停止词或停止序列
在实际应用中,可以设置特定的停止词或序列:
- 例如设置"\n\n"作为停止序列,当模型生成两个连续换行时停止
- 这可以用于控制输出格式或长度
4. 基于内容的停止
更复杂的系统可能实现基于内容的停止机制:
- 当模型开始重复或循环生成类似内容时停止
- 当回答已经涵盖用户查询的所有方面时停止
实际输出示例分析
以"介绍一下爱因斯坦"为例,完整的生成过程可能如下:
- 输入: "介绍一下爱因斯坦"
- Prefill处理完输入后生成第一个token: "爱"
- Decode循环:
- 循环#1: 输出"因"
- 循环#2: 输出"斯"
- 循环#3: 输出"坦"
- 循环#4: 输出"("
- 循环#5: 输出"Albert"
- ...
- 循环#32: 模型生成句号,完成一个段落
- ...
- 循环#120: 输出"物理学领域做出了革命性贡献。"
- 循环#121: 输出特殊的[EOS]token,停止生成
这个循环会一直持续,直到模型生成结束符或达到预设的最大长度限制。在整个过程中,KV缓存不断扩展,每个新token都利用之前所有token的信息进行预测,确保了输出的连贯性和逻辑性。
通过这种循环机制,大模型能够生成任意长度的连贯文本,而停止机制确保了输出的适当终止。