news 2026/8/31 5:21:08

vLLM:借助分页注意力实现简单、快速且低成本的大语言模型服务

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
vLLM:借助分页注意力实现简单、快速且低成本的大语言模型服务

1、前期知识储备

1.1、什么是自回归解码过程

大语言模型的输出本质上是在计算下一个词出现的概率,,而这个词是来自模型自带的词典,确切的说是token词典。

自回归解码是大语言模型(LLM)在推理阶段(生成回答时)生成文本的核心机制。用一句话概括:把模型自己的上一个输出,当作下一个输入,像接龙一样一个字(或一个Token)一个字地往外蹦,直到说完为止。

1. 数学本质(条件概率)

在数学上,模型生成整句话的概率被分解为每个位置的条件概率乘积:

P(今日, 天气, 真, 好) = P(今日) × P(天气 | 今日) × P(真 | 今日, 天气) × P(好 | 今日, 天气, 真)

这意味着,当模型要生成第 NN 个词时,它必须看过前 N−1N−1 个词。它永远只预测下一个最可能的词,而不是一次性构思好整个段落。

2. 具体的运行流程(步步拆解)

假设用户输入:“请推荐一本书。”
模型内部执行以下循环(Loop):

  1. 初始化:将用户提示词“请推荐一本书”编码为向量矩阵,作为初始上下文。

  2. 第1步(预测):模型根据当前上下文,计算词库中每个词的概率分布,选出概率最高的词,比如“《”

  3. 拼接(关键):将“《”追加到原文后,形成新上下文:“请推荐一本书《”。

  4. 第2步(预测):模型基于新上下文,预测下一个词,比如“三”

  5. 循环往复:继续生成“体”、“》”、“是”、“一”、“本”……直到模型生成一个特殊的结束标记(EOS,End of Sequence),或者达到设定的最大输出长度,循环停止。

3. 为什么叫“自”回归(Auto-regressive)?

  • 自(Auto):指的是模型用自己生成的输出,作为下一步的输入数据。

  • 回归(Regressive):在统计学中,回归指用历史数据预测未来数据。在这里,模型利用历史生成的Token序列,回归预测当前时刻的下一个Token。

4. 如何决定“选哪个词”?——解码策略

模型每次预测都会给几万个词打分,但不一定每次都选最高分(否则会陷入重复和死板)。这就引入了不同的解码策略,属于“自回归”过程中的关键变量:

  • 贪婪解码(Greedy):每次都选最高概率的词(最快,但容易陷入平庸)。

  • 随机采样(Sampling):按概率分布随机抽奖,增加多样性(配合Temperature(温度系数)控制随机性大小)。

  • Top-K / Top-P(核采样):只从概率最高的前K个或前P%的候选词中采样,既保证连贯性,又避免废话。

5. 自回归的致命痛点与优化

自回归过程是串行的(必须等第 NN 个词算完,才能算第 N+1N+1 个词),这导致:

  • 痛点:生成速度慢(受限于内存带宽),且误差会累积——如果前面写错了一个字,后面会“将错就错”地圆下去(产生幻觉)。

  • 优化变体:为了解决慢的问题,业界提出了投机采样(Speculative Decoding),即用一个小模型快速“草稿”出多个词,再由大模型一次性并行验证,但这依然属于“逻辑上的自回归”,只是工程上的提速。

1.2、什么是张量

[在自回归解码过程中,输入给大语言模型的所有词元都会生成注意力键和值张量,这些张量会被保存在GPU内存中,用于生成下一个词元]

1. 从维度上看(由浅入深)

在编程和数学中,张量的“维数(Rank)”决定了它的样子:

  • 0维张量(标量):就是一个单独的数字,比如5

  • 1维张量(向量):一列数字,比如[1, 2, 3, 4](这就像一个一维表格)。

  • 2维张量(矩阵):一个平面的表格,有行有列,比如 Excel 里的[[1,2],[3,4]]

  • 3维张量(立方体):像一堆表格叠在一起,比如一张彩色图片(RGB三通道,长 x 宽 x 颜色)。

  • 4维及以上:就是更多维度的数据堆叠(比如视频:批量大小 x 通道数 x 高度 x 宽度)。

在大模型里,所有数据——无论是你的“提示词”、模型的“字典编号”,还是你提到的“键和值张量”——都必须转化成这种数字容器,GPU 才能进行数学运算。


2. 回到问题:LLM 中的“键(Key)”和“值(Value)”张量长什么样?

在你提到的自回归解码中,每一层注意力机制都会为输入的每个词元生成K 张量V 张量。它们通常是3维或4维的高维数组,大致结构是:

(批次大小, 注意力头数, 序列长度, 每个头的维度)

举个例子,假设模型有 32 个注意力头,当前已生成 100 个词元,每个头的维度是 128:
那么这一个 K 张量里就包含了32 × 100 × 128 = 409,600个浮点数。这些数字密密麻麻地排列在 GPU 的显存(VRAM)里,等待做矩阵乘法。


3. 为什么要用“张量”这么复杂的东西?(物理意义)

你可能会有疑问:为什么不干脆叫“矩阵”或“数组”?

  • 因为“张量”自带“变换法则”。在深度学习框架(如 PyTorch、TensorFlow)中,张量不仅存数字,还自动记录了每个数字的数据类型(精度)设备位置(CPU/GPU),以及最重要的——计算图梯度(训练时用来反向传播)。

  • 在推理(解码)时,你提到的 K 和 V 张量之所以必须保存在 GPU 内存中,正是因为它们是高维稠密数据。GPU 拥有数千个计算核心,专为并行处理这种“大块头”的张量矩阵乘法而设计。CPU 处理这种数据会慢几十上百倍。


4. 直观类比:如果“文本”是盖楼,“张量”就是钢筋水泥

  • 文本(“我喜欢你”)是抽象的符号。

  • 词表映射:把“我”转为编号234,这是把概念变成数字。

  • 张量(Embedding + K/V):模型并不把“234”当作普通数字,而是把它“撑开”成一个几百维的向量(1维张量),比如[0.12, -0.98, 0.45, ...]

  • 自回归保存:当生成下一个字时,模型把前面所有字对应的 K/V 高维张量堆叠在一起(变成一个巨大的 3D 张量),一次性扔进 GPU 的矩阵乘法器里,算出当前注意力分数。


总结一句大白话:

张量就是大模型世界里通用的“乐高积木”。文本、图片、音频、甚至模型内部的记忆(K/V缓存),都被统一抽象成这种带维度的数字积木。GPU 的职责就是疯狂地、并行地把这些积木拿来做乘法加法(矩阵运算),从而算出下一个词的概率。

1.3、KV Cache 机制

KV Cache 之所以成为显存“大户”,根源在于“必要”且“巨大”。它的存在是为了避免重复计算、加速推理,但其规模会随模型和任务呈线性乃至爆发式增长。而 PagedAttention 则像一位高效的内存管家,通过引入操作系统的分页技术,从根本上解决了 KV Cache 带来的显存浪费和碎片化问题。

KV Cache 之所以成为显存“大户”,根源在于“必要”且“巨大”。它的存在是为了避免重复计算、加速推理,但其规模会随模型和任务呈线性乃至爆发式增长。而 PagedAttention 则像一位高效的内存管家,通过引入操作系统的分页技术,从根本上解决了 KV Cache 带来的显存浪费和碎片化问题。


为什么 KV Cache 会占用大量显存?

1. 它为什么是“必要”的?

在自回归解码中,生成每个新 Token 时,都需要计算它和之前所有 Token的注意力。如果不加缓存,每生成一个新字,都要把前面所有的 K 和 V 矩阵重新算一遍,这会产生天文数字般的重复计算。

KV Cache 的核心价值就是“以空间换时间”:把之前算好的 K 和 V 矩阵存起来,之后生成新 Token 时直接复用,避免了重复计算。这虽然省下了计算时间,却占用了宝贵的显存空间。

2. 它究竟有多大?(用公式说话)

KV Cache 的大小并非固定,它与Batch Size序列长度以及模型本身的结构直接相关。

单个 Token 的 KV Cache 计算公式

单个 Token 的 KV Cache (字节) =2(K和V两组) ×层数×注意力头数×每个头的维度×数据类型字节数

然后用它乘以总 Token 数,就能得到最终占用:

总 KV Cache 大小 = 单个 Token 的 KV Cache × Batch Size × 序列长度

举个例子,感受一下“巨大”
以 Llama 2 7B 模型为例,它的配置大约是 32 层,每层有 32 个注意力头,每个头维度是 128。当使用 16 位浮点数(2字节)时:

  • 一个 Token 的 KV Cache 就是:2 × 32 × 32 × 128 × 2 = 524,288字节,约0.5 MB

  • 这看起来不大,但如果批次大小(Batch Size)为 16,且要处理长度为 4096 的序列,那么总大小就是:0.5 MB × 16 × 4096 = 32 GB

  • 在极端情况下(长文本、大Batch),KV Cache 与模型权重的显存占比甚至可能达到9:1。这就好比一个 40GB 显存的 A100 显卡,模型权重只占一小部分,而 KV Cache 可能吃掉40% 甚至更多的显存。

3. 为什么“浪费”和“碎片”问题严重?

除了体量大,传统的内存分配方式(为每个请求预先分配一块连续的最大可能空间)还存在两个严重问题:

  • 巨大的内部浪费(Internal Fragmentation):系统按最大可能长度(如 4096 Token)为每个请求预留显存。但实际对话可能很短,导致预留但未使用的“内部碎片”大量浪费。研究表明,这种浪费可高达40% 到 60%

  • 严重的外部碎片(External Fragmentation):不同请求的序列长度各异,它们申请和释放显存的时间也不同,这会导致显存空间被分割成许多无法被有效利用的小块,即“外部碎片”。


PagedAttention 如何省显存?

PagedAttention 的核心思想很简单:借鉴操作系统的虚拟内存和分页机制,将 KV Cache 从“连续的大块”管理,转变为“非连续的小块”管理。

1. 核心机制:分页与动态映射
  • 切分为块(Block):将显存预先划分为固定大小的“块”(Block),每个块能存储固定数量(如 16 个)Token 的 KV 数据。

  • 按需分配:不再为整个请求预留连续空间。需要多少 Token,就动态分配多少个块来存储。

  • 逻辑到物理的映射:维护一个“块表”,记录每个请求“逻辑上连续”的 Token,实际存储在哪些“物理上可能不连续”的块中。

这样一来,每个请求的 KV Cache 就像一张散落在显存各处的拼图,通过块表拼凑起来,彻底解决了对“连续大块内存”的依赖。

2. 它带来了哪些具体收益?
  • 几乎消除内部浪费:预留空间的浪费被限制在最后一个数据块内,显存利用率从 40% 提升到 96% 以上

  • 解决外部碎片:所有块大小一致,分配和释放变得简单,从根本上杜绝了内存碎片化。

  • 支持内存共享:多个请求如果共享相同的提示词前缀(如系统提示词),可以共用同一块 KV Cache 数据,进一步节省显存。

3. 带来的性能飞跃

通过这些优化,PagedAttention 实现了巨大的性能提升:

  • 吞吐量提升:相比传统方法,vLLM 的吞吐量最高可提升 20-24 倍

  • 并发能力增强:在相同显存下,可将并发请求数提升 2-8 倍

  • 显存利用率GPU 显存的有效利用率提升了 40% 以上

总的来说,KV Cache 的显存占用是模型规模和并发需求的“硬成本”,而 PagedAttention 通过更智能的内存管理,将这笔“硬成本”的利用效率提升到了接近理想的状态。

1.4、分页注意力机制存储的块,,和普通序列有什么差异

1. 内存分配方式(最根本的差异)

  • 传统序列(预留式)“先占地,再入住”。只要用户发来请求,系统就根据设定的最大长度(比如2048个Token),在显存中一次性划出一块连续的、足够大的空地。不管用户最后只说了一句“你好”还是写了一篇论文,这块地都被占死了。

  • 分页块(按需式)“来多少人,租多大房”。系统不提前预留。模型每生成16个Token,就去显存的“空闲块池”里申请一个固定大小的块。生成到第50个Token,就申请4个块(48个Token用满,第4块只存2个)。

2. 物理空间的连续性

  • 传统序列:要求物理地址绝对连续。就像停车场必须给你连在一起的几个车位,中间不能有别人的车。这导致当显存碎片化时,明明总空闲很大,但因为找不到连续的大块,新请求会被硬生生拒绝(OOM,内存不足)。

  • 分页块物理地址完全允许不连续。逻辑上的第1、2、3块,在物理显存里可能分散在第100号、第5号、第888号块。连续性只存在于“块表”的逻辑索引中,不存在于物理硬件上。

3. 内存浪费(碎片率)

  • 传统序列巨大的内部浪费。假设最大长度2048,用户只说了10个词,那么剩下的2038个Token位置全是空的(但显存已被占用),利用率极低。业界统计,这种浪费通常在40% - 80%之间。

  • 分页块仅末尾微小的浪费。浪费仅存在于最后一个块中(比如块容量16,只存了2个Token,浪费了14个空位)。整体显存利用率可以飙升至96% 以上

4. 存储的数据结构(显存里长什么样)

  • 传统序列:在显存中是一块单一的、巨大的连续张量(Tensor)。读取时只需要一个起始地址指针,后续地址直接偏移即可。

  • 分页块:由“物理数据块” + “逻辑块表(Block Table)”组成。物理数据块里存着真正的K/V数值;逻辑块表则是一个数组(如[5, 100, 888]),专门记录这些散落块的实际物理编号。

5. 面对多请求(Batch)时的并发能力

  • 传统序列:为了减少碎片,系统通常会强制所有请求对齐到同一个最大长度。比如Batch里有3个短句和1个长句,为了合在一起计算,系统会拼命给短句填充空白(Padding),导致计算资源和显存被大量无效数据浪费。

  • 分页块:每个请求独立维护自己的块表,长短不一。GPU在计算注意力时,只需根据各自的块表去显存不同角落抓取真实数据,无需填充(Zero Padding),因此可以塞进比传统方法多2-8倍的并发请求。

分页块,,大小是固定的,数量动态生成的。

就是说第二个块没存满,后面来的数据会接着存。

所以除了最后一个块,其他的块都是存满的

2、原文地址

vLLM: Easy, Fast, and Cheap LLM Serving with PagedAttention | vLLM Blog

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/31 5:20:21

图解JavaScript原型链:从new到__proto__再到constructor

我以前一直觉得,原型链是JavaScript里最容易“背了又忘”的概念。面试前背一轮,new、prototype、__proto__、constructor来回抄十遍,可真到排查一个问题,比如“为什么我给某个对象挂了个方法,另外一处就莫名奇妙多出来…

作者头像 李华
网站建设 2026/8/31 5:19:44

CAESAR II管道应力分析实战教程:从建模到工况设置与结果排查

管道应力分析这件事,很多工程师是到了项目出图阶段才开始接触。前面管道布置、设备选型都完成了,然后应力分析工程师拿过模型说:这里热膨胀应力超了,那边支架载荷太大,需要调整走向。于是又是改图、又是补支架、又是跟…

作者头像 李华
网站建设 2026/8/31 5:15:41

基于YOLO的智能道路安全系统实战:从数据准备到部署全流程解析

简介:本资源是一套面向高校毕业设计与深度学习初学者的YOLO实战项目——智能道路安全系统,聚焦行人、车辆、交通标志等关键目标的实时检测与风险预警,解决城市交通管理中的安全隐患识别与辅助决策问题。压缩包共2000个文件,主体为…

作者头像 李华
网站建设 2026/8/31 5:11:25

从零构建行为评分服务:算法裁定善恶的工程实践与可解释性设计

“善恶报应,但由算法控制,你愿意吗?”这个标题看起来很科幻,像是某个短剧或者 AI 生成内容的一句话梗概。但稍微把镜头拉近一点,你会发现它并不是纯粹的脑洞:今天的信用评分、社区行为分、内容推荐权重、用…

作者头像 李华
网站建设 2026/8/31 5:09:22

OpenCV人脸检测入门:Python摄像头实时检测项目实战

在刚接触计算机视觉时,做一个小型摄像头人脸检测项目特别适合建立整体认知。它不会涉及复杂的模型训练,也能在较短时间内看到可视化效果,能给人最基本的目标检测概念。这个项目只有几十行代码,却能完整覆盖图像读取、灰度处理、目…

作者头像 李华