QuantizationModifier + oneshot("datafree") 完整调用链分析
示例调用:
oneshot(model=model, recipe=QuantizationModifier(targets="Linear", scheme="W4A16", ignore=["lm_head"]), pipeline="datafree")
目录
-
[第一阶段:oneshot 入口 → Session 初始化](#第一阶段:oneshot 入口 → Session 初始化)
-
[第二阶段:Modifier.initialize → on_initialize](#第二阶段:Modifier.initialize → on_initialize)
-
[第三阶段:scheme="W4A16" 的解析链路 🔑](#第三阶段:scheme="W4A16" 的解析链路 🔑)
-
[第四阶段:apply_quantization_config → 将 scheme 挂载到模型层](#第四阶段:apply_quantization_config → 将 scheme 挂载到模型层)
-
[第五阶段:on_start → 权重校准](#第五阶段:on_start → 权重校准)
-
[第六阶段:Pipeline 推断 → "datafree"](#第六阶段:Pipeline 推断 → "datafree")
第一阶段:oneshot 入口 → Session 初始化
oneshot(model, recipe, pipeline) # src/llmcompressor/entrypoints/oneshot.py:250
└─ Oneshot(**local_args, **kwargs) # :413
└─ one_shot() # :414 → __call__():175
└─ apply_recipe_modifiers() # :189
└─ session.initialize(recipe=self.recipe, ...) # :226
└─ CompressionLifecycle.initialize() # src/llmcompressor/core/lifecycle.py:73
│
├─ Recipe.create_instance(recipe) # recipe.py:95
│ └─ Recipe.from_modifiers(...) # recipe.py:130 → :45
│ 因为 recipe = QuantizationModifier(...) 已经是 Modifier 对象,
│ 直接包装成 Recipe(modifiers=[QuantizationModifier])
│
└─ mod.initialize(state, **kwargs) # 对每个 modifier 调用 lifecycle.initialize()
关键代码位置
| 步骤 | 文件 | 行号 |
|---|---|---|
oneshot() 函数 |
src/llmcompressor/entrypoints/oneshot.py |
L250-416 |
Oneshot.__call__() |
src/llmcompressor/entrypoints/oneshot.py |
L175-197 |
apply_recipe_modifiers() |
src/llmcompressor/entrypoints/oneshot.py |
L199-247 |
session.initialize() |
src/llmcompressor/core/session_functions.py |
- |
Recipe.create_instance() |
src/llmcompressor/recipe/recipe.py |
L87-165 |
Recipe.from_modifiers() |
src/llmcompressor/recipe/recipe.py |
L44-85 |
第二阶段:Modifier.initialize → on_initialize
Modifier.initialize(state, **kwargs) # src/llmcompressor/modifiers/modifier.py:61
└─ self.on_initialize(state=state, **kwargs) # :79
└─ QuantizationModifier.on_initialize() # src/llmcompressor/modifiers/quantization/quantization/base.py:50
│
├─ QuantizationMixin.has_config(self) # 检查 scheme/kv_cache_scheme/config_groups 之一非空
│
└─ QuantizationMixin.initialize_quantization(state.model) # base.py:65
│
├─ reset_quantization_status(module) # 重置所有匹配模块的量化状态
│ # compressed-tensors/src/.../initialize.py
│
├─ apply_quantization_config(model, self.resolved_config) # 🔑 核心! 见第三、四阶段
│ # compressed-tensors/src/.../apply.py:97
│
└─ model.apply(disable_quantization) # 先禁用量化, 等 on_start 再启用
关键代码位置
| 步骤 | 文件 | 行号 |
|---|---|---|
Modifier.initialize() |
src/llmcompressor/modifiers/modifier.py |
L61-100 |
QuantizationModifier.on_initialize() |
src/llmcompressor/modifiers/quantization/quantization/base.py |
L50-67 |
initialize_quantization() |
src/llmcompressor/modifiers/quantization/quantization/mixin.py |
- |
reset_quantization_status() |
compressed-tensors/src/.../lifecycle/initialize.py |
- |
第三阶段:scheme="W4A16" 的解析链路 🔑
这是最核心的环节------"W4A16" 这个字符串如何一步步变成具体的量化参数。
3.1 入口: resolved_config
# src/llmcompressor/modifiers/quantization/quantization/mixin.py:188
@property
def resolved_config(self):
# lazy init: 只计算一次
if self._resolved_config is None:
self._resolved_config = self.resolve_quantization_config()
return self._resolved_config
3.2 resolve_quantization_config()
# src/llmcompressor/modifiers/quantization/quantization/mixin.py:320
def resolve_quantization_config(self) -> QuantizationConfig:
"""
此时:
self.scheme = "W4A16" (字符串)
self.targets = ["Linear"]
self.config_groups = None (未设置, 因为没有传 config_groups)
"""
# Step 1: 检查是否为预设 scheme 名称
is_preset_scheme("W4A16") # quant_scheme.py:135
└─ "W4A16".upper() = "W4A16" ∈ PRESET_SCHEMES → True!
# Step 2: 将字符串 scheme 转换为 {"W4A16": targets} 字典
scheme = {"W4A16": ["Linear"]} # :350
# Step 3: 遍历 scheme 中的每个 key
for key in scheme.keys():
is_preset_scheme("W4A16") → True # :358
└─ preset_name_to_scheme("W4A16", ["Linear"]) # :359 → quant_scheme.py:113
3.3 preset_name_to_scheme(): 查表获取量化参数
# compressed-tensors-main/src/compressed_tensors/quantization/quant_scheme.py:113
def preset_name_to_scheme(name: str, targets: list[str]) -> QuantizationScheme:
name = name.upper() # → "W4A16"
if name not in PRESET_SCHEMES:
raise KeyError(...)
scheme_args = deepcopy(PRESET_SCHEMES[name]) # 深拷贝 W4A16 常量
return QuantizationScheme(
targets=targets, # ["Linear"]
**scheme_args, # weights=QuantizationArgs(...)
)
3.4 PRESET_SCHEMES["W4A16"] 的定义
# compressed-tensors-main/src/compressed_tensors/quantization/quant_scheme.py:286-295
W4A16 = dict(
weights=QuantizationArgs(
num_bits=4, # 4-bit 量化
type=QuantizationType.INT, # 整数类型量化
strategy=QuantizationStrategy.GROUP, # group-wise 策略
group_size=128, # 每 128 个元素一组, 共享 scale/zp
symmetric=True, # 对称量化 (zero_point 固定为 0)
dynamic=False, # 静态量化 (scale 预先计算, 不依赖输入)
),
)
# ⚠️ 注意: W4A16 只有 weights, 没有 input_activations 和 output_activations!
# 这意味着只量化权重 (Weight 4-bit), 激活保持 FP16 (Activation 16-bit)
3.5 最终生成的 QuantizationConfig
# compressed-tensors-main/src/compressed_tensors/quantization/quant_config.py:94
QuantizationConfig(
config_groups={
"group_0": QuantizationScheme(
targets=["Linear"], # 目标: 所有 Linear 层
weights=QuantizationArgs( # 权重量化参数
num_bits=4,
type=QuantizationType.INT,
strategy=QuantizationStrategy.GROUP,
group_size=128,
symmetric=True,
dynamic=False,
),
input_activations=None, # ← 不量化激活输入!
output_activations=None, # ← 不量化激活输出!
),
},
quantization_status=QuantizationStatus.INITIALIZED,
ignore=["lm_head"], # ← lm_head 层不参与量化
kv_cache_scheme=None, # ← KV Cache 不量化
)
3.6 PRESET_SCHEMES 完整表格
| 预设名 | 权重 | 输入激活 | 输出激活 | 说明 |
|---|---|---|---|---|
W4A16 |
4-bit INT, GROUP, group_size=128, static | None | None | 仅权重量化 |
W4A16_ASYM |
4-bit INT, GROUP, group_size=128, asymmetric, static | None | None | 非对称权重量化 |
W8A16 |
8-bit INT, CHANNEL, static | None | None | 8-bit 权重量化 |
W8A8 / INT8 |
8-bit INT, CHANNEL, static | 8-bit INT, TOKEN, dynamic | None | 权重+激活量化 |
W4A8 |
4-bit INT, GROUP, group_size=128, static | 8-bit INT, TOKEN, dynamic | None | 4-bit权重+8-bit激活 |
W4AFP8 |
4-bit INT, GROUP, group_size=128, static | 8-bit FLOAT, TOKEN, dynamic | None | 4-bit权重+FP8激活 |
FP8 |
8-bit FLOAT, TENSOR, static | 8-bit FLOAT, TENSOR, static | None | FP8 权重+激活 |
FP8_DYNAMIC |
8-bit FLOAT, CHANNEL, static | 8-bit FLOAT, TOKEN, dynamic | None | FP8 动态激活 |
FP8_BLOCK |
8-bit FLOAT, BLOCK(128,128), static | 8-bit FLOAT, GROUP(128), dynamic | None | FP8 块量化 |
NVFP4A16 |
4-bit FLOAT, TENSOR_GROUP, group_size=16 | None | None | NV FP4 权重量化 |
NVFP4 |
4-bit FLOAT, TENSOR_GROUP, group_size=16 | 4-bit FLOAT, dynamic | None | NV FP4 全量化 |
MXFP4A16 |
4-bit FLOAT, GROUP, group_size=32 | None | None | MX FP4 权重量化 |
MXFP4 |
4-bit FLOAT, GROUP, group_size=32 | 4-bit FLOAT, dynamic | None | MX FP4 全量化 |
MXFP8A16 |
8-bit FLOAT, GROUP, group_size=32 | None | None | MX FP8 权重量化 |
MXFP8 |
8-bit FLOAT, GROUP, group_size=32 | 8-bit FLOAT, dynamic | None | MX FP8 全量化 |
3.7 QuantizationArgs 各字段含义
| 字段 | 类型 | 说明 |
|---|---|---|
num_bits |
int | 量化 bit 数 (4, 8) |
type |
QuantizationType | INT (整数量化) / FLOAT (浮点量化) |
strategy |
QuantizationStrategy | TENSOR / CHANNEL / GROUP / TOKEN / TENSOR_GROUP / BLOCK |
group_size |
int | GROUP 策略时的分组大小 |
symetric |
bool | True=对称量化 (zero_point=0), False=非对称 |
dynamic |
bool/DynamicType | 是否动态量化 (scale 在推理时从输入计算) |
observer |
str | 校准 observer 类型 (如 "minmax", "mse" 等) |
关键代码位置
| 步骤 | 文件 | 行号 |
|---|---|---|
resolved_config property |
src/llmcompressor/modifiers/quantization/quantization/mixin.py |
L188 |
resolve_quantization_config() |
src/llmcompressor/modifiers/quantization/quantization/mixin.py |
L320 |
is_preset_scheme() |
compressed-tensors/src/.../quantization/quant_scheme.py |
L135 |
preset_name_to_scheme() |
compressed-tensors/src/.../quantization/quant_scheme.py |
L113 |
PRESET_SCHEMES 表 |
compressed-tensors/src/.../quantization/quant_scheme.py |
L406-428 |
W4A16 定义 |
compressed-tensors/src/.../quantization/quant_scheme.py |
L286-295 |
QuantizationConfig |
compressed-tensors/src/.../quantization/quant_config.py |
L94-272 |
第四阶段:apply_quantization_config → 将 scheme 挂载到模型层
apply_quantization_config(model, config) # compressed-tensors/src/.../apply.py:97
│
├─ 构建 target_to_scheme 映射 # :125-128
│ {"Linear" → QuantizationScheme(W4A16)}
│
└─ for name, submodule in match_named_modules( # :131-147
│ model,
│ target_to_scheme={"Linear": scheme},
│ ignore=["lm_head"] # ← lm_head 被排除
│ ):
│ │ # 遍历模型中所有 Linear 层 (排除 lm_head)
│ │ # 例如: model.layers.0.self_attn.q_proj
│ │ # model.layers.0.self_attn.k_proj
│ │ # model.layers.0.self_attn.v_proj
│ │ # model.layers.0.self_attn.o_proj
│ │ # model.layers.0.mlp.gate_proj
│ │ # model.layers.0.mlp.up_proj
│ │ # model.layers.0.mlp.down_proj
│ │ # ... (所有 decoder layer 的 Linear 子模块)
│ │
│ ├─ submodule.quantization_scheme = scheme # 将 W4A16 scheme 挂到层的属性上
│ │ # 之后可以通过 submodule.quantization_scheme 获取量化配置
│ │
│ ├─ submodule.quantization_status = QuantizationStatus.INITIALIZED
│ │
│ └─ initialize_module_for_quantization(submodule, config_) # 初始化量化参数
│ # compressed-tensors/src/.../lifecycle/initialize.py
│ # - 创建 weight_scale, weight_zero_point 等参数 buffer
│ # - 设置 observer (默认 "minmax")
│ # - 注册 forward hook
│ # - 所有参数初始化为空, 等 on_start 时填充
关键代码位置
| 步骤 | 文件 | 行号 |
|---|---|---|
apply_quantization_config() |
compressed-tensors/src/.../quantization/lifecycle/apply.py |
L97 |
match_named_modules() |
compressed-tensors/src/.../utils/ |
- |
initialize_module_for_quantization() |
compressed-tensors/src/.../quantization/lifecycle/initialize.py |
- |
第五阶段:on_start → 权重校准
注意: 在
session.initialize()中传入了start=-1, 这意味着Modifier.initialize()内部会立即触发on_start。 因为start=-1 < 0, 表示从初始化阶段就开始, 不需要等待 training step。
QuantizationModifier.on_start(state, event) # base.py:69
│
├─ self.started_ = True # 标记已开始
│
├─ QuantizationMixin.start_calibration(state.model) # base.py:75 → mixin.py:235
│ │
│ ├─ 对所有匹配的模块执行: # :245-252
│ │ ├─ _initialize_observers(module) # 初始化 weight observer
│ │ │ # 根据 QuantizationArgs 创建 observer (默认 minmax)
│ │ │ # observer 用于统计 weight 的 min/max 以计算 scale
│ │ │
│ │ └─ _initialize_hooks(module) # 初始化前向 hook
│ │ # ⚠️ W4A16 只有 weight 量化, 无 input/output 量化
│ │ # 所以 hooks 列表为空! 不需要在每次 forward 中记录激活值
│ │
│ └─ model.apply(enable_quantization) # :254 → 启用量化 forward
│ # 将 quantization_status 从 INITIALIZED → CALIBRATION
│
├─ named_modules = match_named_modules(model, resolved_targets, ignore)
│
├─ for _, module in named_modules: # :84
│ update_weight_global_scale(module) # :85 → calibration.py
│ # 更新 weight 的全局 scale (用于 fused layer 等情况)
│
├─ for module in model.modules(): # :92
│ update_fused_layer_weight_global_scales(module) # :93 → utils.py
│ # 对 Attention/MLP fused layers 更新 global scale
│ # 这是 idempotent 操作, 可以运行多次
│
└─ for _, module in tqdm.tqdm(named_modules, "Calibrating weights"): # :95
update_weight_zp_scale(module) # :96 → calibration.py
# 🔑 核心: 计算 weight scale 和 zero_point
#
# 1. 从 observer 读取 weight 的 min/max 统计值
# 2. 根据 num_bits=4, symmetric=True 计算:
# qmin = 0, qmax = 2^4 - 1 = 15 (symmetric 只用到正半轴)
# 或更常见的对称表示: qmin = -8, qmax = 7 (含符号位)
# scale = max(|min|, |max|) / (qmax - qmin) / 2
# 3. zero_point = 0 (symmetric=True)
# 4. 将计算好的 scale 和 zero_point 存入 module.weight_scale 和
# module.weight_zero_point buffer
量化后的 forward 流程
# 在 module.forward() 中, 权重经过 QDQ (Quantize-Dequantize):
weight_q = torch.clamp(
torch.round(weight / scale) + zero_point, # 量化
qmin, qmax
)
weight_dq = (weight_q - zero_point) * scale # 反量化 (模拟量化精度损失)
output = input @ weight_dq.T # 用反量化后的权重计算
关键代码位置
| 步骤 | 文件 | 行号 |
|---|---|---|
QuantizationModifier.on_start() |
src/llmcompressor/modifiers/quantization/quantization/base.py |
L69-96 |
start_calibration() |
src/llmcompressor/modifiers/quantization/quantization/mixin.py |
L235-255 |
update_weight_global_scale() |
src/llmcompressor/modifiers/quantization/calibration.py |
- |
update_weight_zp_scale() |
src/llmcompressor/modifiers/quantization/calibration.py |
- |
enable_quantization / disable_quantization |
compressed-tensors/src/.../quantization/lifecycle/initialize.py |
- |
第六阶段:Pipeline 推断 → "datafree"
CalibrationPipeline.from_modifiers(modifiers, user="datafree") # pipelines/registry.py:32
│
├─ inferred = _infer_pipeline(modifiers) # :60
│ └─ _modifier_requires_calibration(QuantizationModifier) # :61
│ │
│ └─ config = modifier.resolve_quantization_config()
│ config.requires_calibration_data() # quant_config.py:257
│ │
│ │ 对于 W4A16:
│ │ kv_cache_scheme = None → 跳过 :258-259
│ │ input_activations = None → 跳过 :262-264
│ │ output_activations = None → 跳过 :265-267
│ │
│ └─ return False ← 🔑 不需要校准数据!
│
├─ inferred = "datafree"
│
├─ user="datafree" == inferred="datafree" → 无警告 # :66-67
│ 如果 user 和 inferred 不一致, 会打 warning
│
└─ 返回 DataFreePipeline 实例
DataFreePipeline 基本上是 no-op,
因为权重校准已经在 on_start 中一次性完成, 不需要 forward 任何校准数据
requires_calibration_data() 的完整逻辑
# compressed-tensors-main/src/compressed_tensors/quantization/quant_config.py:257
def requires_calibration_data(self):
"""
判断量化配置是否需要外部校准数据 (即是否需要跑 forward pass 来收集激活值统计)
"""
# 1. KV Cache 量化 → 需要校准数据
if self.kv_cache_scheme is not None:
return True
# 2. 遍历所有 config_groups
for _, scheme in self.config_groups.items():
# 输入激活的静态量化或 LOCAL 动态量化 → 需要校准数据
if scheme.input_activations is not None:
if scheme.input_activations.dynamic in (False, DynamicType.LOCAL):
return True
# 输出激活的静态量化 → 需要校准数据
if scheme.output_activations is not None:
if not scheme.output_activations.dynamic:
return True
# 3. 没有任何激活量化 → 不需要校准数据 (datafree)
return False
不同 scheme 对 pipeline 的影响
| Scheme | input_activations | 需要校准数据? | inferred pipeline | 说明 |
|---|---|---|---|---|
W4A16 |
None | ❌ No | datafree |
只量化权重, 无需校准数据 |
W8A16 |
None | ❌ No | datafree |
只量化权重, 无需校准数据 |
W8A8 |
dynamic=True | ❌ No | datafree |
动态激活, scale 实时计算 |
W4A8 |
dynamic=True | ❌ No | datafree |
动态激活, scale 实时计算 |
FP8 |
dynamic=False | ✅ Yes | basic |
静态激活, 需要先跑校准数据 |
FP8_DYNAMIC |
dynamic=True | ❌ No | datafree |
动态激活, 无需校准 |
关键代码位置
| 步骤 | 文件 | 行号 |
|---|---|---|
CalibrationPipeline.from_modifiers() |
src/llmcompressor/pipelines/registry.py |
L32-70 |
_infer_pipeline() |
src/llmcompressor/pipelines/registry.py |
L60-64 |
_modifier_requires_calibration() |
src/llmcompressor/pipelines/registry.py |
L61 |
requires_calibration_data() |
compressed-tensors/src/.../quantization/quant_config.py |
L257-269 |
完整调用链路总结图
用户调用
│
│ oneshot(
│ model=model,
│ recipe=QuantizationModifier(
│ targets="Linear",
│ scheme="W4A16",
│ ignore=["lm_head"],
│ ),
│ pipeline="datafree",
│ )
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ 1. Oneshot.__init__() + __call__() │
│ ├─ parse_args() → model_args, dataset_args, recipe_args │
│ ├─ pre_process() → 加载模型和 tokenizer │
│ └─ apply_recipe_modifiers() │
└───────────────────┬──────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ 2. Session.initialize(recipe=QuantizationModifier, start=-1) │
│ ├─ Recipe.create_instance(QuantizationModifier) │
│ │ └─ from_modifiers → Recipe(modifiers=[QuantizationModifier])
│ │ │
│ └─ QuantizationModifier.on_initialize(state) │
│ │ │
│ ├─ resolve_quantization_config() │
│ │ ┌─────────────────────────────────────────────┐ │
│ │ │ 3. scheme="W4A16" 解析 🔑 │ │
│ │ │ │ │
│ │ │ "W4A16" │ │
│ │ │ │ is_preset_scheme() → True │ │
│ │ │ ▼ │ │
│ │ │ PRESET_SCHEMES["W4A16"] = { │ │
│ │ │ weights: QuantizationArgs( │ │
│ │ │ num_bits=4, │ │
│ │ │ type=INT, │ │
│ │ │ strategy=GROUP, │ │
│ │ │ group_size=128, │ │
│ │ │ symmetric=True, │ │
│ │ │ dynamic=False, │ │
│ │ │ ), │ │
│ │ │ } │ │
│ │ │ ▼ │ │
│ │ │ QuantizationConfig( │ │
│ │ │ config_groups={ │ │
│ │ │ "group_0": QuantizationScheme( │ │
│ │ │ targets=["Linear"], │ │
│ │ │ weights=..., │ │
│ │ │ input_activations=None, │ │
│ │ │ output_activations=None, │ │
│ │ │ ) │ │
│ │ │ }, │ │
│ │ │ ignore=["lm_head"], │ │
│ │ │ ) │ │
│ │ └─────────────────────────────────────────────┘ │
│ │ │
│ └─ apply_quantization_config(model, config) │
│ ┌──────────────────────────────────────────┐ │
│ │ 4. 挂载 scheme 到模型层 │ │
│ │ │ │
│ │ match_named_modules(model, │ │
│ │ targets={"Linear": scheme}, │ │
│ │ ignore=["lm_head"]) │ │
│ │ ↓ │ │
│ │ 对每个匹配的 Linear 层: │ │
│ │ ├─ module.quantization_scheme = scheme │ │
│ │ ├─ module.quantization_status = │ │
│ │ │ INITIALIZED │ │
│ │ └─ initialize_module_for_quantization()│ │
│ └──────────────────────────────────────────┘ │
│ │
│ (start=-1 < 0, 立即触发 on_start) │
│ └─ QuantizationModifier.on_start(state) │
│ ┌───────────────────────────────────────────┐ │
│ │ 5. 权重校准 (一次性, 无数据依赖) │ │
│ │ │ │
│ │ start_calibration(model): │ │
│ │ ├─ _initialize_observers(module) │ │
│ │ ├─ _initialize_hooks(module) # 空! │ │
│ │ └─ enable_quantization(model) │ │
│ │ │ │
│ │ for each Linear layer: │ │
│ │ ├─ update_weight_global_scale(module) │ │
│ │ └─ update_weight_zp_scale(module) │ │
│ │ ① 从 observer 读 weight min/max │ │
│ │ ② 计算 scale = max(|min|,|max|) │ │
│ │ / (2^(4-1)-1) │ │
│ │ ③ zero_point = 0 (symmetric) │ │
│ │ ④ 存入 weight_scale, weight_zp │ │
│ │ │ │
│ │ on_end(): │ │
│ │ ├─ _remove_hooks() │ │
│ │ └─ model.apply(freeze_quantization) │ │
│ └───────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ 6. CalibrationPipeline.from_modifiers(modifiers, user="datafree")│
│ ├─ _infer_pipeline(modifiers) │
│ │ └─ config.requires_calibration_data() │
│ │ ├─ kv_cache_scheme = None → skip │
│ │ ├─ input_activations = None → skip │
│ │ ├─ output_activations = None → skip │
│ │ └─ return False → inferred = "datafree" │
│ │ │
│ └─ user="datafree" == inferred="datafree" → ✅ 无警告 │
│ │
│ DataFreePipeline(): 基本 no-op (权重校准已在 on_start 完成) │
│ │
└──────────────────────────────────────────────────────────────────┘
│
▼
session.finalize() → on_finalize() → 保存模型
关键结论
1. 为什么 W4A16 是 "datafree"?
-
W4A16的input_activations=None,output_activations=None -
没有激活量化, 就不需要跑校准数据来统计激活值的 min/max
-
权重的 scale 可以直接从权重本身的统计 (observer minmax) 一次性计算
-
QuantizationConfig.requires_calibration_data()返回False→ pipeline 自动推断为datafree
2. 不传 pipeline 或传 pipeline="datafree" 行为完全一致
-
pipeline默认值是"independent"(不是"datafree") -
但
_infer_pipeline()的优先级更高:会检查requires_calibration_data() -
对于 W4A16/W8A16 等单纯权重稀疏化的 scheme,一定被推断为
datafree -
user参数只是用来做一致性检查(如果用户指定了需要数据的 pipeline 但实际不需要,会打警告)
3. 量化只对 Linear 层生效 (targets="Linear")
-
match_named_modules(model, {"Linear": scheme}, ignore=["lm_head"])匹配所有torch.nn.Linear -
lm_head在 ignore 列表中,不会被量化(因为 lm_head 量化通常会导致输出质量下降)
4. W4A16 的权重是静态量化 (dynamic=False)
-
意味着 scale/zero_point 在
on_start阶段一次性计算,推理时不再改变 -
与动态量化 (dynamic=True) 不同,动态量化在每次 forward 都重新计算 scale
5. 对称量化 (symmetric=True) 意味着 zero_point=0
-
量化公式简化为:
q = clamp(round(w / scale), qmin, qmax) -
不需要存储/计算 zero_point,减少参数量和运算复杂度
相关文件索引
| 文件路径 | 说明 |
|---|---|
src/llmcompressor/entrypoints/oneshot.py |
oneshot 入口函数 |
src/llmcompressor/recipe/recipe.py |
Recipe 和 Recipe.create_instance |
src/llmcompressor/modifiers/quantization/quantization/base.py |
QuantizationModifier |
src/llmcompressor/modifiers/quantization/quantization/mixin.py |
QuantizationMixin (config 解析, calibration) |
src/llmcompressor/modifiers/quantization/calibration.py |
update_weight_zp_scale 等校准函数 |
src/llmcompressor/pipelines/registry.py |
CalibrationPipeline.from_modifiers, pipeline 推断 |
compressed-tensors-main/src/compressed_tensors/quantization/quant_scheme.py |
QuantizationScheme, PRESET_SCHEMES |
compressed-tensors-main/src/compressed_tensors/quantization/quant_config.py |
QuantizationConfig, requires_calibration_data |
compressed-tensors-main/src/compressed_tensors/quantization/quant_args.py |
QuantizationArgs, QuantizationStrategy, DynamicType |
compressed-tensors-main/src/compressed_tensors/quantization/lifecycle/apply.py |
apply_quantization_config |
compressed-tensors-main/src/compressed_tensors/quantization/lifecycle/initialize.py |
initialize_module_for_quantization |