先泼盆冷水:本地大模型最容易被忽悠的地方,就是硬件需求。你刷到那种“8G 显存能跑 70B”、“NPU 加持流畅对话”的帖子,基本都藏了后半句。我自己从纯 CPU 机器一路折腾到 32GB Mac mini,中间也踩过不少“以为能跑”和“跑起来才发现没法用”的坑。这篇东西就把几件最核心的事摊开讲:MoE 到底吃不吃显存、CPU/GPU/NPU 三条路的真实边界、以及 32GB Mac mini 上怎么把模型调到一个能日常用的状态。全程聚焦一件事——让你的钱花在真正影响体验的硬件上。
1. MoE 架构的真相:参数没全进显存,但也没省下多少
MoE 这个词这两年突然火,是因为很多人发现 Mixtral、DeepSeek 这类模型总参数量大得吓人,但跑起来似乎没有想象中那么吃资源。于是就有了一个流传很广的误解:“MoE 架构只需要把激活的专家放进显存,其他部分可以不用加载。” 这个说法一半对一半错,错的那一半恰恰是花钱时最容易翻车的地方。
1.1 门控路由:为什么只有部分专家被激活
MoE 的全称是 Mixture of Experts,直译是“专家混合”。你可以把它想象成一个大公司:平时接活的时候,不会让所有员工都扑上去,而是由前台(Router,路由网络)看一眼工单内容,再指定两三个最对口的部门去处理。这个“指定”动作在模型里就是门控路由,它决定了每个 token 会经过哪几个专家。
以经典的 Mixtral 8x7B 为例,它总参数量大约是 46.7B,但每个 token 实际激活的参数只有 12.9B 左右。这里那个“8x7B”的意思是 8 组 7B 级别的专家,拼在一起;激活的是其中 2 组。所以计算量确实降下来了,一个 46B 量级的模型,跑起来大约是 13B 量级的计算开销,这是 MoE 最大的优势——速度快、省算力。
1.2 权重加载的“全量”问题
但这里有个关键区别:计算时只用两个专家,不代表你只需要加载两个专家进显存。原因是门控路由是逐 token 动态选择的,这个 token 走专家 A 和 B,下一个 token 可能就走专家 C 和 D。你根本没法预测接下来谁会被点到,所以整组专家的权重都得驻留在内存或者显存里,否则路由一选就缺人。
换成通俗的说法:公司里虽然每次只派两个部门干活,但所有部门的员工都得在工位上坐着,随时准备被点名。你不可能因为今天 A、B 部门加班,就让其他部门回家休息——因为明天可能就轮到 C、D 了。
所以,MoE 模型的实际内存占用,看的是总参数量的量化体积,而不是激活参数量。Mixtral 8x7B 的 FP16 全精度权重大约 90GB,Q4 量化后约 30GB。你觉得这是省了还是没省?比同规模的稠密模型确实省了——因为一个真正的 46B 稠密模型 Q4 也要约 26-30GB,两者几乎一样。但想在 16GB 显存里跑 Mixtral Q4,基本是做梦。
1.3 显存占用的一句话答案
把话说直白一点。问“MoE 架构要全部参数进显存吗”——答案是:要,但推理时的算力开销只按激活参数算。你的显存/内存够不够,看的是模型文件多大;你的显卡够不够快,看的是每 token 激活多少参数。两者不是一回事。
这也是为什么很多人在 Ollama 里看到 Mixtral 8x7B 的列表显示“46.7B 参数”,以为要百 GB 内存,结果发现 Q4 版本 30GB 不到也能硬跑。还有个容易被忽略的点:除了权重,KV Cache(键值缓存)也需要占内存,虽然 MoE 的 KV Cache 因为共享 attention 参数会比同规模稠密模型小一些,但上下文拉长之后仍然不可忽视。
1.4 一个常见的 offload 误区
很多人听说 llama.cpp 支持 offload,就想着“把不常用的专家放内存,常用的放显存”。蛮理想,但现阶段这事情没有开箱即用的实现。你可以用--override-tensor指定某些张量放在 CPU,但实际操作起来要手动挑张量名,专家那么多,试错成本极高。而且当路由频繁点到放在内存里的专家时,CPU 和 GPU 之间来回搬数据的开销,可能会比全量 GPU 推理还慢。
所以我的建议是:除非你真的跑不动了,否则别在 MoE 上搞花式 offload。老老实实按量化后的模型文件大小去配内存,这才是最稳的做法。
2. 算力三条路:CPU、GPU、NPU 各自的真实边界
标题里提到 CPU/GPU/NPU,这三条路不是替代关系,而是决定了你“能跑多大模型”和“跑得多快”的核心分界线。很多人一开始就把目光全放在显卡上,觉得“没显卡就不能玩”,其实在本地部署这件事上,内存带宽的重要性远超显卡本身。我按优先级把三条路的边界理一遍。
2.1 CPU:内存带宽是生死线
对于纯 CPU 推理,GPU 根本不存在,所有的模型权重都存在系统内存里。自回归生成的时候,每生成一个 token,理论上要把整份权重从内存读一遍——因为 transformer 的每一层都要跟权重做矩阵乘法,而权重不可能常驻在 CPU 的寄存器里。
所以决定 CPU 推理速度的,不是 CPU 有几个核心、主频多高,而是内存带宽。公式约等于:
速度(token/s)≈ 内存带宽(GB/s) ÷ 模型权重体积(GB) × 有效系数
有效系数通常取 0.6-0.8,因为还有指令开销、cache miss、并发调度这些损耗。举个例子,一台双通道 DDR4 的机器,内存带宽实测大约 35-40GB/s,跑一个 Q4 量化后约 4.5GB 的 7B 模型,理论速度 8 token/s 左右,实际也就 5-7。而换到双通道 DDR5,带宽 60-80GB/s,也许能到 12-15。工作站那种 8 通道内存的机器,带宽三四百 GB/s,跑同样的模型就会飞起——注意,是内存贵,不是 CPU 贵。
这里引出一个很多人没意识到的结论:CPU 跑大模型完全可行,但你拼的是主板支持多少条内存通道,而不是 CPU 型号多新。我见过有人用很便宜的至强处理器,配上 8 通道内存,照样能把 32B 模型的生成速度跑到让人能接受的水平。所以预算有限又特别想跑大参数模型的人,组一台“内存带宽优先”的洋垃圾工作站,可能是比买显卡更划算的路子。
2.2 GPU:显存容量和带宽的黄金比例
GPU 的优势不用多讲,HBM/显存带宽动辄几百 GB/s 到 1TB/s,比 DDR5 高了一个数量级。跑同样的模型,4090 能到 100-150 token/s,CPU 只有 10 左右,体验完全不是一个层级。
但 GPU 有个死穴——显存容量贵得离谱。8G 显存跑 7B Q4 刚刚好(权重 4.5GB 加上 KV Cache 和运行时占 1-2GB),跑 14B Q4(约 9GB)就得靠 offload 到内存救场,速度断崖式下跌。16G 显存是舒适区:跑 14B Q4 很轻松,32B Q4(约 20GB)还是塞不下。要真正舒服地跑 32B 或 70B 量化模型,你需要 24GB 起步,最好 48GB 以上,那价格就不是普通人随便下单的级别了。
我的看法是:独显用户应该优先关注显存容量,其次才是带宽。因为本地跑模型卡脖子的从来不是快不快,而是塞不塞得下。哪怕你的卡是 4090,跑不了 32B 也是白搭。
2.3 NPU:宣传很热闹,落地很冷静
最近各家都在推 NPU,号称“AI PC”,什么 40 TOPS、50 TOPS,听起来比显卡还猛。但这件事你得先理解一个本质:NPU 的高算力通常集中在“固定形状、低内存占用”的任务上,比如图像处理、语音识别、小模型推理。到了大语言模型这种动辄几个 GB 权重、逐 token 自回归、内存访问极度密集的场景,NPU 的编解码开销、内存带宽和驱动生态往往都不占优势。
以 Apple Silicon 的 ANE(Apple Neural Engine)为例,它确实很强,但在跑 LLM 这件事上,多数本地推理框架(llama.cpp、Ollama)默认走的是 Metal GPU,而不是 ANE。原因很简单:LLM 的内存密度太高,ANE 的片上缓存和内存子系统设计并不适合这种大权重流式读取。我实测观察过,跑同一个模型时 GPU 占用明显起来,ANE 基本在打酱油。至少现阶段,不要把“有 NPU”当作买一台电脑跑本地大模型的核心理由。它更多是未来的潜力,而不是现在的标配。
2.4 三条路径的选型建议
用表格总结一下我个人的选型思路,方便你对照自己的情况:
| 路径 | 最大瓶颈 | 适合场景 | 典型速度(7B Q4) |
|---|---|---|---|
| CPU | 内存带宽 | 大模型能跑就行,预算有限,或者需要跑超大参数模型 | 5-15 token/s |
| GPU | 显存容量 | 追求流畅交互,跑 7B-14B 量级 | 50-150 token/s |
| NPU | 生态和内存子系统 | 目前更适合小模型、专用场景 | 因实现而异,不推荐作为首选 |
这表格不是替你决定买什么,而是帮你认清:本地部署的硬件本质是“内存装得下的参数上限”和“带宽决定的生成速度”之间的权衡。先想清楚你要跑哪个量级的模型,再回头挑硬件,顺序不能反。
3. 32GB Mac mini 实战:从选模型到稳定部署
说完了理论,聊聊我自己这阵子一直在用的 32GB Mac mini。这不是什么性能猛兽,但它是很多人家庭办公桌上真会放的一台机器。我要坦白地说:32GB 的统一内存确实能做不少事,但前提是你懂得怎么“纪律性”地使用内存,否则照样崩。
3.1 我的硬件与软件栈
我这台是 M2 芯片的基础款 Mac mini,内存统一 32GB,GPU 核心数不多,内存带宽在 100GB/s 级别。这个带宽数字要记在心里:它比 DDR5 双通道高一点,但和独显完全没法比。所以我对它的预期就很明确——跑 7B-14B 模型能到一个“能日常聊天”的速度,跑 32B 可能就是“能跑但有点卡”。
软件栈我用的是 Ollama + llama.cpp 双轨。日常聊天和 API 调用走 Ollama,需要精细调参或者折腾新模型格式的时候直接用 llama.cpp 的llama-server。macOS 上两个方案都能正常调用 Metal GPU,这是省心的地方。
3.2 哪些模型和量化档位值得试
先给一个我已经跑过的清单,所有数据都是 Q4_K_M 量化下实测的,上下文默认 4096:
| 模型 | 参数量 | 权重体积 | 实测速度 | 体验评价 |
|---|---|---|---|---|
| Llama 3.1 8B | 8B | 约 5GB | 22-28 token/s | 轻松流畅,日常首选 |
| Qwen 2.5 7B | 7B | 约 4.7GB | 25-30 token/s | 中文表现好,推荐 |
| Phi-3.5 14B(MoE) | 14B | 约 9GB | 12-15 token/s | 还行,算力感明显 |
| Qwen 2.5 14B | 14B | 约 9.5GB | 11-14 token/s | 速度可接受,质量不错 |
| Qwen 2.5 32B | 32B | 约 20GB | 6-8 token/s | 能跑,但聊天有等待感 |
| Mixtral 8x7B | 46.7B | 约 30GB | 4-6 token/s | 勉强能用,偏慢 |
注意,这个速度已经是比较理想的状态,因为每次测试时模型基本全部 offload 到 Metal GPU。如果上下文拉到 16K、32K,KV Cache 还会额外吃掉几 GB 内存,速度也会进一步下滑。
量化档位方面,我建议 32GB Mac mini 上第一优先选 Q4_K_M。这个档位体积小、速度好、质量损失在可控范围内。如果你跑的是 8B 这类小模型,可以升到 Q6_K 或 Q8_0 提升输出质量,代价是权重体积大 30%-50%,速度略降。但 14B 以上别轻易升档,否则内存分分钟爆掉。
3.3 内存分区、上下文长度和关键参数
Mac 的统一内存和显存是同一块,所以你在部署时要注意几个实操细节,这些细节往往决定你“能用”还是“经常 OOM”。
首先是最容易被忽略的 KV Cache。很多人只算模型权重,忘了上下文越长、KV Cache 越肥。以 Qwen 2.5 14B 为例,它的 GQA 机制让 KV Cache 相对较小,但 32K 上下文照样能吃到 2-3GB。所以如果你只配了 4096 上下文,那内存规划可以松一口气;如果你想让模型读长文档,请把 KV Cache 的占用算进预算里。
其次,Ollama 默认的并发数是 1,这是它的保守策略。你如果只是自己聊天,没必要调它。但如果你要跑服务端并发,建议直接用OLLAMA_NUM_PARALLEL=1,不要为了“显得能并发”而调到 4,否则每个并发请求都会复制一份完整的 KV Cache,32GB 内存会瞬间见底。
第三个细节:macOS 有内存压缩和 swap 机制,跑模型时系统日志里不一定会立刻 OOM,而是悄悄用 swap。一旦开始 swap,你的生成速度会从 20 token/s 跌到 2 token/s,体感就是“卡死”。所以建议开一个终端窗口挂htop,盯住 memory pressure。只要看到黄色甚至红色,就该降级模型了。
3.4 从 Ollama 换到 llama-server 的两个理由
我用 Ollama 半年后,开始越来越多地用 llama-server 替代。不是 Ollama 不行,而是本地调优时它给了你太少控制权。举两个例子:
第一,llama-server 支持--ctx-size、--batch-size、--threads、--n-gpu-layers这些参数手动调优。比如我跑 32B 模型时会把--n-gpu-layers从默认的 99 降到 50 左右,故意留一部分层给 CPU,让 GPU 显存腾给 KV Cache。这听上去有点反直觉,但实测在内存紧张的时候,牺牲一点点速度换稳定的内存占用,是值得的。
第二,llama-server 有自己的日志系统,启动时直接输出模型加载了多少层到 GPU、每层的耗时、KV Cache 大小这些诊断信息。Ollama 到 0.5 版本也加了一些日志,但不如 llama-server 透明。对于调优的人来说,能看见真实数据,比啥都重要。
启动命令我常驻这样一条,供参考:
llama-server \ --model /path/to/qwen2.5-14b-q4_k_m.gguf \ --ctx-size 8192 \ --n-gpu-layers 99 \ --threads 8 \ --batch-size 512 \ --parallel 1跑一段时间如果发现内存压力高,就先把--ctx-size降到 4096,再考虑动--n-gpu-layers。
4. 从单机到“给人用”:并发、吞吐与极限场景
标题里的“实战调优”如果只是自己聊天,其实到上一节就已经够了。但很多人最终要面对的场景是:我想给自己的小团队甚至 200 人规模的使用者部署一个本地大模型。这时候,你不会只关心单条消息多快,而是关心“同一时刻多少人用还不崩”。这就涉及并发和吞吐的概念,和单机自用完全是另一套逻辑。
4.1 为什么单次推理的 token/s 会骗你
假设你单机跑 Qwen 2.5 14B,实测 12 token/s。这个数字超级漂亮,你感觉跟真人对话没差。但注意,这是一个人独占整台机器的速度。当并发数变成 4,模型的推理引擎不会魔术般地把一份权重复制四份然后并行跑——它确实可以 batch 处理,但吞吐量的提升远没有线性。在 Mac mini 这种内存带宽吃紧的平台上,并发 4 时每个请求的有效速度可能跌到 3-5 token/s,因为内存带宽被多个请求抢了。
所以这里要区分两个指标:
- 单用户延迟(latency):你发一句话,到第一个 token 出现的时间间隔;
- 系统吞吐(throughput):单位时间内整个服务能生成多少 token。
自用场景看 latency,多人场景看 throughput。而吞吐的瓶颈,恰好又是那块 100GB/s 的内存带宽。多并发时,权重还是那一份,但每个用户都在抢带宽去读它,那就必然要分摊。
4.2 200 人规模的配置思路
专门说一下“搭建一个 200 人用的本地大模型需要多少钱”这个问题,因为我被问过太多次。先给结论:一台 32GB Mac mini 连门都摸不到,别幻想。
200 人同时在线,假设同时活跃比例 10%,也就是 20 个并发请求。你至少需要能同时容纳 20 份 KV Cache 的内存,加上权重。就算用 14B Q4 模型,权重 9GB,每份 4096 上下文的 KV Cache 约 1GB,20 份就是 20GB。权重加 Cache 已经 29GB——32GB 机器基本没有任何余量,系统本身还要占 10GB 左右。这还没算框架、API 层和系统开销。所以做这个规模,内存起步建议 128GB,最好是 192GB。
算力方面,20 路并发跑 14B,需要的吞吐大概是 20 × 15 token/s = 300 token/s 以上。这条机器现在哪个环节都不满足。正确的思路是:
- 模型选小不选大。能上 7B-8B 就用这个量级,别迷信 32B。对并发服务来说,单个模型越小,单位内存和带宽能服务的用户越多。
- 用支持连续批处理(continuous batching)的推理框架,比如 vLLM、SGLang 这类专为服务设计的工具,而不是 Ollama、llama.cpp。它们能把多个请求拼成一个 batch 喂给 GPU,吞吐量提升非常明显。
- 考虑量化。服务端我一般选 Q4_K_M,它是在吞吐、质量、内存三者的平衡点上。不要为了那一点质量提升而升到 Q6/Q8,在 200 人规模下那点质量提升换来的成本增长不值得。
顺带提一句:有一类场景是“内部知识库问答”,那和纯模型推理不同。你可以在模型很小的情况下,靠 RAG(检索增强生成)把精度拉上去。比如用一个 7B 模型,外挂向量数据库检索文档,效果可能比一个裸的 32B 模型还好。这算是从另一个维度“省硬件”。
4.3 单机 Mac mini 的并发极限参考
我自己实测过 Mac mini 32GB 的并发上限,供参考:跑 8B Q4 模型时,--ctx-size 8192、--parallel 2能稳定运行,并发 3 到 4 时速度开始明显劣化,到 5 就几乎不可用。体感上,它适合“三个人同时问问题不会互相卡死”的轻量场景,再多人就得考虑内存更猛的机器了。
如果你只有这么一台 Mac mini,又确实想给身边几个人用,我的建议很简单:把模型压到 8B,上下文限制在 4096,并发锁死 2,然后跟使用者约定“别同时提问”。这不是技术方案,但这是硬件的物理边界,你没法绕过。
5. 一些值得记下来的调优经验和边界确认
最后这部分是零碎但很实用的东西,是我在 Mac mini 上反复撞墙之后总结出来的,可能比前面的理论更直接有用。
5.1 上下文长度不是越大越好
很多人拿到模型第一件事就是把--ctx-size拉到 32K,以为上下文越大越能“记住”更多内容。但实际上,普通聊天场景根本用不满 4096。拉长上下文除了让 KV Cache 多占内存、拖慢速度之外,并没有额外的好处——模型并不会因为上下文窗口大就“更聪明”。
我的经验是:日常聊天用 4096,做文档总结时临时拉到 8192,再高那就该考虑是不是换一条路,比如把文档先切片做 RAG,而不是整段塞进上下文。这比无脑拉长窗口科学得多。
5.2 内存交换是最大的隐形杀手
macOS 的 swap 机制很“智能”,它会尽量让你感觉不到内存满了。但代价就是速度雪崩。我踩过一次很深的坑:跑 Qwen 2.5 32B 的 Q4,当时看着内存占用才 60%,觉得稳了,结果生成速度只有 3 token/s。打开活动监视器才发现,内存已经压到红区,系统开始大量swap。
从那以后我形成了一个习惯:跑模型之前先看一眼“内存压力”图表。macOS 的内存压力分为绿、黄、红三档,绿色代表内存充足,黄色代表系统开始加权,红色就是已经进入交换。一旦到了黄档,果断降级到更小的模型,别硬撑。这个习惯帮我避免了很多“卡死然后强制重启”的悲剧。
还有个小技巧:macOS 的vm.compress_algorithm这类设置没必要去改。系统默认的压缩已经足够好,手动改参数经常得不偿失。
5.3 Metal GPU 观察与 ANE 的现实
在 Mac mini 上跑 llama.cpp 时,可以用活动监视器看到 GPU 的占用情况。我观察到的现象是:生成 token 时 GPU 利用率会猛增,但 ANE 那条曲线几乎不动。这说明至少在当前软件栈下,Apple Silicon 的大模型推理主力确实是 GPU,而不是 NPU。券商宣传的“XX TOPS NPU”在本地 LLM 场景里暂时是打酱油的状态。
如果你特别想让 Core ML 路径把 ANE 用上,可以关注 Apple 社区里一些模型转换项目,它们能生成专门优化的 Core ML 模型包。但说实话,从易用性、模型覆盖率和更新速度来看,大多数普通用户用Ollama或者llama.cpp就足够了。我不建议为了 NPU 去折腾 Core ML 工具链,除非你有特别强烈的功耗要求。
5.4 日常使用中的“启动时间”被严重低估
本地部署和云端的差别还在于:模型加载是有冷启动时间的。Ollama 默认会在空闲 5 分钟后卸载模型,你再发起请求,得重新从磁盘加载权重到内存,一个 14B Q4 模型冷启动要 10-30 秒,体验非常差。解决办法有三条:
- 用
OLLAMA_KEEP_ALIVE环境变量把模型常驻时间延长到几小时,甚至设成 -1 表示永久驻留; - 用 llama-server 作为后台服务,它默认不会自动卸载;
- 根据你的内存余量决定:32GB 内存驻留一个 8B 模型(约 6GB)完全没问题,驻留 14B(约 10GB)也还行,但驻留 32B(约 22GB)就会和系统抢内存,导致不必要的 swap。
我的个人配置是:常驻一个 8B Q4 模型,偶尔手动切换到 14B,32B 只在特定任务时临时加载。这套配置下,日常聊天几乎无感,又不牺牲内存余量。
5.5 升级硬件的优先级排序
如果你看完前面这些,打算花点钱升级,我给一个优先级排序,能帮你避开冤枉钱:
- 内存容量。这是第一优先,它决定你能不能跑更大的模型。如果内存不够,其他都是零。
- 内存带宽。同样容量的内存,带宽翻倍直接带来速度翻倍。买工作站、选多通道、甚至换 CPU 平台,都是为了带宽。
- GPU 显存容量。如果你走独显路线,显存容量优先于显卡核心次数。
- 显存带宽。这是最后再考虑的。只要容量够了,带宽是锦上添花。
很多人一上来先买最贵的显卡,结果卡在显存容量不够;还有人买了大内存笔记本,结果带宽只有 80GB/s,跑模型依然卡。这个排序可能会帮你少走弯路。
我自己把 Mac mini 从 16GB 换到 32GB 以后的感受是:这是一个“够用”和“勉强够用”的分界线。它跑不了顶配的 70B,也撑不起多人服务,但作为一个个人开发的副驾、一个本地知识库的推理节点、一个不用把数据传到云端的离线工具,它已经能胜任了。如果你和我一样不是那么追求大参数模型,而是想把手头的机器用透,那这篇文章写的这些边界和调法,应该能帮你省下不少折腾的时间。