想学习大型语言模型中的量化吗?
2024/9/25 21:03:21
本文主要是介绍想学习大型语言模型中的量化吗?,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
1. 图片由作者提供:流程展示了量化的需求。(笑脸和生气的脸的图片由Yan Krukau提供,https://www.pexels.com/)
在解释上面的图表之前,让我先来介绍你在本文中将学到的重点。
- 首先,你将了解 量化 的 是什么 和 为什么。
- 接下来,你将进一步学习 如何 通过一些简单的数学推导来进行 量化。
- 最后,我们将一起在 PyTorch 中编写一些 代码 来执行 LLM 权重参数的量化和去量化。
让我们一个个依次展开讲解。
1. 什么是量化以及为什么需要它?量化 是一种将较大尺寸的模型(大型语言模型或其他深度学习模型)压缩为较小尺寸的方法。在量化过程中,主要会对模型的权重参数和激活值进行量化。让我们通过简单的模型大小计算来验证这一说法。
2. 图像由作者绘制:左图:基础模型大小计算(单位GB),右图:量化模型大小计算(单位GB)
在上图中,基础模型Llama 3 8B的大小为32GB。经过INT8量化后,大小减少到8GB(减少了75%)。使用INT4量化后,大小进一步减少到4GB(减少了约90%)。 这是一个巨大的模型大小缩减。这确实令人惊叹,不是吗?这要归功于量化论文的作者们以及我对数学力量的深深感激。
现在你已经了解了量化是什么,让我们来谈谈为什么需要量化。 请看图1,作为一名有抱负的AI研究人员、开发人员或架构师,如果你希望在你的数据集上进行模型微调或推理,由于内存和处理器的限制,你可能无法在你的机器或移动设备上完成这些任务。可能,像我一样,你会有一个生气的表情,就像选项1b。这让我们来到了选项1a,你可以让云服务商为你提供所有你需要的资源,并轻松地使用任何你想要的模型。但这会花费你很多钱。如果你负担得起,那很好。但如果你的预算有限,好消息是你还有选项2可用。这就是你可以使用量化方法来减少模型的大小,并方便地在你的应用场景中使用它。如果你量化做得好,你会得到与原始模型几乎相同的准确率。
2. 量化是如何工作的?一个简单的数学推导。注意: 一旦你在本地机器上完成了微调或其他任务,并且你想将模型投入生产,我建议你将模型部署到云端,以向客户提供可靠、可扩展和安全的服务。
从技术角度讲,量化将模型的权重值从高精度(如 FP32)映射到低精度(如 FP16|BF16|INT8)。虽然有许多可用的量化方法,但在本文中,我们将学习一种广泛使用的量化方法,称为线性量化方法。线性量化有两种模式:A. 非对称量化 和 B. 对称量化。我们将依次学习这两种方法。
A. 非对称线性量化: 非对称量化方法将原始张量范围(Wmin, Wmax)中的值映射到量化张量范围(Qmin, Qmax)中的值。
3. 图像由作者:非对称线性量化
- Wmin, Wmax: 原始张量(数据类型:FP32,32位浮点数)的最小和最大值。大多数现代LLM中权重张量的默认数据类型是FP32。
- Qmin, Qmax: 量化张量(数据类型:INT8,8位整数)的最小和最大值。我们也可以选择其他数据类型,如INT4、INT8、FP16和BF16进行量化。在我们的示例中,我们将使用INT8。
- 缩放值(S): 在量化过程中,缩放值将原始张量的值缩小以获得量化张量。在反量化过程中,它将量化张量的值放大以获得反量化值。缩放值的数据类型与原始张量相同,即FP32。
- 零点(Z): 零点是量化张量范围内直接映射到原始张量范围内值0的非零值。零点的数据类型是INT8,因为它位于量化张量范围内。
- 量化: 图表中的“A”部分显示了将 [Wmin, Wmax] 映射到 [Qmin, Qmax] 的量化过程。
- 反量化: 图表中的“B”部分显示了将 [Qmin, Qmax] 映射到 [Wmin, Wmax] 的反量化过程。
那么,我们如何从原始张量值推导出量化张量值呢? 这其实很简单。如果你还记得高中数学,你很容易就能理解下面的推导过程。让我们一步一步来(我建议你在推导方程时参考上面的图,以便更清楚地理解)。
我知道你们中很多人可能不想看下面的数学推导。但相信我,这一定会帮助你们把概念弄得更清楚,并在以后进行量化编码时节省大量时间。我在研究这个内容时也有同感。
- 潜在问题1:如果Z的值超出范围怎么办?解决方案: 使用简单的if-else逻辑将Z的值调整为Qmin,如果Z小于Qmin;将Z的值调整为Qmax,如果Z大于Qmax。这在下面图像4中的图A中有很好的描述。
- 潜在问题2:如果Q的值超出范围怎么办?解决方案: 在PyTorch中,有一个名为clamp的函数,它可以将值调整到特定范围内(例如我们的例子中的范围是-128到127)。因此,clamp函数将Q值调整为Qmin,如果Q小于Qmin;将Q值调整为Qmax,如果Q大于Qmax。问题解决了,让我们继续。
4. 图像由作者提供:零点和量化张量超出范围
侧注: 对于INT8类型,量化张量的值范围是(-128, 127),即有符号整数类型。如果量化张量的值的数据类型是UINT8,即无符号整数类型,则范围将是(0, 255)。
B. 对称线性量化: 在对称方法中,原始张量范围中的0点映射到量化张量范围中的0点。因此,这被称为对称。由于0点在范围的两侧都映射到0,所以在对称量化中没有Z(零点)。整体映射发生在原始张量范围的(-Wmax, Wmax)到量化张量范围的(-Qmax, Qmax)之间。下图显示了量化和去量化的对称映射情况。
5. 图像由作者:对称线性量化
既然我们在非对称段定义了所有参数,这里也适用。让我们进入对称量化数学推导。
不对称量化和对称量化之间的区别:
现在你已经学习了什么是线性量化、为什么使用线性量化以及如何进行线性量化,这引领我们来到了本文的最后部分,编码部分。
3. 使用 PyTorch 编写代码来执行 LLM 权重参数的量化和去量化。如我之前所说,量化可以应用于模型的权重、参数和激活值。但是为了简单起见,我们仅在编码示例中量化权重参数。在开始编码之前,我们先快速看一下在 transformer 模型中,权重参数值在量化后的变化情况。这将有助于我们更好地理解。
6. 作者绘制图像: transformer架构中权重参数的量化
在我们将16个原始的FP32权重参数量化到INT8之后,内存占用从512位减少到了128位(减少了25%)。这证明了对于大型模型,这种减少将会更加显著。
下面你可以看到FP32、Signed INT8和Unsigned UINT8等数据类型在实际内存中的分布。我在实际计算中使用了2's补码。你可以自己练习计算并验证结果。
7. 作者绘制的图像:FP32、INT8、UINT8 数据类型分布和计算示例
现在,既然我们已经涵盖了你需要开始编码的所有内容。我建议你跟着一起操作,以便熟悉推导过程。
A. 不对称量化代码: 我们一步一步地来编写代码。
步骤 1: 我们将首先为原始权重张量(大小:4x4,数据类型:FP32)分配随机值。
# !pip install torch; 如果你还没有安装 torch 库,请先运行此命令进行安装 # 导入 torch 库 import torch original_weight = torch.randn((4,4)) print(original_weight)
原始权重张量(original_weight)以FP32格式
步骤2: 我们将定义两个函数,一个用于量化,另一个用于反量化。
def 不对称量化(original_weight): # 定义你想要量化的数据类型,在这个例子中是INT8。 量化数据类型 = torch.int8 # 从原始权重中获取Wmax和Wmin值,原始权重是FP32类型。 Wmax = original_weight.max().item() Wmin = original_weight.min().item() # 从量化数据类型中获取Qmax和Qmin值。 Qmax = torch.iinfo(量化数据类型).max Qmin = torch.iinfo(量化数据类型).min # 使用缩放公式计算缩放值。数据类型 - FP32。 # 如果你想知道公式是如何推导的,请参阅本文的数学部分。 S = (Wmax - Wmin)/(Qmax - Qmin) # 使用零点公式计算零点值。数据类型 - INT8。 # 如果你想知道公式是如何推导的,请参阅本文的数学部分。 Z = Qmin - (Wmin/S) # 检查Z值是否超出范围。 if Z < Qmin: Z = Qmin elif Z > Qmax: Z = Qmax else: # 零点数据类型应该是INT8,与量化值相同。 Z = int(round(Z)) # 我们现在有了原始权重、缩放值和零点值,可以使用我们在数学部分推导出的公式计算量化权重。 量化权重 = (original_weight/S) + Z # 我们还需要对其进行四舍五入,并使用torch clamp函数确保量化权重不会超出范围,保持在Qmin和Qmax之间。 量化权重 = torch.clamp(torch.round(量化权重), Qmin, Qmax) # 最后将数据类型转换为INT8。 量化权重 = 量化权重.to(量化数据类型) # 返回最终的量化权重。 return 量化权重, S, Z def 不对称去量化(量化权重, 缩放值, 零点值): # 使用本文数学部分推导出的去量化计算公式。 # 同时确保将量化权重转换为float,以避免两个INT8值(量化权重和零点值)之间的减法产生意外结果。 去量化权重 = 缩放值 * (量化权重.to(torch.float32) - 零点值) return 去量化权重
步骤 3: 我们将通过调用 asymmetric_quantization 函数来计算量化权重、缩放因子和零点。您可以在下面的截图中看到输出结果,注意量化权重的数据类型为 int8,缩放因子为 FP32,零点为 INT8。
量化权重, 缩放因子, 零点 = asymmetric_quantization(原始权重) print(f"量化权重: {量化权重}") print("\n") print(f"缩放因子: {缩放因子}") print("\n") print(f"零点: {零点}")
量化权重,缩放因子和零点值
步骤4:现在我们已经得到了所有的量化权重、缩放因子和零点值。让我们通过调用 asymmetric_dequantization 函数来获取去量化的权重值。注意,去量化的权重值是 FP32 类型的。
dequantized_weight = 不对称去量化(quantized_weight, scale, zero_point) print(dequantized_weight)
去量化的权重值
步骤5: 通过计算它们之间的量化误差,让我们找出最终去量化的权重值与原始权重张量相比的准确性。
量化误差 = (去量化的权重 - 原始权重).square().mean() print(量化误差)
输出结果:量化误差小了很多。因此,我们可以认为非对称量化方法做得非常好。
B. 对称量化代码: 我们将使用之前在非对称方法中编写的相同代码。对称量化方法中唯一需要改变的是确保零输入值始终为0。因为在对称量化中,零输入值始终映射到原始权重张量中的0值。因此,我们无需编写额外的代码即可继续进行。
就这样! 我们已经到了这篇帖子的结尾。希望这篇帖子能帮助你建立对量化的基本直觉,并对数学推导部分有清晰的理解。
我的最终想法……
- 在这篇帖子中,我们涵盖了所有必要的主题,让你能够参与任何与大语言模型(LLM)或深度学习量化相关的任务。
- 虽然我们已经成功地对权重张量进行了量化,并且在大多数情况下也达到了很好的精度。但这在某些情况下可能还不够。如果你希望在更大的模型上进行更精确的量化,你需要执行通道量化(对权重矩阵的每一行或每一列进行量化)或组量化(将行或列分成更小的组并分别进行量化)。这些技术更复杂,我将在接下来的帖子中介绍它们。
敬请期待,感谢您的阅读!
链接到 Google Colab 笔记本
参考文献
- 从huggingface博客:__https://huggingface.co/docs/optimum/en/concept_guides/quantization#pratical-steps-to-follow-to-quantize-a-model-to-int8
- 从huggingface博客:__https://huggingface.co/blog/hf-bitsandbytes-integration
这篇关于想学习大型语言模型中的量化吗?的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-15Tailwind开发入门教程:从零开始搭建第一个项目
- 2024-11-14Emotion教程:新手入门必备指南
- 2024-11-14音频生成的秘密武器:扩散模型在音乐创作中的应用
- 2024-11-14从数据科学家到AI开发者:2023年构建生成式AI网站应用的经验谈
- 2024-11-14基于AI的智能调试助手创业点子:用代码样例打造你的调试神器!
- 2024-11-14受控组件学习:从入门到初步掌握
- 2024-11-14Emotion学习入门指南
- 2024-11-14Emotion学习入门指南
- 2024-11-14获取参数学习:初学者指南
- 2024-11-14受控组件学习:从入门到实践