← 全部笔记

模型本地部署调优

先区分容量不足、输入处理慢和生成慢,再选择调整项。用相同任务比较调整前后的速度、内存占用和质量;降低占用或提高局部速度,都要确认没有损害任务效果。

本文以独显上的 llama.cpp 和 GGUF 模型为例,解释容量与速度的影响因素,帮助选择调优方向。具体启动方式和参数支持以所用后端的版本文档为准;llama.cpp 的启动与参数说明见官方 server 文档。

token 是模型处理文字的单位,可以是一个字,也可以是一个词或词的一部分。prefill 指模型开始生成前处理输入的阶段。

运行前提

工具可以选择 Ollama、LM Studio,也可以直接使用 llama.cpp。GGUF 是存放模型权重的文件格式。准备好文件,选择支持该模型的工具版本,并留出足够的内存和显存,通常就能把模型跑起来。

需要图像能力时,应选择支持图像输入的模型,并准备配套的视觉模块文件,通常命名为 mmproj-*.gguf。只使用文本能力时,可以按后端支持情况跳过视觉模块。

本机单人使用,可以先把并行数设为 1。 并行数决定同时处理多少个请求。若每个请求都保留相同的上下文容量,提高并行数通常会增加缓存和计算空间;模型权重一般可以共享,所以总显存占用不会简单翻倍。有些后端会让多个请求平分已设置的总上下文容量,需要看清设置含义。

如何跑快

跑快,是在满足任务需求的前提下,减少 prefill 时间,提高每秒生成的 token 数量。

为了达到这个速度目标,独显部署首先应尽量把模型权重和主要计算放到 GPU 上,并给运行过程中占用最高的时刻留出显存余量。显存不足、数据频繁在显存和内存之间搬运时,速度可能从每秒几十个 token 降到几个。

调优时,先把输入处理和生成两个阶段分开看:

  • 输入处理慢:重点看输入长度、前缀缓存和计算批次。
  • 开始生成后吐字慢:重点看模型是否放到 GPU 上计算、显存带宽,以及投机解码是否有效。

显存用在哪里,如何减少占用

估算时,把模型权重、缓存、计算临时空间,以及需要开启的图像和投机解码功能一起考虑,再为桌面显示和其他应用留出余量。下面分别说明这五部分的占用和调整方式。

1. 模型权重

模型权重是训练得到的一大组数字。27B 表示约 270 亿个参数,按常见的 16 位精度(FP16/BF16)存储,每个参数约占 2 字节,权重约为 54GB。

量化就是用更少的位数存储这些数字,减少占用,同时可能损失一些模型能力。8 位和 4 位的权重大小,分别大致是 16 位的二分之一和四分之一。实际估算时,直接看下载后的 GGUF 文件大小更方便。

容量紧张时,可以选择更小的量化版本,但需要复测质量。量化名称中的 S、M、L、XXS 等表示具体方案的变体,大小差距随模型而变。例如,一个 GGUF 文件大小为 10,934,860,704 字节,约为 10.93GB,即 10.18GiB,可以据此粗估权重占用,再加上后面几部分的空间。

共享 GPU 内存使用的是系统内存,访问带宽明显低于独立显存;仅看到共享内存占用,不能断定已经发生严重换页降速,需要结合运行期间的占用变化、搬运和速度判断。

速度优先时,尽量保留全层 GPU,并为运行峰值留出显存余量;先调整缓存、计算批次等占用,实在装不下再考虑主动 CPU 分层。 gpu-layers = all 表示全层卸载到 GPU,并不保证这些数据始终驻留在独立显存中。

主动 CPU 分层和显存不足后的运行时迁移是两回事:前者让部分层主要在 CPU 上计算,在 CPU/GPU 分界处传递中间结果,通常是用速度换容量;后者仍要求 GPU 计算,但 Windows/驱动可能反复在内存与显存之间迁移权重或缓存,使 GPU 等待搬运。当全 GPU 方案已经严重换页时,主动分层可能反而更快、更稳定;这不是说 CPU 分层优于装得下且有余量的全 GPU 方案。两者取舍应以相同请求的实测为准。

2. KV Cache:上下文缓存

KV Cache 保存模型为前面的 token 算出的 K、V 数字数组,供后续计算复用。

例如,“小明把钥匙放进抽屉。”假设切成以下 token,下面展示模型其中一层算出的 K、V:

[小] [明] [把] [钥匙] [放进] [抽屉] [。]
 ↓    ↓    ↓     ↓      ↓      ↓     ↓
K₁V₁ K₂V₂ K₃V₃  K₄V₄   K₅V₅   K₆V₆  K₇V₇

接着处理“钥匙在哪里?”时,模型产生代表当前信息需求的 Q,用 Q 与前面的 K 比较,算出各位置的关注比例,再按比例汇总对应的 V。例如,假设给“钥匙”25%、“放进”20%、“抽屉”35%,其余位置共 20%,就按这些比例混合各位置的 V,供后续计算答案。

K 决定各处信息取多少,V 是实际被取用的信息。 把它们缓存下来,后续就能直接读取,省去重复计算。

前缀缓存则把这种复用延伸到下一次请求:

第一次:小明把钥匙放进抽屉。钥匙在哪里?
第二次:小明把钥匙放进抽屉。小明放了什么?

两次开头相同。后端如果保留并复用了这段前缀的 K、V,第二次就只需接着处理新增问题,减少 prefill 计算。Agent 对话只追加内容时,也可以这样复用。

KV Cache 是推理过程中保存的状态;llama-server 的 RAM prompt 缓存则是一种保存、恢复上下文状态的复用机制,其中可以包含 KV 状态。可以把它理解为在活动状态之外再保留可复用的副本,但它不是显存的透明二级缓存,也不是显存溢出后的共享 GPU 内存。其系统内存预算见下文“如何控制系统内存”。

在 llama.cpp 的常见配置中,加载模型时就会按设置的上下文容量申请缓存空间,之后再随着输入和生成填入 K、V。因此,即使当前对话很短,把上下文上限开大通常也会增加显存占用。

容量紧张时,可以根据任务需要降低上下文上限。上下文上限决定能装多少,实际用了多少则会影响计算速度。同样设置 64K 上限,实际只用了 4K 和已经用了 60K,输入处理耗时和生成速度都可能差很多。

以 Llama 3.1 8B 为例,单个会话的 KV Cache 大约占用:

上下文容量 F16 缓存 Q8 缓存 Q4 缓存
8K token 1 GiB 0.53 GiB 0.28 GiB
32K token 4 GiB 2.1 GiB 1.1 GiB
64K token 8 GiB 4.3 GiB 2.3 GiB

这个例子中,上下文容量翻倍,缓存占用也翻倍。不同模型的缓存大小不同,可以从加载日志查看自己模型的实际占用。

显存紧张时,可以尝试后端支持的 Q8 或 Q4 缓存,再检查任务质量和速度。

3. 计算缓冲区

模型做计算时,还需要临时存放中间结果。可以把这部分空间理解为计算时使用的草稿纸,占用多少与模型、一次处理多少 token 等有关。

显存紧张时,可以尝试调小实际计算批次(Physical Batch,也叫 ubatch),让模型每批处理更少的 token。这样通常能减少部分临时空间,但可能延长输入处理时间。

Flash Attention 建议在支持时开启。 它让注意力计算少搬运数据、少保存大型中间结果,通常能减少临时空间,并提高计算速度。

4. 图像能力

配套的 mmproj 文件提供把图像转换成模型能使用的信息所需的模块,其文件大小可以帮助估算额外的权重占用。

不需要图像能力时,可以不加载视觉模块。处理图像还需要临时计算空间,转换后的图像信息也会占用上下文。图像数量和分辨率越高,通常需要的空间越多,因此要观察处理图像时的显存峰值。

5. MTP 与投机解码

投机解码是先猜出后面几个 token,再交给主模型一起验证。猜中的部分可以一次采用,从而减少逐个生成的开销。

候选可以由两种方式产生:

  • MTP(多 token 预测):使用模型配套的预测模块,需要相应权重和运行时支持。
  • 独立草稿模型:额外加载一个兼容的小模型,配合主模型进行投机解码。

这两种方式都可能增加权重、缓存和计算缓冲的占用。GGUF 已包含的 MTP 权重计入模型权重即可,运行时额外分配的缓存和缓冲另算。

猜中的越多,越有机会提速,但猜测和验证本身也要花时间、占显存。用相同请求比较开启前后的生成速度,就能判断是否值得保留;容量紧张或没有实际提速时,可以关闭。

还可以调整每次最多猜几个 token。先用后端默认值,再尝试 2、4、6 等不同长度,观察猜测被接受的比例和实际生成速度,保留更快的一组。

如何控制系统内存

显存装得下,不代表系统内存也够用。除模型加载和运行时分配外,保存供后续请求复用的上下文状态也可能占用 RAM。先观察进程驻留内存、系统可用内存和提交额度,不把某一项缓存预算当作进程总内存上限。

以下参数含义与默认值于 2026-10-15 对照llama.cpp 官方 server 参数文档核对;本机实际支持情况与默认值以该版本的 --help 为准。

  • RAM prompt 缓存:--cache-ram N。限制此类缓存的预算,单位为 MiB;1024 表示 1 GiB,0 表示禁用,-1 表示不限。所查文档列出的默认值为 8192(8 GiB)。内存紧张时可以调小;代价是旧上下文更容易被淘汰,后续请求可能需要更多重算。它不直接缩小 GPU 上活动的 KV Cache,也不等于关闭当前槽位的连续追加复用。
  • 上下文检查点:--ctx-checkpoints N。限制每个槽位保存的检查点数量,供需要回退到较早状态时恢复使用。所查文档列出的默认上限为 32,不代表启动就存满,也不能按 32 份完整 KV Cache 估算。对于前缀固定、主要追加内容的单人 Agent,可以先试 4 个,再检查压缩历史、编辑消息或切换任务时的重算开销;4 不是通用最优值。

这两项涉及的状态大小、存放位置及预算关系取决于模型和后端实现,不应简单相加来推算进程占用。每次只改一项,比较长对话中的内存峰值和 prefill 耗时;不能仅凭配置上限断定内存耗尽的原因。

如何验证调整有效

每次只改一个参数,用相同请求比较调整前后的结果,重复几次再判断:

  • 速度:分别记录 prefill 耗时和生成速度,同时记录实际输入、输出的 token 数量。前缀缓存命中会缩短 prefill,比较时应保持缓存条件一致。
  • 容量:运行实际的长请求,观察显存峰值,确认仍有余量;启用图像或投机解码时,把这些功能一起纳入测试。
  • 质量:用日常的长对话和工具调用任务检查回答是否准确、工具是否用对。降低权重或缓存精度后,尤其不能只看速度和占用。

需要单独测 llama.cpp 的输入处理和生成速度时,可以使用其自带的 llama-bench。投机解码和实际 Agent 请求的效果,则在日常使用的服务中对比。

Arkk

专题 · 关于