大模型数值精度

0 阅读9分钟

1 大模型数值精度

1.1 计算机对小数的表达方式

以8.56,FP16精度为例

  • 符号位 (Sign, S)(1)signal(-1)^{signal},0代表正数,1代表负数,这里S=1。
  • 指数位 (Exponent, E):计算机是二进制,所以进行区间长度的确认也是2的幂级进行划分,如2^(-2)~2^(-1)区间长度是[0.25,0.5)[0.25,0.5),下一个区间是2^(-1)~2^(0)区间长度是[0.5,1),区间长度由小逐步变大的。指数位的范围是2的5次方,偏置项是2^(5-1)-1 = 15 那8.56就是处于2^(3)~2^(4)之间,指数位为3, 3+15(BIAS),即最后的指数位结果为18,E=18.
  • 尾数位 (Mantissa/Fraction, M):rem = 8.56 - 2 ^ 3 = 0.56, 区间长度为2^4 - 2^3 = 8,将8切分为1024(1024是FP16约定的份数)份。 D=8/1024=0.0078,0.56大约为72份,72*8/1024=0.5625,尾数位为72,M=72
  • 计算机表示:fp16就用8.5625近似表示8.56,符号位为0,指数位表示18,尾数位表示72,二进制表示为0_10010_0001001000。

在这里需要注意的是,由2^(-2)~2^(-1)2^(-1)~2^(0)再到2^(0)~2^(1),数字越大,区间宽度越宽,这个时候再进行1024份划分,精度可能会下降,即数值越大,精度可能越低

1.2 FP16

  • 内存占用:8bit一个字节,一共占用2个字节;
  • 位数划分:符号位1,指数位5,尾数位10
  • 偏置值 (Bias)::对于 5 位指数,偏置值为 2511=152^{5−1}−1=15
  • 特殊指数值的规定:根据 IEEE 754 标准:E = 0 (00000):  用于表示 0 或 非规格化数 (Subnormal numbers)E = 31 (11111):  用于表示 无穷大 (Infinity)  或 非数 (NaN)1 ≤ E ≤ 30:  用于表示 规格化数 (Normalized numbers)
  • FP16精度转数值计算公式Value=(1)S×2E15×(1+M/210)Value=(−1)^S×2^{E−15}×(1+M/{2^{10}})
  • 范围:最大值为0 11110 1111111111215(1+1023/1024)=215(2210)=21625=6553632=655042^{15}*(1+1023/1024) = 2^{15}*(2-2^{-10})=2^{16}-2^5=65536−32=65504最大值65504,最小值为-65504;最大正数为65504,最小规格化正数为0 00001 0000000000,即2115(1+0/1024)=2142^{1-15}*(1+0/1024) = 2^{-14}最小规格化正数为2142^{-14},这是保持标准精度的最小值;
  • 应用场景:模型训练,相比BF16精度更好,但是数值范围也相对较小

1.3 BF16

  • 内存占用:8bit 一个字节,一共占用 2 个字节;
  • 位数划分:符号位 1,指数位 8,尾数位 7
  • 偏置值 (Bias) :对于 8 位指数,偏置值为 2811=1272^{8−1}−1=127 。
  • 特殊指数值的规定1 ≤ E ≤ 254:  用于表示 规格化数 (Normalized numbers)
  • BF16 精度转数值计算公式Value=(1)S×2E127×(1+M/27)Value=(−1)S×2^{E−127}×(1+M/2^7)
  • 范围:最大值为0 11111110 1111111,2127×(1+127/128)=2127×(227)=212821203.39×10382^{127}×(1+127/128)=2^{127}×(2−2^{−7})=2^{128}−2^{120}≈3.39×10^{38} 即最大值约 3.39e38,最小值为 -3.39e38;最大正数为 3.39e38,最小规格化正数为0 00000001 0000000,即 21127×(1+0/128)=21262^{1−127}×(1+0/128)=2^{−126} ,最小规格化正数为 21262^{−126} ,这是保持标准精度的最小值;
  • 应用场景:深度学习训练(如 Google TPU),相比 FP16 数值范围更大(与 FP32 相同),不易溢出,但精度略低于 FP16。

1.4 FP32

  • 内存占用:8bit 一个字节,一共占用 4 个字节;
  • 位数划分:符号位 1,指数位 8,尾数位 23
  • 偏置值 (Bias) :对于 8 位指数,偏置值为 2811=1272^{8−1}−1=127
  • 特殊指数值的规定1 ≤ E ≤ 254: 用于表示 规格化数 (Normalized numbers)
  • FP32 精度转数值计算公式Value=(1)S×2E127×(1+M/223)Value=(−1)^S×2E−127×(1+M/223)
  • 范围:最大值为0 11111110 111111111111111111111112127×(1+(2231)/223)=2127×(2223)=212821043.4028235×10382^{127}×(1+(223−1)/223)=2^{127}×(2−2^{−23})=2^{128}−2^{104}≈3.4028235×1038 即最大值约 3.40e38,最小值为 -3.40e38;最大正数为 3.40e38,最小规格化正数为0 00000001 00000000000000000000000,即 21−127×(1+0/223)=2−126 ,最小规格化正数为 2−126 ,这是保持标准精度的最小值;
  • 应用场景:通用 CPU/GPU 计算,高精度模型训练与推理,科学计算,数值稳定性最好,但内存占用和计算成本较高。

1.5 INT8

  • 内存占用:8bit 一个字节,一共占用 1 个字节;
  • 位数划分:符号位 1,数值位 7(有符号整型);
  • 偏置值 (Bias):无 (量化推理中通常使用 Scale 缩放因子和 ZeroPoint 零点偏移);
  • 特殊指数值的规定:无 (不支持 NaN/Inf,溢出即 wrap-around 或饱和);
  • INT8 量化转数值计算公式Value=Scale×(Int8ZeroPoint)Value = Scale \times (Int8 - ZeroPoint) (反量化公式);
  • 范围:最大值为01111111,即 271=1272^7 - 1 = 127最大值 127,最小值为 -12810000000);若用于量化,实际表示的浮点范围取决于 Scale,通常映射到 [-1, 1] 或激活值范围;最小精度间隔为 1(原始整型),量化后精度为 Scale;
  • 应用场景:模型推理量化(Post-training Quantization),边缘设备部署,显著减少内存带宽压力,计算速度快,但训练阶段通常不使用。

1.6 INT4

  • 内存占用:4bit 半个字节,2 个数值占用 1 个字节;
  • 位数划分:符号位 1,数值位 3(有符号整型);
  • 偏置值 (Bias):无 (量化推理中通常使用 Scale 缩放因子和 ZeroPoint 零点偏移);
  • 特殊指数值的规定:无 (不支持 NaN/Inf);
  • INT4 量化转数值计算公式Value=Scale×(Int4ZeroPoint)Value = Scale \times (Int4 - ZeroPoint) (反量化公式);
  • 范围:最大值为0111,即 231=72^3 - 1 = 7最大值 7,最小值为 -81000);若用于量化,实际表示的浮点范围取决于 Scale;最小精度间隔为 1(原始整型),量化后精度为 Scale;
  • 应用场景:超大语言模型(LLM)推理量化(如 LLM.int4()),显存极度受限场景,精度损失较 INT8 更大,通常需要混合精度或异常值处理来弥补。

1.7 NF4 (Normal Float 4)

  • 内存占用:4bit 半个字节,2 个数值占用 1 个字节;
  • 位数划分:4 位索引位(无独立符号/指数/尾数,基于查找表);
  • 偏置值 (Bias):无 (基于正态分布的分位数进行量化);
  • 特殊指数值的规定:无 (16 个离散浮点值,不支持 NaN/Inf);
  • NF4 转数值计算公式Value=Scale×LookupTable[Index]Value = Scale \times LookupTable[Index] (Index 为 4 位二进制对应的 0-15 整数);
  • 范围:最大值为1111(对应查找表中最大值),最大值约 1.0,最小值约 -1.0;NF4 专门针对权重服从正态分布的特性设计,16 个量化级别在 0 附近分布更密集,最小精度间隔在 0 附近更优,这是 QLoRA 技术的核心,相比 INT4 能更好地保留权重信息;
  • 应用场景:大模型高效微调(QLoRA),在保持 4bit 低内存占用的同时,比 INT4 更好地保留模型精度,适合显存有限的微调任务。

1.8 精度差异总结

常见数值精度的差异

数据类型总位数符号位指数位尾数位 / 量化方式动态范围(最小正数 ~ 最大正数)精度(约十进制位数)典型应用
FP323218231.4×10⁻⁴⁵ ~ 3.4×10³⁸7 位通用计算,训练基线
FP161615105.96×10⁻⁸ ~ 65,5043~4 位混合精度训练,推理
BF1616187同 FP32(约 1.2×10⁻³⁸ ~ 3.4×10³⁸)2~3 位大模型训练(如 TPU、Ampere GPU)
INT881-均匀整数(无小数)-128 ~ 127(有符号)或 0~255(无符号)1 位(整数值)推理加速,量化部署
INT441-均匀整数(无小数)-8 ~ 7(有符号)或 0~15(无符号)1 位(整数值)超大语言模型(LLM)推理量化
NF44隐式对称-基于正态分布分位数的 16 个离散值依赖于缩放因子(通常归一化到约 [-1, 1])约 1 位(log10(16)≈1.2)4 比特量化微调(QLoRA)

2 数值带来的问题

2.1 溢出问题

  • 上溢出:数值区间都是左闭右开,比如FP16的最大保留精度正数是65504,一直到2152^{-15},也就是在[65505,65536)之间都是可以被FP16表示的,但是一旦大于等于65536就产生上溢出,表现为inf
  • 下溢出:同理,比如0.000000001,0.000000001 小于 FP16 所能表示的最小正数(约 5.96e-8),因此在 FP16 精度下会下溢为 0,如果此时一个数字除以该数字,就会造成上溢出。

2.2 NAN的产生

NaN 和 Inf 是相关的。通常先是数值溢出变成 Inf(无穷大),然后 Inf 参与进一步的计算(如 Inf 减去 Inf,或者 Inf 乘以 0)就变成了 NaN

  • 梯度爆炸:在深度神经网络中,梯度在反向传播过程中不断累积乘法,如果网络权重初始化不当或学习率过高,梯度值会变得极大,超出数值精度范围(即上溢),就会变成无穷大(Inf)。Inf 参与任何后续运算(如加法、乘法、梯度更新)都会直接导致 NaN。
  • 除零错误:在损失函数、归一化层(如 BatchNorm)或自定义运算中,如果分母在计算过程中恰好变成了 0,就会产生 NaN
  • 对负数进行 log 运算:损失函数中经常使用对数运算(如交叉熵损失),如果模型的输出经过 Softmax 后,由于数值问题(如下溢)导致某个类别的预测概率恰好是 0,那么计算 log⁡(0)log(0) 就会得到负无穷大(-Inf),-Inf 参与后续运算(如求和、平均)后就会变为 NaN。

2.3 舍入误差

梯度即便不会出现上/下溢出,如果梯度值和模型的参数值相差太远,也会发生舍入误差的问题,假设模型参数weight=232^{-3},学习率η=222^{-2},梯度gradient=212,weight=weight+n×gradient=23+22×212=23gradient=2^{-12}, weight'= weight +n × gradient =2^{-3} +2^{-2}×2^{-12} =2^{-3}

2.4 解决方案

  • 缩放

image.png

3 大模型显存占用

  • 1.模型状态(model states):模型参数(fp16) + 模型梯度(fp16) + Adam状态(fp32的模型参数备份,fp32的momentum和fp32的variance);假设模型参数量为Φ,则共需要2Φ+2Φ+(4Φ+4Φ+4Φ)=4Φ+12Φ=16Φ字节存储,可以看到,Adam状态占比75%。
  • 2.剩余状态(residual states):除了模型状态之外的显存占用,包括激活值(activation)、各种临时缓冲区(buffer)以及无法使用的显存碎片(fragmentation)。 GPT-2含有1.5B个参数,如果用fp16格式(只需要3GB显存)但是模型状态实际上需要耗费24GB,也就是一般来说进行全参数微调需要的显存占用是模型本身占用的8倍。