Hy3 本地部署先量显存,量化位宽决定能不能起服务

文章导读
先回答判断方向:本地把 Hy3 起起来,卡点几乎都在权重占用上。权重占用由参数量和量化位宽两个数决定,先把这两个数落到纸上,再去调上下文和并发,试错次数会少很多;反过来先改配置后看日志,很容易把显存不足和配置写错混在一起。
📋 目录
  1. 壹 用 nvidia-smi 和显存预算表算出权重占用上限
  2. 贰 在配置里固定量化档位与最大上下文长度
  3. 叁 起服务后从日志确认权重加载与显存分配阶段
  4. 肆 用一条最短请求验证服务真的能出结果
  5. 伍 显存仍然不够时按顺序退让的三步
A A

先回答判断方向:本地把 Hy3 起起来,卡点几乎都在权重占用上。权重占用由参数量和量化位宽两个数决定,先把这两个数落到纸上,再去调上下文和并发,试错次数会少很多;反过来先改配置后看日志,很容易把显存不足和配置写错混在一起。

先算权重占用上限再选量化档位,是本地部署 Hy3 比较省事的路径:用 参数量 × 位宽 ÷ 8 估出权重 GB,和 nvidia-smi 的 free 显存做减法,剩下的才留给 KV cache 与运行时开销。表内数值要按实际模型文件核对,位宽是名义值,真实占用会随权重格式、是否分片、上下文长度和并发数浮动;本页给的是可自填的骨架,不是某张卡上的固定结论。

用 nvidia-smi 和显存预算表算出权重占用上限

权重占用的通用算式是:权重占用(GB)≈ 参数量(以十亿计)× 量化位宽(bit)÷ 8。例如按 16 bit 存,占用大致是参数量数值的两倍;换成 8 bit 减半,4 bit 再减半。这个算式给的是下限,实际会略高,因为还有 embedding、归一化层和框架自身开销。先把总参数量和激活参数量分开记录:总参数量决定权重能占多大显存,激活参数量更多影响每次前向的计算量和部分激活显存,两者不能混成一格填。

先填这张表,再决定要不要试:

项目填写值来源/算法说明
总参数量(B)___模型 config权重占用的主体,按全部参数算
激活参数量(B)___模型 config影响前向计算与部分激活显存
量化位宽(bit)___启动配置16 / 8 / 4 等,属名义值
权重占用(GB)___总参数量 × 位宽 ÷ 8是下限,实际略高于此值
显卡可用显存(GB)___nvidia-smi 的 free需扣掉其他进程已占用
余额(GB)___可用显存 − 权重占用为负就不要试这一档

读 nvidia-smi 时重点看 free 而不是 total:total 是卡的总容量,used 里包含别人的进程和桌面环境占用,只有 free 才是你能拿到的。可以先看一次总量,再在启动服务的过程中用轮询观察变化:

Hy3 本地部署先量显存,量化位宽决定能不能起服务
nvidia-smi `--query-gpu`=memory.total,memory.used,memory.free `--format`=csv
nvidia-smi -l 1   # 启动服务前后各观察一段时间,看 used 增量落在哪一步

如果 used 在加载权重阶段就开始持续攀升并接近 total,说明档位选高了;如果权重阶段很平、到请求进来才涨,问题多半在 KV cache 和并发。把这一步的读数填进上表再往下走。

在配置里固定量化档位与最大上下文长度

量化档位、最大序列长度、批大小三项互相牵制,只动其中一项通常看不到稳定结果。量化档位决定权重占用,最大序列长度和批大小共同决定 KV cache 占用,而 KV cache 是按并发份数复制的。下面是一份通用配置骨架,字段名以你实际使用的框架为准:

model_path: /path/to/hy3            # 占位,换成实际权重目录
quantization: <none|int8|int4>     # 字段名以实际框架为准
dtype: <bfloat16|float16>          # 与量化档位可能互斥,二选一
max_model_len: <4096>              # 最大序列长度,占位
max_num_seqs: <1>                  # 并发/批大小,先给最小
gpu_memory_utilization: <0.9>      # 允许框架占用的显存比例,占位

取舍方向可以这样定:位宽往下降,权重能塞进显存但输出质量可能变差,且低比特通常要配套特定权重量化格式,不是所有权重文件都能直接用;最大序列长度往下降,KV cache 线性变小,代价是长文本会被截断;并发数往下降,每份 KV cache 变少,代价是同时处理请求数变少。保守做法是首次启动把并发设成 1、长度设成一个你能接受的短值,先确认能起来,再逐项往上加。

Hy3 本地部署先量显存,量化位宽决定能不能起服务

起服务后从日志确认权重加载与显存分配阶段

服务起不来时,先分清失败停在哪一类,再决定下一步动作。日志关键行大致分三类:

  • 权重加载类,形如 Loading weights ...、Loading checkpoint shards 1/2、文件路径或格式不匹配的报错。这类失败说明权重没读进来,多与路径、分片文件缺失、量化格式与实际权重不匹配有关,下一步是核对目录和权重文件清单。
  • 显存分配类,形如 CUDA out of memory、KV cache 块数分配失败、gpu_memory_utilization 相关提示。这类失败说明权重之后的空间不够,下一步是回到预算表,按第 5 节的顺序退让。
  • 首 token 生成类,形如 Engine started、prefill/decode 计时、请求级报错。进程已经起来、权重也加载完了,问题在请求侧,下一步是检查请求字段、输入长度和超时设置。

三类日志的处理方向不同:加载类改配置没用,先查文件;分配类改文件没用,先改档位和长度;请求类说明前两步已经过了,别再去动量化。把日志最后一段的报错行原样对照这三类,基本能定位卡点。

用一条最短请求验证服务真的能出结果

进程活着不代表能跑通,权重加载完成也不代表能出 token。先发一条最小请求,地址、端口和字段名以实际服务为准:

Hy3 本地部署先量显存,量化位宽决定能不能起服务
curl -s `--max-time` 60 http://127.0.0.1:<port>/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{"model":"<model-name>","messages":[{"role":"user","content":"你好"}],"max_tokens":16,"stream":false}'

期望返回里 choices[0].message.content 有一段非空文本,usage 里能看到输入输出 token 数。如果返回的是空内容或没有 choices 字段,说明服务通了但生成环节有问题。超时判定用 `--max-time` 卡住,同时另开一个终端跑 nvidia-smi -l 1:请求发出后 used 明显上升、请求结束回落,说明走的是正常路径;如果请求期间 used 不变,多半请求没进到模型。

显存仍然不够时按顺序退让的三步

按下面的顺序退让,每一步只动一个变量,改完重跑第 4 节那条最短请求:

  1. 降量化档位。从高比特降到低比特,权重占用按比例下降,通常是最直接的一步。代价是输出质量可能下降,而且低比特权重需要对应格式的文件,不是改个字段就能生效,改完要重新确认生成结果是否还能接受。
  2. 降最大上下文长度。KV cache 随序列长度增长,把 max_model_len 调小能腾出空间。代价是超过长度的输入会被截断或直接拒绝,适合先验证服务能起来,长文本需求后面再单独评估。
  3. 降并发。把 max_num_seqs 或批大小压到 1,每份 KV cache 只保留一份。代价是同时能处理的请求变少,排队时间可能变长,但单请求的显存占用会更可控。

如果三步走完余额仍然为负,说明这张卡在当前模型规模下确实放不下,此时再考虑换卡或换更小的模型,比继续调参数更省时间。