如何在有限内存中适配大型语言模型:量化

大型语言模型适配有限内存:量化

大型语言模型可用于文本生成、翻译、问答等任务。然而,LLM(大型语言模型)也非常庞大,需要大量内存。这使得它们对手机和平板等小型设备来说具有挑战性。

将参数乘以选择的精度大小,以确定模型大小(以字节为单位)。假设我们选择的精度是float16(16位 = 2字节)。假设我们想要使用BLOOM-176B模型。我们需要1760亿个参数 * 2字节 = 352GB来加载模型!

大型语言模型

换句话说,要加载所有参数权重,我们需要12(!)台32GB的计算机!如果我们想要使LLM可携带,这太多了。为了克服这一困境,我们开发了减少LLM内存占用的技术。最流行的技术有:

  • 量化将LLM的权重转换为较低精度的格式,减少存储它们所需的内存。
  • 知识蒸馏涉及训练一个较小的LLM以模仿较大LLM的行为。这可以通过将知识从较大LLM转移到较小LLM来实现。

这些技术使得在小内存中容纳LLM成为可能。这为在各种设备上使用LLM打开了新的可能性。今天,我们将讨论量化(请关注知识蒸馏)。

量化

让我们从一个简单的例子开始。我们需要将2023转换为二进制:

将整数转换为二进制

正如你所看到的,这个过程相对简单。为了存储数字2023,我们将需要12+位(1位用于+或—符号)。对于数字,我们可能使用int16类型。

存储整数和浮点数之间有很大的区别。让我们尝试将20.23转换为二进制:

将浮点数转换为二进制

如您所见,浮点部分(尾数)被计算为1/2^n的组合,即使有10位专用于浮点部分,也无法计算得非常精确。整数部分(指数)设置为5位,覆盖了所有小于32的数字。总体上,我们使用16位(FP16)来存储最接近20.23的值,但这是保持浮点数的最有效方式吗?如果整数部分的数字要大得多,比如202.3呢?

如果我们查看标准浮点类型,我们会注意到,要存储202.3,我们需要使用FP32,从计算的角度来看,这是不合理的。相反,我们可以使用bfloat16来保存范围(指数)为8位,精度(尾数)为7位。这样可以扩大可能小数的范围,而不会失去太多精度。

FP32、FP16、BFloat16数据类型

需要明确的是,在进行训练时,我们需要尽可能多的精度。但是,在推理过程中,将速度和大小优先于第6位小数点是有意义的。

我们能将内存使用从bfloat16减少到int8吗?

零点和绝对最大值量化

实际上,我们可以这样做,有几种方法可以进行量化:

  • 零点量化通过将固定范围(-1,1)转换为int8(-127,127),然后将int8转换回bfloat16,从而节省一半的内存。
零点量化
  • 绝对最大值量化与零点量化类似,但是我们将其设置为(-abs(max),abs(max))而不是设置为自定义范围(-1,1)。
绝对最大值量化

让我们来看一下这些实践在矩阵乘法示例中的应用:

精确矩阵乘法

零点量化:

矩阵乘法的零点量化

绝对最大值量化:

矩阵乘法的绝对最大值量化

正如可以注意到的那样,大值[-1579,-1780]的得分相当低(零点量化为[-1579,-1752],绝对最大值量化为[-1565,-1786])。为了克服这些问题,我们可以分离异常值乘法:

分离异常值乘法

如您所见,结果更接近真实值。

但是,有没有一种方法可以在不损失太多质量的情况下使用更少的空间?

令我非常惊讶的是,有一种方法!如果我们不是独立地将每个数字转换为较低类型,而是考虑误差并将其用于调整,会怎样?这种技术称为GPTQ。

与以前的量化类似,我们找到小数部分的最接近匹配,使总的转换误差尽量接近零。

GPTQ近似。步骤1

我们按行方式填写矩阵。

GPTQ近似。步骤2

与异常值分离计算相结合,结果相当不错:

带有过滤异常值的GPTQ矩阵乘法

现在我们可以比较所有方法:

结果比较

LLM.int8()方法表现得非常好!GPTQ方法会损失一些质量,但可以使用两倍于int8方法的GPU内存。

在代码中,你可能会找到类似以下的内容:

from transformers import BitsAndBytesConfig# 配置BitsAndBytesConfig以进行4位量化bnb_config = BitsAndBytesConfig(    load_in_4bit=True,    bnb_4bit_use_double_quant=True,    bnb_4bit_quant_type="nf4",    bnb_4bit_compute_dtype=torch.bfloat16,)# 在预设配置中加载模型pretrained_model = AutoModelForCausalLM.from_pretrained(    model_id,    quantization_config=bnb_config,)

load_in_4bit标志指定模型应以4位精度加载。bnb_4bit_use_double_quant标志指定使用双重量化。bnb_4bit_quant_type标志指定量化类型。bnb_4bit_compute_dtype标志指定计算数据类型。

总结一下,我们学习了如何在内存中存储小数、如何通过一些精度损失来减少内存占用,以及如何使用4位量化运行选定的模型。

原文发表在我的LinkedIn页面上。