MOSS-Transcribe-Diarize + SGLang-Omni 推理方案:显存与解码参数详解
**类型:Explanation(理解导向)**。本文解释
temperature、mem-fraction-static、max_new_tokens三个参数为何影响转写质量、显存占用与可支持音频时长,帮助你在遇到重复退化、OOM 或输出截断时做出正确的调参决策。本文只讨论上述三个参数;
top_p、top_k等其他采样参数不在讨论范围内。
1. 概述:三个参数各管什么
MOSS-Transcribe-Diarize(SGLang-Omni 引擎)在解码一段音频时,有三个独立维度的配置参数。按生效层级分两类:
| 参数 | 层级 | 控制维度 | 一句话 |
|---|---|---|---|
mem-fraction-static |
启动参数 | 显存 / KV 容量 | sglang-omni 启动时预分配多少显存给 KV cache pool,决定能支持多长的音频;改后需重启服务 |
max_new_tokens |
请求参数 | 解码输出上限 | 单次解码最多生成多少 token,随每次转录请求下发;改即生效,无需重启 |
temperature |
请求参数 | 采样质量 | 0=贪心(argmax);>0 采样,影响输出确定性与重复退化;随请求下发 |
启动参数(
mem-fraction-static)在服务启动时固定,调整需重启服务;请求参数(max_new_tokens、temperature)由调用方在每次 HTTP 请求里下发,调整即时生效。
三者共同决定三类结果:转写质量(temperature)、是否 OOM(mem-fraction-static + max_new_tokens)、支持多长音频(mem-fraction-static -> KV 容量 -> input + max_new)。其中 mem-fraction-static 与 max_new_tokens 通过”KV 容量”耦合,temperature 则与两者独立(只管采样)。
2. mem-fraction-static:静态 KV pool 与显存权衡
机制
sglang-omni 启动时按 mem-fraction-static × 总显存 预分配一个静态 KV cache pool(模型权重 + KV 池 + 开销),用于存放解码过程的 KV cache。这个 pool 在启动时就占住显存,不随请求变化,因此调它必须重启服务。
KV 容量公式
本模型(Qwen3 解码器:28 层 / 8 KV head / head_dim 128 / bf16):
1 | bytes_per_token = 2(K+V) × 28 × 8 × 128 × 2 = 114688 bytes |
kv_capacity 是 pool 能容纳的最大 token 数。音频的 input token + 解码 output token 之和必须 ≤ kv_capacity,否则 OOM 或截断。
16G / 24G 取值
| 显存 | fraction | kv_capacity | 说明 |
|---|---|---|---|
| 16G | 0.80 | ≈92766 | 本地笔记本(RTX 5080 Laptop GPU)实测可用 |
| 24G | 0.55 | ≈96434 | 推荐默认(≤60min 音频) |
| 24G | 0.60 | ≈107670 | >60min 才需要 |
| 24G | 0.80 | ≈152609 | 容量足够,但静态显存占用过大,可能 OOM(见下) |
为什么 24G 不能用 0.80
注意:0.80 在 24G 上的 OOM 不是 KV 容量不足(容量 ≈152609,绰绰有余),而是总显存超载。0.80 × 24 = 19.2GB 静态预留,实际 idle 占用约 20.66GB,加上推理动态开销(~5.3GB,推算),峰值可能超过 24GB。
实测 0.80 在 24G 推理时触发 OOM:
1 | torch.OutOfMemoryError: CUDA out of memory. Tried to allocate 598.00 MiB. |
音频编码器(whisper conv1)申请显存时,空闲量不足 -> OOM。降到 0.55 把静态压到约 13.2GB,峰值约 18.5GB,留出余量给动态开销,才放得下。
mem-fraction-static 不影响解码速度
实测(24G 卡,50min 音频):0.55 与 0.60 两种取值的单轮耗时完全一致(均 199.9s)。因为 pool 多余的容量不参与解码计算,只要音频”放得下”,pool 大小不影响解码耗时。所以调它是纯粹的”显存 vs 容量”权衡,不以速度为代价。
注:下文峰值显存为按 0.60 实测动态开销(~5.3GB)推算,仅 50min 音频实测过,更长音频未实测。
3. max_new_tokens:输出上限与 KV 容量约束
作用
max_new_tokens 限制单次解码最多生成的 token 数(输出文本上限)。sglang-omni 按此值预留解码空间。它是请求参数,由调用方在每次转录请求里下发。
约束:input + max_new ≤ kv_capacity
音频的 input token(与时长线性,约 13.4 token/s)加上 max_new_tokens 必须 ≤ kv_capacity(由 mem-fraction-static 决定)。否则要么 OOM(超显存),要么输出被截断。
50min 测试音频实测:
- input ≈ 40193 token
- output ≈ 19366 token(实际输出,远小于 max_new)
- 取 max_new=32768:input + max_new = 72961,在 16G/0.80 的 kv_capacity(92766)内放得下,且 output(19366)< 32768 不截断。
32768(2^15)作为默认:output 余量足(50min 余 69%),KV 余量足(60min input≈48232,48232+32768=81000 < 92766,可支持至约 74min)。更大的 max_new(如 49152)在 60min 会超 kv_capacity(97384 > 92766)可能导致 OOM,故不取。
与 mem-fraction-static 的耦合
max_new_tokens 能设多大,受 mem-fraction-static 决定的 kv_capacity 限制。fraction 调小 -> kv_capacity 缩小 -> max_new 上限降低 / 可支持音频变短。两者通过 kv_capacity 耦合。
4. temperature:贪心解码与重复退化
贪心解码的确定性
转录请求默认 temperature=0.0(贪心,argmax):每步选最高概率 token,输出对同一份 logits 完全确定。实测同一 GPU 上 3 轮转写结果完全一致(段数 623/623/623 或 793/793/793)。
4090D 上的重复退化
同一份模型权重(md5 一致)、同一份 sglang-omni 代码(hash 一致)、同一 sglang(0.5.12.post1)/ torch(2.11.0+cu130)/ CUDA(13.0)版本,在两块不同 GPU 上贪心解码,产出差异巨大:
| GPU | 架构 | 段数(贪心) | 连续重复段 | 最高重复 |
|---|---|---|---|---|
| RTX 5080 Laptop GPU(16G) | Blackwell | 623 | 7 | 正常口语(最高 ×6) |
| 4090D(24G) | Ada | 793 | 163 | 单字循环 ×102、短语循环 ×49 |
4090D 上出现严重的自回归重复退化(repetition degeneration):模型卡在单 token/短语循环里出不来(如某单字重复 102 次、某短语重复 49 次),段数虚高、质量下降。5080 Laptop 不触发。
根因:不同 GPU 架构(Blackwell vs Ada)的 tensor core / kernel 实现与累加顺序不同,产生微小但不同的 logits。贪心解码是”蝴蝶效应”过程,微小 logits 差异逐步放大,在 4090D 上把解码器推进重复循环。本质是模型对长音频的重复退化较为敏感,5080 Laptop 的数值路径恰好绕开,4090D 的触发。这不是配置或代码错误,而是模型本身的数值不稳定性在不同硬件上的表现差异。
temperature=0.1 的修复
将 temperature 从 0.0 提到 0.1(轻采样),给 logits 加一点随机扰动,让解码器跳出”最高概率锁死”的循环:
| 指标 | 4090D 贪心(temp=0) | 4090D temp=0.1 |
|---|---|---|
| 段数 | 793 | 610 / 611 |
| 连续重复段 | 163 | 2 |
| 最高重复 | 单字循环 ×102 | 正常口语(最高 ×6) |
| 单轮耗时 | 199.9s | 161s |
退化消除(610 段,接近 5080 Laptop 的 623 正常水平),单字循环消失,且更快(少解码了约 180 个退化小段)。
代价:采样引入非确定性,输出不再逐字可复现(temp=0.1 下两轮差 1 段,610 vs 611,近似稳定)。若需完全确定性,可设 temperature=0(但在 4090D 等卡上会退化),或改用 repetition_penalty(见下)。
备注:接口限制
转录接口 /v1/audio/transcriptions 不接受 repetition_penalty 参数, top_p / top_k 暂时不在讨论范围内。因此目前使用 temperature 缓解退化;若要”贪心 + 重复惩罚”兼顾确定性与质量, 可能需要 patch 转录接口透传 repetition_penalty(底层 SamplingParams 已支持)。
5. 三参数的相互作用
1 | mem-fraction-static ──-> kv_capacity ──-> 限制 input + max_new_tokens 之和 |
mem-fraction-static(启动参数)决定池子大小 -> 限制 max_new + input 能多大 -> 决定音频时长上限与 OOM 风险;改需重启服务。max_new_tokens(请求参数)在 fraction 给定的容量内设定输出上限;改即时生效。temperature(请求参数)独立管采样,不影响显存/容量,但影响质量与确定性;改即时生效。
调参决策表
| 现象 | 调哪个参数 | 方向 | 生效方式 |
|---|---|---|---|
| OOM(显存不足) | mem-fraction-static |
调小(腾显存);或调小 max_new_tokens |
启动参数,需重启服务 |
| 输出被截断(音频没转完) | max_new_tokens / mem-fraction-static |
调大 max_new(须确保 ≤ kv_capacity);或调大 fraction 增容 | max_new 即时;fraction 需重启服务 |
| 重复退化(段数虚高、单字循环) | temperature |
从 0 提到 0.1 | 请求参数,即时 |
| 想要完全确定性 | temperature |
设 0(注意:某些 GPU 会退化,需权衡) | 即时 |
| 换 GPU 后质量变差 | temperature |
多半是数值差异触发退化,试 0.1 | 即时 |
6. 小结
mem-fraction-static(启动参数):管显存与 KV 容量(音频时长上限)。16G 用 0.80,24G 用 0.55(≤60min)。不影响速度。改重启服务。max_new_tokens(请求参数):管解码输出上限。须满足input + max_new ≤ kv_capacity。默认 32768 覆盖至约 74min。temperature(请求参数):管采样质量与确定性。0=贪心(确定,但 4090D 等卡可能退化);0.1=轻采样(消除退化,轻微非确定)。
决策树:看到 OOM -> 调 mem-fraction-static / max_new_tokens;看到截断 -> 调大 max_new_tokens(守 kv_capacity);看到重复退化 -> 调 temperature 到 0.1。
数据来源:50min 测试音频在 RTX 5080 Laptop GPU(16G,笔记本,Blackwell)与 RTX 4090D(24G,Ada)上的实测。两机模型权重(md5
092f865a...)、sglang-omni 代码、sglang/torch/CUDA 版本均一致,唯一变量为 GPU。峰值显存与更长音频的容量为按 KV 容量公式与 0.60 实测动态开销(~5.3GB)推算,仅 50min 音频实测过;0.80/24G 的 OOM 为实测(推理时触发)。局限:实测仅用单一 50min 测试音频,样本单一;退化是否触发、段数、耗时、峰值显存等结论可能随音频不同(时长/内容/说话人/语种)而变化,本文结论为该样本下的观测,不代表所有输入下的普遍表现。