量化
推理主线上的权重不是 Tensor,是 QTensor:从 GGUF 文件里原样读进来的量化块,乘法时由专门的内核直接在块上算。这一部分讲 candle-core/src/quantized/(约 1.1 万行,比整个 Metal 后端还大),也就是推理主线的前两步:读文件、建权重,以及每个投影层的那一次量化矩阵乘。格式和算法都照搬 llama.cpp,所以 candle 能直接读 llama.cpp 生态里的 GGUF 文件。
- GGUF 文件:魔数、版本、13 种元数据值、张量目录和对齐;SmolLM2 两个 GGUF 里有什么(Q4_K_M 文件大半其实是 Q5_0);按偏移读张量;0.11.0 加的分配上限挡住了哪些坏文件,又为什么让
BufReader下的解析慢了 5 倍;还会 panic 的两种输入。 - 量化格式:
GgmlDType的 15 种类型,BlockQ8_0、BlockQ4K、BlockQ6K、BlockQ8K的字段和每块字节数;量化和反量化;点积的另一边先量化成VecDotType;NEON 和sdot;实测往返误差、和 GGUF 文件逐字节对照、点积速度。 - QTensor 与 QMatMul:
QStorage在 CPU 和 Metal 上怎么存;QMatMul三种形态由 dtype 和两个环境变量决定;CPU 先量化激活再逐行点积,Metal 一行用 mv、多行用 mm 内核;实测三种形态的速度,以及 Q8_0 和 Q4_K_M 生成时的速度和 CPU、Metal 结果的差别。
flowchart TB file["GGUF 文件"] -->|Content read| content["元数据 和 张量目录"] content -->|Content tensor| qt["QTensor 块数组"] qt --> qmm["QMatMul"] qmm --> cpu["CPU vec_dot"] qmm --> metal["Metal mv mm 内核"] qmm -.->|反量化| plain["普通 Tensor matmul"]
第一页是文件,第二页是块,第三页是计算。三页的实验都在 M2 Max、macOS 27、rustc 1.99.0 上跑,用本地的 SmolLM2-135M-Instruct-Q8_0.gguf 和 Q4_K_M.gguf。CUDA 上的量化内核(cuda.rs、fast_mmq.rs、fast_mmvq.rs)只读了源码,没有实测。量化模型怎么用这些权重搭出一次前向,见量化 llama 的一次前向;不依赖 candle 自己写一遍 Q8_0 和 GGUF 头部解析,见动手:mini-quant。
💬 关联讨论
发起讨论加载中...