- 大模型
- 本地部署
- 人工智能
【免费下载链接】K2-Horizon-MoVA-36B-A4B-bf16
想让一个拥有100 个专家的 36B 参数 MoE 大模型在 Mac 上跑得飞快?本文带你拆解 K2-Horizon-MoVA-36B-A4B-bf16 的 MLX 移植原理:k2_horizon.py 这个不到 26KB 的文件,如何把一个 PyTorch 实现"翻译"成 Apple Silicon 上的 35.3 tok/s 生成速度。无需读一行源码也能看懂,读完你会明白 MoE 模型在 Mac 上到底卡在哪里、又被怎么打通的。
一、先认识这个模型:36B 参数,只激活 4B
K2-Horizon-MoVA-36B-A4B 是 IFM 出品的稀疏 MoE(Mixture-of-Experts,混合专家)模型,配置藏在 config.json 里,几个关键数字:
| 配置项 | 数值 | 通俗解释 |
|---|---|---|
num_experts | 100 | 每一层 MoE 有 100 个"专家" |
num_experts_per_tok | 8 | 每个 token 只挑 8 个专家干活 |
mova_num_experts | 64 | 注意力里的 Value 也有 64 个专家 |
mova_num_experts_per_tok | 4 | 每个 token 只用其中 4 个 |
num_hidden_layers | 48 | 48 层,前 3 层是普通稠密层,后 45 层全是稀疏层 |
max_position_embeddings | 524288 | 支持 512K 超长上下文 |
💡 名字里的 "A4B" 就是"36B 总参数、每 token 只激活约 4B"的意思——这就是 MoE 的精髓:养肥了,用瘦了。
它还有个独门绝技叫MoVA(Mixture-of-Values)注意力:普通 Transformer 的 Value 向量是一次线性投影算出来的,而 K2-Horizon 的 Value 来自 64 个"价值专家"中被路由选中的 4 个。可以说,连注意力的 Value 都在玩 MoE——这也是移植难度最大的部分。
本仓库是官方模型的未量化 bf16 MLX 版本,磁盘占 70 GB,需要128 GB 内存的 Mac(128GB Mac Studio 实测峰值 75 GB)。
二、为什么这个仓库自带一份模型代码?
打开 config.json 会看到一行特殊的字段:
"model_file": "k2_horizon.py"这是整个仓库的设计关键。mlx-lm(MLX 官方的推理工具)内置了上百种模型架构,但k2_horizon这种"MoE + MoVA"的组合它还没收录。于是作者直接把移植好的模型文件 k2_horizon.py 放进仓库,让 mlx-lm 在加载模型时动态读取这个文件作为架构定义。
这样做的好处:
- ✅ 你
pip install -U mlx-lm装到的就是官方原版,不需要 patch - ✅ 代码就在本地,愿意的话可以先读一遍再用(加载时需要
--trust-remote-code参数,仓库 README.md 有说明) - ✅ 一旦官方 mlx-lm 原生支持该架构,这个文件随时可以退役
三、移植核心:6 个让"100个专家"提速的关键点
k2_horizon.py 文件头部的注释写得很直白:"数学完全一样,实现方式不同"。差异集中在 6 处,逐一看:
1️⃣ gather_mm:100 个专家,一次内核调用全部算完
这是全文最核心的性能优化。
PyTorch 原版处理 MoE 的方式是:用 Python 循环遍历被选中的专家,每次找出"哪些 token 派给了这个专家",算完再用index_add_原地累加。这种写法依赖原地修改和数据依赖的分支——而 MLX 的惰性计算图里,这两样要么很慢、要么根本不可用。
MLX 移植版的思路彻底反过来:借助 mlx-lm 提供的SwitchGLU/SwitchLinear模块,底层是gather_mm内核——把"所有 (token, 专家) 配对"排成一队,一次 kernel 调用把所有选中专家的矩阵乘法打包完成,彻底消灭 Python 循环。
🚀 类比:原版是"100 个窗口排队叫号、一次办一个",
gather_mm是"开一条批量流水线,一趟全过"。
2️⃣ sanitize:加载权重时把专家"叠"成一张大表
gather_mm有个前提:某个专家的所有权重必须存在一个连续的大张量里,而不能是 100 个各自独立的小张量(原版 PyTorch checkpoint 正是后者)。
k2_horizon.py 里的Model.sanitize方法(约 L588 起)在加载 checkpoint 时做了一次性的"叠砖":
mlp.experts.0/1/.../99的 gate/up/down 三组权重 → 堆叠成[100, out, in]的单张量- 64 个 Value 专家的权重 → 堆叠成
[64, out, in]的单张量
所以你在磁盘上看到的 15 个分片文件(model-00001-of-00015.safetensors等,分片清单见 model.safetensors.index.json)里,专家权重已经是"叠好"的布局了。
3️⃣ 排序小优化:批量来时"按专家分堆"
[k2_horizon.py](https://link.gitcode.com/i/e1e4a833727b719645ad3587462085db)的SwitchValueExperts里藏着一个巧妙的阈值:当 (token, 专家) 配对数≥ 64(比如处理长提示词时),先把 token按专家排序再交给gather_mm,这样内核读取每个专家的权重时内存访问是连续的,带宽利用率更高;排序完再按逆序还原。
而生成阶段每次只处理 1 个 token,配对数不足 64,直接跳过排序,省下无谓开销。一个if同时优化了预填充和解码两个场景。
4️⃣ mx.fast 融合内核:RoPE、注意力、RMSNorm 全换"芯片直连"
PyTorch 原实现是一步步的 eager 算子,MLX 版全部换成了mx.fast.*融合内核:
mx.fast.rms_norm:归一化一步到位,不再拆成平方、均值、开方三步mx.fast.rope:旋转位置编码的频率表由内核现场计算,省去预建表- 融合
scaled_dot_product_attention:原生支持 GQA(32 个 Q 头共享 8 个 KV 头),不用像原版那样repeat_kv复制 KV 头再算 softmax
在 Apple Silicon 的统一内存架构下,这些融合内核能显著减少数据在内存与计算单元之间的搬运。
5️⃣ fp32 加权求和:更准,不只是更快
原版 PyTorch 在 bf16 下把路由权重转成 bf16、用 bf16 累加专家输出;MLX 版则全程在 fp32 里做加权和,最后才转回 bf16。副作用只有一个:结果比 PyTorch 的 bf16 输出"更准一点",差异纯粹来自舍入——这是作者用 fp32 逐层对拍验证过的。
6️⃣ KV 缓存对齐 mlx-lm 生态
缓存和掩码不再用 HuggingFace 的Cache类,而是换成 mlx-lm 自己的 KVCache 对象;位置编码的偏移量直接从缓存的offset读取。这样mlx_lm.generate、mlx_lm.chat、mlx_lm.server全家族命令开箱即用,长对话、多轮上下文都走得通。
四、移植后如何自证"没算错"?逐层对拍到 1e-6
换框架移植最怕"跑起来了但数值悄悄错了"。这个移植的验证相当硬核:
- 用fp32 + 真实权重,与 IFM 官方 PyTorch 代码(
modeling_k2_horizon.py)逐层对比,全部 48 层相对误差 ≤ 1e-6 - 贪心解码(带 KV 缓存)每个位置预测的 token 完全一致
- 冒烟测试:
17 * 23→ 391(推理正确)、"澳大利亚首都" → Canberra、多轮服务端对话上下文正确
另外几个"宁可报错也不算错"的防御性设计值得点赞:ModelArgs的__post_init__(k2_horizon.py L87 起)会主动拒绝未移植的 partial RoPE 和滑窗注意力,YaRN 参数不符时直接NotImplementedError——fail loudly,而不是 silently wrong。
五、实测性能:128GB Mac 上 35.3 tok/s
在Mac Studio M4 Max 128GB上的实测数据(引自 README.md):
| K2-Horizon MLX 版本 | 磁盘 | 峰值内存 | WikiText-2 困惑度 | 生成速度 |
|---|---|---|---|---|
| bf16(本仓库) | 70 GB | 75.0 GB | 11.368 | 35.3 tok/s |
| 8-bit | 37 GB | 39.9 GB | 11.372(+0.04%) | 51.2 tok/s |
| 4-bit | 25 GB | 26.5 GB | 11.508(+1.2%) | 59.6 tok/s |
两点实用结论:
- 内存:bf16 版短提示词峰值 75 GB,在 128GB Mac 的默认 Metal 工作集上限内,无需
sysctl调参。KV 缓存约 192 KB/token(128K token 约 24 GB),要跑长上下文建议换 8-bit 版。 - 速度:MoE 的妙处尽显——bf16 虽然最"重",但因为每 token 只激活 4B 参数,依然能做到 35.3 tok/s;量化版更快(59.6 tok/s)而质量几乎无损,可以按自己的内存预算二选一。
六、快速上手:三步跑起来
第 1 步,安装/升级 mlx-lm:
pip install -U mlx-lm第 2 步,生成你的第一句话(记得带--trust-remote-code,新版 mlx-lm 必须加):
mlx_lm.generate --model mlx-community/K2-Horizon-MoVA-36B-A4B-bf16 --trust-remote-code --prompt "Explain mixture-of-experts in two sentences." --max-tokens 2048第 3 步(可选),用mlx_lm.chat或mlx_lm.server起交互式对话/本地服务,参数用法相同。
⚠️两个新手常见坑:
- 这个模型会先思考再回答:回答前会输出一段
<ifm|think> … </ifm|think>的推理内容。max_tokens要留足空间,不然回答会被思考"挤掉"。思考力度可用聊天模板 chat_template.jinja 中的reasoning_effort参数调节:"high"(默认)/"medium"/"low",简单问题调低能省不少 token。 mlx_lm.server目前不会把思考内容拆分到单独的reasoning字段,它会和正文一起出现在content里,解析时注意。
七、仓库文件速查
| 文件 | 作用 |
|---|---|
| k2_horizon.py | 核心:k2_horizon 架构的 MLX 移植实现 |
| config.json | 模型配置,含model_file指向移植代码 |
| README.md | 官方说明:转换方式、内存需求、基准测试 |
| chat_template.jinja | 对话模板,支持reasoning_effort思考力度 |
| generation_config.json | 生成配置(BOS/EOS token) |
| model.safetensors.index.json | 15 个权重分片的索引 |
| tokenizer.json / tokenizer_config.json | 分词器(25 万词表) |
写在最后
这个仓库的价值,不只是"又一个模型权重镜像",而是给出了一份教科书级的 MLX 移植示范:MoE 专家用gather_mm批量化、权重加载时预堆叠、按场景自适应的排序优化、全程 fp32 的数值保守策略、逐层 1e-6 精度对拍——每一招都恰好打在"让稀疏大模型吃满 Apple Silicon 统一内存带宽"这个靶心上。
100 个专家听起来很吓人,但在gather_mm和融合内核面前,它们不过是同一条流水线上的工序。128GB 的 Mac 已经够你尝鲜这个 512K 上下文的原生 bf16 版本了。
- 大模型
- 本地部署
- 人工智能
【免费下载链接】K2-Horizon-MoVA-36B-A4B-bf16
相关推荐
K2-Horizon-MoVA-36B-A4B-oQ4e 在 Apple Silicon 上能跑多快?48 TPS 实测与内存占用
K2 Horizon MoVA 36B A4B oQ4e 在 Apple Silicon 上能跑多快?48 TPS 实测与内存占用 K2 Horizon MoV
如何在Mac上快速跑通36B大模型:K2-Horizon-MoVA-36B-A4B-4bit五分钟上手指南(mlx-lm)
如何在Mac上快速跑通36B大模型:K2 Horizon MoVA 36B A4B 4bit五分钟上手指南(mlx lm) K2 Horizon MoVA 36
人工智能大模型模型量化本地部署开箱K2-Horizon-MoVA-36B-A4B-bf16:Apple Silicon上运行36B MoE大模型与512K超长上下文的完整指南
开箱K2 Horizon MoVA 36B A4B bf16:Apple Silicon上运行36B MoE大模型与512K超长上下文的完整指南 想在 Mac
大模型本地部署人工智能
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考