M1 Max 本地部署 Ling-3.0-tiny INT4:32GB Mac 实测约 64 token/s

最近看到 X上不少人用 NVIDIA DGX Spark 部署千亿级别的大模型,看着有点眼馋,但是咱穷啊,玩不起 spark。能玩的就是自己这台老爷机,2022年的第一代Mac Studio。

xcodebuild -showComponent MetalToolchain

如果你想在 Mac 上部署一个能聊天、推理、调用工具,同时响应速度足够快的本地模型,Ling-3.0-tiny 是一个值得关注的选择。

它并不是传统意义上的“1B 小模型”。Ling-3.0-tiny 拥有约 7.9B 总参数,但采用稀疏 MoE 架构,每个 token 只激活约 1.3B 参数。模型包含 128 个路由专家,每个 token 激活其中 8 个,并配合共享专家工作。这样的设计兼顾了模型容量和推理成本,也让它有机会在 Apple Silicon 上获得较流畅的本地体验。

这次部署最初使用 FP8 权重,后来为了进一步降低磁盘和内存压力,我完整删除了 FP8 原始权重及 Ollama 导入副本,改用官方发布的 Ling-3.0-tiny-int4。最终结果如下:

  • 模型名称为 ling-tiny-int4:latest;
  • 8K 上下文下,可正常使用 MLX 与 Metal GPU 推理;
  • 三轮长文本测试平均生成速度约 64.37 token/s;
  • 单轮速度稳定在 63.67–65.18 token/s;
  • 首次加载约 6.93 秒;
  • 模型驻留时占用约 6.48GB 统一显存;
  • 当前完整项目占用约 13GB;
  • Ollama API 的聊天、思考模式和工具能力均可使用。

这套方案的难点不在模型本身,而在实验版 Ollama、Metal Toolchain、网络下载,以及很容易被忽略的模型重复副本。

一、测试环境与部署路线

测试机器是一台 Mac Studio,配置如下:

  • Apple M1 Max;
  • 24 核 GPU;
  • 32GB 统一内存;
  • macOS 15.7.4;
  • arm64 架构。

Ling-3.0-tiny 原生支持更长的上下文,但不建议在 32GB Mac 上一开始就追求 128K。上下文越长,KV Cache 和统一内存压力越大。对本地聊天、代码辅助和个人 Agent 来说,8K 是更稳妥的起点。

本次部署路线是:

brew install go cmake

为什么不直接使用普通版 Ollama?

因为本次实测所需的 Ling 与 Bailing MoE V3 支持仍来自 Ollama 的实验 PR,需要源码编译。vLLM 和 SGLang 更适合 NVIDIA/CUDA 环境;在 Apple Silicon 上,这套 Ollama、MLX 和 Metal 路线更贴近原生硬件。

二、部署前先规划磁盘

这是整个部署过程中最容易低估的问题。

当前 INT4 方案的主要占用如下:

  • Hugging Face 原始 INT4 权重约 5.4GB;
  • Ollama 导入模型约 5.8GB;
  • Ollama 源码、编译结果和 MLX 运行库约 1.3GB;
  • 其他缓存和配置文件约几十 MB;
  • 完整项目合计约 13GB。

考虑到下载临时文件、编译过程和 macOS swap,建议部署前至少准备 20GB 可用空间,推荐预留 30GB 以上。

如果项目位于 Git、Codex 或其他自动版本管理工具监控的目录中,应在下载模型前创建 .gitignore:

cd "$LING_HOME/ollama-ling"
 ​
 TOOLCHAINS="$TOOLCHAINS" \
 OLLAMA_MODELS="$LING_HOME/ollama-models" \
 OLLAMA_CONTEXT_LENGTH=8192 \
 OLLAMA_KEEP_ALIVE=5m \
 OLLAMA_HOST=127.0.0.1:11434 \
 ./ollama serve

这里有个坑

我第一次部署时,主要是在codex中,新建了一个项目来帮我部署。因为没有提前排除这些大目录,自动变更快照把模型权重再次写入 .git,导致 Git 数据异常膨胀,系统盘空间迅速下降。清理重复数据并补上忽略规则后,当前项目根目录的 .git 只有约 132KB。

这类占用不是模型本身造成的,却可能比模型更危险。下载数 GB 权重前,务必先检查忽略规则。

三、下载 Ling-3.0-tiny INT4

建一个独立工作目录,后面的路径都从这里展开:

curl -sS http://127.0.0.1:11434/api/ps

使用 Hugging Face CLI 下载官方 INT4 仓库:

uvx --from huggingface_hub hf download \
   inclusionAI/Ling-3.0-tiny-int4 \
   --local-dir "$LING_HOME/models/Ling-3.0-tiny-int4"

我这里的网络并不顺利。Xet 连接有一段时间完全不增长,切换标准 HTTP 后又遇到读取超时。CLI 支持断点续传,可以把超时时间放宽:

export OLLAMA_MODELS="$LING_HOME/ollama-models"

这份模型共有 32 个 safetensors 权重分片。下载完成后先检查数量:

echo '__METAL_VERSION__' | \
   xcrun -sdk macosx metal -E -x metal -P -

输出应为 32,并且目录中应存在:

cd "$LING_HOME"
 ​
 git clone --depth 1 https://github.com/ollama/ollama.git ollama-ling
 cd ollama-ling
 ​
 git fetch --depth 1 origin \
   refs/pull/17643/head:bailing-moe-v3
 git switch bailing-moe-v3

本次实操还将 44 个仓库文件的精确字节数与 Hugging Face 官方 API 逐一比对,并对 32 个权重分片计算 SHA-256。最终结果为:文件大小异常 0、哈希异常 0、索引缺失分片 0。

对于 5GB 以上的模型,不建议只凭“命令执行完成”判断下载成功。分片数量、索引引用和哈希至少应确认一项,有条件时最好全部校验。

四、编译支持 Ling 的 Ollama

先安装依赖:

library=Metal
 description="Apple M1 Max"
 MLX engine initialized device=gpu
 Model architecture arch=BailingMoeV3ForCausalLM

拉取 Ollama 源码和 Ling 支持分支:

Metal Toolchain 这个坑,我卡了一次

机器上已经装了 Xcode 和 Command Line Tools,编译 MLX 时仍然提示缺少 Metal 编译工具。Metal Toolchain 是单独的组件:

export TOOLCHAINS="com.apple.dt.toolchain.Metal.32023.864"

装完可以直接测试:

curl -sS http://127.0.0.1:11434/api/generate \
   -H 'Content-Type: application/json' \
   -d '{"model":"ling-tiny-int4:latest","keep_alive":0}'

我的机器还有一层问题。xcodebuild 显示组件已经安装,xcrun metal 仍然找不到它。查看组件详情:

printf 'FROM %s\n' \
   "$LING_HOME/models/Ling-3.0-tiny-int4" \
   > "$LING_HOME/Modelfile.ling"

输出里会有 Toolchain Identifier。这台机器对应的是:

HF_HUB_DISABLE_XET=1 \
 HF_HUB_DOWNLOAD_TIMEOUT=600 \
 uvx --from huggingface_hub hf download \
   inclusionAI/Ling-3.0-tiny-int4 \
   --local-dir "$LING_HOME/models/Ling-3.0-tiny-int4"

这个值跟系统和 Xcode 版本有关。别整段照抄,按自己机器的输出填写。

确认 Metal 可用后编译:

xcodebuild -downloadComponent MetalToolchain

编译会生成 ./ollama 主程序,build/lib/ollama 里还有 MLX 动态库、Metal kernel 和 runner。后面运行模型需要这些文件,所以不能只复制一个 ollama 就把 build 删掉。

五、导入 INT4 模型

为了避免和系统里已有的稳定版 Ollama 模型混在一起,本次把模型库存放在项目内部:

curl -sS http://127.0.0.1:11434/api/chat \
   -H 'Content-Type: application/json' \
   -d '{
     "model": "ling-tiny-int4:latest",
     "messages": [{
       "role": "user",
       "content": "计算 17 × 23,只输出最终数字。"
     }],
     "think": false,
     "stream": false,
     "options": {
       "num_ctx": 8192,
       "temperature": 0
     }
   }'

创建 Modelfile:

find "$LING_HOME/models/Ling-3.0-tiny-int4" \
   -maxdepth 1 -name 'model-*.safetensors' | wc -l

执行实验导入:

Ling-3.0-tiny INT4 权重
         ↓
 支持 Ling 的 Ollama 实验分支
         ↓
 MLX Runner
         ↓
 Metal / Apple Silicon GPU
         ↓
 Ollama API :11434

本次导入器识别出 26947 个 tensors,并显示:

cd "$LING_HOME/ollama-ling"
 ​
 TOOLCHAINS="$TOOLCHAINS" \
 OLLAMA_MODELS="$OLLAMA_MODELS" \
 ./ollama create ling-tiny-int4 \
   --experimental \
   -f "$LING_HOME/Modelfile.ling"

这里和之前的 FP8 方案不同。INT4 权重已经是 compressed-tensors 的 4bit 分组量化格式,导入器会保留源量化,而不是把 FP8 再转换为 MXFP8。

最终 Ollama 模型名为:

ling-tiny-int4:latest

六、启动 Ollama 服务

32GB 统一内存建议先使用 8K 上下文:

OLLAMA_KEEP_ALIVE=5m 表示请求结束后模型继续驻留五分钟,连续对话不需要反复加载。如果内存紧张,可以改成 0,让每次请求完成后立即卸载模型;代价是下一次请求又要等待约数秒加载。

另开终端检查模型:

cd "$LING_HOME/ollama-ling"
 ​
 cmake -B build .
 cmake --build build --parallel 8

正确情况下会看到:

ling-tiny-int4:latest

七、验证 GPU 与基础推理

发送一个关闭思考模式的简单请求:

模型正确返回:

391

服务日志同时显示:

curl -sS http://127.0.0.1:11434/api/tags

看到这些信息,才能确认模型确实使用 MLX 和 Metal GPU,而不是静默回退到 CPU。

八、M1 Max 的 token 速度实测

只有 2–3 个 token 的算术回答不适合做速度基准,因为首 token 延迟和计时误差会严重影响结果。本次先用一个短请求完成模型预热,然后连续进行三轮长文本生成。

测试条件如下:

  • 模型:ling-tiny-int4:latest;
  • 上下文:8192;
  • 思考模式:关闭;
  • 温度:0;
  • 每轮最大输出:384 tokens;
  • 三轮均完整生成 384 tokens。

实测结果:

  • 第一轮:64.26 token/s;
  • 第二轮:65.18 token/s;
  • 第三轮:63.67 token/s;
  • 三轮平均:64.37 token/s。

三轮结果的波动很小,因此在这台 M1 Max 上,INT4 版本更有代表性的稳定生成速度约为 64 token/s。

预热请求的首次模型加载耗时约 6.93 秒。模型加载后,提示词处理速度分别约为 215.76、264.52 和 275.83 token/s。长对话的提示词越长,提示词处理时间越值得关注,不能只看生成速度。

Ollama API 中的 duration 单位是纳秒,生成速度计算公式为:

生成速度 = eval_count ÷ (eval_duration ÷ 1,000,000,000)

对应的 jq 表达式是:

jq '.eval_count * 1000000000 / .eval_duration'

测速时建议至少生成 256 tokens,并明确记录是否开启思考模式、上下文长度、温度和模型是否已预热,否则不同结果之间没有可比性。

九、如何开启或关闭思考模式

Ling-3.0-tiny 支持思考模式,但它不是 Ollama 服务的全局开关,而是由每次请求中的 think 参数决定。

关闭思考模式:

"think": false

开启思考模式:

"think": true

本次 64.37 token/s 的测试明确设置了 think: false。开启思考后,模型会生成额外的推理内容,总响应时间和总 token 数都会增加,因此应单独测试,不能直接与关闭思考模式的速度混在一起比较。

十、部署后的磁盘管理

当前项目总占用约 13GB,主要分为:

  • models/Ling-3.0-tiny-int4/ 约 5.4GB,保存 Hugging Face 原始权重;
  • ollama-models/ 约 5.8GB,保存 Ollama 实际加载的模型;
  • ollama-ling/ 约 1.3GB,保存源码、主程序、MLX 库和 runner;
  • .cache/ 约 41MB;
  • .git/ 约 132KB。

模型成功导入后,日常推理只依赖 ollama-models/ 和 ollama-ling/。如果近期不准备重新导入,可以删除 5.4GB 的原始 models/Ling-3.0-tiny-int4/,以后需要时再从 Hugging Face 下载。

不要删除 ollama-models/,否则 ling-tiny-int4:latest 会消失。也不要随意删除 ollama-ling/build/,否则 MLX runner 可能无法启动。

查看当前驻留模型:

https://huggingface.co/inclusionAI/Ling-3.0-tiny-int4

主动卸载模型但保留 Ollama 服务:

.DS_Store
 .cache/
 models/
 ollama-models/
 ollama-ling/

完全关闭服务,可以在运行 ollama serve 的终端按 Ctrl-C。

十一、实际体验与适用场景

在Mac上我有一个非常喜欢的划词截图翻译软件 Bob,可以接入ollama的本地模型。

preserving source quantization
 successfully imported ling-tiny-int4 with 479 layers

实际体验下来,翻译的速度比 DeepL的快很多。

config.json
model.safetensors.index.json
tokenizer.json
tokenizer_config.json

简单总结一下:

从本次部署看,Ling-3.0-tiny INT4 的优势比较明确:

  • 约 7.9B 总参数、约 1.3B 激活参数,在模型容量与推理成本之间取得平衡;
  • M1 Max 上稳定约 64 token/s,聊天、摘要和代码辅助足够流畅;
  • 模型驻留约占 6.48GB 统一显存,32GB Mac 有较充足的余量;
  • 支持普通聊天、思考模式、工具能力和 Ollama API;
  • 数据和推理都留在本机,适合处理不希望上传云端的内容;
  • 相比此前的 FP8 双副本,INT4 方案减少了模型磁盘占用。

它适合本地聊天、代码辅助、个人 Agent、隐私文本处理和低并发 API 服务,但不适合高并发生产环境。当前 Apple Silicon 支持仍依赖实验版 Ollama,部署门槛高于普通的一键 ollama pull。

这次实操最重要的结论不是“模型能不能跑”,而是:Ling-3.0-tiny INT4 已经能在 M1 Max 上以约 64 token/s 的速度稳定工作;真正需要提前规划的,是实验运行时、下载链路、原始权重与 Ollama 副本造成的磁盘占用。

相关链接

  • Ling-3.0-tiny INT4 模型:https://huggingface.co/inclusionAI/Ling-3.0-tiny-int4
  • Ollama Ling 支持 PR:https://github.com/ollama/ollama/pull/17643
  • Ling-3.0-tiny 正式开源:https://mp.weixin.qq.com/s/YyrzNIZpnas4lerpC63DaQ