Qwen3-0.6B w4a8lwc+lac量化实战:AMCT 在昇腾 NPU 上的 int4/int8 与 mxfp4/mxfp8 双格式 PTQ 完整流程
【免费下载链接】amctAMCT是CANN提供的昇腾AI处理器亲和的模型压缩工具仓。项目地址: https://gitcode.com/cann/amct
本指南以 CANN AMCT 开源仓库中的 Qwen3 量化示例 为蓝本,完整讲解如何在昇腾 NPU 单卡(npu:0)上,对 Qwen3-0.6B 模型执行 w4a8(4 bit 权重 / 8 bit 激活)量化,并以lwc(可学习权重裁剪)+lac(可学习激活裁剪)两种算法组合完成离线数据提取、PTQ 参数训练与量化评估。读完本文后,你将掌握python3 -m amct_pytorch.eval / extract_ptq_data / ptq三个 CLI 入口的完整调用链,能够在int4/int8与mxfp4/mxfp8两种数据格式间一键切换,并理解仓库中 LWC/LAC 算法在源码层面的实现原理。
示例背景与适用场景
本示例对应仓库 Issue [#182] 任务 1:在int4/int8 与 mxfp4/mxfp8两种格式下,对 Qwen3-0.6B 执行w4a8+lwc/lac量化。示例目录位于 examples/models/qwen3,与qwen3.6、deepseekv4等模型示例平级,目录下的 README.md 与 README_en.md 为该实践的权威说明。
该示例的 CLI 用法与 Qwen3.6-MoE 量化实践 保持一致,统一走python3 -m模块入口;int 与 mxfp 两种格式共用仓库内的同一份量化策略文件 amct_pytorch/configs/w4a8.yaml,仅通过--quant_dtype参数切换数据格式,无需复制或修改策略文件。
适用前提(以仓库文档为准):
- 硬件:昇腾 NPU 单卡,设备号
npu:0; - 软件:已 source CANN 环境(如一站式平台路径
/home/developer/Ascend/cann/set_env.sh),并安装amct_pytorch(或将仓库根目录加入PYTHONPATH); - 模型:本地 Qwen3-0.6B 权重目录,运行参数
--model_name qwen3、--trust_remote_code; - 数据:校准集使用 Pileval(
mit-han-lab/pile-val-backup,默认nsamples=128),评估集使用 WikiText2(wikitext-2-raw-v1test 集)。
理解 w4a8 量化策略:仓库内共享的 bit_config
int 与 mxfp 两种格式共享同一份策略文件 amct_pytorch/configs/w4a8.yaml,其内容如下:
# Uniform W4A8 across all linears. w_bits: 4 a_bits: 8 moe: routed: w_bits: 4 # group-level: bump all shared-expert a_bits: 8 shared: w_bits: 8 # group-level: bump all shared-expert a_bits: 8关键信息解读:
- 顶层
w_bits: 4/a_bits: 8对所有线性层统一施加 w4a8 约束; moe.routed与moe.shared是 MoE 路由专家与共享专家分组级的位宽覆盖:routed 专家保持 4 bit 权重,而 shared 专家提升到 8 bit 权重(注释中 "bump all shared-expert" 即指把共享专家位宽上调以保护共享路径精度);- 从 CLI 参数解析源码看(amct_pytorch/cli/llm/args.py),
--bit_config接收的是“描述各角色位宽的 yaml 文件路径”,即该文件由 PTQ 训练与量化评估两个阶段共同消费。
BF16 基线的策略文件 amct_pytorch/configs/bf16.yaml 则是一个空配置(全部保持 16 bit),用于 BF16 原始精度评估,保证量化前后使用同一评估口径。
环境准备与前置检查
仓库文档明确给出了以下环境要求与注意事项,动手前请逐项确认:
- CANN 环境:按你的安装路径 source 环境,例如一站式平台执行
source /home/developer/Ascend/cann/set_env.sh;如需数据集镜像,自行设置HF_ENDPOINT。 - amct_pytorch 安装:安装
amct_pytorch,或将仓库根目录加入PYTHONPATH后直接使用python3 -m入口。 - 本地依赖修复:部分镜像自带的
torchaudio存在缺陷,会在transformers导入阶段崩溃;需要先修复该本地依赖,再使用官方python3 -m入口。本示例不附带环境包装脚本(env wrapper)。 - 模型与产物目录:
MODEL指向本地 Qwen3-0.6B 权重目录;产物目录(examples/models/qwen3/outputs/...)为相对占位目录,不要提交数据或权重到仓库。
标准流程:四步走完双格式量化
整个流程在仓库根目录执行,按序分为四个阶段。两个格式共用步骤 1(BF16 基线)与步骤 2(离线数据提取),步骤 3、4 分别对 int 与 mxfp 各跑一遍。
步骤 1:BF16 基线评估(两种格式共享)
source /path/to/cann/set_env.sh export MODEL=/path/to/Qwen3-0.6B # placeholder; point to local weights python3 -m amct_pytorch.eval \ --trust_remote_code \ --model "${MODEL}" \ --model_name qwen3 \ --seq_len 4096 \ --granularity block \ --device npu:0 \ --eval_mode bf16 \ --bit_config amct_pytorch/configs/bf16.yaml从 amct_pytorch/cli/llm/args.py 的源码可知,--eval_mode仅接受bf16与quant两个取值:bf16直接使用原始模型路径做评估,quant则重建量化模块并按位宽切换量化器。BF16 与量化评估必须使用同一权重目录、同一评估配置(seq_len=4096、granularity=block),这样计算出的 PPL 差值(PPL delta)才具备可比性。
步骤 2:离线提取 PTQ 校准数据(attn + mlp)
每个目标各执行一次extract_ptq_data,校准数据分别落盘:
# Attention 线性层 python3 -m amct_pytorch.extract_ptq_data \ --trust_remote_code \ --model "${MODEL}" \ --model_name qwen3 \ --seq_len 4096 \ --granularity block \ --device npu:0 \ --quant_target attn-linear \ --nsamples 128 \ --data_dir examples/models/qwen3/outputs/qwen3_0_6b/ptq_data/attn-linear # MLP python3 -m amct_pytorch.extract_ptq_data \ --trust_remote_code \ --model "${MODEL}" \ --model_name qwen3 \ --seq_len 4096 \ --granularity block \ --device npu:0 \ --quant_target mlp \ --nsamples 128 \ --data_dir examples/models/qwen3/outputs/qwen3_0_6b/ptq_data/mlp--quant_target一次只针对一个目标(attn-linear或mlp),这是后续 PTQ 分目标训练的前提。--nsamples默认 128,来自 Pileval 校准集。
步骤 3:int4/int8 格式的 PTQ 训练与量化评估
对 Attention 与 MLP 分别执行ptq训练,然后统一做量化评估:
# 3a) Attention 线性层 PTQ python3 -m amct_pytorch.ptq \ --trust_remote_code \ --model "${MODEL}" \ --model_name qwen3 \ --seq_len 4096 \ --granularity block \ --device npu:0 \ --data_dir examples/models/qwen3/outputs/qwen3_0_6b/ptq_data/attn-linear \ --quant_dtype int \ --algos lwc lac \ --bit_config amct_pytorch/configs/w4a8.yaml \ --quant_target attn-linear \ --start_block_idx 0 \ --end_block_idx 28 \ --epochs 15 \ --base_lr 1e-5 \ --output_dir examples/models/qwen3/outputs/qwen3_0_6b_w4a8_int # 3b) MLP PTQ python3 -m amct_pytorch.ptq \ --trust_remote_code \ --model "${MODEL}" \ --model_name qwen3 \ --seq_len 4096 \ --granularity block \ --device npu:0 \ --data_dir examples/models/qwen3/outputs/qwen3_0_6b/ptq_data/mlp \ --quant_dtype int \ --algos lwc lac \ --bit_config amct_pytorch/configs/w4a8.yaml \ --quant_target mlp \ --start_block_idx 0 \ --end_block_idx 28 \ --epochs 15 \ --base_lr 1e-5 \ --output_dir examples/models/qwen3/outputs/qwen3_0_6b_w4a8_int # 3c) int 格式量化评估(同时加载 attn + mlp 的 PTQ 参数) python3 -m amct_pytorch.eval \ --trust_remote_code \ --model "${MODEL}" \ --model_name qwen3 \ --seq_len 4096 \ --granularity block \ --device npu:0 \ --eval_mode quant \ --quant_target attn-linear mlp \ --quant_dtype int \ --algos lwc lac \ --bit_config amct_pytorch/configs/w4a8.yaml \ --attn_linear_param_dir examples/models/qwen3/outputs/qwen3_0_6b_w4a8_int/ptq_params/qwen3/attn-linear \ --moe_mlp_param_dir examples/models/qwen3/outputs/qwen3_0_6b_w4a8_int/ptq_params/qwen3/mlp注意评估阶段的差异:--quant_target attn-linear mlp一次加载两个目标,PTQ 参数分别通过--attn_linear_param_dir与--moe_mlp_param_dir指定。
步骤 4:mxfp4/mxfp8 格式
mxfp 格式不改变任何流程结构:将步骤 3 中的所有--quant_dtype int换成--quant_dtype mxfp,并把--output_dir与两个 param dir 统一切换到outputs/qwen3_0_6b_w4a8_mxfp下即可。从 amct_pytorch/cli/llm/args.py 的源码可以看到,--quant_dtype支持int、mxfp、hifp、fp四种取值,int与mxfp即本示例用到的两种格式。
命令行参数速查表
以下是本示例核心参数的含义(依据 README_en.md 与 common.sh 整理):
| Arg / env | 含义 |
|---|---|
MODEL | 本地权重目录(占位符,须替换为实际路径) |
--model_name qwen3 | Dense Qwen3 路径分支 |
--seq_len 4096 | 校准与评估的序列长度 |
--granularity block | 块级(block)粒度 |
--quant_dtype int\|mxfp | int4/int8 或 mxfp4/mxfp8 |
--algos lwc lac | 量化算法组合 |
--bit_config | 仓库内 amct_pytorch/configs/w4a8.yaml |
--quant_target | extract/PTQ 阶段一次一个目标:attn-linear或mlp;量化评估一次加载两者 |
--nsamples | Pileval 校准样本数,默认 128 |
--end_block_idx | Qwen3-0.6B 默认 28 层 |
其余训练超参(--start_block_idx 0、--end_block_idx 28、--epochs 15、--base_lr 1e-5)在 amct_pytorch/cli/llm/args.py 中都有默认值:--base_lr默认 1e-5(可学习变换的学习率),--epochs默认 15,--end_block_idx默认 61(本示例按 Qwen3-0.6B 实际层数收敛到 28)。
脚本化运行:一行命令跑完整流程
除逐条执行 CLI 外,示例还提供了一组 shell 脚本,统一由 scripts/common.sh 提供共享默认值与运行时参数打印:
- scripts/extract_ptq_data.sh:依次执行 attn-linear 与 mlp 两次离线数据提取;
- scripts/ptq_attn.sh:Attention 线性层 PTQ 训练;
- scripts/ptq_mlp.sh:MLP PTQ 训练;
- scripts/eval_bf16.sh:BF16 基线评估;
- scripts/eval_quant.sh:量化评估(同时加载 attn 与 mlp 参数)。
脚本中的所有可调变量均可在调用前通过环境变量覆盖,例如MODEL=/path/to/Qwen3-0.6B、QUANT_DTYPE=mxfp。以common.sh为例,其默认值完全对齐上文的 CLI 参数:MODEL_NAME=qwen3、DEVICE=npu:0、SEQ_LEN=4096、GRANULARITY=block、NSAMPLES=128、QUANT_DTYPE=int、ALGOS="lwc lac"、START_BLOCK_IDX=0、END_BLOCK_IDX=28、EPOCHS=15、BASE_LR=1e-5。int 与 mxfp 的产物根目录由QUANT_DTYPE自动分流到outputs/qwen3_0_6b_w4a8_int或outputs/qwen3_0_6b_w4a8_mxfp,两个bit_config分别指向仓库内的 w4a8.yaml 与 bf16.yaml。每个脚本运行前都会调用print_runtime_args打印完整的运行时参数,便于复现与排查。
算法原理:源码视角下的 LWC 与 LAC
lwc与lac是仓库内置的可学习裁剪算法,实现在 amct_pytorch/algorithms/quant/auto_clip.py,通过 amct_pytorch/algorithms/registry_factory.py 的ALGO_REGISTRY注册:
LWC(Learnable Weight Clipping,注册名lwc,targets=("weight",)):对权重做可学习裁剪。从源码看,它对每个裁剪维度维护一对可学习参数clip_factor_max/clip_factor_min(初值 4.0),经 sigmoid 映射后乘到当前 min/max 上再执行torch.clamp;当quant_dtype == "mxfp"时,权重会被 reshape 为(-1, 32)按 mxfp 的 32 元素分块维度计算裁剪因子(见_update_clip_dim与apply_clip)。LAC(Learnable Activation Clipping,注册名lac,targets=("activation",)):对激活做可学习裁剪,采用 per-tensor 的标量裁剪因子(--is_per_tensor控制激活裁剪的统计口径)。其export_ptq_params/load_ptq_params方法将clip_factor_min、clip_factor_max、maxval、minval四个张量导出/加载为 PTQ 参数——这正是ptq训练产物目录中ptq_params的来源,也是量化评估阶段--attn_linear_param_dir/--moe_mlp_param_dir加载的内容。
两者组合(--algos lwc lac)即"权重可学习裁剪 + 激活可学习裁剪"的完整 PTQ 训练目标;在 amct_pytorch/algorithms/quant/let.py 的文档字符串中还说明,若要还原完整的 OmniQuant 方法,可使用--algos let lwc。--algos参数在源码中声明为nargs="*"(args.py),框架会按注册目标(weight/activation)路由到对应算法。
产物目录与规模参考
示例默认在examples/models/qwen3/outputs/下按相对占位目录组织产物(数据与权重不提交仓库):
| Artifact | Path | Size |
|---|---|---|
| PTQ 离线数据,Attention | ./outputs/qwen3_0_6b/ptq_data/attn-linear | 28.002 GiB |
| PTQ 离线数据,MLP | ./outputs/qwen3_0_6b/ptq_data/mlp | 28.002 GiB |
| 离线数据合计(attn+mlp) | 两个目录之和 | 56.004 GiB |
| int PTQ 参数,Attention | ./outputs/qwen3_0_6b_w4a8_int/ptq_params/qwen3/attn-linear | 1.30 MiB |
| int PTQ 参数,MLP | ./outputs/qwen3_0_6b_w4a8_int/ptq_params/qwen3/mlp | 1.67 MiB |
| mxfp PTQ 参数,Attention | ./outputs/qwen3_0_6b_w4a8_mxfp/ptq_params/qwen3/attn-linear | 42.20 MiB |
| mxfp PTQ 参数,MLP | ./outputs/qwen3_0_6b_w4a8_mxfp/ptq_params/qwen3/mlp | 63.14 MiB |
统计口径说明(沿用仓库文档):量化时间从该格式第一次ptq开始,到 attn 与 mlp 两组参数全部写出为止(不含模型下载与环境搭建);离线数据大小为 extract 输出合计,单位为 GiB。
结果对照:int4/int8 与 mxfp4/mxfp8 的精度与耗时
仓库文档给出的实测结果为(BF16 与量化评估均使用同一MODEL、同一seq_len=4096/granularity=block配置):
| Model | Data type | Algorithm | Format | BF16 PPL | Quant PPL | PPL delta | PTQ minutes | Offline data (GiB) |
|---|---|---|---|---|---|---|---|---|
| Qwen3-0.6B | w4a8 | lwc+lac | int4/int8 | 19.157549 | 47.486488 | 28.328939 | 37.35 | 56.004 |
| Qwen3-0.6B | w4a8 | lwc+lac | mxfp4/mxfp8 | 19.157549 | 23.898159 | 4.740610 | 48.88 | 56.004 |
该表是仓库文档记录的本示例实测结果,可复现性取决于硬件型号、CANN 版本、模型权重与数据镜像等环境因素,在自己的环境中运行时应以实际输出为准。两个值得留意的现象:同一算法组合下mxfp4/mxfp8的 PPL 掉点(4.74)显著小于int4/int8(28.33),而 mxfp 的 PTQ 训练耗时(48.88 分钟)略高于 int(37.35 分钟),且 mxfp 的 PTQ 参数体积明显更大——这与 mxfp 格式按 32 元素分块组织缩放因子的实现(见上文 LWC 对 mxfp 的 reshape 逻辑)相吻合。
常见问题与注意事项
- 评估口径一致性:BF16 与量化评估必须使用同一权重目录、同一
seq_len/granularity,否则 PPL delta 失去意义; - 目标分解执行:
--quant_target在 extract/PTQ 阶段一次只能指定一个目标(attn-linear或mlp),只有量化评估阶段才同时加载两个目标的参数; - 环境变量覆盖:使用
scripts/*.sh时,所有默认值均可通过同名环境变量在调用前覆盖(如MODEL、QUANT_DTYPE、NSAMPLES、END_BLOCK_IDX),脚本会先打印完整运行时参数; - 格式切换:int 与 mxfp 共用同一份 w4a8.yaml,只改
--quant_dtype与产物目录即可,无需复制策略文件; - 不提交产物:
outputs/下的离线数据与 PTQ 参数均为相对占位目录,不应提交到仓库。
按上述流程在昇腾 NPU 上完整跑通后,即可获得 Qwen3-0.6B 在 int4/int8 与 mxfp4/mxfp8 两种格式下的 w4a8 量化参数与 PPL 评估结果,并可直接复用同一套流程与 w4a8.yaml 策略扩展到其他稠密模型或 MoE 模型的 Attention/MLP 目标量化。
【免费下载链接】amctAMCT是CANN提供的昇腾AI处理器亲和的模型压缩工具仓。项目地址: https://gitcode.com/cann/amct
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考