浮点数在计算机中的存储方式由符号位(sign)、指数位(exponent)和小数位(fraction,也称为尾数、mantissa)组成。以下是对这些部分的详细说明:
-
符号位(Sign Bit):
- 占用1个位元,用于表示浮点数的正负。
- 如果符号位为0,则表示浮点数为正数;如果符号位为1,则表示浮点数为负数。
-
指数位(Exponent Bits):
- 指数位的数量取决于浮点数的精度,例如单精度浮点数(32位)有8个指数位,而双精度浮点数(64位)有11个指数位。
- 指数位用于表示浮点数的范围。为了便于表示正负指数,通常使用“偏移指数”或“偏移值”(Bias)来编码指数。例如,对于单精度浮点数,偏移值为127;对于双精度浮点数,偏移值为1023。
- 实际的指数值计算方式为:存储的指数值减去偏移值。
-
小数位(Fraction Bits):
- 小数位数决定了浮点数的精确度,即可以表示的有效数字的数量。
- 在IEEE 754标准中,小数部分是归一化的(Normalized),即最高位通常为1(在单精度和双精度浮点数中),这一位不存储,而是默认为1。这种存储方法被称为“隐式位”(Implicit Bit)。
- 例如,在单精度浮点数中,小数部分实际上是24位长(包括隐式位),但只有23个位被显式存储。
不同精度浮点数格式
以下是几种常见数值精度的介绍,包括fp64、fp32、fp16、bf16、int8和int4:
1. FP64(Double Precision, 64-bit Floating Point)
- 表示范围:约 ±1.8 × 10³⁰⁸
- 精度:15-17位十进制数字
- 结构:
- 符号位:1位
- 指数位:11位
- 小数位:52位
- 特点:FP64提供了非常高的精度,适用于需要高精度计算的科学和工程应用,但计算资源消耗大,性能较低。
2. FP32(Single Precision, 32-bit Floating Point)
- 表示范围:约 ±3.4 × 10³⁸
- 精度:6-9位十进制数字
- 结构:
- 符号位:1位
- 指数位:8位
- 小数位:23位
- 特点:FP32是目前最常用的浮点数格式,提供了较好的精度与计算效率的平衡,广泛用于图形处理、神经网络训练等领域。
3. FP16(Half Precision, 16-bit Floating Point)
- 表示范围:约 ±6.5 × 10⁴
- 精度:3-4位十进制数字
- 结构:
- 符号位:1位
- 指数位:5位
- 小数位:10位
- 特点:FP16具有较低的精度和表示范围,但计算速度快,内存占用小,常用于混合精度训练和推理加速。
4. BF16(Brain Floating Point, 16-bit Floating Point)
- 表示范围:约 ±3.4 × 10³⁸
- 精度:与FP32相同的范围,但精度较低
- 结构:
- 符号位:1位
- 指数位:8位
- 小数位:7位
- 特点:BF16与FP32拥有相同的指数位数量,因此表示范围相同,但小数位减少到7位。虽然精度较低,但它更适合深度学习训练,因为它的范围更广且与FP32兼容。
5. INT8(8-bit Integer)
- 表示范围:-128到127(有符号),0到255(无符号)
- 精度:整数
- 结构:
- 全部用于表示整数值(无符号或有符号)
- 特点:INT8是一种定点数格式,用于对浮点数的量化,极大地减少了模型的存储需求和计算量。常用于推理阶段的加速和模型部署。
6. INT4(4-bit Integer)
- 表示范围:-8到7(有符号),0到15(无符号)
- 精度:整数
- 结构:
- 全部用于表示整数值(无符号或有符号)
- 特点:INT4进一步压缩了数据表示范围和存储需求,适用于极低功耗和高效推理的场景。它是浮点数的极端量化形式,但由于其表示范围极小,通常需要结合专门的算法进行使用。
混合精度训练
在这里的混合精度训练,指代的是单精度 float和半精度 float16 混合。
float16和float相比恰里,总结下来就是两个原因:内存占用更少,计算更快。
Float16的问题:
1.数据溢出问题:Overflow / Underflow
2.舍入误差(Rounding Error)
解决方法
1.FP32 权重备份
这种方法主要是用于解决舍入误差的问题。其主要思路,可以概括为:weights, activations, gradients 等数据在训练中都利用FP16来存储,同时拷贝一份FP32的weights,用于更新
这主要是因为,在更新权重的时候,往往公式: 权重 = 旧权重 + lr * 梯度,而在深度模型中,lr * 梯度 这个值往往是非常小的,如果利用 fp16 来进行相加的话, 则很可能会出现上面所说的『舍入误差』的这个问题
2.Loss Scale
训练到了后期,梯度(特别是激活函数平滑段的梯度)会特别小,fp16 表示容易产生 underflow 现象。为了解决梯度过小的问题,论文中对计算出来的loss值进行scale,由于链式法则的存在,loss上的scale会作用也会作用在梯度上
3.提高算数精度
利用fp16进行乘法和存储,利用fp32来进行加法计算。 这么做的原因主要是为了减少加法过程中的舍入误差,保证精度不损失。
流程
模型量化
模型量化是指以较低的推理精度损失将连续取值(通常为float32或者大量可能的离散值)的浮点型权重近似为有限多个离散值(通常为int8)的过程。
通过以更少的位数表示浮点数据,模型量化可以减少模型尺寸,进而减少在推理时的内存消耗,并且在一些低精度运算较快的处理器上可以增加推理速度。
GPTQ(Gradient-based Post-Training Quantization)
GPTQ 是一种后训练量化(PTQ)技术,旨在对模型权重进行4-bit的量化,主要用于提高GPU推理性能。GPTQ通过最小化每个权重的均方误差来实现压缩。在推理时,模型动态地将权重从int4反量化为float16,这样可以在保持低内存使用的同时提高性能。GPTQ的一个显著优势是,它能够在不显著影响模型精度的情况下,大幅度减少模型的大小和内存占用【37†source】【38†source】【40†source】。
GPTQ的主要步骤包括:
- 初始化:从预训练模型中加载数据,并设置量化配置(如位数、量化组大小等)。
- 权重量化:使用某种策略逐层处理权重矩阵,最小化量化引入的误差。
- 保存和反量化:在推理时,将量化后的权重动态反量化以执行计算。
AWQ(Activation-aware Weight Quantization)
AWQ 是一种激活感知的权重量化方法,旨在识别并保留那些对模型性能至关重要的权重。AWQ通过分析激活值的重要性,跳过一小部分对模型性能影响较小的权重,从而减少量化损失。这种方法尤其在4-bit量化下表现出色,能够在保持高性能的同时实现显著的加速【37†source】【39†source】。
AWQ的关键特点包括:
- 激活感知:识别出对模型输出影响较大的激活值,并优先进行量化。
- 选择性量化:仅对一部分权重进行量化,从而减少因量化带来的性能损失。
应用场景
这两种量化方法主要应用于大规模语言模型的部署和推理,尤其是在资源受限的环境下。GPTQ适用于需要极致压缩和推理加速的场景,而AWQ在需要保留重要模型特征的情况下效果更佳。
参考:
https://zhuanlan.zhihu.com/p/103685761
https://lulaoshi.info/deep-learning/train-speedup/mixed-precision.html#%E6%B7%B7%E5%90%88%E7%B2%BE%E5%BA%A6%E8%AE%AD%E7%BB%83
https://blog.csdn.net/wjjc1017/article/details/136274364
https://blog.csdn.net/qq_43814415/article/details/138316945
https://zhuanlan.zhihu.com/p/627436535
https://zhuanlan.zhihu.com/p/657886517
https://blog.csdn.net/penriver/article/details/136411485