8.0 A Visual Guide to Quantization

这篇文章详细介绍了量化技术在大语言模型(LLM)中的应用,特别是如何通过减少模型参数的比特数来提高内存效率和计算速度。对于投资者来说,这篇文章揭示了量化技术在AI和机器学习领域的巨大潜力,尤其是在硬件资源有限的情况下运行大型模型的能力。这为投资者提供了一个新的视角,即在AI基础设施和优化技术方面的投资机会。此外,文章还提到了具体的量化方法和技术,如GPTQ和BitNet,这些技术可能会成为未来AI模型优化的重要方向。

ONE SENTENCE SUMMARY:

量化技术通过减少模型参数的比特宽度来压缩大型语言模型,从而在保持精度的同时降低内存需求。

MAIN POINTS:

TAKEAWAYS:

正如其名称所示,大型语言模型(LLMs)通常过于庞大,无法在普通消费者硬件上运行。这些模型的参数可能超过数十亿,通常需要具有大量显存的GPU来加速推理过程。

因此,越来越多的研究集中在通过改进训练、适配器等方法使这些模型变得更小。该领域的一个主要技术被称为 量化

在这篇文章中,我将介绍量化在语言建模中的应用,并逐步探索相关概念,以帮助大家对这一领域形成直观的理解。我们将探讨各种方法、应用场景以及量化背后的原理。

感谢阅读 探索语言模型 !订阅免费获取关于 人工智能与心理学的交叉点 的新文章以及即将出版的书籍: 动手学大型语言模型

作为视觉指南,期待通过大量可视化内容来帮助大家理解量化的直观概念!

目录

第一部分: LLMs的“问题”

LLMs因其包含的参数数量而得名。如今,这些模型通常具有数十亿个参数(主要是 权重 ),存储这些参数可能非常昂贵。

在推理过程中,激活是输入和权重的乘积,这同样可能非常庞大。

因此,我们希望尽可能高效地表示数十亿个值,最小化存储给定值所需的空间。

让我们从头开始,探索数值是如何表示的,然后再进行优化。

如何表示数值

一个给定的值通常表示为浮点数(在计算机科学中称为 浮点数 ):一个带有小数点的正数或负数。

这些值由“ ”或二进制数字表示。 IEEE-754 标准描述了位如何通过三种功能之一来表示值: 符号 指数 小数( 或尾数

这三个方面可以结合起来,通过一组特定的位值来计算一个值:

我们使用的位数越多,表示的值通常就越精确:

内存限制

我们可用的位数越多,可以表示的值的范围就越大。

给定表示法可以表示的数值范围称为 动态范围 ,而两个相邻值之间的距离称为 精度

注意:在实际操作中,更多因素与推理过程中所需的(V)RAM数量有关,比如上下文大小和架构。

这些位的一个巧妙特性是我们可以计算出设备存储给定值所需的内存量。由于内存中的一个字节有8位,我们可以为大多数形式的浮点表示创建一个基本公式。

注意:在实际操作中,推理过程中所需的(V)RAM数量还与上下文大小和架构等因素有关。

现在假设我们有一个拥有700亿参数的模型。大多数模型本身是以32位浮点数(通常称为 全精度 )表示的,这样仅加载模型就需要 280GB 的内存。

因此,尽量减少表示模型参数所需的位数(以及训练过程中所需的位数)是非常有吸引力的。然而,随着精度的降低,模型的准确性通常也会下降。

我们希望在保持准确性的同时减少表示值的位数……这就是 量化 的用武之地!

第二部分: 量化简介

量化旨在将模型参数的精度从较高的位宽(如32位浮点数)降低到较低的位宽(如8位整数)。

图片改编自 Slava Sidorov 的原作。

在减少表示原始参数的位数时,通常会有一些精度(粒度)损失。

为了说明这一效果,我们可以取任意一张图片,并仅用8种颜色来表示它:

图片改编自 Slava Sidorov 的原作。

注意,放大后的部分比原图更“颗粒化”,因为我们只能用更少的颜色来表示它。

量化的主要目标是减少表示原始参数所需的位数(颜色),同时尽可能保留原始参数的精度。

常见数据类型

首先,让我们看看常见的数据类型,以及使用它们而不是32位(称为 全精度 FP32 )表示的影响。

FP16

让我们看一个从32位到16位(称为 半精度 FP16 )浮点数的例子:

注意到 FP16 的取值范围比 FP32 小了不少。

BF16

为了获得与原始 FP32 相似的取值范围,引入了 bfloat 16 作为一种“截断的 FP32”:

BF16 使用与 FP16 相同数量的位,但可以取更广泛的值,常用于深度学习应用。

INT8

当我们进一步减少位数时,就进入了 基于整数的表示 领域,而不是浮点表示。举例来说,从 FP32 到 INT8,后者只有 8 位,结果是原始位数的四分之一:

根据硬件的不同,整数计算可能比浮点计算更快,但这并不总是如此。然而,使用较少位数进行计算通常会更快。

每次减少位数时,都会进行一次映射,将初始的FP32表示“压缩”到较低的位数。

实际上,我们不需要将整个FP32范围[-3.4e38, 3.4e38]映射到INT8。我们只需要找到一种方法,将数据的范围(模型的参数)映射到INT8。

常见的压缩/映射方法是 对称 非对称 量化,它们都是 线性映射 的形式。

让我们来探讨这些从FP32量化到INT8的方法。

对称量化

在对称量化中,原始浮点值的范围被映射到量化空间中以零为中心的对称范围。在前面的例子中,注意量化前后的范围如何保持在零的两侧对称。

这意味着浮点空间中零的量化值在量化空间中也是零。

一种对称量化的好例子叫做绝对最大值( absmax )量化。

给定一组值,我们取 最高 的绝对值( α )作为进行线性映射的范围。

注意,[-127, 127] 范围的值表示受限范围。非受限范围是 [-128, 127],这取决于量化方法。

由于这是一个以零为中心的线性映射,公式非常简单。

我们首先使用以下公式计算一个比例因子 ( s ):

然后,我们使用 s 来量化输入 x

填入数值后,我们将得到以下结果:

为了恢复原始的FP32值,我们可以使用之前计算的 缩放因子 s )来 反量化 这些量化值。

应用量化和反量化过程来恢复原始值如下所示:

你可以看到某些值,例如 3.08 3.02 被分配给INT8,即 36 。当你将这些值反量化回FP32时,它们会失去一些精度,变得无法区分。

这通常被称为 量化误差 ,我们可以通过计算原始值和反量化值之间的差异来得出这个误差。

一般来说,位数越低,量化误差就越大。

非对称量化

与此相对,非对称量化并不是围绕零对称的。相反,它将浮点范围内的最小值( β )和最大值( α )映射到量化范围的最小值和最大值。

我们将探讨的方法称为 零点量化

注意到0的位置发生了变化了吗?这就是为什么它被称为 非对称量化 。在范围[-7.59, 10.8]中,最小值和最大值到0的距离不同。

由于其位置的变化,我们必须计算INT8范围内的零点以进行线性映射。同样,我们还需要计算一个 比例因子 ( s ),但使用的是INT8范围的差值[-128, 127]

注意到由于需要计算INT8范围内的 零点 ( z )来调整权重,这个过程变得更加复杂。

和之前一样,让我们填入公式:

要将量化后的 INT8 数据反量化回 FP32,我们需要使用之前计算的 缩放因子 s )和 零点 z )。

除此之外,反量化过程非常简单:

当我们将对称量化和非对称量化并排放置时,可以很快看出这两种方法的区别:

请注意对称量化的零中心特性与非对称量化的偏移。

范围映射和剪裁

在我们之前的例子中,我们探讨了如何将给定向量中的值范围映射到低位表示。虽然这允许向量值的全范围被映射,但它有一个主要缺点,即 异常值

想象一下,你有一个包含以下值的向量:

这是我们之前使用的absmax方法。请注意,如果不进行剪裁,非对称量化也会发生同样的行为。

请注意,其中一个值比其他所有值都大得多,可以被视为异常值。如果我们要映射这个向量的全范围,所有小值将被映射到相同的低位表示,并失去它们的区分因素:

这是我们之前使用的absmax方法。请注意,如果不进行裁剪,非对称量化也会发生相同的行为。

相反,我们可以选择 裁剪 某些值。裁剪涉及设置原始值的不同动态范围,使得所有的异常值都被映射为相同的值。

在下面的例子中,如果我们手动将动态范围设置为[-5, 5],所有超出该范围的值将被映射为-127或127,无论它们的实际值是多少:

主要的优点是 非异常值 的量化误差显著减少。然而, 异常值 的量化误差会增加。

校准

在例子中,我展示了一种选择[-5, 5]这个任意范围的简单方法。选择这个范围的过程被称为 校准 ,其目的是找到一个包含尽可能多值的范围,同时最小化量化误差。

执行这个校准步骤对于不同类型的参数并不相同。

权重 (和偏置)

我们可以将LLM的权重和偏置视为 静态 值,因为它们在运行模型之前是已知的。例如, Llama 3的~20GB文件 主要由其权重和偏置组成。

由于偏置(biases)的数量显著少于权重(weights),通常是数百万级别,而权重则是数十亿级别,因此偏置通常保持较高的精度(如 INT16),而量化的主要工作集中在权重上。

对于静态且已知的权重,选择范围的校准技术包括:

例如,选择一个百分位数会导致类似于我们之前看到的剪裁行为。

激活

在大型语言模型(LLM)中不断更新的输入通常被称为“ 激活 ”。

注意,这些值被称为激活值,因为它们通常会经过一些激活函数,如 sigmoid 或 relu。

与权重不同,激活值会随着每次输入数据的变化而变化,这使得准确量化它们变得具有挑战性。

由于这些值在每个隐藏层之后都会更新,我们只能在推理过程中输入数据通过模型时知道它们的值。

总体来说,有两种方法可以校准权重和激活值的量化方法:

第三部分: 训练后量化

训练后量化(PTQ)是最流行的量化技术之一。它涉及在训练模型 对模型的参数(包括权重和激活值)进行量化。

权重的量化可以使用对称或非对称量化。

然而,激活值的量化需要通过模型推理来获取它们的潜在分布,因为我们不知道它们的范围。

激活值的量化有两种形式:

动态量化

在数据通过隐藏层后,收集其激活值:

这种激活分布随后用于计算量化输出所需的 零点 z )和 缩放因子 s )值:

每次数据通过新层时都会重复这个过程。因此,每一层都有其独立的 z s 值,因此有不同的量化方案。

静态量化

与动态量化不同,静态量化不会在推理过程中计算 零点 z )和缩放因子( s ),而是在推理之前计算。

为了找到这些值,使用 校准数据集 并将其提供给模型以收集这些潜在的分布。

在收集了这些数值后,我们可以计算出必要的 s z 值,以便在推理过程中进行量化。

当你进行实际推理时, s z 值不会被重新计算,而是全局使用这些值来量化所有激活值。

一般来说,动态量化往往更准确一些,因为它只尝试为每个隐藏层计算 s z 值。然而,这可能会增加计算时间,因为这些值需要被计算。

相比之下,静态量化虽然不如动态量化准确,但速度更快,因为它已经知道用于量化的 s z 值。

4位量化的领域

低于8位的量化一直是一个难题,因为每减少一位,量化误差就会增加。幸运的是,有几种聪明的方法可以将位数减少到6位、4位,甚至2位(尽管通常不建议使用低于4位的方法)。

我们将探讨在HuggingFace上常见的两种方法:

GPTQ

GPTQ可以说是实践中用于4位量化的最知名方法之一。 1

它使用非对称量化,并逐层进行处理,每层独立处理后再继续下一层:

在这个逐层量化过程中,它首先将层的权重转换为逆 Hessian 。它是模型损失函数的二阶导数,告诉我们模型输出对每个权重变化的敏感程度。

简而言之,它本质上展示了每个层中权重的 (逆) 重要性

Hessian矩阵中较小值对应的权重更为关键,因为这些权重的微小变化可能会导致模型性能的显著变化。

在逆Hessian矩阵中,较低的值表示更“重要”的权重。

接下来,我们对权重矩阵第一行的权重进行量化和反量化:

这个过程使我们能够计算 量化误差( q ,然后我们可以使用之前计算的逆Hessian矩阵( h_1 )对其进行加权。

本质上,我们是在基于权重的重要性创建一个加权的量化误差:

接下来,我们将这个加权量化误差重新分配到该行的其他权重上。这可以保持网络的整体功能和输出。

例如,如果我们对第二个权重,即0.3( x_2 )进行此操作,我们会将量化误差( q )乘以第二个权重的逆Hessian( h _2

我们可以对给定行中的第三个权重进行相同的处理:

我们重复这个重新分配加权量化误差的过程,直到所有值都被量化。

这之所以效果很好,是因为权重通常是相互关联的。因此,当一个权重有量化误差时,相关的权重会相应地更新(通过逆Hessian矩阵)。

注意 作者 使用了几种技巧来加速计算和提高性能,例如向Hessian矩阵添加阻尼因子、“懒惰批处理”和使用Cholesky方法预计算信息。我强烈建议查看 这个YouTube视频 了解更多信息。

提示 :如果你想要一种针对性能优化和提高推理速度的量化方法,可以查看 EXL2

GGUF

虽然GPTQ是一种很好的在GPU上运行完整LLM的量化方法,但你可能并不总是有这种能力。相反,我们可以使用GGUF将LLM的任何层卸载到CPU上。 2

这允许你在没有足够VRAM时同时使用CPU和GPU。

量化方法GGUF经常更新,可能取决于比特量化的级别。然而,基本原理如下。

首先,给定层的权重被分成包含一组“子”块的“超级”块。从这些块中,我们提取比例因子( s )和alpha( α ):

为了量化给定的“子”块,我们可以使用之前使用的 absmax 量化方法。记住它是通过比例因子 s 来乘以给定的权重:

比例因子是使用“子”块的信息计算的,但使用“超”块的信息进行量化,而“超”块有其自己的比例因子:

这种基于块的量化使用“超”块的比例因子( s_super )来量化“子”块的比例因子( s_sub )。

每个比例因子的量化级别可能不同,“超”块的比例因子通常比“子”块的比例因子具有更高的精度。

为了说明这一点,我们来探讨几个量化级别(2位、4位和6位):

注意:根据量化类型的不同,可能需要一个额外的最小值(m)来调整零点。这些值的量化方式与比例因子(s)相同。

查看 原始拉取请求 以了解所有量化级别的概述。另请参阅 此拉取请求 以获取有关使用重要矩阵进行量化的更多信息。

第4部分: 量化感知训练

在第3部分中,我们看到如何在训练后对模型进行量化。此方法的一个缺点是,这种量化没有考虑实际的训练过程。

这就是量化感知训练(QAT)的用武之地。与在训练后进行量化(PTQ)不同,QAT旨在在训练过程中学习量化过程。

QAT通常比PTQ更准确,因为量化已经在训练过程中被考虑。其工作原理如下:

在训练过程中,引入所谓的“ ”量化。这是先将权重量化为例如INT4,然后再反量化回FP32的过程:

这个过程允许模型在训练期间考虑量化过程,包括损失的计算和权重的更新。

QAT 尝试探索“ ”极小值的损失景观,以最小化量化误差,因为“ ”极小值往往会导致更大的量化误差。

例如,想象一下如果我们在反向传播过程中不考虑量化。我们会根据梯度下降选择损失最小的权重。然而,如果这个权重处于“ ”极小值中,就会引入更大的量化误差。

相反,如果我们考虑量化,在“ ”极小值中会选择一个不同的更新权重,从而大大降低量化误差。

因此,虽然PTQ在高精度(例如FP32)下损失较低,但QAT在低精度(例如INT4)下损失更低,这正是我们的目标。

1-bit LLMs的时代:BitNet

如前所述,4-bit已经相当小了,但如果我们进一步减少呢?

这就是 BitNet 的用武之地,它将模型的权重表示为单个1-bit,使用 -1 1 来表示一个权重。 3

它通过将量化过程直接注入Transformer架构来实现这一点。

请记住,Transformer架构是大多数LLM的基础,由涉及线性层的计算组成:

这些线性层通常用更高的精度表示,如FP16,并且大多数权重都在这些层中。

BitNet用他们称之为 BitLinear 的东西替换了这些线性层:

注意:在论文中他们使用了γ而不是α,但由于我们在示例中使用了α,所以这里也使用α。另外,β与我们在零点量化中使用的不同,而是平均绝对值。

BitLinear层的工作方式与常规线性层相同,都是基于权重乘以激活值来计算输出。

相比之下,BitLinear层使用1位表示模型的权重,使用INT8表示激活值:

注意:在论文中他们使用了γ而不是α,但由于我们在示例中使用了α,所以这里也使用α。另外,β与我们在零点量化中使用的不同,而是平均绝对值。

BitLinear层类似于量化感知训练(QAT),在训练期间执行一种“假”量化,以分析权重和激活值量化的效果:

注意:在论文中他们使用了γ而不是α,但由于我们在示例中使用了α,所以这里也使用α。另外,β与我们在零点量化中使用的β不同,而是平均绝对值。

让我们一步步了解BitLinear。

权重量化

在训练过程中,权重以INT8格式存储,然后使用一种基本策略—— 符号函数 量化为1位。

本质上,它将权重的分布移动到以0为中心,然后将0左边的所有值分配为-1,右边的所有值分配为1:

此外,它还跟踪一个值 β( 平均 绝对值 ,我们将在后面的反量化中使用。

激活量化

为了量化激活值,BitLinear使用 absmax量化 将激活值从FP16转换为INT8,因为它们在矩阵乘法(×)中需要更高的精度。

此外,它还跟踪 α( 最高 绝对值 ,我们将在后续的反量化过程中使用这个值。

反量化

我们跟踪了 α( 激活值的最高绝对值 β( 权重的平均绝对值 ,因为这些值将帮助我们将激活值反量化回FP16。

输出激活值将使用{ α , γ}重新缩放,以将其反量化到原始精度:

就是这样!这个过程相对简单,并且允许模型仅用两个值表示,即 -1 1

使用这个过程,作者观察到随着模型规模的增长,1-bit和FP16训练之间的性能差距变得越来越小。

然而,这仅适用于较大的模型(>30B参数),而较小模型的差距仍然很大。

所有大型语言模型都在1.58比特中

BitNet 1.58b 被引入以改进前面提到的扩展问题。 4

在这种新方法中,每个权重不仅可以是 -1 1 ,还可以取 0 作为值,使其成为 三元 。有趣的是,仅仅添加 0 就大大改进了BitNet,并允许更快的计算。

0的力量

那么为什么添加0是一个重大改进呢?

这与 矩阵乘法 有很大关系!

首先,让我们探讨一下矩阵乘法的工作原理。在计算输出时,我们将一个权重矩阵与一个输入向量相乘。下面是权重矩阵第一层的第一次乘法的可视化:

请注意,这种乘法涉及两个动作, 各个权重与输入 相乘 ,然后将它们 相加

相比之下,BitNet 1.58b 设法避免了乘法操作,因为三值权重本质上告诉你以下内容:

因此,如果你的权重量化到1.58位,你只需要进行加法运算:

这不仅可以显著加快计算速度,还允许进行 特征过滤

通过将某个权重设置为0,你现在可以忽略它,而不是像1位表示那样要么加上要么减去权重。

量化

为了进行权重量化,BitNet 1.58b 使用 absmean 量化,这是一种我们之前见过的absmax量化的变体。

它只是压缩权重的分布,并使用绝对均值( α )来量化值。然后将它们四舍五入到-1、0或1:

与 BitNet 相比,激活量化是相同的,除了一个不同点。激活值不再缩放到 [ 0 , 2ᵇ⁻¹ ] 范围,而是使用 absmax 量化 缩放到 [ -2ᵇ⁻¹ , 2ᵇ⁻¹ ] 范围。

就是这样!1.58 位量化主要需要两个技巧:

13B BitNet b1.58 在延迟、内存使用和能耗方面比 3B FP16 LLM 更高效”

因此,我们得到了仅有 1.58 位计算效率的轻量级模型!

结论

这就结束了我们的量化之旅!希望这篇文章能让你更好地理解量化、GPTQ、GGUF 和 BitNet 的潜力。谁知道未来的模型会有多小呢?!

要查看更多与 LLM 相关的可视化内容并支持本新闻通讯,请查看我与 Jay Alammar 合著的书。这本书即将发布!

你可以在 O’Reilly 网站上免费试阅这本书,或在 Amazon 上预购。所有代码将上传到 Github。

资源

希望这是一个易于理解的量化入门!如果你想深入了解,我建议以下资源:

1

Frantar, Elias 等人. "Gptq: 准确的生成预训练变换器的后训练量化." arXiv 预印本 arXiv:2210.17323 (2022).

2

你可以在他们的 GGML 仓库这里 找到更多关于 GGUF 的信息。

3

Wang, Hongyu 等人. "Bitnet: 为大语言模型扩展 1-bit 变换器." arXiv 预印本 arXiv:2310.11453 (2023).

4

Ma, Shuming 等人. "1-bit 大语言模型的时代: 所有大语言模型都在 1.58 bits." arXiv 预印本 arXiv:2402.17764 (2024).

5

Dettmers, Tim 等人. "Qlora: 高效微调量化的大语言模型." 神经信息处理系统进展 36 (2024).

文章来源:A Visual Guide to Quantization

关键问题与行动计划

关键问题 1: 如何评估量化技术在大语言模型(LLM)中的实际应用和市场需求?

行动计划:

  1. 市场调研:研究团队将针对不同的行业(例如金融、医疗、教育等)展开深入的市场调研,以了解哪些现有工具和工作流可以从量化技术中受益。特别关注那些对计算资源要求高且需要实时处理的应用场景。
  2. 用户反馈收集:数据团队可以利用替代数据来源(如线上论坛、用户评论、社交媒体等)收集和分析用户对于现有LLM工具的反馈,从中找出对量化技术的需求和潜在改进点。

关键问题 2: 量化技术在降低计算成本和提高模型效率方面的实际效果如何?

行动计划:

  1. 技术评估:研究团队将对现有的量化技术(如GPTQ、GGUF、BitNet等)进行技术评估,分析其在不同应用场景下的性能表现和计算成本。通过实验和数据分析,评估这些技术在实际应用中的效果。
  2. 案例研究:数据团队将收集和分析已经采用量化技术的企业和项目的案例,评估其在降低计算成本和提高模型效率方面的实际效果。特别关注那些在资源受限环境下取得显著成果的案例。

关键问题 3: 量化技术在未来的发展趋势和潜在的技术突破点是什么?

行动计划:

  1. 前沿研究跟踪:研究团队将跟踪最新的学术论文、技术报告和行业动态,特别关注那些在量化技术领域取得突破性进展的研究。定期撰写技术趋势报告,预测未来的发展方向和潜在的技术突破点。
  2. 专家访谈:数据团队将安排与量化技术领域的专家、学者和行业领袖的访谈,获取他们对未来技术发展的见解和预测。通过这些访谈,识别出可能的投资机会和技术合作伙伴。

请告诉我们你对此篇总结的改进建议,如存在内容不相关、低质、重复或评分不准确,我们会对其进行分析修正