篇2-bitsandbytes-具体观-算法与实现剖析

三部曲之二 · 看懂:潜入水下的技术细节

本篇承接整体观建立的认知地图,潜入水下逐一审视 bitsandbytes 的核心技术实现------LLM.int8()、QLoRA/NF4、8-bit 优化器,以及支撑它们的极致工程。每个技术点遵循"原理(论文)→ 代码 → 例子"三段式剖析,让读者真正"看懂"代码背后的学术根源与工程取舍。


一、总述:三大算法支柱 + 一条工程脊梁

bitsandbytes 的技术内核可拆成三大算法支柱 + 一条工程脊梁 。三大算法支柱是:① LLM.int8() 的混合精度分解;② QLoRA/NF4 的信息论最优 4-bit 数据类型;③ 8-bit Optimizers 的 block-wise 量化状态。一条工程脊梁是:paged memory + 多后端 + Triton 共同支撑的"在任何加速器上都能跑、显存不够也能跑"的可访问性。

这三大算法均来自学术论文,且都在 ICLR/NeurIPS 等顶会发表------bitsandbytes 是少有的"论文即产品"的库,每一行核心代码背后都有可追溯的学术贡献。本篇将逐一展开:先讲原理(引用论文),再讲代码(指明文件与行号),最后用具体数字例子说明。这样做的目的是让读者不仅知道"怎么用",更知道"为什么这样设计"。


二、分述:五大技术点逐一剖析

2.1 LLM.int8():vector-wise 量化 + 离群值分离

【原理】 LLM.int8() 的核心洞见是:大模型推理时的量化误差不是均匀分布的,而是被少数"离群值特征"(outlier features)主导------这些特征的激活值远大于其他列,一旦被压到 int8 就会剧烈放大其他列的相对误差。论文给出了一个让人印象深刻的数字:当模型规模超过 6.7B 时,约 0.1% 的列承载了 99.9% 的量化误差。

对策是"混合精度分解"(mixed-precision decomposition):① 对绝大多数列用 vector-wise absmax 量化------每行算一个 absmax 缩放因子,把行内值归一化到 -127, 127 后存 int8;② 对超过阈值 threshold 的离群列单独抠出来,用 fp16 做 matmul;③ 把两路结果加起来。这样既保住了 int8 的速度与显存优势,又消灭了离群值导致的精度塌陷。

论文LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale(Dettmers et al., NeurIPS 2022)--- 首次提出"大模型量化误差由离群值特征主导"的洞见,并用混合精度分解把 175B 模型推理显存砍半且无性能损失。

【代码】 入口是 Linear8bitLt(file:///workspace/bitsandbytes/nn/modules.py#L1018)。它的构造函数接收一个 threshold 参数(modules.py#L1056(file:///workspace/bitsandbytes/nn/modules.py#L1056)),默认 0.0 表示禁用混合精度分解。Int8Params._quantize.to(device) 时调用 bnb.functional.int8_vectorwise_quant(B) 算出 CB(int8 权重)与 SCB(每行 absmax),见 modules.py#L737-L748(file:///workspace/bitsandbytes/nn/modules.py#L737-L748)。

前向路径在 Linear8bitLt.forward(file:///workspace/bitsandbytes/nn/modules.py#L1180-L1194):它把 state(含 threshold)传给 bnb.matmul,后者是 autograd 函数 autograd/_functions.py(file:///workspace/bitsandbytes/autograd/_functions.py) 里注册的 matmulMatmulLtStateautograd/_functions.py#L57-L99(file:///workspace/bitsandbytes/autograd/_functions.py#L57-L99))持有一组缓冲区(CB/SB/SCB/idx),其中 threshold 决定哪些激活列被划为离群值。

跨层离群值池化由 GlobalOutlierPooler(file:///workspace/bitsandbytes/autograd/_functions.py#L25-L51) 单例负责------它把多层观察到的离群列索引合并成一个集合,让小模型(离群值不那么系统性)也能稳定地走混合精度路径。构造时 threshold > 0 and not has_fp16_weights 会启用 use_poolmodules.py#L1089-L1090(file:///workspace/bitsandbytes/nn/modules.py#L1089-L1090))。

【例子】 假设激活矩阵 X 形状 (batch, hidden)threshold=6.0。某次前向中第 17 列出现 X[:, 17] = 7.2(>6.0),其他列都 <6.0。那么 LLM.int8() 会:

  1. X 的第 17 列抠出来形成 X_outlier(形状 (batch, 1)),其余列形成 X_normal(形状 (batch, hidden-1));
  2. X_normal 按 vector-wise absmax 量化成 int8,与 int8 权重做 igemm
  3. X_outlier 与对应权重列直接做 fp16 matmul;
  4. 把两路结果按列拼回完整输出。
    效果:175B 模型从 350GB fp16 显存降到约 175GB int8,且 zero-shot 性能与 fp16 几乎一致。

2.2 QLoRA / NF4:信息论最优的 4-bit 数据类型

【原理】 QLoRA 解决的是"如何在不损失精度的前提下把权重压到 4-bit"。它的两个核心创新:

① NF4(Normal Float 4-bit) ------一种信息论最优 的 4-bit 数据类型。其构造原理是:假设神经网络权重(已归一化到 -1, 1)近似服从标准正态分布 N(0, 1),那么把 N(0,1) 的累积分布函数(CDF)等分成 16 段,每段取中点作为该量化电平的代表值,就能让每个量化电平承载等概率质量------这等价于最大化量化后数据的熵,即"信息论最优"。相比均匀量化(FP4),NF4 在正态分布权重上的均方误差显著更低。

② Double Quantization(双重量化) ------把第一层量化产生的 absmax(每 64 个权重一个 float16 absmax)本身再做一次 8-bit 量化,平均每参数再省 0.37 bit。这样一份 4-bit 权重的总开销从"4 bit + 0.5 bit absmax"降到"4 bit + 0.13 bit"。

QLoRA 的完整配方是:NF4 量化冻结权重 + 在每个量化层上插入可训练的 LoRA 低秩适配器 + 用 8-bit 优化器训练适配器。结果是 65B 模型可在单卡 48GB 上微调,且效果与 16-bit 全量微调相当。

论文QLoRA: Efficient Finetuning of Quantized LLMs(Dettmers et al., NeurIPS 2023)--- 提出 NF4 数据类型与双重量化,使 65B 模型在单卡 48GB 上微调成为可能,并证明 4-bit 微调与 16-bit 全量微调效果相当。

【代码】 入口是 Params4bit(file:///workspace/bitsandbytes/nn/modules.py#L213-L242) 与 Linear4bit(file:///workspace/bitsandbytes/nn/modules.py#L504-L637)。Params4bittorch.nn.Parameter 的子类,额外持有 quant_stateblocksize(默认 64)、quant_type("fp4" 或 "nf4")、compress_statistics(是否启用双重量化)。它有一个特别精巧的设计------为支持 FSDP 状态字典遍历,用 @propertyquant_state 的嵌套字段(absmax/code/state2/offset/nested_absmax...)代理出来(modules.py#L271-L339(file:///workspace/bitsandbytes/nn/modules.py#L271-L339)),并刻意用 @property 而非 __getattr__,因为前者能被 torch.compile 追踪、后者会触发 graph break(注释见 modules.py#L261-L269(file:///workspace/bitsandbytes/nn/modules.py#L261-L269))。

量化发生在 Params4bit._quantize(file:///workspace/bitsandbytes/nn/modules.py#L381-L395):调用 bnb.functional.quantize_4bit(w, blocksize=64, compress_statistics=True, quant_type="nf4"),返回 w_4bit(uint8 packed)与 quant_state。NF4 的 16 个量化电平由 functional.py(file:///workspace/bitsandbytes/functional.py) 中的 create_normal_map 生成(LinearNF4 类的 docstring 明确指向它,见 modules.py#L676-L686(file:///workspace/bitsandbytes/nn/modules.py#L676-L686))。FP4 则由 create_fp8_map(signed=True, exponent_bits=2, precision_bits=1, total_bits=4)(file:///workspace/bitsandbytes/functional.py#L227-L293) 生成------这是一个通用的浮点格式构造器,注释明确说明 bitsandbytes 的 FP4 就是它的一个特例。

前向在 Linear4bit.forward(file:///workspace/bitsandbytes/nn/modules.py#L609-L637):调 bnb.matmul_4bit(x, weight, bias, quant_state),autograd 函数里做反量化 + matmul。值得注意的是它还做了 CPU AVX-512 BF16 优化路径检查(support_avx512bf16_for_cpumodules.py#L573(file:///workspace/bitsandbytes/nn/modules.py#L573))------在 Intel CPU 上能走专门优化。

Triton 路径的 NF4 量化 kernel 在 backends/triton/kernels_4bit.py(file:///workspace/bitsandbytes/backends/triton/kernels_4bit.py#L19-L71):quantize_fp4_blockwise_kernel 用纯 Python 写出 block-wise absmax + 量化阈值比较 + 4-bit pack。这段代码直接展示了 NF4 的"查表"本质------通过一系列 tl.where(A_absf > 阈值, ...) 的嵌套比较,把归一化后的浮点值映射到 4-bit 编码。这是"算法 = 代码"的最直白体现。

【例子】 一个 fp16 权重张量 W 形状 (out, 4096)blocksize=64quant_type="nf4"compress_statistics=True。QLoRA 的处理是:

  1. W 按 64 切成 4096/64 = 64 个 block,每个 block 算 absmax;
  2. 每 block 内 W_norm = W / absmax,归一化到 -1, 1
  3. 用 NF4 查表把 W_norm 映射到 16 个电平之一,存成 4-bit;
  4. 把两个 4-bit pack 进一个 uint8,体积压到原 fp16 的 1/4;
  5. (双重量化)把 64 个 absmax 也 8-bit 量化,存为 state2
    最终显存:4096 * out * 0.5 byte + 64 * 2 byte (absmax) + 64 * 1 byte (state2),远低于 fp16 的 4096 * out * 2 byte

2.3 8-bit Optimizers:block-wise 量化状态

【原理】 训练大模型时,优化器状态(Adam 的 m 与 v)往往比模型本身还占显存------Adam 的两份状态各占与参数同大的 fp32 空间,意味着 1B 参数的模型训练时优化器就要吃掉 8GB。8-bit Optimizers 的核心思想:按 block 量化 m/v 状态到 int8,并用动态量化映射表(dynamic quantization map)保精度。

关键洞见是"局部统计优于全局统计"------梯度的统计量是非平稳的,全局 absmax 会把不同尺度区域的细节平均掉;按 block 算局部 absmax 则能保持每个区域的相对结构。论文证明这种 block-wise 量化在语言建模与图像分类上达到与 32-bit Adam 几乎相同的收敛与泛化性能。

bitsandbytes 进一步引入动态量化映射表(dynamic map):不是均匀分 256 个 int8 电平,而是根据梯度分布形状生成非均匀电平(在 0 附近密集、在尾部稀疏),从而在有限 bit 预算下最大化信息熵。这本质上是 NF4 思想的 int8 版本------"让数据分布决定量化策略"。

论文8-bit Optimizers via Block-wise Quantization(Dettmers et al., ICLR 2022)--- 证明 block-wise 量化的稳态误差低于全局量化,使 8-bit Adam 在不损失性能的前提下把优化器显存砍半。

bitsandbytes 还集成了几种更新型的优化器:

论文The AdEMAMix Optimizer: Better, Faster, Older(Pagliardini, Ablin, Grangier, 2024)--- 用一快一慢两个 EMA 累积梯度,发现梯度可保持相关长达数万步;1.3B 模型在 101B token 上训练可媲美 AdamW 在 197B token 上的效果(节省 95% 训练量)。
论文Symbolic Discovery of Optimization Algorithms(Chen et al., Google, NeurIPS 2023)--- 用进化搜索从程序空间里"搜出"Lion 优化器,只用单一动量缓冲 + sign 函数,比 AdamW 省一半显存且效果相当甚至更好。

【代码】 基类是 Optimizer8bit(file:///workspace/bitsandbytes/optim/optimizer.py#L117)。它的 __init__ 接收 optim_bits(32/8)与 is_paged(是否分页),并预生成两种量化映射表:self.name2qmap["dynamic"] = F.create_dynamic_map(signed=True)["udynamic"]optimizer.py#L157-L159(file:///workspace/bitsandbytes/optim/optimizer.py#L157-L159))。

具体优化器分两类:Optimizer2State(Adam 系列,需 m/v 两份状态)与 Optimizer1State(Lion/SGD 系列,只需一份动量)。以 Adam(file:///workspace/bitsandbytes/optim/adam.py#L9) 为例,它继承 Optimizer2State 并把名字字符串 "adam" 传下去------这个名字决定了 C++ 侧调哪个 kernel。看 csrc/pythonInterface.cpp(file:///workspace/csrc/pythonInterface.cpp#L80-L94):宏 MAKE_FUNC32(adam, ADAM, float, fp32) 展开成 adam32bit_grad_fp32(...),宏 MAKE_BLOCKWISE8(adam, ADAM, ...) 展开 8-bit 版本。AdEMAMix 与 Lion 也通过同样的宏机制注册(pythonInterface.cpp#L87-L94(file:///workspace/csrc/pythonInterface.cpp#L87-L94))。

一个特别人性化的设计是 GlobalOptimManager(file:///workspace/bitsandbytes/optim/optimizer.py#L26-L114)------它允许参数级 配置覆盖。比如想让 embedding 层保留 32-bit 状态、其他层用 8-bit,只需 mng.override_config(model.embed.weight, "optim_bits", 32)optimizer.py#L60-L111(file:///workspace/bitsandbytes/optim/optimizer.py#L60-L111))。StableEmbeddingEmbedding 在构造时默认注册 32-bit override(modules.py#L99(file:///workspace/bitsandbytes/nn/modules.py#L99)),因为 embedding 梯度稀疏且尺度差异大,对量化最敏感。

为了与 FSDP 兼容,Optimizer8bit.state_dict 把量化张量(state1/state2/absmax/qmap 等)包进一个嵌套 dict __bnb_optimizer_quant_state__,让 FSDP 在跨 rank 收集时跳过它们(optimizer.py#L161-L187(file:///workspace/bitsandbytes/optim/optimizer.py#L161-L187))。这是工程层面对分布式训练生态的细致照顾。

【例子】bnb.optim.AdamW8bit(params, lr=1e-4) 训练一个 7B 模型:

  • fp32 状态:m 占 28GB + v 占 28GB = 56GB(远超单卡);
  • 8-bit 状态:m 占 7GB + v 占 7GB + absmax 等约 2GB = 16GB;
  • 配合 paged 版本 bnb.optim.PagedAdamW8bit,状态可分页到 CPU,单卡 24GB 也能跑。

2.4 极致工程:paged memory + 多后端 + Triton

【原理】 bitsandbytes 的工程脊梁支撑了三大算法在"显存不足"与"硬件不主流"两个边界场景下的可用性。

① paged memory :基于 CUDA 统一虚拟寻址(Unified Virtual Addressing, UVA)。GPU 驱动为 CPU+GPU 共享地址空间,cudaMallocManaged 分配的内存在物理上可能在 CPU RAM,但 GPU 访问时由驱动按页迁移。bitsandbytes 用此机制让优化器状态可"分页"到 CPU,前向时按需 prefetch 回 GPU------把"显存不够"从硬错误降级为性能损失。

② 多后端 :六类后端(CPU/CUDA/XPU/HPU/MPS/Triton)共享统一 ops 接口。后端发现用 PyTorch 风格的 entry_points------任何注册了 bitsandbytes.backends 入口点的第三方包都会被自动加载(**init** .py#L52-L70(file:///workspace/bitsandbytes/init .py#L52-L70) 的 _import_backends())。这样新硬件支持可以独立成包发布,无需修改 bitsandbytes 主干。

③ Triton 路径:Triton 是一个用 Python 写 GPU kernel 的中间语言与编译器,自动处理内存合并、共享内存管理、跨 SM 调度。bitsandbytes 在 backends/triton/(file:///workspace/bitsandbytes/backends/triton) 提供一份纯 Python 的 4-bit/8-bit/优化器 kernel,作为 CUDA 之外的可移植后备------同一份 kernel 既能跑 NVIDIA 也能跑 AMD(通过 LLVM 后端到 GCN)。

论文Triton: an intermediate language and compiler for tiled neural network computations(Tillet, Kung, Cox, MAPL 2019)--- 提出 block 作为头等语言构造 + LLVM 编译管线,让 GPU kernel 开发从"手挡 CUDA"降级为"自动挡 Triton",30 行 Python 可达 200 行 CUDA 的性能。

【代码】 paged memory 实现在 functional.py#L25-L109(file:///workspace/bitsandbytes/functional.py#L25-L109):GlobalPageManager 单例维护 paged_tensors 列表,get_paged(*shape)lib.cget_managed_ptr 申请统一内存并包成 PyTorch tensor,prefetch_tensor(A)lib.cprefetch 触发迁移。当单 GPU 时,_cuda_device_of 退化成 contextlib.nullcontext 省掉 cudaGetDevice 开销(functional.py#L80-L88(file:///workspace/bitsandbytes/functional.py#L80-L88))------这种"按环境剪枝"的微优化遍布全库。

Triton kernel 在 backends/triton/kernels_4bit.py(file:///workspace/bitsandbytes/backends/triton/kernels_4bit.py)、kernels_8bit_quant.pykernels_optim.py。每个文件都用 @triton.jit 装饰器把 Python 函数 JIT 编译成 GPU kernel。注意 kernels_4bit.py 的注释直接说明它是 "Triton implementation of similar CUDA kernel to avoid loading code from csrc/kernels.cu::dQuantizeFP4"------即用 Triton 复刻 CUDA 路径作为可移植后备。

库加载逻辑在 cextension.py(file:///workspace/bitsandbytes/cextension.py):get_native_library() 根据 torch.version.hip / torch.cuda.is_available() / torch._C._has_xpu 选库,get_cuda_bnb_library_path 在打包的多个 libbitsandbytes_cuda118.solibbitsandbytes_cuda121.so... 里挑最匹配的(cextension.py#L22-L80(file:///workspace/bitsandbytes/cextension.py#L22-L80)),还支持 BNB_CUDA_VERSION 环境变量强制指定版本。加载失败时退化成 ErrorHandlerMockBNBNativeLibrary,把错误延迟到首次调用------这样导入不会崩,给用户一个可调试的窗口(cextension.py#L171-L329(file:///workspace/bitsandbytes/cextension.py#L171-L329))。

【例子】 训练 13B 模型,单卡只有 24GB:

  • 模型本身 fp16 = 26GB(已超);
  • 用 4-bit QLoRA 量化后 = 6.5GB;
  • LoRA 适配器 + 梯度 ≈ 2GB;
  • 8-bit AdamW 状态 ≈ 2GB;
  • PagedAdamW8bit,状态在 CPU/GPU 间分页,激活走梯度检查点------总共约 12GB,跑得起来。

2.5 CUDA kernel 分架构优化(简述)

bitsandbytes 的 CUDA 实现遵循"按 GPU 架构分文件"的工程范式:csrc/gemm_4bit_simt.cu(file:///workspace/csrc/gemm_4bit_simt.cu)(pre-SM75 SIMT 路径)、csrc/gemm_4bit_sm75.cu(file:///workspace/csrc/gemm_4bit_sm75.cu)(Turing Tensor Core)、csrc/gemm_4bit_sm80.cu(file:///workspace/csrc/gemm_4bit_sm80.cu)(Ampere Tensor Core)。这样每代架构能用各自最优的指令(WMMA/MMA、特定的 shared memory 布局、bank-conflict-free 的 tile 形状),同时保持代码可读性。这是"性能优先"工程哲学在 CUDA 层的体现------具体的指令选择与 tile 调优思路,留给第三篇"深刻不忘观"升华。


三、总述收束:三大算法 + 工程脊梁的协同

把这五个技术点合起来看,bitsandbytes 的技术内核呈现出一种"算法 + 工程"的双螺旋结构:算法侧 用 LLM.int8() 的混合精度分解、QLoRA 的 NF4 信息论最优、8-bit 优化器的 block-wise 动态量化,把"压缩"这件事做到了当前学术前沿;工程侧用 paged memory 把"显存不够"软化、用多后端抽象把"硬件不主流"覆盖、用 Triton 把"GPU 编程门槛"降低,让这些前沿算法真正能在消费级硬件上跑起来。

更重要的是,这五者并非孤立------8-bit 优化器配合 QLoRA 才能让 65B 微调成为现实;LLM.int8() 的离群值池化与多后端抽象让 Apple Silicon 也能跑 8-bit 推理;Triton 路径让 AMD ROCm 用户不必等官方 kernel。这种"算法支撑工程目标、工程让算法触达用户"的协同,正是 bitsandbytes 能成为 HuggingFace 生态共同底座的根本原因。

下一篇我们将跃出水面,追问这些算法与工程背后的设计哲学与思想根源。


配图清单

图 2-1:LLM.int8() 混合精度分解流程

图注 :本图强调"离群值列 vs 普通列"的双路径------离群值列因 absmax 过大被抠出走 fp16,其余列走 int8 vector-wise 量化。两路结果按列拼接回完整输出。threshold 参数决定哪些列被划为离群值。
#mermaid-svg-klik4FUQ6nOyizdE{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-klik4FUQ6nOyizdE .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-klik4FUQ6nOyizdE .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-klik4FUQ6nOyizdE .error-icon{fill:#552222;}#mermaid-svg-klik4FUQ6nOyizdE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-klik4FUQ6nOyizdE .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-klik4FUQ6nOyizdE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-klik4FUQ6nOyizdE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-klik4FUQ6nOyizdE .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-klik4FUQ6nOyizdE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-klik4FUQ6nOyizdE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-klik4FUQ6nOyizdE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-klik4FUQ6nOyizdE .marker.cross{stroke:#333333;}#mermaid-svg-klik4FUQ6nOyizdE svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-klik4FUQ6nOyizdE p{margin:0;}#mermaid-svg-klik4FUQ6nOyizdE .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-klik4FUQ6nOyizdE .cluster-label text{fill:#333;}#mermaid-svg-klik4FUQ6nOyizdE .cluster-label span{color:#333;}#mermaid-svg-klik4FUQ6nOyizdE .cluster-label span p{background-color:transparent;}#mermaid-svg-klik4FUQ6nOyizdE .label text,#mermaid-svg-klik4FUQ6nOyizdE span{fill:#333;color:#333;}#mermaid-svg-klik4FUQ6nOyizdE .node rect,#mermaid-svg-klik4FUQ6nOyizdE .node circle,#mermaid-svg-klik4FUQ6nOyizdE .node ellipse,#mermaid-svg-klik4FUQ6nOyizdE .node polygon,#mermaid-svg-klik4FUQ6nOyizdE .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-klik4FUQ6nOyizdE .rough-node .label text,#mermaid-svg-klik4FUQ6nOyizdE .node .label text,#mermaid-svg-klik4FUQ6nOyizdE .image-shape .label,#mermaid-svg-klik4FUQ6nOyizdE .icon-shape .label{text-anchor:middle;}#mermaid-svg-klik4FUQ6nOyizdE .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-klik4FUQ6nOyizdE .rough-node .label,#mermaid-svg-klik4FUQ6nOyizdE .node .label,#mermaid-svg-klik4FUQ6nOyizdE .image-shape .label,#mermaid-svg-klik4FUQ6nOyizdE .icon-shape .label{text-align:center;}#mermaid-svg-klik4FUQ6nOyizdE .node.clickable{cursor:pointer;}#mermaid-svg-klik4FUQ6nOyizdE .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-klik4FUQ6nOyizdE .arrowheadPath{fill:#333333;}#mermaid-svg-klik4FUQ6nOyizdE .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-klik4FUQ6nOyizdE .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-klik4FUQ6nOyizdE .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-klik4FUQ6nOyizdE .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-klik4FUQ6nOyizdE .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-klik4FUQ6nOyizdE .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-klik4FUQ6nOyizdE .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-klik4FUQ6nOyizdE .cluster text{fill:#333;}#mermaid-svg-klik4FUQ6nOyizdE .cluster span{color:#333;}#mermaid-svg-klik4FUQ6nOyizdE div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-klik4FUQ6nOyizdE .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-klik4FUQ6nOyizdE rect.text{fill:none;stroke-width:0;}#mermaid-svg-klik4FUQ6nOyizdE .icon-shape,#mermaid-svg-klik4FUQ6nOyizdE .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-klik4FUQ6nOyizdE .icon-shape p,#mermaid-svg-klik4FUQ6nOyizdE .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-klik4FUQ6nOyizdE .icon-shape .label rect,#mermaid-svg-klik4FUQ6nOyizdE .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-klik4FUQ6nOyizdE .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-klik4FUQ6nOyizdE .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-klik4FUQ6nOyizdE :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-klik4FUQ6nOyizdE .split>*{fill:#fef3c7!important;stroke:#b45309!important;color:#78350f!important;stroke-width:2px!important;}#mermaid-svg-klik4FUQ6nOyizdE .split span{fill:#fef3c7!important;stroke:#b45309!important;color:#78350f!important;stroke-width:2px!important;}#mermaid-svg-klik4FUQ6nOyizdE .split tspan{fill:#78350f!important;}#mermaid-svg-klik4FUQ6nOyizdE .fp16>*{fill:#dbeafe!important;stroke:#1e40af!important;color:#1e3a8a!important;}#mermaid-svg-klik4FUQ6nOyizdE .fp16 span{fill:#dbeafe!important;stroke:#1e40af!important;color:#1e3a8a!important;}#mermaid-svg-klik4FUQ6nOyizdE .fp16 tspan{fill:#1e3a8a!important;}#mermaid-svg-klik4FUQ6nOyizdE .int8>*{fill:#dcfce7!important;stroke:#166534!important;color:#14532d!important;}#mermaid-svg-klik4FUQ6nOyizdE .int8 span{fill:#dcfce7!important;stroke:#166534!important;color:#14532d!important;}#mermaid-svg-klik4FUQ6nOyizdE .int8 tspan{fill:#14532d!important;}#mermaid-svg-klik4FUQ6nOyizdE .io>*{fill:#f3e8ff!important;stroke:#6b21a8!important;color:#581c87!important;}#mermaid-svg-klik4FUQ6nOyizdE .io span{fill:#f3e8ff!important;stroke:#6b21a8!important;color:#581c87!important;}#mermaid-svg-klik4FUQ6nOyizdE .io tspan{fill:#581c87!important;} 是 离群列
否 普通列
输入激活 X

(batch, hidden)
逐列检查

某列最大值是否超过threshold?
FP16 路径

抠出离群列
INT8 路径

vector-wise absmax
FP16 matmul

与权重对应列
按行 absmax 量化

归一化到 INT8
INT8 igemm

cuBLASLt
按列拼接回完整输出
输出 (batch, out_features)

图 2-2:QLoRA NF4 量化 + Double Quantization 数据管道

图注 :本图强调 quant_state 的嵌套结构------第一层 absmax 量化权重,第二层 state2(双重量化)量化 absmax 本身。这种嵌套正是"信息论最优 + 极致内存"的代码体现。
#mermaid-svg-bYgvXpwdudkE208K{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-bYgvXpwdudkE208K .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-bYgvXpwdudkE208K .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-bYgvXpwdudkE208K .error-icon{fill:#552222;}#mermaid-svg-bYgvXpwdudkE208K .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-bYgvXpwdudkE208K .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-bYgvXpwdudkE208K .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-bYgvXpwdudkE208K .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-bYgvXpwdudkE208K .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-bYgvXpwdudkE208K .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-bYgvXpwdudkE208K .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-bYgvXpwdudkE208K .marker{fill:#333333;stroke:#333333;}#mermaid-svg-bYgvXpwdudkE208K .marker.cross{stroke:#333333;}#mermaid-svg-bYgvXpwdudkE208K svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-bYgvXpwdudkE208K p{margin:0;}#mermaid-svg-bYgvXpwdudkE208K .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-bYgvXpwdudkE208K .cluster-label text{fill:#333;}#mermaid-svg-bYgvXpwdudkE208K .cluster-label span{color:#333;}#mermaid-svg-bYgvXpwdudkE208K .cluster-label span p{background-color:transparent;}#mermaid-svg-bYgvXpwdudkE208K .label text,#mermaid-svg-bYgvXpwdudkE208K span{fill:#333;color:#333;}#mermaid-svg-bYgvXpwdudkE208K .node rect,#mermaid-svg-bYgvXpwdudkE208K .node circle,#mermaid-svg-bYgvXpwdudkE208K .node ellipse,#mermaid-svg-bYgvXpwdudkE208K .node polygon,#mermaid-svg-bYgvXpwdudkE208K .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-bYgvXpwdudkE208K .rough-node .label text,#mermaid-svg-bYgvXpwdudkE208K .node .label text,#mermaid-svg-bYgvXpwdudkE208K .image-shape .label,#mermaid-svg-bYgvXpwdudkE208K .icon-shape .label{text-anchor:middle;}#mermaid-svg-bYgvXpwdudkE208K .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-bYgvXpwdudkE208K .rough-node .label,#mermaid-svg-bYgvXpwdudkE208K .node .label,#mermaid-svg-bYgvXpwdudkE208K .image-shape .label,#mermaid-svg-bYgvXpwdudkE208K .icon-shape .label{text-align:center;}#mermaid-svg-bYgvXpwdudkE208K .node.clickable{cursor:pointer;}#mermaid-svg-bYgvXpwdudkE208K .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-bYgvXpwdudkE208K .arrowheadPath{fill:#333333;}#mermaid-svg-bYgvXpwdudkE208K .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-bYgvXpwdudkE208K .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-bYgvXpwdudkE208K .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bYgvXpwdudkE208K .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-bYgvXpwdudkE208K .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bYgvXpwdudkE208K .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-bYgvXpwdudkE208K .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-bYgvXpwdudkE208K .cluster text{fill:#333;}#mermaid-svg-bYgvXpwdudkE208K .cluster span{color:#333;}#mermaid-svg-bYgvXpwdudkE208K div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-bYgvXpwdudkE208K .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-bYgvXpwdudkE208K rect.text{fill:none;stroke-width:0;}#mermaid-svg-bYgvXpwdudkE208K .icon-shape,#mermaid-svg-bYgvXpwdudkE208K .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bYgvXpwdudkE208K .icon-shape p,#mermaid-svg-bYgvXpwdudkE208K .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-bYgvXpwdudkE208K .icon-shape .label rect,#mermaid-svg-bYgvXpwdudkE208K .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bYgvXpwdudkE208K .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-bYgvXpwdudkE208K .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-bYgvXpwdudkE208K :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-bYgvXpwdudkE208K .input>*{fill:#f3e8ff!important;stroke:#6b21a8!important;color:#581c87!important;}#mermaid-svg-bYgvXpwdudkE208K .input span{fill:#f3e8ff!important;stroke:#6b21a8!important;color:#581c87!important;}#mermaid-svg-bYgvXpwdudkE208K .input tspan{fill:#581c87!important;}#mermaid-svg-bYgvXpwdudkE208K .step>*{fill:#dbeafe!important;stroke:#1e40af!important;color:#1e3a8a!important;}#mermaid-svg-bYgvXpwdudkE208K .step span{fill:#dbeafe!important;stroke:#1e40af!important;color:#1e3a8a!important;}#mermaid-svg-bYgvXpwdudkE208K .step tspan{fill:#1e3a8a!important;}#mermaid-svg-bYgvXpwdudkE208K .output>*{fill:#dcfce7!important;stroke:#166534!important;color:#14532d!important;}#mermaid-svg-bYgvXpwdudkE208K .output span{fill:#dcfce7!important;stroke:#166534!important;color:#14532d!important;}#mermaid-svg-bYgvXpwdudkE208K .output tspan{fill:#14532d!important;}#mermaid-svg-bYgvXpwdudkE208K .nested>*{fill:#fef3c7!important;stroke:#b45309!important;color:#78350f!important;stroke-width:2px!important;}#mermaid-svg-bYgvXpwdudkE208K .nested span{fill:#fef3c7!important;stroke:#b45309!important;color:#78350f!important;stroke-width:2px!important;}#mermaid-svg-bYgvXpwdudkE208K .nested tspan{fill:#78350f!important;} FP16 权重 W

(out, in)
按 blocksize=64 切块

每块 64 个权重
每块算 absmax

得到 16 个 FP16 absmax
块内归一化

W_norm = W / absmax
NF4 查表量化

16 个电平 信息论最优
两个 4-bit pack 进一个 uint8
Double Quantization

absmax 本身再做 8-bit 量化
state2 嵌套量化状态
uint8 权重

体积 = FP16 的 1/4
quant_state 嵌套结构

absmax + code + state2?

图 2-3:8-bit 优化器 block-wise 量化状态布局

图注:本图强调"按 block 切分 + 全局管理器协调"的结构。每个 block 有自己的局部 absmax,避免全局统计平均掉细节;GlobalOptimManager 允许某些参数(如 embedding)单独保留 32-bit 状态。
#mermaid-svg-ZsFEfcNLT5b2JXaP{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ZsFEfcNLT5b2JXaP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ZsFEfcNLT5b2JXaP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ZsFEfcNLT5b2JXaP .error-icon{fill:#552222;}#mermaid-svg-ZsFEfcNLT5b2JXaP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ZsFEfcNLT5b2JXaP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ZsFEfcNLT5b2JXaP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ZsFEfcNLT5b2JXaP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ZsFEfcNLT5b2JXaP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ZsFEfcNLT5b2JXaP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ZsFEfcNLT5b2JXaP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ZsFEfcNLT5b2JXaP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ZsFEfcNLT5b2JXaP .marker.cross{stroke:#333333;}#mermaid-svg-ZsFEfcNLT5b2JXaP svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ZsFEfcNLT5b2JXaP p{margin:0;}#mermaid-svg-ZsFEfcNLT5b2JXaP .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ZsFEfcNLT5b2JXaP .cluster-label text{fill:#333;}#mermaid-svg-ZsFEfcNLT5b2JXaP .cluster-label span{color:#333;}#mermaid-svg-ZsFEfcNLT5b2JXaP .cluster-label span p{background-color:transparent;}#mermaid-svg-ZsFEfcNLT5b2JXaP .label text,#mermaid-svg-ZsFEfcNLT5b2JXaP span{fill:#333;color:#333;}#mermaid-svg-ZsFEfcNLT5b2JXaP .node rect,#mermaid-svg-ZsFEfcNLT5b2JXaP .node circle,#mermaid-svg-ZsFEfcNLT5b2JXaP .node ellipse,#mermaid-svg-ZsFEfcNLT5b2JXaP .node polygon,#mermaid-svg-ZsFEfcNLT5b2JXaP .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ZsFEfcNLT5b2JXaP .rough-node .label text,#mermaid-svg-ZsFEfcNLT5b2JXaP .node .label text,#mermaid-svg-ZsFEfcNLT5b2JXaP .image-shape .label,#mermaid-svg-ZsFEfcNLT5b2JXaP .icon-shape .label{text-anchor:middle;}#mermaid-svg-ZsFEfcNLT5b2JXaP .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ZsFEfcNLT5b2JXaP .rough-node .label,#mermaid-svg-ZsFEfcNLT5b2JXaP .node .label,#mermaid-svg-ZsFEfcNLT5b2JXaP .image-shape .label,#mermaid-svg-ZsFEfcNLT5b2JXaP .icon-shape .label{text-align:center;}#mermaid-svg-ZsFEfcNLT5b2JXaP .node.clickable{cursor:pointer;}#mermaid-svg-ZsFEfcNLT5b2JXaP .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ZsFEfcNLT5b2JXaP .arrowheadPath{fill:#333333;}#mermaid-svg-ZsFEfcNLT5b2JXaP .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ZsFEfcNLT5b2JXaP .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ZsFEfcNLT5b2JXaP .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZsFEfcNLT5b2JXaP .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ZsFEfcNLT5b2JXaP .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZsFEfcNLT5b2JXaP .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ZsFEfcNLT5b2JXaP .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ZsFEfcNLT5b2JXaP .cluster text{fill:#333;}#mermaid-svg-ZsFEfcNLT5b2JXaP .cluster span{color:#333;}#mermaid-svg-ZsFEfcNLT5b2JXaP div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ZsFEfcNLT5b2JXaP .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ZsFEfcNLT5b2JXaP rect.text{fill:none;stroke-width:0;}#mermaid-svg-ZsFEfcNLT5b2JXaP .icon-shape,#mermaid-svg-ZsFEfcNLT5b2JXaP .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZsFEfcNLT5b2JXaP .icon-shape p,#mermaid-svg-ZsFEfcNLT5b2JXaP .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ZsFEfcNLT5b2JXaP .icon-shape .label rect,#mermaid-svg-ZsFEfcNLT5b2JXaP .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZsFEfcNLT5b2JXaP .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ZsFEfcNLT5b2JXaP .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ZsFEfcNLT5b2JXaP :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-ZsFEfcNLT5b2JXaP .state>*{fill:#dbeafe!important;stroke:#1e40af!important;color:#1e3a8a!important;}#mermaid-svg-ZsFEfcNLT5b2JXaP .state span{fill:#dbeafe!important;stroke:#1e40af!important;color:#1e3a8a!important;}#mermaid-svg-ZsFEfcNLT5b2JXaP .state tspan{fill:#1e3a8a!important;}#mermaid-svg-ZsFEfcNLT5b2JXaP .block>*{fill:#dcfce7!important;stroke:#166534!important;color:#14532d!important;}#mermaid-svg-ZsFEfcNLT5b2JXaP .block span{fill:#dcfce7!important;stroke:#166534!important;color:#14532d!important;}#mermaid-svg-ZsFEfcNLT5b2JXaP .block tspan{fill:#14532d!important;}#mermaid-svg-ZsFEfcNLT5b2JXaP .qmap>*{fill:#fef3c7!important;stroke:#b45309!important;color:#78350f!important;}#mermaid-svg-ZsFEfcNLT5b2JXaP .qmap span{fill:#fef3c7!important;stroke:#b45309!important;color:#78350f!important;}#mermaid-svg-ZsFEfcNLT5b2JXaP .qmap tspan{fill:#78350f!important;}#mermaid-svg-ZsFEfcNLT5b2JXaP .mgr>*{fill:#fce7f3!important;stroke:#be185d!important;color:#831843!important;stroke-width:2px!important;}#mermaid-svg-ZsFEfcNLT5b2JXaP .mgr span{fill:#fce7f3!important;stroke:#be185d!important;color:#831843!important;stroke-width:2px!important;}#mermaid-svg-ZsFEfcNLT5b2JXaP .mgr tspan{fill:#831843!important;} override_config

optim_bits=32
模型参数

(可训练)
m 一阶动量状态
v 二阶动量状态
按 block 切分

每块 2048 元素
按 block 切分

每块 2048 元素
每块局部 absmax

量化到 INT8
每块局部 absmax

量化到 INT8
dynamic qmap

非均匀电平
dynamic qmap

非均匀电平
GlobalOptimManager 单例
特定参数

保留 FP32 状态


使用指南与案例

三大算法的最小可用代码

8-bit 推理(LLM.int8())

python 复制代码
from bitsandbytes.nn import Linear8bitLt

# threshold > 0 启用混合精度分解;threshold=6.0 是论文推荐值
layer = Linear8bitLt(4096, 4096, has_fp16_weights=False, threshold=6.0)
layer.load_state_dict({"weight": pretrained_weight, "bias": pretrained_bias})
layer = layer.to(0)  # 触发 int8_vectorwise_quant
out = layer(x)

4-bit 微调(QLoRA / NF4)

python 复制代码
from bitsandbytes.nn import LinearNF4
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training

# 用 NF4(信息论最优)+ 双重量化
model = MyModel()
for name, module in model.named_modules():
    if isinstance(module, torch.nn.Linear):
        setattr(model, name, LinearNF4(module.in_features, module.out_features, compute_dtype=torch.bfloat16))
model = model.to(0)  # 触发 quantize_4bit

model = prepare_model_for_kbit_training(model)
lora_config = LoraConfig(r=16, lora_alpha=32, target_modules=["q_proj", "v_proj"])
model = get_peft_model(model, lora_config)

8-bit 优化器

python 复制代码
import bitsandbytes as bnb

# 标准 AdamW 替换
optimizer = bnb.optim.AdamW8bit(model.parameters(), lr=1e-4, weight_decay=0.01)

# 显存仍不够时用分页版本
optimizer = bnb.optim.PagedAdamW8bit(model.parameters(), lr=1e-4)

# 想要 AdEMAMix 的双 EMA 加速收敛
optimizer = bnb.optim.AdEMAMix8bit(
    model.parameters(), lr=1e-3,
    betas=(0.9, 0.999, 0.9999),  # beta_fast, beta_slow
    alpha=5.0,  # 慢 EMA 混合权重
)

# 想要 Lion 的省显存 + 符号动量
optimizer = bnb.optim.Lion8bit(model.parameters(), lr=1e-4)

参数级精度覆盖(细粒度控制)

python 复制代码
mng = bnb.optim.GlobalOptimManager.get_instance()
mng.register_parameters(model.parameters())  # 在 CPU 上注册
model = model.cuda()
optimizer = bnb.optim.Adam(model.parameters(), lr=1e-3, optim_bits=8)

# embedding 对量化最敏感,单独保留 32-bit
mng.override_config(model.embed.weight, "optim_bits", 32)
# 输出层也建议 32-bit
mng.override_config(model.lm_head.weight, "optim_bits", 32)

三种量化类型的选择建议

场景 推荐方案 论文依据
大模型推理(>6.7B) Linear8bitLt(threshold=6.0) LLM.int8() 论文
大模型微调(QLoRA) LinearNF4 + PEFT LoRA QLoRA 论文
训练时优化器爆显存 bnb.optim.AdamW8bitPagedAdamW8bit 8-bit Optimizers 论文
想要更快收敛 bnb.optim.AdEMAMix8bit AdEMAMix 论文
极致省显存 bnb.optim.Lion8bit Lion 论文
AMD/Apple Silicon 默认走 ROCm/MPS 后端,或用 Triton 路径 Triton 论文

反面案例:何时该用 bitsandbytes

  • 对数值精度极敏感的科学计算:量化的舍入误差虽小但存在,不建议用于数值仿真。
  • 极小模型(<100M):量化带来的省显存收益不显著,但 kernel 启动开销相对变大。
  • 非 Transformer 架构的特殊算子:bitsandbytes 主要优化了 Linear/Embedding/matmul,自定义算子不在覆盖范围。
  • 需要训练后部署到无 PyTorch 环境:bitsandbytes 是运行时量化,不是 compile-time;离线部署考虑用 GPTQ/AWQ 导出静态量化模型。

相关案例文件

  • examples/int8_inference_huggingface.py(file:///workspace/examples/int8_inference_huggingface.py) --- HuggingFace 模型 8-bit 推理
  • examples/cpu/cpu_training.py(file:///workspace/examples/cpu/cpu_training.py) --- CPU 上 8-bit 优化器训练
  • examples/xpu/paged_xpu_training.py(file:///workspace/examples/xpu/paged_xpu_training.py) --- Intel XPU 分页训练
  • examples/compile_inference.py(file:///workspace/examples/compile_inference.py) --- torch.compile + 量化推理
  • benchmarking/inference_benchmark.py(file:///workspace/benchmarking/inference_benchmark.py) --- 推理性能基准

上一篇01-整体观-项目全貌解读

下一篇03-深刻观-哲学与升华 将跃出水面,追问设计哲学与算法思想根源。

相关推荐
普通攻击往后拉11 小时前
Leetcode 206. 反转链表
算法·leetcode·链表
@syh.11 小时前
【贪心】矩阵消除游戏
算法·游戏·矩阵
可编程芯片开发12 小时前
基于零极点配置的PID控制系统simulink建模与仿真
算法
徐小夕12 小时前
开源!我用SQLite + DuckDB打造了一款可视化AI问数平台
前端·算法·github
Hrain-AI12 小时前
2026 企业 AI 智能体平台横评:8 大主流平台 7 维度实测对比
人工智能·算法·机器学习
Angel Q.13 小时前
因子分析和生成模型有什么关系?从“幕后因素”到“生成数据”
算法
FBI HackerHarry浩15 小时前
AI大模型开发V2第四阶段线性回归
人工智能·算法·线性回归
Jerry16 小时前
LeetCode 108. 将有序数组转换为二叉搜索树
算法