fp32-int4系列数据格式深入理解
晚上在看一篇公众号的文章讲的是不同精度类型的数据让我顿生了很多疑惑以前做的大多是传统的深度学习模型想要进行训练更新梯度都是需要使用浮点类型才可以的但是现在是大模型的时代很多都是int类型的微调这里主要就是记录学习的内容。核心困惑INT4量化不是只能用于推理吗为什么QLoRA还能用INT4训练一句话解答存储精度 ≠ 计算精度。INT4用于存储权重省显存计算时转换为FP16/BF16保精度。两者可以同时存在。文档定位从零基础完整理解深度学习中各种数值格式的原理、区别和使用场景。目录第一章从零理解计算机中的数第二章浮点数格式家族第三章整数格式家族第四章所有格式总览对比第五章存储精度 vs 计算精度——解开核心困惑第六章量化技术详解第七章QLoRA的INT4训练原理——彻底搞懂第八章实战——如何选择数据格式第九章PyTorch中的精度控制附录术语表第一章从零理解计算机中的数1.1 计算机只能存储0和1计算机的所有数据最终都是二进制0和1 数字 5 → 二进制 00000101 字母 A → 二进制 01000001 小数 3.14 → 二进制 问题来了小数浮点数怎么用0和1表示 这就是浮点数格式要解决的问题。1.2 定点数 vs 浮点数定点数(Fixed Point) 小数点位置固定 例用16位表示小数前8位整数后8位小数 00000011.00101100 3.171875 问题范围小精度固定 浮点数(Floating Point) 小数点位置可以浮动 类似科学计数法3.14 3.14 × 10⁰ 0.314 × 10¹ 在计算机中用二进制的科学计数法 1.575 1.575 × 2⁰ 1.1001 × 2⁰ 浮点数 符号位 指数位 尾数位1.3 IEEE 754标准IEEE 754是最通用的浮点数标准定义了多种精度格式 FP32单精度1位符号 8位指数 23位尾数 32位 FP64双精度1位符号 11位指数 52位尾数 64位 符号位(Sign)0表示正数1表示负数 指数位(Exponent)表示数量级类似10^x中的x 尾数位(Mantissa)表示有效数字类似3.14中的314FP32的位结构┌─────────────────────────────────────────────────────────────┐ │ FP32 (32位) 结构 │ │ │ │ 位号: 31 30 29 28 27 26 25 24 23 22 21 ... 0 │ │ ┌───┬──────────────────────┬──────────────────────────┐│ │ │ S │ E (指数) │ M (尾数) ││ │ │1位│ 8位 │ 23位 ││ │ └───┴──────────────────────┴──────────────────────────┘│ │ │ │ 值 (-1)^S × 2^(E-127) × (1.M) │ │ │ │ 示例: 3.14 在FP32中的表示 │ │ S 0 (正数) │ │ E 128 (即2^1 2) │ │ M 1.57 的二进制 ≈ 1.10010001111010111000011 │ │ 0 10000000 10010001111010111000011 │ │ │ │ 范围: ±3.4 × 10^38 │ │ 精度: 约7位有效十进制数字 │ │ 大小: 4 字节 │ └─────────────────────────────────────────────────────────────┘第二章浮点数格式家族2.1 FP32——单精度浮点传统默认┌─────────────────────────────────────────────────────────────┐ │ FP32 格式 │ │ │ │ 位结构: [S 1位][E 8位][M 23位] 共32位 4字节 │ │ │ │ ┌───┬────────┬───────────────────────────────────────┐ │ │ │ S │ EEEEEEEE│ MMMMMMMMMMMMMMMMMMMMMMMMM │ │ │ │1位│ 8位 │ 23位 │ │ │ └───┴────────┴───────────────────────────────────────┘ │ │ │ │ 范围: ±3.4 × 10^38 │ │ 精度: 约7位有效十进制数字 │ │ 最小值: ~1.2 × 10^-38 │ │ 每个参数: 4字节 │ │ │ │ 7B模型显存: 7B × 4B 28GB │ │ │ │ 优点: 精度最高范围大不会溢出 │ │ 缺点: 显存占用大计算速度相对较慢 │ │ 用途: 传统训练默认格式现在一般被BF16替代 │ └─────────────────────────────────────────────────────────────┘2.2 TF32——TensorFloat-32Ampere架构引入┌─────────────────────────────────────────────────────────────┐ │ TF32 格式 │ │ │ │ 位结构: [S 1位][E 8位][M 10位] 共19位 │ │ │ │ ┌───┬────────┬──────────────┐ │ │ │ S │ EEEEEEEE│ MMMMMMMMMM │ │ │ │1位│ 8位 │ 10位 │ │ │ └───┴────────┴──────────────┘ │ │ │ │ 范围: 和FP32相同±3.4 × 10^38 │ │ 精度: 和FP16类似约3位有效十进制数字 │ │ │ │ 关键特性: │ │ - 只在Tensor Core内部使用 │ │ - 代码层面仍是FP32输入输出 │ │ - 硬件自动将FP32截断为TF32进行矩阵运算 │ │ - 用户无需修改代码 │ │ - Ampere(A100/RTX3090)架构自动启用 │ │ │ │ 用途: FP32训练的隐形加速器无需改代码 │ └─────────────────────────────────────────────────────────────┘2.3 FP16——半精度浮点┌─────────────────────────────────────────────────────────────┐ │ FP16 格式 │ │ │ │ 位结构: [S 1位][E 5位][M 10位] 共16位 2字节 │ │ │ │ ┌───┬──────┬──────────────┐ │ │ │ S │ EEEEE│ MMMMMMMMMM │ │ │ │1位│ 5位 │ 10位 │ │ │ └───┴──────┴──────────────┘ │ │ │ │ 范围: ±65,504 │ │ 精度: 约3位有效十进制数字 │ │ 每个参数: 2字节 │ │ │ │ 7B模型显存: 7B × 2B 14GB ← 比FP32省一半 │ │ │ │ 优点: 显存省一半计算速度快Tensor Core加速 │ │ 缺点: 范围小±65,504容易溢出 │ │ 梯度可能超出范围 → 需要loss scaling │ │ │ │ 用途: 混合精度训练配合loss scaling使用 │ └─────────────────────────────────────────────────────────────┘FP16的溢出问题FP16的最大值: 65,504 问题场景 某些中间计算结果可能超过65,504 例梯度累积过程中梯度值可能很大 → 溢出为 inf (无穷大) → NaN → 训练崩溃 解决方案1Loss Scaling损失缩放 将loss乘以一个大数如1024让梯度也变大 反向传播后将梯度除以同样的数恢复 → 确保小梯度不会因精度不足变成0 解决方案2使用BF16范围更大不会溢出2.4 BF16——Brain Float 16Google Brain提出┌─────────────────────────────────────────────────────────────┐ │ BF16 格式 │ │ │ │ 位结构: [S 1位][E 8位][M 7位] 共16位 2字节 │ │ │ │ ┌───┬────────┬───────────┐ │ │ │ S │ EEEEEEEE│ MMMMMMM │ │ │ │1位│ 8位 │ 7位 │ │ │ └───┴────────┴───────────┘ │ │ │ │ 范围: 和FP32相同±3.4 × 10^38 │ │ 精度: 约2位有效十进制数字精度较低 │ │ 每个参数: 2字节 │ │ │ │ 7B模型显存: 7B × 2B 14GB │ │ │ │ 优点: 范围大不溢出 显存省一半 不需要loss scaling │ │ 缺点: 精度比FP16低 │ │ │ │ → AI训练的首选格式2024-2026年主流 │ └─────────────────────────────────────────────────────────────┘BF16 vs FP16 对比图┌─────────────────────────────────────────────────────────────┐ │ BF16 vs FP16 核心区别 │ │ │ │ FP16: [S][EEEE E][MMMM MMMMMM] │ │ 指数5位 → 范围小±65,504但尾数10位 → 精度高 │ │ │ │ BF16: [S][EEEE EEEE][MMMM MMM] │ │ 指数8位 → 范围大±3.4e38但尾数7位 → 精度低 │ │ │ │ 直觉理解 │ │ FP16 像一把短尺子量程小但刻度细 │ │ BF16 像一把长尺子量程大但刻度粗 │ │ │ │ 对于AI训练 │ │ 范围比精度更重要 → 选BF16 │ │ 梯度值变化范围很大 → FP16容易溢出 → BF16不会 │ └─────────────────────────────────────────────────────────────┘2.5 FP8——8位浮点Hopper架构引入┌─────────────────────────────────────────────────────────────┐ │ FP8 格式 │ │ │ │ FP8有两种变体 │ │ │ │ E4M3推理/前向传播 │ │ ┌───┬──────┬───────┐ │ │ │ S │ EEEE │ MMM │ │ │ │1位│ 4位 │ 3位 │ 共8位 1字节 │ │ └───┴──────┴───────┘ │ │ 范围: ±448 精度: 较高 │ │ │ │ E5M2训练/梯度 │ │ ┌───┬───────┬──────┐ │ │ │ S │ EEEEE │ MM │ │ │ │1位│ 5位 │ 2位 │ 共8位 1字节 │ │ └───┴───────┴──────┘ │ │ 范围: ±57344 精度: 较低 │ │ │ │ 7B模型显存: 7B × 1B 7GB ← 只需FP32的1/4 │ │ │ │ 用途: H100/B200等Hopper架构的训练和推理加速 │ │ 需要: Hopper(H100) 或 Ada Lovelace(RTX4090)架构 │ └─────────────────────────────────────────────────────────────┘为什么FP8有两种变体前向传播E4M3精度优先 计算模型输出需要较高精度 范围较小±448但精度较高3位尾数 反向传播E5M2范围优先 计算梯度值可能很大或很小 范围较大±57344但精度较低2位尾数 → 和BF16的逻辑一样梯度需要更大范围2.6 FP4——4位浮点Blackwell架构引入┌─────────────────────────────────────────────────────────────┐ │ FP4 格式 │ │ │ │ ┌───┬─────┬───┐ │ │ │ S │ EEE │ M │ │ │ │1位│ 3位 │0位│ 共4位 0.5字节 │ │ └───┴─────┴───┘ │ │ │ │ 7B模型显存: 7B × 0.5B 3.5GB ← 只需FP32的1/8 │ │ │ │ 用途: Blackwell(B200)架构的推理加速 │ │ 精度: 很低通常只用于推理不用于训练 │ └─────────────────────────────────────────────────────────────┘第三章整数格式家族3.1 整数格式基础整数格式没有指数位只有符号位数值位 INT32: [S 1位][数值 31位] 范围: ±2,147,483,647 INT16: [S 1位][数值 15位] 范围: ±32,767 INT8: [S 1位][数值 7位] 范围: ±127 (或0-255无符号) INT4: [S 1位][数值 3位] 范围: ±7 (或0-15无符号) INT格式的特点 - 不能表示小数只有整数 - 需要额外的缩放因子来映射到实际值 - 计算速度快整数运算比浮点快 - 显存极省3.2 INT8——8位整数量化┌─────────────────────────────────────────────────────────────┐ │ INT8 格式 │ │ │ │ 位结构: [S 1位][数值 7位] 共8位 1字节 │ │ 或无符号: [数值 8位] 共8位 1字节 │ │ │ │ ┌──┬─────────┐ │ │ │S │ DDDDDDD │ 有符号INT8: -128 到 127 │ │ │1位│ 7位 │ │ │ └──┴─────────┘ │ │ ┌──────────────┐ │ │ │ DDDDDDDD │ 无符号INT8: 0 到 255 │ │ │ 8位 │ │ │ └──────────────┘ │ │ │ │ 7B模型显存: 7B × 1B 7GB │ │ │ │ 关键问题INT8怎么表示小数 │ │ → 量化(Quantization)用缩放因子将浮点数映射到整数 │ │ → 实际值 INT8值 × 缩放因子(scale) │ │ │ │ 示例 │ │ 原始权重范围: [-1.5, 2.0] │ │ 映射到INT8: [-128, 127] │ │ scale (2.0 - (-1.5)) / (127 - (-128)) 0.0137 │ │ 原始值 0.5 → INT8值 round(0.5 / 0.0137) 36 │ │ 反量化: 36 × 0.0137 0.4932 ≈ 0.5 │ │ │ │ 精度损失: ~0.2%通常可接受 │ │ │ │ 用途: 模型推理部署主流方案 │ │ 工具: bitsandbytes, GPTQ, AWQ │ └─────────────────────────────────────────────────────────────┘3.3 INT4——4位整数量化┌─────────────────────────────────────────────────────────────┐ │ INT4 格式 │ │ │ │ 位结构: [S 1位][数值 3位] 共4位 0.5字节 │ │ 或无符号: [数值 4位] 共4位 0.5字节 │ │ │ │ 有符号INT4: -8 到 7 │ │ 无符号INT4: 0 到 15 │ │ │ │ 7B模型显存: 7B × 0.5B 3.5GB ← 只需FP32的1/8 │ │ │ │ INT4量化方式NF4为例 │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ 原始权重分布通常是正态分布 │ │ │ │ │ │ │ │ ╭────╮ │ │ │ │ ╱ ╲ │ │ │ │ ╱ ╲ │ │ │ │ ╱ ╲ │ │ │ │ ───╱────────────╲─── │ │ │ │ -3 -2 -1 0 1 2 3 │ │ │ │ │ │ │ │ NF4将16个量化级别按正态分布放置 │ │ │ │ 零附近的量化级别更密集因为大部分权重在0附近 │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ 用途: QLoRA微调4-bit量化基座 LoRA适配器训练 │ │ 工具: bitsandbytes (NF4), GPTQ (INT4), AWQ │ └─────────────────────────────────────────────────────────────┘3.4 NF4——NormalFloat 4-bitQLoRA专用┌─────────────────────────────────────────────────────────────┐ │ NF4 格式 │ │ │ │ NF4 NormalFloat 4-bit 正态分布最优的4-bit量化格式 │ │ │ │ 核心洞察 │ │ 神经网络的权重通常服从正态分布均值≈0 │ │ → 量化级别应该按照正态分布来放置 │ │ → 零附近更密集远离零的地方更稀疏 │ │ │ │ 16个NF4量化级别近似值 │ │ -1.0, -0.69, -0.53, -0.39, -0.29, -0.19, -0.10, 0.0, │ │ 0.08, 0.17, 0.27, 0.37, 0.51, 0.67, 0.89, 1.0 │ │ │ │ 对比均匀INT4 │ │ -7/8, -6/8, -5/8, -4/8, -3/8, -2/8, -1/8, 0, │ │ 1/8, 2/8, 3/8, 4/8, 5/8, 6/8, 7/8 │ │ │ │ NF4的优势 │ │ 对正态分布的数据信息论最优 │ │ 量化误差比均匀INT4小约30% │ │ → QLoRA选择NF4而非普通INT4 │ └─────────────────────────────────────────────────────────────┘第四章所有格式总览对比4.1 格式对比表┌────────┬─────┬─────┬──────────┬─────────┬────────┬─────────────┐ │ 格式 │位数 │字节 │ 范围 │ 精度 │7B显存 │ 主要用途 │ ├────────┼─────┼─────┼──────────┼─────────┼────────┼─────────────┤ │ FP32 │ 32 │ 4 │ ±3.4e38 │ 7位有效 │ 28GB │ 传统训练 │ │ TF32 │ 19 │ --- │ ±3.4e38 │ 3位有效 │ --- │ Tensor Core │ │ FP16 │ 16 │ 2 │ ±65,504 │ 3位有效 │ 14GB │ 混合精度训练 │ │ BF16 │ 16 │ 2 │ ±3.4e38 │ 2位有效 │ 14GB │ 训练首选 │ │ FP8 │ 8 │ 1 │ ±448/57K │ 2-3位 │ 7GB │ Hopper训练 │ │ FP4 │ 4 │ 0.5 │ 很小 │ 很低 │ 3.5GB │ Blackwell推理│ │ INT8 │ 8 │ 1 │ ±127 │ 整数 │ 7GB │ 推理部署 │ │ INT4 │ 4 │ 0.5 │ ±7 │ 整数 │ 3.5GB │ QLoRA/推理 │ │ NF4 │ 4 │ 0.5 │ 自定义 │ 整数 │ 3.5GB │ QLoRA专用 │ └────────┴─────┴─────┴──────────┴─────────┴────────┴─────────────┘4.2 显存占用对比7B模型在不同精度下的显存占用 FP32: ████████████████████████████ 28GB FP16: ██████████████ 14GB BF16: ██████████████ 14GB FP8: ███████ 7GB INT8: ███████ 7GB INT4: ████ 3.5GB NF4: ████ 3.5GB 节省比例相对于FP32 FP16/BF16: 省50% FP8/INT8: 省75% INT4/NF4: 省87.5%4.3 精度 vs 范围 二维图┌─────────────────────────────────────────────────────────────┐ │ 精度 vs 范围 二维空间 │ │ │ │ 范围(大) │ │ ↑ │ │ │ FP32 │ │ │ • │ │ │ BF16 • │ │ │ • │ │ │ TF32 • │ │ │ • │ │ │ │ │ │ │ │ │ FP8(E5M2) │ │ │ • │ │ │ │ │ │ FP8(E4M3) │ │ │ • FP16 │ │ │ • │ │ │ │ │ │ │ │ │ │ │ │ INT4/NF4 │ │ │ • INT8 │ │ │ • │ │ └─────────────────────────────────────────────→ 精度(高) │ │ │ │ 理想位置右上角范围大精度高→ 但位数有限必须权衡 │ │ FP32最平衡但最费显存 │ │ BF16用范围换精度 → AI训练最佳权衡 │ └─────────────────────────────────────────────────────────────┘第五章存储精度 vs 计算精度——解开核心困惑5.1 你的困惑INT4量化不是只能用于推理吗为什么QLoRA还能用INT4训练 这个问题的答案在于区分两个概念 1. 存储精度Storage Precision权重用什么格式存在显存中 2. 计算精度Compute Precision实际计算时用什么格式 关键结论 存储精度 ≠ 计算精度 可以用INT4存储权重但在计算时转换为FP16/BF165.2 存储精度 vs 计算精度┌─────────────────────────────────────────────────────────────┐ │ 存储精度 vs 计算精度 分离架构 │ │ │ │ ════════ 标准训练FP32════════ │ │ │ │ 权重存储: FP32 (4字节/参数) │ │ 计算精度: FP32 │ │ 梯度存储: FP32 │ │ 优化器: FP32 │ │ 显存: 7B × 4B × 4 ≈ 112GB │ │ │ │ ════════ 混合精度训练AMP════════ │ │ │ │ 权重存储: FP32 (主副本) FP16/BF16 (计算副本) │ │ 计算精度: FP16/BF16 │ │ 梯度存储: FP16/BF16 │ │ 优化器: FP32维护主副本 │ │ 显存: 约70GB (省约35%) │ │ │ │ ════════ QLoRA训练INT4存储 BF16计算════════ │ │ │ │ 权重存储: NF4/INT4 (0.5字节/参数) ← 只用于存储 │ │ 计算精度: BF16 (2字节/参数) ← 计算时转换为BF16 │ │ LoRA适配器: BF16 (2字节/参数) ← 可训练参数用BF16 │ │ 梯度: BF16只对LoRA参数计算梯度 │ │ 优化器: BF16/FP32只对LoRA参数 │ │ 显存: 约10GB (省约90%) │ │ │ │ 关键理解 │ │ INT4权重 → 反量化为BF16 → 做矩阵乘法(BF16) → 结果(BF16) │ │ 梯度只通过LoRA参数反向传播不更新INT4权重 │ └─────────────────────────────────────────────────────────────┘5.3 不同方法的存储/计算组合┌────────────────┬──────────────┬──────────────┬───────────────┐ │ 方法 │ 权重存储精度 │ 计算精度 │ 可训练参数精度 │ ├────────────────┼──────────────┼──────────────┼───────────────┤ │ 标准FP32训练 │ FP32 │ FP32 │ FP32 │ │ 混合精度(AMP) │ FP32FP16 │ FP16/BF16 │ FP32(主副本) │ │ 纯BF16训练 │ BF16 │ BF16 │ BF16 │ │ QLoRA │ NF4/INT4 │ BF16 │ BF16(LoRA) │ │ INT8推理 │ INT8 │ INT8 │ 无(只推理) │ │ INT4推理 │ INT4/NF4 │ INT4/FP16 │ 无(只推理) │ │ FP8训练 │ FP8 │ FP8 │ FP32(主副本) │ └────────────────┴──────────────┴──────────────┴───────────────┘5.4 类比理解存储精度 vs 计算精度的类比——图书馆管理系统 INT4存储 把书压缩打包放在仓库里省空间 BF16计算 读者借阅时解压打开阅读需要完整内容 仓库里(INT4)书被压缩到最小省存储空间 阅读时(BF16)解压还原为完整版本才能正常阅读 类比到QLoRA 显存中存储的基座模型权重 INT4压缩的书省显存 GPU计算时读取的权重 BF16解压后的书保精度 LoRA适配器 新写的批注用正常字迹BF16书写可以修改 你不能修改压缩的书INT4基座权重冻结 但你可以在书上添加批注LoRA适配器可训练 阅读时书和批注一起看INT4反量化为BF16 LoRA BF16 → 合并输出第六章量化技术详解6.1 什么是量化量化(Quantization) 将高精度数值映射为低精度数值 原始FP320.123456789, -0.987654321, 1.234567890 量化后INT836, -127, 127 需要存储 - 量化后的整数值 - 缩放因子(scale)用于反量化 - 零点偏移(zero_point)某些量化方法需要 反量化原始值 ≈ 量化值 × scale zero_point6.2 量化方法分类┌─────────────────────────────────────────────────────────────┐ │ 量化方法分类 │ │ │ │ 按时间点分类 │ │ ├── 训练后量化(PTQ, Post-Training Quantization) │ │ │ 训练完成后直接量化不需要重新训练 │ │ │ 代表GPTQ, AWQ, bitsandbytes │ │ │ │ │ └── 量化感知训练(QAT, Quantization-Aware Training) │ │ 训练过程中模拟量化效果 │ │ 代表QLoRA的NF4量化训练 │ │ │ │ 按粒度分类 │ │ ├── 逐层量化每层一个scale │ │ ├── 逐通道量化每个输出通道一个scale更精确 │ │ └── 分组量化每g个元素一个scaleQLoRA使用g64 │ │ │ │ 按对称性分类 │ │ ├── 对称量化zero_point 0 │ │ └── 非对称量化zero_point ≠ 0更灵活 │ └─────────────────────────────────────────────────────────────┘6.3 GPTQ——训练后INT4量化GPTQ是一种高效的训练后量化方法 工作原理 1. 逐层量化权重矩阵 2. 用Hessian矩阵信息最小化量化误差 3. 每量化一个权重调整后续权重来补偿误差 特点 - 只需一次前向传播用少量校准数据 - 量化速度快几分钟完成7B模型 - 精度损失小 - 推理时直接使用INT4计算 示例 原始FP16模型14GB→ GPTQ INT4量化 → 量化模型3.5GB 推理精度损失通常1%6.4 AWQ——激活感知量化AWQ Activation-aware Weight Quantization 核心思想 不是所有权重都同等重要 对激活值大的通道对应的权重要保护少量化或高精度 工作原理 1. 分析激活值的分布 2. 找到显著通道激活值大的 3. 对显著通道用更高精度量化 4. 整体精度损失更小 特点 - 精度通常优于GPTQ - 推理速度也很快 - 社区广泛使用6.5 bitsandbytes——QLoRA的量化后端bitsandbytes是HuggingFace生态中最常用的量化库 支持的量化方式 - 8-bit量化用于推理 - 4-bit NF4量化用于QLoRA训练 - 4-bit FP4量化另一种4-bit格式 - 双重量化(Double Quantization)对量化参数也量化 QLoRA的量化过程 1. 加载FP16/BF16模型 2. 对每一层的权重矩阵做NF4量化 3. 量化时使用分组量化每64个元素一个scale 4. scale也进一步量化为FP32双重量化 5. 存储NF4权重 FP32 scale第七章QLoRA的INT4训练原理——彻底搞懂7.1 QLoRA的完整数据流┌─────────────────────────────────────────────────────────────┐ │ QLoRA 完整数据流 │ │ │ │ ═══ 存储状态显存中═══ │ │ │ │ 基座模型权重: NF4格式3.5GB for 7B │ │ ┌──────────────────────────────────────┐ │ │ │ NF4压缩权重 FP32缩放因子 │ │ │ │ 冻结不可训练 │ │ │ └──────────────────────────────────────┘ │ │ │ │ LoRA适配器: BF16格式~50MB for 7B │ │ ┌──────────────────────────────────────┐ │ │ │ A矩阵: [r × hidden_dim] BF16 │ │ │ │ B矩阵: [hidden_dim × r] BF16 │ │ │ │ 可训练 │ │ │ └──────────────────────────────────────┘ │ │ │ │ 优化器状态: BF16/FP32只存LoRA参数的 │ │ ┌──────────────────────────────────────┐ │ │ │ Adam的m和v: BF16 │ │ │ │ 只有LoRA参数的优化器状态 │ │ │ └──────────────────────────────────────┘ │ │ │ │ ═══ 前向传播计算时═══ │ │ │ │ 输入 x (BF16) │ │ │ │ │ ├──→ NF4权重 → 反量化 → BF16权重 → 矩阵乘法 → Wx (BF16) │ │ │ │ │ ├──→ LoRA: x → A(x) → B(A(x)) → BAx (BF16) │ │ │ │ │ └──→ 输出 Wx BAx (BF16) │ │ │ │ ═══ 反向传播计算梯度═══ │ │ │ │ 损失 L (BF16) │ │ │ │ │ ├──→ ∂L/∂A ∂L/∂output × ∂output/∂A (计算LoRA A的梯度)│ │ ├──→ ∂L/∂B ∂L/∂output × ∂output/∂B (计算LoRA B的梯度)│ │ │ │ │ └──→ 不计算∂L/∂W基座权重冻结不更新 │ │ │ │ ═══ 参数更新 ═══ │ │ │ │ A ← A - lr × ∂L/∂A (只更新LoRA参数) │ │ B ← B - lr × ∂L/∂B (只更新LoRA参数) │ │ W 不更新INT4权重保持不变 │ └─────────────────────────────────────────────────────────────┘7.2 反量化过程详解NF4权重存储→ BF16权重计算的过程 步骤1读取NF4值4-bit整数0-15 例NF4值 7 步骤2查NF4量化级别表 第7个级别 0.0NF4设计中中间值对应0 步骤3乘以分组缩放因子 BF16值 0.0 × scale_group 0.0 实际代码bitsandbytes内部 def dequantize_nf4(weight_nf4, scales): # 1. NF4 → FP16/BF16 weight_fp16 nf4_to_fp16(weight_nf4) # 查表 # 2. 乘以缩放因子 weight_fp16 weight_fp16 * scales return weight_fp16 这个反量化操作在每次前向传播时都会执行 计算完成后BF16结果被使用NF4原始值保留在显存中7.3 为什么INT4权重不会被梯度更新核心原因计算图中INT4权重是常量不是变量 PyTorch的autograd机制 可训练参数requires_gradTrue → 反向传播时计算梯度 冻结参数 requires_gradFalse → 不计算梯度不更新 QLoRA中 基座模型权重requires_gradFalseINT4格式冻结 LoRA A矩阵 requires_gradTrueBF16格式可训练 LoRA B矩阵 requires_gradTrueBF16格式可训练 所以 - 梯度只流过LoRA参数 - INT4权重不参与梯度计算 - INT4权重只在前向传播时被读取和反量化7.4 QLoRA的显存分析7B模型在QLoRA训练时的显存分解 基座模型权重(NF4): 7B × 0.5B 3.5 GB 缩放因子(FP32): ~0.2 GB 反量化临时BF16权重: ~14 GB前向传播时临时创建可复用 LoRA适配器(BF16): ~50 MB LoRA梯度(BF16): ~50 MB 优化器状态(BF16): ~100 MB 激活值(BF16): ~5-10 GB取决于batch_size和序列长度 ───────────────────── 总计: 约 10-20 GB ← 单张RTX 3090/4090就能跑 对比 标准BF16训练: 7B × 2B(参数) 2B(梯度) 8B(优化器) 激活 ≈ 70 GB QLoRA训练: 3.5B(NF4) 0.1B(LoRA) 0.15B(优化器) 激活 ≈ 15 GB 节省约80%第八章实战——如何选择数据格式8.1 决策树你的情境是什么 训练模型 ├── 全量微调 │ ├── 显存充足 → BF16首选 │ └── 显存紧张 → DeepSpeed ZeRO BF16 │ ├── LoRA微调 │ ├── 显存充足(24GB per 7B) → BF16基座 BF16 LoRA │ └── 显存紧张(24GB per 7B) → QLoRA (NF4基座 BF16 LoRA) │ └── 推理/部署 ├── 追求精度 → BF16 ├── 平衡精度和速度 → INT8 ├── 极致压缩 → INT4 (GPTQ/AWQ) └── H100硬件 → FP88.2 不同场景的推荐格式┌──────────────────┬──────────────┬─────────────────────────────┐ │ 场景 │ 推荐格式 │ 原因 │ ├──────────────────┼──────────────┼─────────────────────────────┤ │ 全量微调(7B) │ BF16 │ 范围大不溢出省显存 │ │ LoRA微调(7B) │ BF16基座 │ 24GB显存够用精度最高 │ │ QLoRA微调(7B) │ NF4基座 │ 12GB显存即可性价比最高 │ │ 推理部署(高性能) │ BF16/FP16 │ 精度损失极小 │ │ 推理部署(低成本) │ INT8 │ 显存省75%精度损失1% │ │ 推理部署(极致) │ INT4(GPTQ) │ 显存省87.5%精度损失~2% │ │ H100训练 │ FP8 │ 速度提升2x显存省50% │ │ 边缘设备推理 │ INT4(INT4) │ 显存最小适合手机/IoT │ └──────────────────┴──────────────┴─────────────────────────────┘8.3 精度损失对比不同量化方案的精度损失以7B模型为例 BF16基准: 0% 精度损失 INT8量化: ~0.5-1% 精度损失通常可忽略 INT4(GPTQ): ~1-3% 精度损失可接受 INT4(AWQ): ~1-2% 精度损失通常优于GPTQ INT4(NF4 QLoRA): ~2-5% 精度损失但可以微调恢复 FP4: ~3-5% 精度损失 关键点 INT8量化后的模型精度损失通常可以忽略 INT4量化后精度损失明显但配合LoRA微调可以恢复 → 这就是QLoRA的核心价值第九章PyTorch中的精度控制9.1 混合精度训练(AMP)importtorchfromtorch.cuda.ampimportautocast,GradScaler# 创建模型FP32modelMyModel().cuda()optimizertorch.optim.Adam(model.parameters(),lr1e-3)scalerGradScaler()# 用于FP16的loss scalingfordata,targetindataloader:optimizer.zero_grad()# 自动混合精度前向传播用FP16/BF16withautocast(dtypetorch.bfloat16):outputmodel(data)losscriterion(output,target)# 反向传播BF16不需要GradScalerFP16需要scaler.scale(loss).backward()scaler.step(optimizer)scaler.update()# 注BF16不需要GradScaler范围大不溢出# 只有FP16才需要GradScaler9.2 模型加载时指定精度importtorchfromtransformersimportAutoModelForCausalLM# FP32加载默认modelAutoModelForCausalLM.from_pretrained(model_path)# BF16加载推荐modelAutoModelForCausalLM.from_pretrained(model_path,torch_dtypetorch.bfloat16)# FP16加载modelAutoModelForCausalLM.from_pretrained(model_path,torch_dtypetorch.float16)# INT8量化加载推理modelAutoModelForCausalLM.from_pretrained(model_path,load_in_8bitTrue)# INT4/NF4量化加载QLoRA训练fromtransformersimportBitsAndBytesConfig bnb_configBitsAndBytesConfig(load_in_4bitTrue,bnb_4bit_quant_typenf4,bnb_4bit_compute_dtypetorch.bfloat16,# 计算时用BF16bnb_4bit_use_double_quantTrue)modelAutoModelForCausalLM.from_pretrained(model_path,quantization_configbnb_config)9.3 手动控制精度importtorch# 创建BF16张量x_bf16torch.tensor([1.5,2.7,3.14],dtypetorch.bfloat16)# 创建FP16张量x_fp16torch.tensor([1.5,2.7,3.14],dtypetorch.float16)# 精度转换x_fp32x_bf16.float()# BF16 → FP32x_bf16x_fp32.bfloat16()# FP32 → BF16x_fp16x_fp32.half()# FP32 → FP16# 查看张量精度print(x_bf16.dtype)# torch.bfloat16# 模型整体转换精度model_bf16model.bfloat16()# 整个模型转为BF16model_fp16model.half()# 整个模型转为FP16附录术语表FP32: 32位单精度浮点 (1823) FP16: 16位半精度浮点 (1510) BF16: 16位Brain浮点 (187)AI训练首选 TF32: 19位Tensor浮点 (1810)Tensor Core内部使用 FP8: 8位浮点E4M3(推理)和E5M2(训练)两种变体 FP4: 4位浮点Blackwell架构引入 INT32: 32位整数 INT8: 8位整数推理部署主流格式 INT4: 4位整数极致压缩 NF4: NormalFloat 4-bitQLoRA专用的最优4-bit格式 量化(Quantization): 将高精度数值映射为低精度数值 反量化(Dequantization): 将低精度数值还原为高精度 缩放因子(Scale): 量化时的缩放系数 零点(Zero Point): 非对称量化的偏移量 分组量化(Group Quantization): 每g个元素共享一个scale GPTQ: 训练后INT4量化方法 AWQ: 激活感知的INT4量化方法 QLoRA: 4-bit量化基座 LoRA微调 AMP: Automatic Mixed Precision自动混合精度 Loss Scaling: FP16训练时防止梯度下溢的技术 存储精度: 权重在显存中的存储格式 计算精度: GPU实际计算时使用的格式 反量化开销: INT4→BF16转换的额外计算成本