用 KV 缓存量化解锁长文本生成

news2024/11/25 6:55:11

很高兴和大家分享 Hugging Face 的一项新功能: KV 缓存量化 ,它能够把你的语言模型的速度提升到一个新水平。

太长不看版: KV 缓存量化可在最小化对生成质量的影响的条件下,减少 LLM 在长文本生成场景下的内存使用量,从而在内存效率和生成速度之间提供可定制的权衡。

你是否曾尝试过用语言模型生成很长的文本,却因为内存不足而望洋兴叹?随着语言模型的尺寸和能力不断增长,支持生成更长的文本意味着内存蚕食的真正开始。于是,磨难也随之而来了,尤其是当你的系统资源有限时。而这也正是 KV 缓存量化的用武之地。

KV 缓存量化到底是什么?如果你不熟悉这个术语,没关系!我们拆成两部分来理解: KV 缓存量化

键值缓存或 KV 缓存是一种优化自回归模型生成速度的重要方法。自回归模型需要逐个预测下一个生成词元,这一过程可能会很慢,因为模型一次只能生成一个词元,且每个新预测都依赖于先前的生成。也就是说,要预测第 1000 个生成词元,你需要综合前 999 个词元的信息,模型通过对这些词元的表征使用矩阵乘法来完成对上文信息的抽取。等到要预测第 1001 个词元时,你仍然需要前 999 个词元的相同信息,同时还还需要第 1000 个词元的信息。这就是键值缓存的用武之地,其存储了先前词元的计算结果以便在后续生成中重用,而无需重新计算。

具体来讲,键值缓存充当自回归生成模型的内存库,模型把先前词元的自注意力层算得的键值对存于此处。在 transformer 架构中,自注意力层通过将查询与键相乘以计算注意力分数,并由此生成值向量的加权矩阵。存储了这些信息后,模型无需冗余计算,而仅需直接从缓存中检索先前词元的键和值。下图直观地解释了键值缓存功能,当计算第 K+1 个词元的注意力分数时,我们不需要重新计算所有先前词元的键和值,而仅需从缓存中取出它们并串接至当前向量。该做法可以让文本生成更快、更高效。

0471a97991639bbeb9febc57cd05df48.png

下一个名词是量化,它是个时髦词,主要用于降低数值的精度以节省内存。量化时,每个数值都会被舍入或截断以转换至低精度格式,这可能会导致信息丢失。然而,仔细选择量化参数和技术可以最大限度地减少这种损失,同时仍取得令人满意的性能。量化方法多种多样,如果你想知道更多信息以更深入了解量化世界,可查阅我们之前的博文。

  • 用 bitsandbytes、4 比特量化和 QLoRA 打造亲民的 LLMhttps://hf.co/blog/zh/4bit-transformers-bitsandbytes

有一利必有一弊,KV 缓存能够加速自回归生成,但在文本长度或者 batch size 变大时,它也随之带来了内存瓶颈。估算一下,当用 7B Llama-2 模型处理 10000 个词元的输入时,我们需要多少内存来存储 KV 缓存。存储一个词元的 KV 缓存所需的内存大致为 2 * 2 * 层数 * 键值抽头数 * 每抽头的维度 ,其中第一个 2 表示键和值,第二个 2 是我们需要的字节数 (假设模型加载精度为 float16 )。因此,如果上下文长度为 10000 词元,仅键值缓存所需的内存我们就要:

2 * 2 * 32 * 32 * 128 * 10000 ≈ 5GB

该内存需求几乎是半精度模型参数所需内存的三分之一。

因此,通过将 KV 缓存压缩为更紧凑的形式,我们可以节省大量内存并在消费级 GPU 上运行更长上下文的文本生成。实验表明,通过将 KV 缓存量化为较低的精度,我们可以在不牺牲太多质量的情况下显著减少内存占用。借助这一新的量化功能,我们现在可以用同样的内存支持更长的生成,这意味着你可以扩展模型的上下文长度,而不必担心遇到内存限制。

实现细节

Transformers 中的键值缓存量化很大程度上受启发于KIVI: A Tuning-Free Asymmetric 2bit Quantization for kv Cache论文。该论文对大语言模型引入了 2 比特非对称量化,且不会降低质量。KIVI 采用按通道的量化键缓存以及按词元量化值缓存的方法,因为研究表明,就 LLM 而言,键在某些通道上容易出现高幅度的异常值,而值并无此表现。因此,采用按通道量化键和按词元量化值的方法,量化精度和原始精度之间的相对误差要小得多。

  • KIVI: A Tuning-Free Asymmetric 2bit Quantization for kv Cachehttps://arxiv.org/abs/2402.02750

在我们集成至 transformers 时,键和值都是按通道量化的 [译者注: 原文为按词元量化,比照代码后改为按通道量化]。量化的主要瓶颈是每次添加新词元 (即每个生成步骤) 时都需要对键和值进行量化和反量化,这可能会减慢生成速度。为了解决这个问题,我们决定保留固定大小的余留缓存 (residual cache),以原始精度存储键和值。当余留缓存达到其最大容量时,存储在里面的键和值都会被量化,然后将其内容从余留缓存中清空。这个小技巧还有助于保持准确性,因为一些最新的键和值始终以其原始精度存储。设置余留缓存长度时主要需要考虑内存效率的权衡。虽然余留缓存以其原始精度存储键和值,但这可能会导致总体内存使用量增加。我们发现使用余留长度 128 作为基线效果不错。

  • 修改为按通道量化参考代码https://github.com/huggingface/transformers/blob/main/src/transformers/cacheutils.py#L404

因此,给定形状为 batch size, num of head, num of tokens, head dim 的键或值,我们将其分组为 num of groups, group size 并按组进行仿射量化,如下所示:

X_Q = round(X / S) - Z

这里:

  • XQ 是量化后张量

  • S 是比例,计算公式为 (maxX - minX) / (maxvalforprecision - minvalforprecision)

  • Z 是零点,计算公式为 round(-minX / S)

目前,支持 KV 量化的后端有:quanto后端支持 int2int4 量化;HQQ后端支持 int2int4int8 量化。如欲了解 quanto 的更多详情,可参阅之前的博文。尽管我们目前尚不支持其它量化后端,但我们对社区贡献持开放态度,我们会积极集成新后端相关的 PR。我们的设计支持社区贡献者轻松将不需要校准数据且可以动态计算低比特张量的量化方法集成进 transformers。此外,你还可以在配置中指定缺省量化参数,从而自主调整你的量化算法,如: 你可根据你的用例决定是使用按通道量化还是按词元量化。

  • quantohttps://github.com/huggingface/quanto

  • HQQhttps://github.com/mobiusml/hqq/tree/master

  • Quanto: PyTorch 量化工具包https://hf.co/blog/zh/quanto-introduction

比较 FP16 缓存和量化缓存的性能

一图胜千言,我们准备了几幅图,以让大家一目了然了解量化缓存与 FP16 缓存的表现对比。这些图向大家展示了当我们调整 KV 缓存的精度设置时,模型生成的质量是如何随之变化的。我们在PG-19数据集上测量了Llama2-7b-chat的困惑度。实验中使用的量化参数为: nbits=4, groupsize=64, resilduallength=128, pertoken=True

  • PG-19https://hf.co/datasets/emozilla/pg19-test

  • Llama2-7b-chathttps://hf.co/meta-llama/Llama-2-7b-chat-hf

可以看到,两个后端的 int4 缓存的生成质量与原始 fp16 几乎相同,而使用 int2 时出现了质量下降。你可在此处获取重现脚本。

  • 复现脚本链接https://gist.github.com/zucchini-nlp/a7b19ec32f8c402761d48f3736eac808

5cfd46a4d5fea539121bcc4668829024.png

我们还在LongBench基准上测量了生成质量,并将其与 KIVI 论文的结果进行比较,结论与上文一致。下表的结果表明,在所有测试数据集中, Quanto int4 的精度与 fp16 相当甚至略优 (数值越高越好)。

  • LongBenchhttps://hf.co/datasets/THUDM/LongBench

数据集KIVI fp16KIVI int2Transformers fp16Quanto int4Quanto int2
TREC63.067.563.063.055.0
SAMSum41.1242.1841.1241.314.04
TriviaQANANA84.2884.7663.64
HotPotQANANA30.0830.0417.3
PassageretrievalenNANA8.59.54.82

现在,我们来谈谈内存节省和速度之间的权衡。当我们量化模型中的 KV 缓存时,对内存的需求减少了,但有时这同时也会降低生成速度。虽然将缓存量化为 int4 可以节省大约 2.5 倍内存,但生成速度会随着 batch size 的增加而减慢。用户必须自己权衡轻重: 是否值得牺牲一点速度以换取内存效率的显著提高,这由你的实际用例的需求及其优先级排序决定。

以下给出了原始精度版和量化版 KV 缓存在各性能指标上的对比,复现脚本见此处。

  • 复现脚本链接https://gist.github.com/zucchini-nlp/56ce57276d7b1ee666e957912d8d36ca

a85d90ec9665d6b37308ad6fd4c477b2.png 69c4dcab732f5f5e44270efbb234ccde.png 79b227e04d9aff7187a87a5eaa8f5a09.png

想知道再叠加权重量化会发生什么吗?当然,把这些技术结合使用可以进一步减少模型的内存占用,但也带来一个问题 - 它可能会进一步减慢速度。事实上,我们的实验表明,权重量化与 KV 缓存量化一起使用会导致速度降低三倍。但我们并未放弃,我们一直在努力寻找让这个组合无缝运行的方法。目前 quanto 库中缺少相应的优化算子,我们对社区任何有助于提高计算效率的贡献持开放态度。我们的目标是确保你的模型平稳运行,同时保持高水准的延迟和准确性。

还需要注意的是,对输入提示的首次处理 (也称为预填充阶段) 仍然需要一次性计算整个输入的键值矩阵,这可能是长上下文的另一个内存瓶颈。这就是为什么生成第一个词元相关的延迟往往比后续词元更高的原因。还有一些其他策略可以通过优化注意力计算来减少预填充阶段的内存负担,如局部加窗注意力、Flash Attention等。如果预填充阶段内存不足,你可以使用 🤗 Transformers 中的 FlashAttention 以及 KV 缓存量化来进一步减少长输入提示的内存使用量。更多详情,请参阅文档。

  • 局部加窗注意力https://arxiv.org/abs/2004.05150

  • Flash Attentionhttps://arxiv.org/abs/2307.08691

  • Transformers 文档https://hf.co/docs/transformers/main/en/perfinfergpuone#flashattention-2

如果你想知道如果将内存使用量推至极限,我们最长可以支持多少个词元的上下文,那么在 80GB A100 中启用 Flash Attention 时,量化 KV 缓存可以支持多达 128k 个词元。而使用半精度缓存时,最长为 40k 个词元。

如何在 🤗 Transformers 中使用量化 KV 缓存?

要在 🤗 Transformers 中使用 KV 缓存量化,我们必须首先运行 pip install quanto 安装依赖软件。要激活 KV 缓存量化,须传入 cacheimplementation="quantized" 并以字典格式在缓存配置中设置量化参数。就这么多!此外,由于 quanto 与设备无关,因此无论你使用的是 CPU/GPU/MPS (苹果芯片),都可以量化并运行模型。

你可在此找到一个简短的Colab 笔记本及使用示例。

  • Colab 笔记本https://colab.research.google.com/drive/1YKAdOLoBPIore77xR5Xy0XLN8Etcjhui?usp=sharing

>>> import torch
>>> from transformers import AutoTokenizer, AutoModelForCausalLM

>>> tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-chat-hf")
>>> model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b-chat-hf", torch_dtype=torch.float16, device_map="cuda:0")
>>> inputs = tokenizer("I like rock music because", return_tensors="pt").to(model.device)

>>> out = model.generate(**inputs, do_sample=False, max_new_tokens=20, cache_implementation="quantized", cache_config={"backend": "quanto", "nbits": 4})
>>> print(tokenizer.batch_decode(out, skip_special_tokens=True)[0])
I like rock music because it's loud and energetic. It's a great way to express myself and rel

>>> out = model.generate(**inputs, do_sample=False, max_new_tokens=20)
>>> print(tokenizer.batch_decode(out, skip_special_tokens=True)[0])
I like rock music because it's loud and energetic. I like to listen to it when I'm feeling

总结

还有很多可以减少键值缓存内存占用的方法,如MultiQueryAttention、GroupedQueryAttention以及最近的KV 缓存检索等等。虽然其中一些方法与模型架构相耦合,但还是有不少方法可以在训后阶段使用,量化只是此类训后优化技术其中一种。总结一下本文:

  • MultiQueryAttentionhttps://arxiv.org/abs/1911.02150

  • GroupedQueryAttentionhttps://arxiv.org/abs/2305.13245

  • KV 缓存检索https://arxiv.org/abs/2403.09054

  1. 需要在内存与速度之间折衷: 通过将 KV 缓存量化为较低精度的格式,内存使用量可以显著减少,从而支持更长的文本生成而不会遇到内存限制。但,用户必须根据实际用例决定能不能接受放弃一点点生成速度的代价。

  2. 保持准确性: 尽管精度有所降低, int4 KV 缓存量化仍可将模型准确性保持在令人满意的程度,确保生成的文本保持上下文相关性和一致性。

  3. 灵活性: 用户可以根据自己的具体要求灵活地选择不同的精度格式,以为不同的用例及需求进行定制。

  4. 进一步优化的潜力: 虽然 KV 缓存量化本身具有显著的优势,但它也可以与其他优化技术 (例如权重量化) 结合使用,以进一步提高内存效率和计算速度。

致谢

特别感谢Younes和Marc在量化技术上的帮助和建议,他们的专业知识极大地促进了此功能的开发。

  • Youneshttps://hf.co/ybelkada

  • Marchttps://hf.co/marcsun13

此外,我还要感谢Joao的宝贵支持。

  • Joaohttps://hf.co/joaogante

更多资源

  1. Zirui Liu, Jiayi Yuan, Hongye Jin, Shaochen Zhong, Zhaozhuo Xu, Braverman, V., Beidi Chen, & Hu, X. (2023).KIVI : Plug-and-play 2bit KV Cache Quantization with Streaming Asymmetric Quantization.https://arxiv.org/abs/2402.02750

  2. Databricks 博文:LLM Inference Performance Engineering: Best Practiceshttps://www.databricks.com/blog/llm-inference-performance-engineering-best-practices

  3. Coleman Hooper, Sehoon Kim, Hiva Mohammadzadeh, Michael W. Mahoney, Yakun Sophia Shao, Kurt Keutzer, & Amir Gholami. (2024).KVQuant: Towards 10 Million Context Length LLM Inference with KV Cache Quantization.https://arxiv.org/abs/2401.18079

  4. T. Dettmers, M. Lewis, Y. Belkada, and L. Zettlemoyer, (2022).LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale.https://arxiv.org/abs/2208.07339

  5. A. Gholami, S. Kim, Z. Dong, Z. Yao, M. W. Mahoney, and K. Keutzer, (2021). A Survey of Quantization Methods for Efficient Neural Network Inference.


英文原文: https://hf.co/blog/kv-cache-quantization

原文作者: Raushan Turganbay 

译者: Matrix Yao (姚伟峰),英特尔深度学习工程师,工作方向为 transformer-family 模型在各模态数据上的应用及大规模模型的训练推理。

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

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

相关文章

.net 调用海康SDK以及常见的坑解释

📢欢迎点赞 :👍 收藏 ⭐留言 📝 如有错误敬请指正,赐人玫瑰,手留余香!📢本文作者:由webmote 原创📢作者格言:新的征程,我们面对的不仅仅是技术还有人心,人心不可测,海水不可量,唯有技术,才是深沉黑夜中的一座闪烁的灯塔 !序言 在工控领域,很多时候需要…

【博士每天一篇文献-算法】Progressive Neural Networks

阅读时间:2023-12-12 1 介绍 年份:2016 作者:Andrei A. Rusu,Neil Rabinowitz,Guillaume Desjardins,DeepMind 研究科学家,也都是EWC(Overcoming catastrophic forgetting in neural networks)算法的共同作者。 期刊: 未录用&am…

.NET MAUI Sqlite数据库操作(一)

一、安装 NuGet 包 安装 sqlite-net-pcl 安装 SQLitePCLRawEx.bundle_green 二、配置数据库(数据库文件名和路径) namespace TodoSQLite; public static class Constants {public const string DatabaseFilename "TodoSQLite.db3";//数据库…

ModuleNotFoundError: No module named ‘MySQLdb‘

python项目运行遇到报错:ModuleNotFoundError: No module named ‘MySQLdb’ 解决办法 1、安装依赖 pip install pymysql2、新增配置 在项目的__init__.py文件中添加以下代码即可解决。 import pymysql pymysql.install_as_MySQLdb()

tim定时器 输入捕获模式下 TIM–ICStructinit(TIM–ICStructinit) 这个值 解析

主要需要看着图来理解 1.这是stm中文手册的图 2.这是解析 我觉得写的不错 注:有个很坑的地方 我觉得是stm32中文手册的问题 他写的解释只写了tim输入2 3 4和ic1 2 3 4,少写了一个输入1 第一次看见很不好理解

vue2动态路由实现

实现一个简单的动态路由&#xff1a; 1、先定义菜单页面组件的结构&#xff0c;使用的是elementUI的NavMenu 导航菜单 <template><div><el-menu default-active"1" router><el-submenu :index"item.path" v-for"item in menu_…

Android14音频进阶之CarAudioManager::getOutputDeviceForUsage流程分析(七十七)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒体系统工程师系列【原创干货持续更新中……】🚀 优质视频课程:AAOS车载系统+AOSP…

C数据结构:排序

目录 冒泡排序 选择排序 堆排序 插入排序 希尔排序 快速排序 hoare版本 挖坑法 前后指针法 快速排序优化 三数取中法 小区间优化 快速排序非递归 栈版本 队列版本 归并排序 归并排序非递归 ​编辑 计数排序 各排序时间、空间、稳定汇总 冒泡排序 void Bub…

嵌入式linux系统中设备树的经典使用方法

第一:设备树简介 大家好,今天主要给大家分享一下,如何使用linux系统里面的设备树,详细分析如下。 可以参考的官方文档有: 官方文档(可以下载到 devicetree-specification-v0.2.pdf): https://www.devicetree.org/specifications/ 内核文档: …

MNIST手写字符分类

MNIST手写字符分类 文章目录 MNIST手写字符分类1 数据集2 模型构建3 训练4 模型保存5 推理6 模型导出7 导出模型测试 1 数据集 MNIST手写字符集包括60000张用于训练的训练集图片和10000张用于测试的测试集图片&#xff0c;所有图片均归一化为28*28的灰度图像。其中字符区域为白…

LabVIEW水箱液位控制系统

介绍了如何使用LabVIEW软件和硬件工具开发水箱液位控制系统。系统集成了数据采集、实时控制和模拟仿真技术&#xff0c;展示了高精度和高可靠性的特点&#xff0c;适用于需要精细水位调节的工业应用。 项目背景 在制造和化工行业&#xff0c;液位控制是保证生产安全与效率的关…

第3章 Unity 3D着色器系统

3.1 从一个外观着色器程序谈起 新建名为basic_diffuse.shader的文件&#xff0c;被一个名为basic_diffuse.mat的材质文件所引用&#xff0c;而basic_diffuse.mat文件则被场景中名为Sphere的game object的MeshRenderer组件所使用。 basic_diffuse.shader代码文件的内容如下所示…

51.Python-web框架-Django开始第一个应用的增删改查

目录 1.概述 2.创建应用 创建app01 在settings.py里引用app01 3.定义模型 在app01\models.py里创建模型 数据库迁移 4.创建视图 引用头 部门列表视图 部门添加视图 部门编辑视图 部门删除视图 5.创建Template 在app01下创建目录templates 部门列表模板depart.ht…

java+vue3+el-tree实现树形结构操作

基于springboot vue3 elementPlus实现树形结构数据的添加、删除和页面展示 效果如下 代码如下&#xff0c;业务部分可以自行修改 java后台代码 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.daztk.mes.common.annotation.LogOperation…

高通Android 12 右边导航栏改成底部显示

最近同事说需要修改右边导航栏到底部&#xff0c;问怎么搞&#xff1f;然后看下源码尝试下。 1、Android 12修改代码路径 frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java a/frameworks/base/services/core/java/com/android/server/wm/Display…

树莓派4B_OpenCv学习笔记6:OpenCv识别已知颜色_运用掩膜

今日继续学习树莓派4B 4G&#xff1a;&#xff08;Raspberry Pi&#xff0c;简称RPi或RasPi&#xff09; 本人所用树莓派4B 装载的系统与版本如下: 版本可用命令 (lsb_release -a) 查询: Opencv 版本是4.5.1&#xff1a; 学了这些OpenCv的理论性知识&#xff0c;不进行实践实在…

Roboflow 图片分类打标

今天准备找个图片标注工具&#xff0c;在网上搜了一下&#xff0c;看 Yolo 的视频中都是用 Roboflow 工具去尝试了一下&#xff0c;标注确实挺好用的&#xff0c;可以先用一些图片训练一个模型&#xff0c;随后用模型进行智能标注。我主要是做标注然后到处到本地进行模型的训练…

springboot的WebFlux 和Servlet

Spring Boot 中的 Servlet 定义&#xff1a; 在 Spring Boot 中&#xff0c;Servlet 应用程序通常基于 Spring MVC&#xff0c;它是一个基于 Servlet API 的 Web 框架。Spring MVC 提供了模型-视图-控制器&#xff08;MVC&#xff09;架构&#xff0c;用于构建 Web 应用程序。…

【Mac】增加 safari 体验的插件笔记

Safari 本身的功能不全面&#xff0c;探索积累了一点插件笔记&#xff0c;提升使用体验&#xff1b;但后面因为插件或会影响运行速度&#xff0c;就全部都禁止了。做个笔记记录一下。 Cascadea 相当于 stylus&#xff0c;可以自定义页面。测试过几个&#xff0c;只有几个可行。…

Java:爬虫htmlunit抓取a标签

如果对htmlunit还不了解的话可以参考Java&#xff1a;爬虫htmlunit-CSDN博客 了解了htmlunit之后&#xff0c;我们再来学习如何在页面中抓取我们想要的数据&#xff0c;我们在学习初期可以找一些结构比较清晰的网站来做测试爬取&#xff0c;首先我们随意找个网站如下&#xff…