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

作者:happyprince日期:2026/8/4

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

本篇承接整体观建立的认知地图,潜入水下逐一审视 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) 里注册的 matmulMatmulLtState([autograd/_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_pool([modules.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_cpu,[modules.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 参数决定哪些列被划为离群值。

是 离群列

否 普通列

输入激活 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 本身。这种嵌套正是"信息论最优 + 极致内存"的代码体现。

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 状态。

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())

1from bitsandbytes.nn import Linear8bitLt
2
3# threshold > 0 启用混合精度分解;threshold=6.0 是论文推荐值
4layer = Linear8bitLt(4096, 4096, has_fp16_weights=False, threshold=6.0)
5layer.load_state_dict({"weight": pretrained_weight, "bias": pretrained_bias})
6layer = layer.to(0)  # 触发 int8_vectorwise_quant
7out = layer(x)
8

4-bit 微调(QLoRA / NF4)

1from bitsandbytes.nn import LinearNF4
2from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
3
4#  NF4(信息论最优)+ 双重量化
5model = MyModel()
6for name, module in model.named_modules():
7    if isinstance(module, torch.nn.Linear):
8        setattr(model, name, LinearNF4(module.in_features, module.out_features, compute_dtype=torch.bfloat16))
9model = model.to(0)  # 触发 quantize_4bit
10
11model = prepare_model_for_kbit_training(model)
12lora_config = LoraConfig(r=16, lora_alpha=32, target_modules=["q_proj", "v_proj"])
13model = get_peft_model(model, lora_config)
14

8-bit 优化器

1import bitsandbytes as bnb
2
3# 标准 AdamW 替换
4optimizer = bnb.optim.AdamW8bit(model.parameters(), lr=1e-4, weight_decay=0.01)
5
6# 显存仍不够时用分页版本
7optimizer = bnb.optim.PagedAdamW8bit(model.parameters(), lr=1e-4)
8
9# 想要 AdEMAMix 的双 EMA 加速收敛
10optimizer = bnb.optim.AdEMAMix8bit(
11    model.parameters(), lr=1e-3,
12    betas=(0.9, 0.999, 0.9999),  # beta_fast, beta_slow
13    alpha=5.0,  #  EMA 混合权重
14)
15
16# 想要 Lion 的省显存 + 符号动量
17optimizer = bnb.optim.Lion8bit(model.parameters(), lr=1e-4)
18

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

1mng = bnb.optim.GlobalOptimManager.get_instance()
2mng.register_parameters(model.parameters())  #  CPU 上注册
3model = model.cuda()
4optimizer = bnb.optim.Adam(model.parameters(), lr=1e-3, optim_bits=8)
5
6# embedding 对量化最敏感,单独保留 32-bit
7mng.override_config(model.embed.weight, "optim_bits", 32)
8# 输出层也建议 32-bit
9mng.override_config(model.lm_head.weight, "optim_bits", 32)
10

三种量化类型的选择建议

场景推荐方案论文依据
大模型推理(>6.7B)Linear8bitLt(threshold=6.0)LLM.int8() 论文
大模型微调(QLoRA)LinearNF4 + PEFT LoRAQLoRA 论文
训练时优化器爆显存bnb.optim.AdamW8bit 或 PagedAdamW8bit8-bit Optimizers 论文
想要更快收敛bnb.optim.AdEMAMix8bitAdEMAMix 论文
极致省显存bnb.optim.Lion8bitLion 论文
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-深刻观-哲学与升华 将跃出水面,追问设计哲学与算法思想根源。


篇2-bitsandbytes-具体观-算法与实现剖析》 是转载文章,点击查看原文


相关推荐


把随身WiFi改成网盘聚合器:中兴F50挂载本地存储+夸克网盘实战
羑悻的小杀马特.2026/7/27

文章目录 前言1 什么是OpenList?2 中兴F50上安装OpenList服务3 挂载本地存储和网盘存储3.1 挂载本地F50自带的20G存储3.2 挂载夸克网盘 4 穿透OpenList以支持公网访问4.1 如何用?4.2 在F50上安装4.3 配置OpenList的http隧道 5 固定二级子域名(升级任意套餐)总结 前言 中兴F50放在包里时,很多人只把它当作一台提供热点的随身WiFi。需要传文件、打开网盘资料或临时分享内容时,仍然要依赖手机、电脑和多个客户端


【数据库】CRUD-- 增删改
哦虎!2026/7/19

第二章 增删改 文章目录 第二章 增删改前言一、命名二、增1. 新增2. 指定列插入3. 多行插入4. 插入查询结果5. 查询的指定列插入6.注意 三、改1.修改数据2.组内修改 四、删除1. 删除数据2.drop和delete的区别 五、截断表总结 前言 本期主要讲增 删 改, 会稍微使用到查 , 也就是select , 遇到会稍做讲解 , 详细讲解会放到下期~ 一、命名 创建表时,命名有两种 驼峰命名(studentName)蛇形命名(stud


C++内存管理
孬甭_2026/7/11

目录 1 · C / C++ 内存分布 2 · C++内存管理方式 2 - 1 · new / delete 操作内置类型 2 - 2 · new / delete 操作自定义类型 3 · operator new 和 operator delete 函数 4 · new 和 delete 的实现原理 4 - 1 · 内置类型 4 - 2 · 自定义类型 5 · 定位new 6 · new / delete 与 malloc / free 的区别 1 · C / C


EventBus → SharedFlow
plainGeek2026/7/3

EventBus → SharedFlow 老写法(Java + EventBus) // 事件定义 public class LoginEvent { private long userId; public LoginEvent(long userId) { this.userId = userId; } public long getUserId() { return userId; } } // 发送 EventBus.getDefault().post(new


【Linux基础】初始Linux
键盘敲碎了雾霭2026/6/25

🎬 博主名称:键盘敲碎了雾霭 🔥 个人专栏: 《C语言》《数据结构》 《C++》 《Matlab》 《Python》 《Linux》 ⛺️指尖敲代码,雾霭皆可破 文章目录 一、Linux发展史1.1 UNIX发展历史1.2 Linux发展历史 二、开源三、应用现状四、发行版本五、os概念六、使用XShell远程登录Linux6.1 会话方式登录6.2 命令行登录6.3 多用户使用 文章结语 一、Linux发展史 要说Linux,还


【Java基础】链表的七十二变——从LRU缓存到手写浏览器前进后退
zzz_23682026/6/16

链表的七十二变——从LRU缓存到手写浏览器前进后退 写在前面的目录 一、真实面试真题引入 二、链表的底层解构——不止是 next 指针   2.1 单向链表:最简单的链式结构   2.2 双向链表:前后眼的设计哲学   2.3 链表反转四步拆解   2.4 哨兵节点——让边界消失   2.5 跳表——给链表加个索引 三、"纯手工、零依赖"原创案例实战   3.1 浏览器前进后退——双向链表实现标签页导航   3.2 LRU 缓存淘汰——HashMap + 双向链表的 O(1) 魔法 四、


安全第一与合规治理:魔芋 AI 正式发布企业级大模型网关 Mai Gateway
青莲网络2026/6/9

在现代企业业务创新中,人工智能(AI)正逐步成为关键驱动力。随着大语言模型的发展,企业应用架构已从传统的微服务、云原生架构,加速向AI原生架构演进。在此过程中,企业在AI集成、系统稳定性、安全合规及管理复杂性方面面临诸多挑战。 为了帮助企业安全、高效地落地 AI 应用,魔芋 AI(MoYu AI)正式推出重磅核心产品——Mai Gateway(大模型网关)。作为企业 AI 应用与模型服务、工具及其他 Agent 之间的核心连接组件,Mai Gateway 通过提供协议转换、安全防护、流量治理和


C++与 Lua的交互
kebeiovo2026/6/1

适用场景:游戏服务端、客户端热更新、业务逻辑解耦、C++高性能底层+Lua灵活业务架构 核心定位:C++负责高性能底层(网络、内存池、epoll、计算),Lua负责业务逻辑、配置解析、热更新,二者结合是游戏行业主流生产方案。 一、交互原理 1.1 交互核心载体 所有C++与Lua的交互,全部围绕 Lua虚拟机(lua_State) 实现; Lua 是编译型 + 解释型的嵌入式脚本语言,自身不依赖独立进程运行,它的代码最终交由 Lua 虚拟机(Lua VM) 执行。 Lua 虚拟机本质


营销数据分析:如何利用IP归属地识别和规避虚假流量
TechWayfarer2026/5/12

一、流量作弊:程序化广告的隐形黑洞 程序化广告已成为数字营销的主流投放方式,但伴随着海量流量而来的,是日益猖獗的虚假流量产业链。根据中国互联网络信息中心发布的数据,截至2025年6月,我国网民规模已达11.23亿人,互联网普及率达79.7%。庞大的用户基数为黑灰产提供了可乘之机。 在程序化广告交易中,虚假流量问题尤为突出。据行业实测数据显示,某DSP平台日均处理超过50亿次广告竞价请求,其中约30%的流量来自数据中心IP或代理IP,这些流量中真正产生转化的不足0.5%,却消耗了平台近四分之一


03ab-PyTorch安装教程 📚
郑恩赐2026/5/3

03ab-PyTorch安装教程 📚 章节阅读路线图 🗺️ flowchart LR A["1. 概述"]:::concept --> B["2. 安装前准备"]:::setup B --> C["3. CPU版本安装"]:::cpu C --> D["4. GPU版本安装"]:::gpu D --> E["5. 验证安装"]:::verify classDef concept fill:#e3f2fd,stroke:#1565c0 clas

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 聚合阅读