核心摘要

大语言模型 (LLM) 推理是将输入文本(Prompt)转化为输出结果的过程。它的核心工作流分为两个阶段:高度并行计算的 Prefill 阶段(处理 Prompt)与串行生成的 Decode 阶段(逐字生成)。在这个过程中,KV Cache 技术是优化显存读取和加速解码的关键。

📋 目录

✨ 核心要点

  • 自回归生成 (Autoregressive):LLM 每次只生成一个 Token,并使用之前生成的所有 Token 作为下一次生成的上下文。
  • 两阶段执行:推理过程被拆分为“算力瓶颈”的 Prefill 阶段和“显存带宽瓶颈”的 Decode 阶段。
  • KV Cache 的重要性:缓存历史的 Key 和 Value 矩阵减少了算力消耗,但极大增加了显存(VRAM)占用。
  • 指标优化:优化 TTFT 能提升用户体验(觉得模型反应快),优化 TPOT 则决定了文本输出的速度。

💡 工具推荐:使用 Token 计数器 — 快速计算你的 Prompt 消耗了多少 Token,从而预估大模型推理成本和延迟。

什么是 LLM 推理?

LLM 推理(Inference)是指经过训练的大语言模型接收输入文本(Prompt)并预测最可能的后续文本的过程。与通过海量数据更新模型权重的训练阶段不同,推理阶段使用的是固定的(冻结的)权重进行前向传播计算。

现代 LLM(如 GPT-4、Llama 3 和 DeepSeek)均采用自回归 (Autoregressive) 机制运行。这意味着模型在预测下一个 Token 时,必须依赖所有已知的历史 Token。新生成的 Token 会被追加到序列末尾,整个过程不断循环,直到模型输出一个停止标记(如 <EOS> Token)为止。

📝 术语链接Token — 大语言模型处理数据的最小基本单位,它可以是一个词、一个词根或单个字符。

LLM 推理是如何工作的?双阶段解析

基于 Transformer 架构的大模型,其推理过程被明确划分为两个阶段:Prefill(预填充)和 Decode(解码)。

1. Prefill 阶段(处理 Prompt)

当你向大模型发送一段 Prompt 时,模型首先进入 Prefill 阶段。在此阶段,模型会并行处理整个输入序列。

由于所有的输入 Token 都是已知的,GPU 可以充分利用高度优化的矩阵乘法(GEMM)进行大规模并发计算。Prefill 阶段的核心目标是:

  1. 计算输入 Prompt 中所有 Token 的注意力分数(Attention Scores)。
  2. 初始化并填充 KV Cache。
  3. 生成输出序列的第一个 Token。

该阶段通常是算力瓶颈 (Compute-bound),意味着它的处理速度主要受限于 GPU 的原始浮点运算能力(FLOPs)。

2. Decode 阶段(生成 Token)

当第一个 Token 生成后,模型就进入了 Decode 阶段。在这个阶段,模型开始逐个生成后续的 Token。

对于每一个新 Token,模型都需要关注(Attend)所有之前生成过的历史 Token。因为这个过程是串行的(必须等上一个字生成完毕,才能生成下一个字),GPU 无法像 Prefill 阶段那样在序列维度上进行并行计算,而是依赖于矩阵-向量乘法(GEMV)。

Decode 阶段是显存带宽瓶颈 (Memory-bandwidth bound)。GPU 将绝大部分时间花在把模型权重和 KV Cache 数据从显存(HBM)搬运到计算核心上。

graph TD A[输入 Prompt] --> B[Prefill 阶段] B -->|并行计算| C[初始化 KV Cache] B -->|生成| D[首个 Token] D --> E[Decode 阶段] C -.-> E E -->|串行计算| F[下一个 Token] F -->|追加到上下文| E F -->|遇到 EOS 停止| G[结束生成] style A fill:#e1f5fe,stroke:#01579b style B fill:#fff3e0,stroke:#e65100 style E fill:#fce4ec,stroke:#c2185b style G fill:#e8f5e9,stroke:#2e7d32

深入理解 KV Cache 原理

在生成下一个 Token 时,注意力机制(Attention)需要用到所有历史 Token 的 Key (K) 和 Value (V) 矩阵。如果每次生成新词都把整句话从头算一遍,计算量将呈二次方级增长($O(N^2)$),这在处理长文本时是不可接受的。

KV Cache 通过将历史 Token 的 K 和 V 张量缓存在 GPU 显存中解决了这个问题。

  • 不使用 KV Cache:每走一步都要重新计算所有历史数据的 $K$ 和 $V$。
  • 使用 KV Cache:模型只需计算最新生成的那个 Token 的 $K$ 和 $V$,将它们追加到缓存中,然后利用缓存的历史数据进行注意力计算。

虽然 KV Cache 大幅减少了计算量,但它消耗了海量的显存。对于 100k Token 的长上下文,仅 KV Cache 本身就可能占用数十 GB 的显存。这也是为什么像 vLLM 中的 PagedAttention(分页注意力)技术对于高并发推理服务器如此关键的原因。

核心性能指标:TTFT 与 TPOT

在评估 LLM 推理引擎(如 vLLM、TGI)时,业界通常关注两个核心延迟指标:

指标 全称 定义 对用户的影响
TTFT Time To First Token (首 Token 延迟) 从发送请求到接收到模型生成的第一个 Token 的耗时。 决定了 AI 的“响应速度”。TTFT 过高会让用户觉得系统卡顿。
TPOT Time Per Output Token (输出每个 Token 耗时) 在 Decode 阶段,平均生成每一个后续 Token 所需的时间。 决定了用户的“阅读速度”。如果 TPOT 是 50ms,意味着模型每秒生成 20 个 Token。

LLM 推理实战指南

下面我们用伪代码展示大模型自回归推理在底层是如何工作的:

python
import torch

def generate_text(model, tokenizer, prompt, max_tokens=50):
    # 分词:将输入文本转为 input_ids
    input_ids = tokenizer.encode(prompt, return_tensors="pt")
    
    # 初始化 KV Cache
    past_key_values = None
    
    generated_tokens = []
    
    for _ in range(max_tokens):
        # 执行前向传播
        with torch.no_grad():
            outputs = model(
                input_ids=input_ids, 
                past_key_values=past_key_values, # 传入历史缓存
                use_cache=True                   # 启用 KV Cache
            )
            
        # 获取最新预测的 Token(这里使用贪心解码 argmax)
        next_token_logits = outputs.logits[:, -1, :]
        next_token = torch.argmax(next_token_logits, dim=-1)
        
        # 追加到生成结果列表中
        generated_tokens.append(next_token.item())
        
        # 更新下一次迭代所需的 KV Cache
        past_key_values = outputs.past_key_values
        
        # 下一轮的输入仅仅是刚才生成的这一个 Token
        input_ids = next_token.unsqueeze(0)
        
        # 停止条件判断
        if next_token.item() == tokenizer.eos_token_id:
            break
            
    return tokenizer.decode(generated_tokens)

🔧 立即体验:使用我们的免费 JSON 格式化工具 在线解析、美化和验证各大模型 API 返回的 JSON 响应数据。

最佳实践:如何优化推理性能

  1. 使用 PagedAttention 框架:直接使用 vLLM 等成熟的推理框架。它们通过 PagedAttention 动态管理 KV Cache 显存,消除了内存碎片,将吞吐量(Batch Size)提升了高达 5 倍。
  2. 应用模型量化 (Quantization):将模型权重从 FP16 降低到 INT8 或 INT4(如使用 AWQ 或 GPTQ 格式)。这不仅降低了显存需求,还极大缓解了 Decode 阶段的显存带宽瓶颈。
  3. 开启连续批处理 (Continuous Batching):不要等待一个 Batch 中所有请求都完成才开始下一轮。只要有请求生成完毕,立刻将新请求插入到 Batch 中,最大化 GPU 利用率。
  4. 控制 Prompt 长度:因为 Prefill 阶段的计算量与 Prompt 长度的平方成正比,保持上下文精简能有效降低 TTFT 和算力成本。

⚠️ 常见错误

  • 忽视 KV Cache 的显存占用正确做法:在部署前务必计算最大并发数下的 KV Cache 大小。生成过程中的 OOM(显存溢出)报错,90% 都是因为 KV Cache 爆满,而非模型权重太大。
  • 在生产环境使用原生的 Hugging Face pipeline正确做法:原生管道未经过深度优化,在生产环境中应该使用 vLLM、TGI 或 TensorRT-LLM 等企业级推理服务器。

常见问题 (FAQ)

Q1:为什么生成的文本越长,大模型回复的速度就越慢?

随着生成序列的增长,KV Cache 的体积也在不断膨胀。在 Decode 阶段,GPU 为了生成单个 Token,必须将整个庞大的 KV Cache 从显存读取到计算核心。这种显存带宽瓶颈导致了随着上下文变长,生成速度(TPOT)会出现轻微的衰减。

Q2:什么是连续批处理 (Continuous Batching/In-flight Batching)?

传统的静态批处理必须等待 Batch 中最长的那句话生成完毕,才能整体处理下一个 Batch。而连续批处理能在 Token 级别动态地将新请求加入到当前 Batch,并剔除已完成的请求,从而极大地提升了并发吞吐量。

Q3:投机解码 (Speculative Decoding) 是如何加速推理的?

投机解码使用一个参数量极小的“草稿模型”快速猜测未来几个 Token,然后交由主模型在一次并行计算(Prefill 方式)中验证。这种做法将“受限于显存带宽”的逐字解码,转化为了“受限于算力”的并行验证,从而在不改变 KV Cache 结构的前提下显著提升了推理速度。

总结

LLM 推理是一场算力与显存带宽之间的精妙平衡。通过深入理解并行的 Prefill 阶段和串行的 Decode 阶段之间的区别,并利用 KV Cache 与 PagedAttention 等优化技术,开发者可以大幅降低大模型的延迟和部署成本。

👉 探索 QubitTool 开发者工具箱 — 使用我们提供的全套免费工具,加速您的 AI 研发工作流。

相关资源