网站快速被百度收录,全国文明城市创建内容,东大桥网站建设,江门网页设计培训价格请大家关注我的知乎博客#xff1a;- 派神 - - 知乎
随着大型语言模型 (LLM) 的参数数量的增长,与其支持硬件#xff08;加速器内存#xff09;增长速度之间的差距越来越大#xff0c;如下图所示#xff1a; 上图显示#xff0c;从 2017 年到 2022 年#xff0c;语言模…请大家关注我的知乎博客- 派神 - - 知乎
随着大型语言模型 (LLM) 的参数数量的增长,与其支持硬件加速器内存增长速度之间的差距越来越大如下图所示 上图显示从 2017 年到 2022 年语言模型的大小显著增加 2017 年 Transformer 模型0.05B 参数2018 年 GPT0.11B 参数、BERT0.34B 参数2020 年 GPT-21.5B 参数、MegatronLM8.3B 参数2021 年 GPT-3175B 参数、T-NLG17B 参数2022 年 MT-NLG530B 参数
加速器(一般指GPU)是专门的硬件用于加速机器学习训练。它们的内存容量对于训练大型模型至关重要。图中显示加速器内存的增长速度远低于模型大小的增长速度
2017 年 TPUv216GB2018 年 V10032GB2020 年 TPUv332GB2021 年 A10040GB2022 年 A10080GB在 2023 年至 2024 年期间最大且最常用的模型的参数量大约在 700 亿左右。
上图显示了模型大小和加速器内存之间不断增长的差距。“差距”表示模型大小的增长速度远远超过了硬件内存容量的增长速度。这种差距对 LLM 的训练和部署提出了重大挑战。2022 年模型规模达到了顶峰但到了 2023 年人们发现中等规模的模型~70B 参数在实际应用中更加实用和高效。然而加速器内存的增长速度没有跟上模型大小的增长速度。 这突出了对更高效的训练技术、模型压缩方法和专用硬件的需求以克服这一瓶颈。
大型模型的规模和复杂性会限制其访问性和实用性因此需要找到解决方案将一个大型、复杂的模型转换为一个更小、更高效的模型同时保留其核心功能从而提高可访问性和实用性。 这使得在资源有限的环境中部署和使用模型成为可能。 模型压缩的方法
为了将一个大型、复杂的模型转换为一个更小、更高效的模型我们需要使用一些模型压缩的方法下面我们来介绍几种模型压缩的方法: 1.剪枝(Pruning)2.知识蒸馏(Knowledge Distillation)3.量化Quantization这里我们会深入讲解 第三种 量化Quantization方法对于剪枝(Pruning)和知识蒸馏(Knowledge Distillation)我们只做简单介绍。
1.剪枝Pruning
剪枝Pruning的核心思想是移除对模型性能提升不大的连接或神经元从而减小模型的大小和复杂度: 2.知识蒸馏Knowledge Distillation。
知识蒸馏Knowledge Distillation的核心思想是使用一个已经训练好的大型模型教师模型来指导一个较小型模型学生模型的训练从而将大型模型的知识“蒸馏”到小型模型中。 知识蒸馏指的是使用一个训练好的大型模型教师模型来指导小型模型学生模型的训练从而使小型模型获得与大型模型相近的性能实现模型压缩的目的。
3. 量化Quantization
在讲解量化方法之前我们先回归一下神经网络 上图是神经网络的一个隐藏层展示了线性层 followed by 激活函数的计算过程其中
w₁, b₁, w₂, b₂, w₃, b₃ 这些是神经网络的权重Weights蓝色w 和偏差Biases紫色b。每个神经元都有一组对应的权重和偏差。x 橙色箭头和 x 指的是输入向量。a₁, a₂, a₃ 这些是每个神经元的输出也就是激活值Activations。公式 aᵢ g(wᵢ ⋅ x bᵢ) 这展示了线性层 followed by 激活函数的计算过程。其中 wᵢ ⋅ x 表示权重向量和输入向量的点积。 bᵢ 表示加上偏差。 g() 表示激活函数。公式 g(z) 1 / (1 e⁻ᶻ) 这定义了激活函数 g(z) 为 sigmoid 函数。sigmoid 函数将线性层的输出映射到 0 到 1 之间引入非线性。
在神经网络中我们可以量化权重和激活值其中
权重 (w̄)神经网络参数激活值 (a)在神经网络层中传播的值例如在线性层中a g(w̄ ⋅ x b)a 是激活值w̄ 和 b 是权重。这里用一个线性层的例子进一步解释了激活值、权重和偏差的关系。
有了神经网络的基础知识后下面我们来真正进入主题我们来讨论一下模型压缩中的一种重要技术量化Quantization。 它通过降低模型参数的精度来减小模型的大小和所需的存储空间如下图所示 量化的核心思想是以较低的精度存储模型的参数在上图中
左侧表格 (FP32) 展示了使用单精度浮点数 (FP32) 存储的模型参数。每个值占用 4 个字节。FP32 to INT8 表示将参数从 FP32 转换为 8 位整数 (INT8) 的量化过程。右侧表格 (INT8) 展示了量化后的模型参数使用 INT8 存储。每个值占用 1 个字节
这里如果以FP32 格式存储每个值需要 4 字节存储空间9 个值共需 36 字节。而以INT8 格式存储每个值只需要 1 字节存储空间同样的 9 个值只需 9 字节。
这里展示量化如何通过降低数值精度从 FP32 到 INT8来显著减少模型的存储空间从 36 字节到 9 字节。 虽然精度有所损失但在很多情况下这种损失对模型性能的影响是可接受的并且可以换取更小的模型尺寸和更快的推理速度。
然而量化是会有代价的那就是它会产生量化误差error,如下图所示 这里我们将FP32 转换为 INT8 导致了精度损失。 每个误差值是原始 FP32 值和量化后 INT8 值之间的差。例如第一个值 13.5 被量化成 13误差为 0.5。最先进的(state-of-the-art)量化方法背后的全部挑战是避免性能下降的前提下尽可能的降低这个误差。
下面我们来介绍一些机器学习中不同的数据表示方式这里会重点介绍整数和浮点数类型的区别如下图所示 这里我们将FP32 转换为 INT8 导致了精度损失。 每个误差值是原始 FP32 值和量化后 INT8 值之间的差。例如第一个值 13.5 被量化成 13误差为 0.5。最先进的(state-of-the-art)量化方法背后的全部挑战是避免性能下降的前提下尽可能的降低这个误差。
整数 (Integer, int8)
Integer (int8): 表示 8 位整数类型。绿色方块序列用 8 个绿色方块直观地表示了 int8 类型每个方块代表一个比特。其中包含的数字 “1 0 0 0 1 0 0 1” 是一个 int8 数值的二进制表示示例。
浮点数 (Floating Point, FP32, FP16, BF16)
三种常见的浮点数类型单精度浮点数 (FP32)、半精度浮点数 (FP16) 和 Brain Floating Point (BF16)彩色条形图用不同颜色的条形图展示了 32 位浮点数 (FP32) 的组成部分及各自占用的位数 符号位 (Sign)1 位用于表示正负。指数位 (Exponent)8 位用于表示数值的范围大小。尾数位 (Fraction)23 位用于表示数值的精度。总共 32 位。
上面我们介绍了整数 (int8) 和浮点数 (FP32, FP16, BF16)的组成部分。 并重点突出了浮点数的三个组成部分符号、指数、尾数及其各自的作用并以 FP32 为例展示了这些部分是如何构成一个 32 位浮点数的。 这有助于理解不同数据类型在精度和存储空间上的权衡以及它们在机器学习中的应用。 虽然图片中没有明确指出 FP16 和 BF16 的具体组成但它们与 FP32 的相似性只是精度和范围有所不同。 下面我们来介绍一种最简单的量化方法线性量化 (Linear Quantization)
线性量化的原理是将一个较大范围的浮点数线性映射到一个较小范围的整数。如下图所示 在上图中
蓝色线段 (FP32) 代表使用单精度浮点数 (FP32) 表示的数值范围从 -234.1 到 251.51。红色线段 (INT8) 代表使用 8 位整数 (INT8) 表示的数值范围从 -128 到 127。虚线 连接 FP32 和 INT8 的值展示了线性映射的关系。例如FP32 中的 -234.1 映射到 INT8 中的 -128FP32 中的 251.51 映射到 INT8 中的 127。
这里我们推荐使用 Hugging Face 的 Quanto 工具将线性量化应用于实际模型。
上图清晰地展示了线性量化的过程将浮点数范围映射到整数范围。 它还指出了可以使用 Quanto 工具进行实际操作并提示了量化技术在大语言模型中的重要应用。 通过上图我们可以更容易理解量化是如何工作的以及为什么它可以用于模型压缩。
4.整数的表示
整数 (Integer)的两种表示方法无符号整数 (Unsigned Integer)和有符号整数 (Signed Integer)下面我们以 8 位整数为例进行了说明 无符号整数 (Unsigned Integer)
n 位无符号整数范围这里给出了 n 位无符号整数的取值范围从 0 到 2 的 n 次方减 1。范围以 8 位无符号整数 (torch.uint8) 为例说明其取值范围是 0 到 255。绿色方块序列用 8 个绿色方块直观地表示了 8 位无符号整数每个方块代表一个字节(byte)。 数值 “1 0 0 0 1 0 0 1” 是一个示例。计算过程下方展示了如何将二进制 “1 0 0 0 1 0 0 1” 转换为十进制 137 的过程每个byte乘以 2 的相应幂次再求和。
有符号整数 (Signed Integer)
表示方法有符号整数使用补码 (Twos Complement) 表示法。n 位有符号整数的取值范围这里给出了 n 位有符号整数的取值范围从 -2 的 (n-1) 次方到 2 的 (n-1) 次方减 1。8 位有符号整数 (torch.int8) 取整范围这里以 8 位有符号整数 (torch.int8) 为例说明其取值范围是 -128 到 127。红绿方块序列用一个红色方块和 7 个绿色方块表示 8 位有符号整数红色方块代表符号位1 表示负数。 数值 “1 0 0 0 1 0 0 1” 是一个示例。计算过程下方展示了如何将补码表示的二进制 “1 0 0 0 1 0 0 1” 转换为十进制 -119 的过程。最高位符号位的权重为负其余位计算方式与无符号整数相同。
这里我们看到无符号整数只能表示非负数(0和正数)而有符号整数可以表达负数0和整数并且有符号数采用的是补码 (Twos Complement) 表示法在补码中最高位代表负的 2 的幂次方其余位则按照标准的二进制转十进制方法计算。
在上图中将二进制 1 0 0 0 1 0 0 1 转换为十进制的方法如下
识别符号位 最左边的byte (1) 是符号位。 1 表示这是一个负数。剩余字节(byte)位计算 除了符号位之外的其余byte按照正常的二进制到十进制转换方法计算 0 * 2⁶ 0 * 2⁵ 0 * 2⁴ 1 * 2³ 0 * 2² 0 * 2¹ 1 * 2⁰ 0 0 0 8 0 0 1 9符号位权重 在 8 位补码中符号位的权重是 -2⁷ (-128)。最终计算 将符号位的权重和剩余byte计算结果相加 -128 9 -119
因此二进制 1 0 0 0 1 0 0 1 的 8 位补码表示对应的十进制数是 -119。
或许有读者不熟悉补码 (Twos Complement)下面我们就来介绍一下补码
补码 (Twos Complement) 是一种用于表示有符号整数的二进制编码方式。它之所以被广泛使用是因为它可以将加法和减法运算统一起来简化了计算机硬件的设计。
补码的作用
表示正负数 补码用最高位最左边的位作为符号位0 表示正数1 表示负数。简化运算 使用补码加法和减法可以用相同的电路实现无需额外的减法电路。 这是补码最主要的优势。唯一表示零 与原码和反码不同补码中零只有一个表示形式避免了歧义。
补码的计算方法
对于一个 n 位的整数
正数 正数的补码与其二进制表示相同。负数 负数的补码计算方法如下 取其绝对值的二进制表示。将所有byte位取反0 变 11 变 0。将结果加 1。
举例说明 (8 位) 5 二进制00000101补码00000101 (与原码相同) -5 绝对值的二进制00000101 取反11111010 加 111111011 (这是 -5 的补码)
补码的运算
加法 直接将两个补码相加忽略最高位的进位。减法 将被减数与减数的补码相加等同于加上减数的相反数。
补码的用途
计算机硬件 几乎所有现代计算机都使用补码来表示和运算整数。 这是因为补码简化了 ALU算术逻辑单元的设计可以只用加法器实现加减法运算。编程语言 许多编程语言的底层也使用补码来表示整数。数字信号处理 在 DSP 中补码也用于表示和处理数字信号。
补码是一种高效的二进制表示法它简化了计算机的运算并且能够清晰地表示正负数和零。 它的广泛应用使得计算机能够更快速、高效地进行整数运算。
下面我们介绍一下在PyTorch 中不同整数数据类型的表示方法如下图所示 上门图片中的表格展示了在 PyTorch 中如何使用不同的整数类型。它列出了每种类型的正式名称 (torch.dtype) 和更常用的别名如果存在方便开发者在代码中使用。 例如你可以使用torch.int32或torch.int来表示 32 位有符号整数。 了解这些数据类型及其别名对于编写高效的 PyTorch 代码至关重要尤其是在处理需要特定整数类型的数据或进行模型量化时。
下面我们来分别介绍一下在PyTorch 中 8 位无符号整数 (torch.uint8) 和有符号整数 (torch.int8) 的表示范围 在上图中 Unsigned integer: [0, 2ⁿ - 1] 表明是无符号整数并给出了 n 位无符号整数的通用取值范围公式从 0 到 2 的 n 次方减 1。 torch.iinfo(torch.uint8) 展示了如何在 PyTorch 中使用 torch.iinfo() 函数获取 torch.uint8 类型的信息。 注意这里标注了 two is 并配上了眼睛 表情符号强调了函数名中有两个 i提醒用户不要拼写错误这是一个很贴心的细节。 iinfo(min0, max255, dtypeuint8) 这是 torch.iinfo() 函数的输出结果显示了 torch.uint8 的最小值 (0)、最大值 (255) 和数据类型 (uint8)。
上图展示了 PyTorch 中torch.uint8类型的取值范围并演示了如何使用torch.iinfo()函数获取数据类型的信息。 强调了 two is 的细节有助于用户避免常见的拼写错误。 这对于理解 PyTorch 中整数类型的表示和使用至关重要尤其是在图像处理等需要处理像素值通常在 0 到 255 之间的情况下。
下面我们来介绍一下在PyTorch 中 8 位有符号整数 (torch.int8) 的表示范围以及补码表示法 [-2ⁿ⁻¹, 2ⁿ⁻¹ - 1] 这里给出 n 位有符号整数(补码表示)的通用取值范围公式。torch.int8 表示这是一个关于 torch.int8 类型的示例。torch.iinfo(torch.int8) 展示了如何在 PyTorch 中使用 torch.iinfo() 函数获取 torch.int8 类型的信息。iinfo(min-128, max127, dtypeint8) torch.iinfo() 函数的输出结果显示了 torch.int8 的最小值 (-128)、最大值 (127) 和数据类型 (int8)。
这里我们演示了在PyTorch 中 torch.int8 类型的取值范围并强调了使用补码表示法。 还演示了如何使用torch.iinfo()函数获取数据类型的信息。 这对于理解 PyTorch 中整数类型的表示和使用至关重要特别是对于模型量化等需要处理低精度数值的情况。
下面我们来看看如何在 PyTorch 中使用torch.iinfo()函数获取不同有符号整型数据类型的信息如下图所示 图中展示了三个使用 torch.iinfo() 函数的例子分别对应三种不同的整型数据类型 torch.iinfo(torch.int64): 调用 torch.iinfo() 函数传入 torch.int64 数据类型这将返回一个 torch.iinfo 对象其中包含了 torch.int64 类型的各种属性信息例如最大值、最小值等。 torch.iinfo(torch.int32): 调用 torch.iinfo() 函数传入 torch.int32 数据类型。 torch.iinfo(torch.int16): 调用 torch.iinfo() 函数传入 torch.int16 数据类型。
上图展示了如何使用torch.iinfo()函数获取 PyTorch 中不同整型数据类型的属性信息。 虽然图中没有显示torch.iinfo()函数的输出结果但它告诉我们如何使用该函数来查询int64、int32和int16的信息例如它们的取值范围、精度等。 通过这些信息开发者可以更好地理解和使用 PyTorch 中的整型数据类型。 5.浮点数的表示
下面我们来介绍一下浮点数的三个组成部分及其在不同浮点数格式中的作用如下图所示 浮点数的三个组成部分包括
Sign符号 正数或负数始终为 1 位。 用于表示数值的正负。Exponent指数 影响可表示的数值范围。 指数部分决定了数值的大小范围类似于科学计数法中的指数。Fraction尾数 影响数值的精度。 尾数部分决定了数值的精度类似于科学计数法中的有效数字。
FP32、BF16、FP16 和 FP8 都是浮点数格式它们对指数和尾数部分使用了特定数量的位。 这意味着不同格式的浮点数在数值范围和精度上有所不同这取决于它们分配给指数和尾数的位数。 符号位始终是 1 位。
下面我们来介绍一下 FP32单精度浮点数的格式以及它如何表示不同范围的数值。 上图中展示了FP32 的三个组成部分以及它们各自占用的位数
Sign符号位 1 位红色部分用于表示正负0 为正1 为负。Exponent指数位 8 位绿色部分用于表示数值的范围大小。Fraction尾数位 23 位蓝色部分用于表示数值的精度。Total 总共 32 位对应“单精度”。
上图数轴和下方的公式解释了 FP32 如何表示不同范围的数值
0 零是一个特殊值通常表示为所有位都为 0。Subnormal values非规格化值 指数位全为 0 (E 0) 的情况。 这部分数值非常接近于 0用于表示非常小的数。 其范围大约是 1.4 x 10⁻⁴⁵ 到 1.2 x 10⁻³⁸。 公式为 (-1)ˢ * F * 2⁻¹²⁶其中 S 是符号位。F 是尾数位表示的小数部分。Normal values规格化值 指数位不全为 0 且不全为 1 (E ≠ 0) 的情况。 这是最常用的范围可以表示大多数的浮点数。 其范围大约是 1.2 x 10⁻³⁸ 到 3.4 x 10³⁸。 公式为 (-1)ˢ * (1 F) * 2ᴱ⁻¹²⁷其中 S 是符号位。F 是尾数位表示的小数部分。E 是指数位表示的无符号整数。
数轴上的分段:
数轴上的方括号和数值表示了不同范围的边界
2⁻¹⁴⁹ : 非规格化值的最小值接近于 0。(1 - 2⁻²³)2⁻¹²⁶: 非规格化值的最大值。2⁻¹²⁶: 规格化值的最小值。(1 1 - 2⁻²³)2¹²⁷: 规格化值的最大值接近于 FP32 能表示的最大值。
上图清晰地展示了 FP32 浮点数的格式、不同组成部分的作用以及如何表示不同范围的数值包括非常接近于零的非规格化值和更大范围的规格化值。 它还提供了计算 FP32 数值的公式有助于更深入地理解浮点数的表示方式。
接下来我们再来介绍一下BF16Brain Floating Point的格式以及它如何表示不同范围的数值。 上图中展示了BF16 的三个组成部分以及它们各自占用的位数
Sign符号位 1 位 (红色)表示数值的正负0 为正1 为负。Exponent指数位 8 位 (绿色)表示数值的范围大小。 与 FP32 相同。Fraction尾数位 7 位 (蓝色)表示数值的精度。 比 FP32 的 23 位少。Total 总共 16 位因此被称为“半精度”。
数轴和数值范围
数轴和下方的公式解释了 BF16 如何表示不同范围的数值
0 零是一个特殊值通常表示为所有位都为 0。Subnormal values非规格化值 指数位全为 0 (E 0)。用于表示非常接近 0 的小数。范围大约是 9.2 x 10⁻⁴¹ 到 1.2 x 10⁻³⁸。 公式(-1)ˢ * F * 2⁻¹²⁶其中 S 为符号位。F 为尾数位表示的小数部分 (0 ≤ F 1)。 注意BF16 的尾数位只有 7 位精度比 FP32 低。Normal values规格化值 指数位不全为 0 且不全为 1 (E ≠ 0)。这是最常用的范围。范围大约是 1.2 x 10⁻³⁸ 到 3.4 x 10³⁸。 公式(-1)ˢ * (1 F) * 2ᴱ⁻¹²⁷其中 S 为符号位。F 为尾数位表示的小数部分 (0 ≤ F 1)。E 为指数位表示的无符号整数。
数轴上的分段和边界值
数轴上的方括号和数值表示了不同范围的边界
2⁻¹³³: 非规格化值的最小正值。(1 - 2⁻⁷)2⁻¹²⁶: 非规格化值的最大值也是最小规格化值。2⁻¹²⁶: 规格化值的最小正值。(1 1 - 2⁻⁷)2¹²⁷: 规格化值的最大值。
与FP32的比较
BF16 的指数位宽度与 FP32 相同因此它们表示的数值范围大致相同。BF16 的尾数位宽度比 FP32 小得多因此 BF16 的精度比 FP32 低。BF16 在深度学习训练中被广泛使用因为它在数值范围和精度之间取得了平衡可以减少内存使用和计算量同时保持合理的模型精度。
上图清晰地解释了 BF16 浮点数的格式、数值范围、精度以及如何表示不同类型的数值。 通过与 FP32 的比较可以看出 BF16 牺牲了一定的精度来换取更小的存储空间和更快的计算速度。
接下来我们来介绍一些FP16半精度浮点数的格式、数值范围以及如何表示不同类型的数值包括非规格化值和规格化值 上图中列出了 FP16 的三个组成部分及它们占用的位数
Sign符号位 1 位 (红色)表示数值的正负0 为正1 为负。Exponent指数位 5 位 (绿色)表示数值的范围大小。Fraction尾数位 10 位 (蓝色)表示数值的精度。Total 总共 16 位。
数轴和数值范围
数轴和下方的公式解释了 FP16 如何表示不同范围的数值
0 零是一个特殊值通常表示为所有位都为 0。Subnormal values非规格化值 指数位全为 0 (E 0)。用于表示非常接近 0 的小数。范围大约是 6.0 x 10⁻⁸ 到 6.1 x 10⁻⁵。 公式(-1)ˢ * F * 2⁻¹⁴其中 S 为符号位。F 为尾数位表示的小数部分 (0 ≤ F 1)。Normal values规格化值 指数位不全为 0 且不全为 1 (E ≠ 0)。这是最常用的范围。范围大约是 6.1 x 10⁻⁵ 到 6.5 x 10⁴。 公式(-1)ˢ * (1 F) * 2ᴱ⁻¹⁵其中 S 为符号位。F 为尾数位表示的小数部分 (0 ≤ F 1)。E 为指数位表示的无符号整数。
数轴上的分段和边界值
数轴上的方括号和数值表示了不同范围的边界
2⁻²⁴: 非规格化数的最小正值。(1 - 2⁻¹⁰)2⁻¹⁴: 非规格化数的最大值也是最小规格化值。2⁻¹⁴: 规格化数的最小正值。(1 1 - 2⁻¹⁰)2¹⁵: 规格化数的最大值。
与FP32的比较
FP16 的指数位和尾数位都比 FP32 少因此 FP16 的数值范围和精度都比 FP32 低。FP16 在深度学习训练中被广泛使用因为它比 FP32 占用更少的内存和带宽可以加快训练速度。 但是使用 FP16 需要注意数值范围和精度 limitations避免出现下溢或上溢等问题。
总而言之上图清晰地解释了 FP16 浮点数的格式、不同组成部分的作用以及如何表示不同范围的数值。 通过与 FP32 的比较可以看出 FP16 牺牲了数值范围和精度来换取更小的存储空间和更快的计算速度。
三种浮点数数据类型的比较(FP32、FP16 和 BF16)
这里主要比较FP32、FP16 和 BF16的精度和最大值,如下图所示 上图的表格中有三列
Data Type数据类型 列出了三种浮点数类型FP32单精度、FP16半精度和 BF16Brain Floating Point。Precision精度 比较了三种数据类型的精度。FP32精度最高 (best)。FP16精度次之 (better)。BF16精度较低但仍然不错 (good)。maximum最大值 比较了三种数据类型可以表示的最大值。FP32最大值约为 10³⁸。FP16最大值约为 10⁴。BF16最大值约为 10³⁸。
上图清晰地展示了 FP32、FP16 和 BF16 之间在精度和数值范围上的差异。FP32 提供最高的精度和最大的数值范围但需要更多的存储空间。FP16 降低了精度和数值范围以节省存储空间。BF16 则在数值范围和精度之间取得了平衡在保持与 FP32 相似数值范围的同时降低了精度以减少存储空间和计算成本使其成为深度学习训练中的常用选择。这也说明 BF16 在某些应用场景下的优势。