手把手复现 REAP 专家剪枝:基于 XYZ-Aquila-mini-OptiQ-4bit-REAP-18B 的 optiq prune-experts 完整指南

原创2026-10-18 16:00:03686 阅读

手把手复现 REAP 专家剪枝:基于 XYZ-Aquila-mini-OptiQ-4bit-REAP-18B 的 optiq prune-experts 完整指南

在 Apple Silicon 上跑 MoE 大模型,内存是绕不开的坎。这篇教程带你在 XYZ-Aquila-mini-OptiQ-4bit-REAP-18B 仓库上手把手复现 REAP 专家剪枝:用 optiq prune-experts 一条命令,把 34.6 GB 的 4bit 量化 MoE 模型剪到 18.7 GB(-46%),而每 token 激活参数不变、推理速度不掉。你会理解 REAP 排序规则的原理、剪枝流程的每一步,并学会验证剪枝质量。

项目背景:REAP-18B 到底剪掉了什么

这是一个基于 MoE(Mixture of Experts,混合专家)架构的视觉语言模型 qwen3_5_moe,40 层,采用 4bit affine 量化(group_size=64,部分敏感层混合 8bit),配置见 config.json。

REAP 变体做了什么,一张表说清楚:

指标 父模型(4bit) REAP-18B 变化
磁盘大小 34.6 GB 18.7 GB −46%
每层路由专家 256 128 −50%
每 token 激活专家 top-8 top-8(不变) 0
推理速度 基准 不变 —

💡 关键点:被保留的专家是从父量化权重逐位复制过来的——不反量化、不重量化、不合并、不重训,所以"变小但不变慢"。

仓库内与本次剪枝直接相关的文件:

文件 作用
README.md 模型说明、压缩数据、使用方法(本文主线依据)
config.json 架构定义、逐层量化位宽配置
optiq_metadata.json OptiQ 混合精度记录:目标 4.5 bpw、实际 4.99 bpw,94 个 8bit 张量 + 296 个 4bit 张量
kv_config.json KV cache 逐层量化配置(如第 11、15 层用 8bit,其余 4bit)
model.safetensors.index.json 2090 个张量到 5 个分片的映射,total_size 约 19.17 GB
model-00001-of-00005.safetensors 等 剪枝后的模型权重分片
tokenizer.json、chat_template.jinja 分词器与对话模板(含图片/视频多模态渲染逻辑)

REAP 原理:为什么是"路由权重 × 输出范数"

REAP(REAP the Experts,ICLR 2026)的核心思想:一个专家"值不值得留",要看它在真实请求中被路由到的强度 × 它输出的有效幅度。

对每个专家,REAP 计算:

分数 = 条件均值( router 权重 × 专家输出范数 | 该专家被激活的 token )
  • router 权重:路由器给这个专家的分派强度——分得越多,越重要;
  • 输出范数:专家输出的幅度——幅度太小,说明它对结果贡献有限;
  • 条件均值:只在该专家"服务过"的 token 上取平均,避免冷门专家被平均稀释。

按分数从高到低排序,删掉排名最低的 128 个,再把路由器的权重矩阵同步"切片"(256 列 → 128 列),模型结构即保持一致、可正常加载。

OptiQ 的独特之处:这一步直接作用于 4bit 量化权重——不需要 BF16 原始权重,也从不反量化幸存者,因此整个过程快、省内存、零额外精度损失。

复现步骤:optiq prune-experts 完整流程

步骤一:准备环境

剪枝在 Apple Silicon(MLX)上完成,准备一个 Python 环境:

pip install mlx-optiq

然后获取本文的参考产物(剪枝后 checkpoint),方便对照验证:

git clone https://gitcode.com/hf_mirrors/mlx-community/XYZ-Aquila-mini-OptiQ-4bit-REAP-18B

⚠️ 执行剪枝本身还需要父模型 mlx-community/XYZ-Aquila-mini-OptiQ-4bit(34.6 GB 的 4bit 量化版),本仓库是剪枝结果,用于对照与直接部署。

步骤二:配置剪枝参数

对照本仓库 README.md 的 Details 表,REAP-18B 的配方是:

  • 保留比例:每层 128 / 256(50%);
  • 分配方式:uniform——每一层都均匀地留 128 个;
  • 激活数:top-8 保持不变;
  • 排序规则:条件均值(conditional mean,由两个候选规则中按"对未剪枝模型打分更优"选出);
  • 校准数据:optiq six-domain 混合,每域 8 条样本(样本量很小,正是 one-shot 压缩的特点)。

步骤三:运行校准 + 剪枝 + 导出

OptiQ 把"校准 → 打分排序 → 删除 → 路由器切片 → 逐位复制"封装在一条命令里:

optiq prune-experts \
  --model mlx-community/XYZ-Aquila-mini-OptiQ-4bit \
  --retain 128 \
  --calib-dataset optiq-six-domain \
  --calib-samples 8 \
  --ranking conditional-mean

执行期间工具会:

  1. 在量化域加载父模型,跑校准样本的前向,统计每个专家的 router 权重 × 输出范数;
  2. 按条件均值对每层 256 个专家排序,保留前 128;
  3. 把路由矩阵同步切到 128 列,保留专家权重逐位复制进新 checkpoint;
  4. 写出新目录的 config.json、分片、index 与元数据——最终产物即本仓库的 5 个 model-*-of-00005.safetensors 分片。

各参数名称以 optiq prune-experts --help 输出为准(不同版本可能有出入),OptiQ 的剪枝方法说明可参考其官方 prune 文档。

步骤四:加载并跑通

剪枝产物是标准 MLX checkpoint,两种用法:

# 起本地服务
pip install mlx-optiq
optiq serve --model mlx-community/XYZ-Aquila-mini-OptiQ-4bit-REAP-18B
# 或直接 mlx_lm 推理
from mlx_lm import load, generate
model, tok = load("mlx-community/XYZ-Aquila-mini-OptiQ-4bit-REAP-18B")
print(generate(model, tok, prompt="Hello", max_tokens=64))

验证清单:4 项检查确认剪枝成功

检查项 预期值 在哪里确认
磁盘大小 18.7 GB(父模型 34.6 GB,−46%) du -sh 模型目录
张量总数 / 总大小 2090 个张量,约 19.17 GB model.safetensors.index.json
每层专家数 128(top-8 激活不变) config.json 的量化分组与 README 说明
KL 散度(对未剪枝父模型) 0.232 optiq 输出 / 人工评测

关于 KL 散度值得多说一句:README 给出的经验线是——KL 明显可见劣化的 checkpoint 通常 > 1.0,而 0.232 处于"人工审阅下生成结果无法区分"的区间。这就是 REAP-18B 的剪枝质量证据。

常见问题排查(FAQ)

  • 磁盘空间不够? 剪枝需要同时容纳父模型(34.6 GB)与产物(18.7 GB),建议预留 60 GB。
  • KL 偏高、质量肉眼可见下降? 优先换用与目标使用场景更贴近的校准数据,并确认排序规则用条件均值而非简单平均。
  • 剪枝后变慢了? 检查 top-8 激活数是否被误改——本配方只收缩"存储的专家库",不改变每 token 的激活量。
  • 加载报结构错误? 确认路由器矩阵与专家库同步切片,且 config.json 的量化配置完整(4bit/8bit 混合的逐层条目都在)。
  • 多模态输入不生效? 检查 chat_template.jinja 与 tokenizer.json 是否随 checkpoint 一起复制完整。

总结:一条命令换 46% 的空间

值
压缩收益 34.6 GB → 18.7 GB(−46%)
专家库 每层 256 → 128,top-8 激活不变
质量 KL 0.232(经验安全阈值 1.0 以内)
成本 无需 BF16 父权重,量化域一次完成

如果你只需要模型,直接 optiq serve 即可;如果你有自己的 MoE 量化 checkpoint,把"步骤二"的参数换成自己的保留比例与校准集,同样这套 optiq prune-experts 流程就能复制出属于你的 REAP 变体。

登录后查看全文
XYZ-Aquila-mini-OptiQ-4bit-REAP-18B