什么是键值缓存?让 LLM 闪电般快速

一、为什么 LLMs 需要 KV 缓存?

大语言模型(LLMs)的文本生成遵循 “自回归” 模式 —— 每次仅输出一个 token(如词语、字符或子词),再将该 token 与历史序列拼接,作为下一轮输入,直到生成完整文本。这种模式的核心计算成本集中在注意力机制上:每个 token 的输出都依赖于它与所有历史 token 的关联,而注意力机制的计算复杂度会随序列长度增长而急剧上升。

以生成一个长度为 n 的序列为例,若不做优化,每生成第 m 个 token 时,模型需要重新计算前 m 个 token 的 “查询(Q)、键(K)、值(V)” 矩阵,导致重复计算量随 m 的增长呈平方级增加(时间复杂度 O (n²))。当 n 达到数千(如长文本生成),这种重复计算会让推理速度变得极慢。KV 缓存(Key-Value Caching)正是为解决这一问题而生 —— 通过 “缓存” 历史计算的 K 和 V,避免重复计算,将推理效率提升数倍,成为 LLMs 实现实时交互的核心技术之一。

二、注意力机制:KV 缓存优化的 “靶心”

要理解 KV 缓存的作用,需先明确注意力机制的计算逻辑。在 Transformer 架构中,注意力机制的核心公式为:

\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V

其中:

  • Q(查询矩阵):维度为(n \times d_k),代表当前 token 对 “需要关注什么” 的查询;
  • K(键矩阵):维度为(n \times d_k),代表历史 token 的 “特征标识”;
  • V(值矩阵):维度为(n \times d_v),代表历史 token 的 “特征值”(通常d_v = d_k);
  • d_k是Q和K的维度(由模型维度d_{\text{model}}和注意力头数决定,如d_k = \frac{d_{\text{model}}}{\text{num\_heads}});
  • QK^T会生成一个(n \times n)的注意力分数矩阵,描述每个 token 与其他所有 token 的关联强度;
  • 经过 softmax 归一化后与V相乘,最终得到每个 token 的注意力输出(维度(n \times d_v))。

三、KV 缓存的核心原理:“记住” 历史,避免重复计算

自回归生成的痛点在于:每轮生成新 token 时,历史 token 的 K 和 V 会被重复计算。例如:

  • 生成第 3 个 token 时,输入序列是[t_1, t_2],已计算过t_1t_2K_1, K_2V_1, V_2
  • 生成第 4 个 token 时,输入序列变为[t_1, t_2, t_3],若不优化,模型会重新计算t_1, t_2, t_3的K和V—— 其中t_1, t_2的K、V与上一轮完全相同,属于无效重复。

KV 缓存的解决方案极其直接:

  1. 缓存历史 K 和 V:每生成一个新 token 后,将其K和V存入缓存,与历史缓存的K、V拼接;
  2. 仅计算新 token 的 K 和 V:下一轮生成时,无需重新计算所有 token 的K、V,只需为新 token 计算K_{\text{new}}V_{\text{new}},再与缓存拼接,直接用于注意力计算。

这一过程将每轮迭代的计算量从 “重新计算 n 个 token 的 K、V” 减少到 “计算 1 个新 token 的 K、V”,时间复杂度从O(n²)优化为接近O(n),尤其在生成长文本时,效率提升会非常显著。

四、代码实现:从 “无缓存” 到 “有缓存” 的对比

以下用 PyTorch 代码模拟单头注意力机制,直观展示 KV 缓存的作用(假设模型维度d_{\text{model}}=64d_k=64):

import torch
import torch.nn.functional as F# 1. 定义基础参数与注意力函数
d_model = 64  # 模型维度
d_k = d_model  # 单头注意力中Q、K的维度
batch_size = 1  # 批量大小def scaled_dot_product_attention(Q, K, V):"""计算缩放点积注意力"""# 步骤1:计算注意力分数 (n×d_k) @ (d_k×n) → (n×n)scores = torch.matmul(Q, K.transpose(-2, -1))  # 转置K的最后两维,实现矩阵乘法scores = scores / torch.sqrt(torch.tensor(d_k, dtype=torch.float32))  # 缩放# 步骤2:softmax归一化,得到注意力权重 (n×n)attn_weights = F.softmax(scores, dim=-1)  # 沿最后一维归一化# 步骤3:加权求和 (n×n) @ (n×d_k) → (n×d_k)output = torch.matmul(attn_weights, V)return output, attn_weights# 2. 模拟输入数据:历史序列与新token
# 历史序列(已生成3个token)的嵌入向量:shape=(batch_size, seq_len, d_model)
prev_embeds = torch.randn(batch_size, 3, d_model)  # 1×3×64
# 新生成的第4个token的嵌入向量:shape=(1, 1, 64)
new_embed = torch.randn(batch_size, 1, d_model)# 3. 模型中用于计算K、V的权重矩阵(假设已训练好)
Wk = torch.randn(d_model, d_k)  # 用于从嵌入向量映射到K:64×64
Wv = torch.randn(d_model, d_k)  # 用于从嵌入向量映射到V:64×64# 场景1:无KV缓存——重复计算所有token的K、V
full_embeds_no_cache = torch.cat([prev_embeds, new_embed], dim=1)  # 拼接为1×4×64
# 重新计算4个token的K和V(包含前3个的重复计算)
K_no_cache = torch.matmul(full_embeds_no_cache, Wk)  # 1×4×64(前3个与历史重复)
V_no_cache = torch.matmul(full_embeds_no_cache, Wv)  # 1×4×64(前3个与历史重复)
# 计算注意力(Q使用当前序列的嵌入向量,此处简化为与K相同)
output_no_cache, _ = scaled_dot_product_attention(K_no_cache, K_no_cache, V_no_cache)# 场景2:有KV缓存——仅计算新token的K、V,复用历史缓存
# 缓存前3个token的K、V(上一轮已计算,无需重复)
K_cache = torch.matmul(prev_embeds, Wk)  # 1×3×64(历史缓存)
V_cache = torch.matmul(prev_embeds, Wv)  # 1×3×64(历史缓存)# 仅计算新token的K、V
new_K = torch.matmul(new_embed, Wk)  # 1×1×64(新计算)
new_V = torch.matmul(new_embed, Wv)  # 1×1×64(新计算)# 拼接缓存与新K、V,得到完整的K、V矩阵(与无缓存时结果一致)
K_with_cache = torch.cat([K_cache, new_K], dim=1)  # 1×4×64
V_with_cache = torch.cat([V_cache, new_V], dim=1)  # 1×4×64# 计算注意力(结果与无缓存完全相同,但计算量减少)
output_with_cache, _ = scaled_dot_product_attention(K_with_cache, K_with_cache, V_with_cache)# 验证:两种方式的输出是否一致(误差在浮点精度范围内)
print(torch.allclose(output_no_cache, output_with_cache, atol=1e-6))  # 输出:True

代码中,“有缓存” 模式通过复用前 3 个 token 的 K、V,仅计算新 token 的 K、V,就得到了与 “无缓存” 模式完全一致的结果,但计算量减少了 3/4(对于 4 个 token 的序列)。当序列长度增至 1000,这种优化会让每轮迭代的计算量从 1000 次矩阵乘法减少到 1 次,效率提升极其显著。

五、权衡:内存与性能的平衡

KV 缓存虽能提升速度,但需面对 “内存占用随序列长度线性增长” 的问题:

  • 缓存的 K 和 V 矩阵维度为(n \times d_k),当序列长度 n 达到 10000,且d_k=64时,单头注意力的缓存大小约为10000 \times 64 \times 2(K 和 V 各一份)=1.28 \times 10^6个参数,若模型有 12 个注意力头,总缓存会增至约 150 万参数,对显存(尤其是 GPU)是不小的压力。

为解决这一问题,实际应用中会采用以下优化策略:

  • 滑动窗口缓存:仅保留最近的k个 token 的 K、V(如 k=2048),超过长度则丢弃最早的缓存,适用于对长距离依赖要求不高的场景;
  • 动态缓存管理:根据输入序列长度自动调整缓存策略,在短序列时全量缓存,长序列时启用滑动窗口;
  • 量化缓存:将 K、V 从 32 位浮点(float32)量化为 16 位(float16)或 8 位(int8),以牺牲少量精度换取内存节省,目前主流 LLMs(如 GPT-3、LLaMA)均采用此方案。

六、实际应用:KV 缓存如何支撑 LLMs 的实时交互?

在实际部署中,KV 缓存是 LLMs 实现 “秒级响应” 的关键。例如:

  • 聊天机器人(如 ChatGPT)生成每句话时,通过 KV 缓存避免重复计算历史对话的 K、V,让长对话仍能保持流畅响应;
  • 代码生成工具(如 GitHub Copilot)在补全长代码时,缓存已输入的代码 token 的 K、V,确保补全速度与输入长度无关;
  • 语音转文本实时生成(如实时字幕)中,KV 缓存能让模型随语音输入逐词生成文本,延迟控制在数百毫秒内。

可以说,没有 KV 缓存,当前 LLMs 的 “实时交互” 体验几乎无法实现 —— 它是平衡模型性能与推理效率的 “隐形支柱”。

总结

KV 缓存通过复用历史 token 的 K 和 V 矩阵,从根本上解决了 LLMs 自回归生成中的重复计算问题,将时间复杂度从O(n²)优化为接近O(n)。其核心逻辑简单却高效:“记住已经算过的,只算新的”。尽管需要在内存与性能间做权衡,但通过滑动窗口、量化等策略,KV 缓存已成为现代 LLMs 推理不可或缺的技术,支撑着从聊天机器人到代码生成的各类实时交互场景。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/92147.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/92147.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

16.Home-懒加载指令优化

问题1:逻辑书写位置不合理问题2:重复监听问题已经加载完毕但是还在监听

Day116 若依融合mqtt

MQTT 1.MQTT协议概述MQTT是一种基于发布/订阅模式的轻量级消息传输协议,设计用于低带宽、高延迟或不稳定的网络环境,广泛应用于物联网领域1.1 MQTT协议的应用场景1.智能家居、车联网、工业物联网:MQTT可以用于连接各种家电设备和传感器&#…

PyTorch + PaddlePaddle 语音识别

PyTorch PaddlePaddle 语音识别 目录 概述环境配置基础理论数据预处理模型架构设计完整实现案例模型训练与评估推理与部署性能优化技巧总结 语音识别(ASR, Automatic Speech Recognition)是将音频信号转换为文本的技术。结合PyTorch和PaddlePaddle的…

施耐德 Easy Altivar ATV310 变频器:高效电机控制的理想选择(含快速调试步骤及常见故障代码)

施耐德 Easy Altivar ATV310 变频器:高效电机控制的理想选择(含快速调试步骤)在工业自动化领域,变频器作为电机控制的核心设备,其性能与可靠性直接影响整个生产系统的效率。施耐德电气推出的 Easy Altivar ATV310 变频…

搭建邮件服务器概述

一、电子邮件应用解析标准邮件服务器(qq邮箱):1)提供电子邮箱(lvbuqq.com)及存储空间2)为客户端向外发送邮件给其他邮箱(diaochan163.com)3)接收/投递其他邮箱…

day28-NFS

1.每日复盘与今日内容1.1复盘Rsync:本地模式、远程模式🍟🍟🍟🍟🍟、远程守护模式🍟🍟🍟🍟🍟安装、配置Rsync启动、测试服务备份案例1.2今日内容NFS优缺点NFS服…

二叉搜索树--通往高阶数据结构的基石

目录 前言: 1、二叉搜索树的概念 2、二叉搜索树性能分析 3、二叉搜索树的实现 BinarySelectTree.h test.cpp 4、key 和 key / value( map 和 set 的铺垫 ) 前言: 又回到数据结构了,这次我们将要学习一些复杂的…

Profinet转Ethernet IP网关接入五轴车床上下料机械手控制系统的配置实例

本案例为西门子1200PLC借助PROFINET转EtherNet/IP网关与搬运机器人进行连接的配置案例。所需设备包括:西门子1200PLC、Profinet转EtherNet/IP网关以及发那科(Fanuc)机器人。开启在工业自动化控制领域广泛应用、功能强大且专业的西门子博图配置…

专题二_滑动窗口_长度最小的子数组

引入:滑动窗口首先,这是滑动窗口的第一道题,所以简短的说一下滑动窗口的思路:当我们题目要求找一个满足要求的区间的时候,且这个区间的left和right指针,都只需要同向移动的时候,就可以使用滑动窗…

解锁高效开发:AWS 前端 Web 与移动应用解决方案详解

告别繁杂的部署与运维,AWS 让前端开发者的精力真正聚焦于创造卓越用户体验。在当今快速迭代的数字环境中,Web 与移动应用已成为企业与用户交互的核心。然而,前端开发者常常面临诸多挑战:用户认证的复杂性、后端 API 的集成难题、跨…

北京JAVA基础面试30天打卡04

1. 单例模式的实现方式及线程安全 单例模式(Singleton Pattern)确保一个类只有一个实例,并提供一个全局访问点。以下是常见的单例模式实现方式,以及如何保证线程安全: 单例模式的实现方式饿汉式(Eager Init…

Redis 缓存三大核心问题:穿透、击穿与雪崩的深度解析

引言在现代互联网架构中,缓存是提升系统性能、降低数据库压力的核心手段之一。而 Redis 作为高性能的内存数据库,凭借其丰富的数据结构、灵活的配置选项以及高效的网络模型,已经成为缓存领域的首选工具。本文将从 Redis 的基本原理出发&#…

耘瞳科技国产化点云处理软件,开启智能化三维测量新时代

在现代工业制造领域,三维点云数据已成为推动生产效率提升、质量控制优化以及智能制造转型的关键技术之一。三维点云数据能够提供高精度的物体表面信息,广泛应用于制造零件的质量检测;通过点云数据与CAD模型的对比分析,可以快速检测…

RabbitMQ面试精讲 Day 8:死信队列与延迟队列实现

【RabbitMQ面试精讲 Day 8】死信队列与延迟队列实现 文章标签 RabbitMQ,消息队列,死信队列,延迟队列,面试技巧,分布式系统 文章简述 本文是"RabbitMQ面试精讲"系列第8天,深入讲解死信队列与延迟队列的实现原理与实战应用。文章详细解析死信队列的触发…

团结引擎 1.5.0 版本发布:Android App View 功能详解

核心亮点 原生安卓应用支持 2D & 3D 双形态呈现 编辑器全流程集成 灵活调控功能 多应用并行展示 智能座舱应用示例 快速入门指南 开发说明 功能支持 实验性功能 资源链接 团结引擎 1.5.0 版本已于 4 月 14 日正式上线。本次更新中,车机版引入了一项突…

基于SpringBoot的OA办公系统的设计与实现

文章目录前言详细视频演示具体实现截图后端框架SpringBoot持久层框架MyBaits成功系统案例:代码参考数据库源码获取前言 博主介绍:CSDN特邀作者、985高校计算机专业毕业、现任某互联网大厂高级全栈开发工程师、Gitee/掘金/华为云/阿里云/GitHub等平台持续输出高质量…

知识随记-----用 Qt 打造优雅的密码输入框:添加右侧眼睛图标切换显示

Qt 技巧:通过 QLineEdit 右侧眼睛图标实现密码可见性切换 文章目录Qt 技巧:通过 QLineEdit 右侧眼睛图标实现密码可见性切换概要整体架构流程技术名词解释技术细节实现效果展示概要 本文介绍如何使用 Qt 框架为 QLineEdit 控件添加一个右侧的眼睛图标&a…

Unity里的对象旋转数值跳转问题的原理与解决方案

文章目录1. 问题描述2. 问题原因3. 解决方案3.1通过多个父子关系从而控制旋转(推荐)3.2 使用四元数进行旋转1. 问题描述 我们现在写一个3D的Unity程序,我们现在设置了一个物体后,我们想旋转使其改为我们想要的情况。但是我们如果…

为什么现代 C++ (C++11 及以后) 推荐使用 constexpr和模板 (Templates) 作为宏 (#define) 的替代品?​

我们用现实世界的比喻来深入理解​​为什么 C 中的宏 (#define) 要谨慎使用,以及为什么现代 C (C11 及以后) 推荐使用 constexpr 和模板 (Templates) 作为替代品。​​🧩 ​​核心问题:宏 (#define) 是文本替换​​想象宏是一个 ​​“无脑的…

PyCharm vs. VSCode 到底哪个更好用

在 Python 开发者中,关于 PyCharm 和 VSCode 的讨论从未停止。一个是功能齐备的集成开发环境(IDE),另一个是轻快灵活的代码编辑器。它们代表了两种不同的开发哲学,选择哪个,往往取决于你的项目需求、个人习…