从一个 Token 到 vLLM:推理、量化、适配的理解

从一个 Token 到 vLLM:大模型推理、KV Cache、量化与模型适配的理解

学大模型量化时,很容易一上来就研究 AWQ、GPTQ,或者直接复制一段量化脚本。

但真正开始做推理部署以后,会发现很多问题绕不过去:

  • 为什么 7B 模型 BF16 大约需要 14 GB 权重显存?
  • 为什么量化成 INT4 后,整张 GPU 的显存却没有严格缩小 4 倍?
  • 为什么上下文和并发一高就容易 OOM?
  • KV Cache 到底缓存了什么?
  • 为什么 TP 能让单卡放不下的模型跑起来?
  • vLLM 的 Paged KV Cache、Continuous Batching 和 Scheduler 到底分别解决什么问题?

这些问题看起来属于不同领域,

其实可以看成一条线:

text 复制代码
Token
  ↓
Embedding
  ↓
Transformer
  ↓
Q / K / V
  ↓
Attention
  ↓
KV Cache
  ↓
Prefill / Decode
  ↓
GPU 显存
  ↓
量化
  ↓
AWQ / GPTQ / ...其他算法
  ↓
TP / PP
  ↓
vLLM

把这条链真正串起来后,很多所谓的"大模型部署参数"就不再是需要死记的配置项,而是可以从原理推出来的东西。


一、先从最基本的东西开始:大模型处理的不是文字,而是张量

假设输入一句话:

text 复制代码
中国的首都是

第一步是 Tokenizer。

它会把文本切成 token,再映射成 token ID:

text 复制代码
文字
 ↓
Tokenizer
 ↓
token IDs

比如某个 token 的 ID 是:

text 复制代码
103

这里的 103 只是词表中的编号,本身没有"103 比 102 更接近某种语义"这样的意义。

真正进入 Transformer 之前,还要经过 Embedding:

text 复制代码
token_id = 103
       ↓
Embedding
       ↓
[0.21, -0.83, 0.14, ...]

假设模型的 hidden_size=4096,那么一个 token 最终会变成一个包含 4096 个数字的向量。

多个 token 放在一起,就形成 Transformer 经常处理的:

text 复制代码
X.shape = [B, S, H]

其中:

text 复制代码
B = Batch Size
S = Sequence Length
H = Hidden Size

例如:

text 复制代码
[2, 512, 4096]

表示:

一批有 2 条序列,每条 512 个 token,每个 token 使用一个 4096 维向量表示。

理解这一点非常重要,因为 Transformer 里面大量计算,本质都是张量和矩阵乘法。

最基础的规则就是:

text 复制代码
[A, B] × [B, C]
=
[A, C]

例如:

text 复制代码
[512, 4096]
×
[4096, 8192]

=

[512, 8192]

这条简单的规则,后面会直接连接到 QKV 和 Tensor Parallel。


二、一层 Transformer 到底做了什么?

先暂时忽略各种实现差异,一层 Transformer 可以粗略理解成:

text 复制代码
                X
                ↓
              Norm
                ↓
            Attention
                ↓
             Residual
                ↓
              Norm
                ↓
               FFN
                ↓
             Residual

这里最重要的部分首先是 Attention。

输入 X 会分别经过三套训练好的权重矩阵:

text 复制代码
Q = X × Wq
K = X × Wk
V = X × Wv

一定要分清两类东西:

text 复制代码
Wq / Wk / Wv
= 模型训练好的权重
= 模型加载以后基本常驻显存

Q / K / V
= 请求运行时根据 X 计算出来的中间结果
= Activation

这也是理解 GPU 显存结构的第一步:

Weight 和 Activation 不是一回事。


三、Q、K、V 到底在做什么?

数学公式可以以后再深入,但需要我们先建立一个足够准确的直觉:

text 复制代码
Q:我现在想找什么?

K:我有什么特征,可以被别人匹配?

V:如果你决定关注我,我提供什么信息?

因此 Attention 可以粗略拆成两步。

第一步:

text 复制代码
Q × K
   ↓
计算不同 token 之间应该关注多少

比如某个 token 对其他位置算出的注意力权重是:

text 复制代码
银行:60%
钱:  30%
其他:10%

第二步,再使用这些权重组合对应的 V:

text 复制代码
银行的 V × 60%
+
钱的 V × 30%
+
其他 V × 10%
        ↓
Attention 输出

所以一句话概括:

Q 和 K 负责决定"看谁、看多少",V 负责提供真正被融合的信息。

Attention 的意义也就很直观了:

让一个 token 有选择地吸收其他 token 的上下文信息。


四、Residual 和 FFN:一个负责保留,一个负责加工

Attention 算完以后,并不是直接把原来的 X 扔掉。

直觉上可以理解为:

text 复制代码
新的表示
=
原来的 X
+
Attention 子层产生的新信息

这就是 Residual Connection 的核心意义。

严格写法会因为 Pre-Norm、Post-Norm 等架构不同而有所区别,但理解时先记住:

模型不是每层重新创造一个表示,而是在已有表示上不断增加新的变化。

Attention 之后还有 FFN。

可以暂时把两者理解成:

text 复制代码
Attention
= token 和其他 token 交流

FFN
= 对当前 token 自己的表示进一步加工

FFN 内部同样存在大量训练好的权重矩阵。

所以一个模型所谓的几十亿参数,并不只是 Wq/Wk/Wv

它大致来自:

text 复制代码
Embedding
+
每一层 Attention 的 Wq/Wk/Wv/Wo
+
每一层 FFN 的权重
+
其他模型参数

而且每一层 Transformer 都有自己独立的一套参数。


五、为什么一定要有 KV Cache?

这是理解大模型推理性能最关键的知识点之一。

假设 Prompt 是:

text 复制代码
中国 的 首都 是

模型首先要处理整段 Prompt。

这个阶段叫:

Prefill

Prefill 会让这些 token 经过所有 Transformer Layer,并在每一层产生对应的 K 和 V。

这些 K/V 不会立刻丢掉,而会保存起来:

text 复制代码
KV Cache

接下来开始生成:

text 复制代码
北
京
...

这个阶段叫:

Decode

Decode 每次只新增少量 token。

新 token 会计算自己的:

text 复制代码
Q / K / V

然后:

text 复制代码
新的 Q
   ↓
和历史 K 做 Attention
   ↓
得到注意力权重
   ↓
读取并组合历史 V

所以历史 token 的 K/V 后面还要不断使用。

历史 Q 呢?

它已经完成了自己当时的"查询任务",后续新 token 并不需要重新使用旧 Q。

于是:

text 复制代码
历史 Q → 通常不需要长期保存

历史 K → 保存
历史 V → 保存

这就是为什么叫:

text 复制代码
KV Cache

而不是:

text 复制代码
QKV Cache

六、一个容易搞错的地方:KV Cache 不保存 token 两两关系

假设有 100 个历史 token。

KV Cache 保存的更接近:

text 复制代码
token1   → K1 + V1
token2   → K2 + V2
token3   → K3 + V3
...
token100 → K100 + V100

也就是大约 100 份 K/V。

不是:

text 复制代码
100 × 100

份 token 关系。

token 与 token 之间的 Attention Score 是运行 Attention 时根据 Q 和 K 动态计算的。

因此:

text 复制代码
KV Cache
= 保存 Attention 后面还要复用的原材料

Attention
= 动态计算这些原材料之间的关系

这一区分对后面计算显存非常重要。


七、KV Cache 为什么会成为显存大户?

一个简化但非常实用的公式是:

text 复制代码
KV Cache
≈
2
× Layers
× Token 数
× KV Heads
× Head Dim
× 每个元素字节数
× 并发序列数

最前面的 2 是:

text 复制代码
K + V

举一个具体例子:

text 复制代码
Layers = 32
KV Heads = 8
Head Dim = 128
dtype = BF16 = 2 Byte

一个 token 在一层中的 K/V:

text 复制代码
2 × 8 × 128 × 2
=
4096 Byte

1024 个 token:

text 复制代码
4096 × 1024
≈ 4 MB / Layer

32 层:

text 复制代码
4 MB × 32
≈ 128 MB

也就是说:

text 复制代码
一个请求
1024 token
≈ 128 MB KV Cache

如果同时有 8 个类似请求:

text 复制代码
128 MB × 8
≈ 1 GB

如果上下文从 1024 增长到 4096:

text 复制代码
KV Cache × 4

如果并发同时也从 1 增长到 8:

text 复制代码
再 × 8

所以部署大模型时有一条非常重要的经验:

上下文长度和并发数不是免费的,它们最终都会变成 KV Cache 显存。


八、GPU 显存到底被谁吃掉了?

做推理部署时,可以先把显存拆成三大块:

text 复制代码
GPU Memory
│
├── Weight
│
├── KV Cache
│
└── Activation / Runtime

Weight

模型训练完成后的参数:

text 复制代码
Wq / Wk / Wv / Wo
FFN weights
...

模型加载以后基本固定。

把上下文从 4K 改成 32K,不会让 Weight 变成 8 倍。

KV Cache

请求运行时产生。

它随着:

text 复制代码
上下文 ↑
并发 ↑

持续增长。

Activation / Runtime

例如:

text 复制代码
X
Q
Attention 中间结果
FFN 中间结果
临时 Buffer
Kernel / Runtime 开销

这部分也是动态的。

因此以后看到 OOM,不应该只问:

"这个模型多少 B?"

而应该问:

text 复制代码
Weight 占多少?
+
KV Cache 要多少?
+
Activation / Runtime 还需要多少?

九、量化真正压缩的是什么?

先看模型规模。

B 是 Billion:

text 复制代码
1B   ≈ 10 亿参数
1.5B ≈ 15 亿参数
7B   ≈ 70 亿参数
70B  ≈ 700 亿参数

再看不同数据类型的理论大小:

text 复制代码
FP32  = 4 Byte
FP16  = 2 Byte
BF16  = 2 Byte
INT8  = 1 Byte
INT4  = 0.5 Byte

因此一个 7B 模型,如果权重使用 BF16:

text 复制代码
7 × 10^9 × 2 Byte
≈ 14 GB

如果理论上把权重变成 INT4:

text 复制代码
7 × 10^9 × 0.5 Byte
≈ 3.5 GB

注意:

参数数量没有变。

还是 70 亿参数。

改变的是:

text 复制代码
每个参数需要多少 bit 来保存

因此更准确的说法是:

BF16 → INT4 后,理论 Weight 存储约缩小到四分之一。

而不是:

整个 GPU 显存一定缩小四倍。

因为 KV Cache、Activation、Runtime、scale、metadata,以及部分未量化参数都还存在。


十、浮点权重到底怎么变成 INT4?

量化不是简单地把:

text 复制代码
1.13

直接塞进 INT4。

一个最简化的对称量化思路是引入:

text 复制代码
scale

例如:

text 复制代码
原始权重 = 1.13
scale = 0.2

量化:

text 复制代码
1.13 ÷ 0.2
=
5.65

round(5.65)
=
6

保存整数:

text 复制代码
6

使用时近似恢复:

text 复制代码
6 × 0.2
=
1.2

于是:

text 复制代码
原始:1.13
恢复:1.20
误差:0.07

这里最重要的是:

text 复制代码

而不是:

text 复制代码
=

这就是 Quantization Error。

量化本质上是在做:

用更少、更粗的离散刻度去近似原来的高精度浮点值。

省下的是空间,付出的是近似误差。


十一、group_size 为什么会影响精度?

假设一组权重是:

text 复制代码
0.1
0.2
8.0
10.0

如果全部共享一个 scale,就很难同时照顾非常小和非常大的数值。

于是实际量化会经常进行分组。

例如:

text 复制代码
group_size = 128

表示:

每 128 个权重共用一组量化参数。

如果有 1024 个权重:

text 复制代码
1024 ÷ 128
=
8 组

如果:

text 复制代码
group_size = 64

则:

text 复制代码
1024 ÷ 64
=
16 组

所以一定不要把 group_size 理解成"有多少组"。

它表示:

每组里面放多少个权重。

通常:

text 复制代码
group_size ↓
→ 分组数量 ↑
→ scale 数量 ↑
→ 每组描述更精细
→ 量化误差通常 ↓

但代价是:

text 复制代码
scale / metadata ↑
存储和访存开销 ↑
实现复杂度 ↑

因此量化从来不是单纯追求最小 group,而是在精度、存储和性能之间取平衡。


十二、AWQ:观察 Activation,量化 Weight

普通 INT4 最大的问题之一,是容易把所有权重"一视同仁"。

但现实是:

两个权重即使都产生 0.1 的量化误差,对模型输出造成的影响也可能完全不同。

AWQ 的核心思想可以先压缩成:

text 复制代码
准备 Calibration 数据
        ↓
运行原模型
        ↓
观察 Activation
        ↓
判断哪些通道 / 权重更重要
        ↓
量化时重点保护

所以:

text 复制代码
AWQ
=
Activation-aware Weight Quantization

最容易误解的一点是:

Activation-aware 不代表把 Activation 量化成 INT4。

例如常见的:

text 复制代码
W4A16

表示:

text 复制代码
Weight     → 4 bit
Activation → 16 bit

AWQ 是:

观察 Activation,帮助自己更聪明地量化 Weight。

因此 Calibration 数据也不能随便选。

如果未来模型主要用于中文聊天,却全部拿 Python 代码做 calibration,那么观察到的 Activation 分布可能并不能很好代表真实业务。

原则很简单:

Calibration 数据应该尽量接近模型未来真正面对的数据分布。


十三、GPTQ:关注量化以后"输出错了多少"

GPTQ 可以从另一个角度理解。

普通量化容易只关心:

text 复制代码
原权重 = 1.20
量化后 = 1.18

误差只有 0.02

看起来似乎无所谓。

但如果这 0.02 最终导致某一层输出:

text 复制代码
10.0 → 7.0

那它显然不能被认为"不重要"。

所以 GPTQ 更关注:

权重量化以后,对这一层输出到底造成了多大影响。

它利用近似二阶信息,在量化过程中决定如何量化并补偿误差,目标是尽可能降低层输出误差。

第一阶段其实不用急着钻 Hessian。

先把 AWQ 和 GPTQ 分成:

text 复制代码
AWQ:
看 Activation
→ 找重要部分
→ 提前重点保护

GPTQ:
看量化对层输出造成的影响
→ 在量化过程中降低 / 补偿误差

这个理解已经足够支撑后续工程实践。


十四、TP 和 PP:多张 GPU 到底怎么一起跑模型?

当模型或者单层矩阵在一张 GPU 上放不下,就需要分布式。

最先要分清:

text 复制代码
TP:切一层内部

PP:切 Transformer Layer

Tensor Parallel

假设:

text 复制代码
X = [1024, 4096]

W = [4096, 8192]

单卡计算:

text 复制代码
X × W
=
[1024, 8192]

如果 TP=4,可以沿某个维度把 W 切成:

text 复制代码
GPU0:[4096, 2048]
GPU1:[4096, 2048]
GPU2:[4096, 2048]
GPU3:[4096, 2048]

每张卡分别计算:

text 复制代码
[1024,4096]
×
[4096,2048]

=

[1024,2048]

四份结果再根据并行方式组合。

因此 TP 不只是:

把权重分开放。

而是:

把同一层的权重和对应计算一起切给多张 GPU。

继续深入以后,还要理解 Column Parallel、Row Parallel、AllReduce、AllGather,以及 Activation 在不同阶段是完整还是分片状态。

但 TP 的核心起点就是这一句:

切同一层的大矩阵。

Pipeline Parallel

PP 更直观。

例如 32 层 Transformer:

text 复制代码
GPU0:
Layer 1 ~ 16

GPU1:
Layer 17 ~ 32

数据跑完 GPU0 的前半段,再把 Activation 交给 GPU1 继续计算。

所以:

text 复制代码
TP → 横着切一层

PP → 纵着切模型层

十五、到这里再看 vLLM,很多参数终于有意义了

例如:

text 复制代码
--tensor-parallel-size
--max-model-len
--max-num-seqs
--gpu-memory-utilization

它们已经不是几个孤立参数。

可以直接映射:

text 复制代码
tensor-parallel-size
→ 一层模型计算如何分到多张 GPU

max-model-len
→ 单条序列允许达到的最大长度
→ 影响潜在 KV Cache 需求

max-num-seqs
→ 单轮可处理 sequence 数量的重要上限之一

gpu-memory-utilization
→ vLLM 可用于模型执行和 KV Cache 等用途的显存预算相关配置

而 vLLM 真正有意思的地方,是它如何高效管理大量请求。

当前官方文档仍将 PagedAttention、Continuous Batching、Chunked Prefill 等列为 vLLM 的核心能力。


十六、Paged KV Cache:不是把 KV 变小,而是把显存管得更好

假设:

text 复制代码
max_model_len = 8192

某个请求实际只用了 1200 token。

如果一开始就必须给它申请完整、连续的 8192-token KV 空间,会造成大量浪费。

Paged KV Cache 的思路类似虚拟内存分页:

text 复制代码
KV Cache
↓
拆成固定大小的 Block

一个请求逻辑上可能是:

text 复制代码
逻辑 Block 0
逻辑 Block 1
逻辑 Block 2

但实际映射到:

text 复制代码
物理 Block 7
物理 Block 19
物理 Block 25

所以:

逻辑连续,但物理显存不要求连续。

当前 vLLM 的 Paged Attention 设计依旧以分块的 paged KV cache 为基础,K/V Cache 被组织在独立的 block 中。

因此一个请求继续生成时:

text 复制代码
最后一个 Block 写满
       ↓
再申请新的空闲 Block
       ↓
更新映射
       ↓
继续写 K/V

不需要搬动前面所有 KV 去重新寻找一整块连续空间。

Paged KV Cache 解决的核心问题是:

KV Cache 的显存分配与碎片管理。

不是量化。


十七、Continuous Batching:Batch 不再是一批人一起上、一起下

传统固定 Batch 很容易出现:

text 复制代码
A:还要生成 100 token
B:已经结束
C:还要生成 50 token

但新请求 D 还要等整个 Batch 结束。

Continuous Batching 的思路是:

text 复制代码
B 结束
↓
位置释放
↓
D 可以进入后续调度

因此 Batch 的组成会随着推理轮次不断改变。

官方文档也明确将 Continuous Batching 作为提高 GPU 利用率的重要机制。

可以把它理解成:

Batch 是动态流动的,不是一辆发车以后必须所有乘客同时到终点的大巴。


十八、Scheduler:真正决定"这一轮谁上 GPU"

Paged KV Cache 管:

text 复制代码
KV 显存怎么分

Continuous Batching 管:

text 复制代码
请求怎么动态进出

而 Scheduler 管:

text 复制代码
这一轮到底让谁计算

当前 vLLM 的 Engine Core 会持续运行调度循环,同时管理 KV Cache,并把工作下发给 GPU Worker。

调度时不仅有"多少个请求"的概念,还有:

text 复制代码
Token Budget

例如:

text 复制代码
max_num_batched_tokens = 2000

表示一轮能够处理的 token 数量存在预算约束。

同时:

text 复制代码
max_num_seqs

限制单轮最多处理多少条 sequence。当前官方 Scheduler 配置仍明确区分这两个维度。

可以理解成:

text 复制代码
max_num_seqs
= 人数上限

max_num_batched_tokens
= 本轮总工作量上限

十九、Chunked Prefill:为什么长 Prompt 不一定一次吃完?

假设:

text 复制代码
token_budget = 2000

当前三个 Decode 请求分别需要:

text 复制代码
A = 1 token
B = 1 token
C = 1 token

那么还剩:

text 复制代码
2000 - 3
=
1997

这时来了一个:

text 复制代码
5000-token Prompt

它不一定非要一口气处理完 5000 token。

可以:

text 复制代码
本轮 Prefill 1997
下一轮继续 Prefill
...

这就是 Chunked Prefill。

当前 vLLM 的 Scheduler 配置明确支持根据剩余 max_num_batched_tokens 将 Prefill 分块。

它的意义不仅是"Prompt 太长,预算放不下"。

更重要的是:

避免一个巨大的 Prefill 长时间霸占 GPU,让正在 Decode 的请求迟迟拿不到下一个 token。

vLLM 官方调优文档也明确说明,调整 max_num_batched_tokens 会在 TTFT、ITL 和吞吐之间产生权衡。


二十、为什么 Prefill 和 Decode 的性能特征不一样?

这是理解推理性能的另一个关键点。

Prefill

例如:

text 复制代码
X = [1000, H]

同一份 Weight 可以一次服务大量 token:

text 复制代码
1000 个 token
×
同一套 W

因此 GPU 可以做大量并行矩阵计算。

Prefill 通常更容易表现出明显的计算密集特征。

Decode

Decode 时可能只有:

text 复制代码
X = [1, H]

当前只新增一个 token。

但是它仍然需要:

text 复制代码
读取大量模型 Weight
+
读取历史 KV Cache

然后才生成这一个新 token。

所以可以粗略记成:

text 复制代码
Prefill:
搬数据以后可以做很多计算

Decode:
为了算少量新 token,
仍然需要读取大量 Weight 和历史 KV

因此 Decode 往往更容易受到显存带宽影响。

这也解释了为什么多个 Decode 请求组成 Batch 后,总吞吐可能反而提高。

例如 32 个 Decode 请求:

text 复制代码
A → 1 token
B → 1 token
...
32 个请求

可以共同组成:

text 复制代码
X ≈ [32, H]

同一套模型 Weight 可以在这一轮同时服务更多 token。

所以:

text 复制代码
并发提高
→ Batch 变大
→ Weight 读取成本被更多 token 分摊
→ GPU 利用率提高
→ 总吞吐通常提高

但要注意:

总吞吐提高,不等于单个请求一定更快。

并发继续升高以后,排队、资源竞争和长上下文 Attention 成本都会开始影响单请求延迟。


二十一、不同请求可以 Batch,但不能乱用彼此的 KV Cache

假设:

text 复制代码
A:
"北京天气怎么样?"

B:
"帮我写一个 Python 函数"

两条请求可以:

text 复制代码
一起组成 Decode Batch

因为它们共同使用同一套模型 Weight。

但它们不能共享彼此的上下文:

text 复制代码
A 当前 Q → A 自己的历史 KV
B 当前 Q → B 自己的历史 KV

所以真正共享的是:

text 复制代码
模型 Weight
+
批量计算机会

而不是:

text 复制代码
请求上下文

一句话:

Batching 合并的是计算,不是把不同用户的语义上下文混在一起。


二十二、为什么同样 Decode 一个 token,8K 上下文比 500 token 更贵?

假设:

text 复制代码
A 历史长度 = 8000
B 历史长度 = 500

这一轮都只生成 1 个新 token。

两者当前 token 都需要经过模型权重。

但 Attention 不一样:

text 复制代码
A:
新的 Q
↓
匹配约 8000 个历史 K
↓
读取并组合约 8000 个历史 V

B:

text 复制代码
新的 Q
↓
匹配约 500 个历史 K
↓
读取并组合约 500 个历史 V

因此即使都只叫:

text 复制代码
Decode 1 token

它们实际计算成本也不是完全一样的。

这也是为什么:

长上下文影响的不只是 KV Cache 容量,也会增加 Decode 阶段 Attention 对历史 KV 的读取和计算成本。


二十三、把整条推理链重新串一次

现在可以得到一张非常有用的脑图:

text 复制代码
用户文本
   ↓
Tokenizer
   ↓
Token IDs
   ↓
Embedding
   ↓
X [B,S,H]
   ↓
┌───────────────────────────────┐
│ Transformer Layer             │
│                               │
│ X                             │
│ ├─ ×Wq → Q                    │
│ ├─ ×Wk → K ──→ KV Cache       │
│ └─ ×Wv → V ──→ KV Cache       │
│                               │
│ Q × K                         │
│ ↓                             │
│ Attention Weight              │
│ ↓                             │
│ 加权组合 V                    │
│ ↓                             │
│ Attention Output              │
│ ↓                             │
│ Residual                      │
│ ↓                             │
│ FFN                           │
│ ↓                             │
│ Residual                      │
└───────────────────────────────┘
        × N Layers
           ↓
       Next Token

模型部署到 GPU 后:

text 复制代码
显存
│
├── Weight
│
├── KV Cache
└── Activation / Runtime

如果显存不够:

text 复制代码
Weight 太大
→ 可以考虑量化 / TP / PP

KV Cache 太大
→ 看上下文、并发、KV dtype、调度策略

Activation / Runtime 太大
→ 看 Prefill、Batch、执行引擎等

如果做量化:

text 复制代码
BF16 Weight
↓
INT8 / INT4
↓
Weight 显存下降

再继续考虑:

text 复制代码
scale
group_size
quantization error
AWQ
GPTQ

如果进入多卡:

text 复制代码
TP
→ 切一层内部的大矩阵和计算

PP
→ 切 Transformer Layer

最后进入 vLLM:

text 复制代码
Paged KV Cache
→ 管 KV 显存

Continuous Batching
→ 动态组织请求

Scheduler
→ 决定本轮谁运行

Chunked Prefill
→ 控制长 Prompt 如何分轮处理

至此,大模型推理部署里最常见的几个概念就真正连到了一起。


最后:不要把量化理解成一个孤立工具

回头看会发现:

text 复制代码
AWQ
GPTQ
TP
PP
KV Cache
PagedAttention
Continuous Batching

看起来是七八个不同概念,但它们实际上都在回答一个问题:

如何让一个已经训练好的大模型,更高效地在有限 GPU 资源上完成推理?

量化解决的是:

text 复制代码
Weight 太大

KV Cache 管理解决的是:

text 复制代码
请求上下文太占显存

TP / PP 解决的是:

text 复制代码
单卡资源不够

Batching 和 Scheduler 解决的是:

text 复制代码
GPU 怎么同时服务更多请求

真正理解这些底层关系以后,再去看:

text 复制代码
--tensor-parallel-size
--max-model-len
--max-num-seqs
--max-num-batched-tokens
--gpu-memory-utilization

就不会再把它们当作"网上别人这么配,所以我也这么配"的神秘参数。

下一阶段真正值得继续深入的,是三个方向:

text 复制代码
① TP 的 Column/Row Parallel 与通信
② vLLM Scheduler / KV Cache Manager 的源码实现
③ AWQ/GPTQ 实际量化 + Benchmark

最后用真实模型做一次完整闭环:

text 复制代码
原始 BF16 模型
        ↓
记录显存和性能 Baseline
        ↓
AWQ / GPTQ
        ↓
INT4 模型
        ↓
vLLM 部署
        ↓
Benchmark
        ↓
对比:
显存
吞吐
TTFT
TPOT / ITL
精度
最大并发

做到这里,模型压缩就不再是"会跑一个脚本"。

而是已经能够解释:

它为什么省显存、为什么可能掉精度、为什么吞吐可能变化、为什么上下文和并发会影响容量,以及这些变化最终如何映射到实际部署配置。

这才是我认为真正有价值的"大模型压缩与推理部署基础"。

相关推荐
随便做点啥9 小时前
32卡×4090 24GB,Qwen3.8-27B-FP8 集群部署报告
服务器·经验分享·docker·vllm
进军的码农2 天前
DeepSeek-V4-Pro 正式版本地部署:联想 ThinkStation P4 硬件架构拆解与推理链路全验证
vllm·deepseek·ai推理·大模型本地部署·联想工作站·thinkstation p4
johnny2332 天前
vLLM理论及实战入门
vllm
我有2只猫3 天前
vLLM Docker 本地部署小模型
docker·容器·vllm
ZJU_统一阿萨姆3 天前
【推理优化进阶】调度器的数学内核:排队论、SLO 与在线决策
开发语言·人工智能·语言模型·系统架构·vllm
Albart5753 天前
vLLM多卡部署终极踩坑:CUDA error worker进程异常退出 完整定位&生产根治方案
cuda·nccl·vllm·大模型部署·多卡推理·大模型踩坑
ZJU_统一阿萨姆4 天前
【推理优化进阶】性能实验科学:工作负载、统计显著性与尾延迟归因
开发语言·人工智能·语言模型·系统架构·vllm
JAI科研5 天前
Deepseek Agent Harness教程(二) | DeepSeek Harness 设计思路
人工智能·深度学习·算法·机器学习·自然语言处理·transformer·vllm
码云骑士5 天前
108-vLLM推理引擎-PagedAttention-连续批处理-吞吐提升10倍
python·vllm