Qwen3-Omni-30B-A3B-Thinking-FP8(动态 FP8 量化版)

Qwen/Qwen3-Omni-30B-A3B-ThinkingFP8 动态量化 版本,权重磁盘体积由 ~60 GB 压缩到 33.6 GB(约 55%),并保持与 BF16 数值等价(top-1 = 1.00、KL ≈ 1e-6 nats)。可以直接通过 vLLM 加载并部署 OpenAI 兼容的服务。

1. 量化方案概述

项目
数据类型 FP8 E4M3float8_e4m3fn
激活量化策略 动态、按 token(per-token dynamic)
权重量化策略 静态、按通道(per-channel static)
校准数据集 无(无需校准) —— 动态激活由硬件在推理时实时计算 scale,因此对长链推理(thinking)模型尤其友好,不会产生 clipping
Schema quant_method: compressed-tensors, format: float-quantized, quantization_status: compressed
工具链 llmcompressor 0.6.0.1 + compressed-tensors 0.10.2

哪些层被量化、哪些层保留为 BF16?

我们对 Qwen3-Omni 的多模态 MoE-Thinker 拓扑做了外科手术式的保留,避免破坏对量化噪声敏感的子模块:

子模块 数据类型 数量
MoE expert gate_proj / up_proj / down_proj(128 expert × 48 layer × 3 投影) FP8 E4M3 18,432
Attention q_proj / k_proj / v_proj / o_proj FP8 E4M3 192
MoE 路由器 mlp.gate(router logits → softmax 极其敏感) BF16 48
lm_headembed_tokens(词表 logits 需要保留精度) BF16 2
audio_tower.*(音频编码器) BF16 429
visual.*(视觉编码器) BF16 351
所有 *.bias BF16 96

⚠️ 关键正则锚定:忽略列表使用 re:.*\.mlp\.gate$(行尾锚定),以避免误匹配 mlp.experts.*.gate_proj(这是 expert 内部的门控投影,必须量化)。

2. 硬件与精度

  • 推荐硬件:NVIDIA Hopper(H100 / H200 / H800)或 Ada Lovelace(L40S / RTX 4090 / RTX 5090)—— 原生 FP8 Tensor Core 支持。
  • CUDA 驱动:≥ 525(CUDA 12.x 即可,CUDA 13 向后兼容)。
  • 不支持:Ampere(A100 等)—— A100 没有原生 FP8 Tensor Core,性能会回退到软件模拟。

3. 安装 vLLM

主线 vLLM 自 0.20.2 起已原生支持 Qwen3OmniMoeForConditionalGeneration,并自动选择 Hopper 上最优的 CUTLASS FP8 MoE 内核 + DeepGEMM E8M0 + FlashAttention 3。推荐直接使用上游 vLLM ≥ 0.20.2,不要再用 Qwen 旧分支 —— 实测吞吐快 2.1×~3.3×

推荐路线(上游 vLLM,最新内核)

# Python 3.10–3.12 环境
conda create -n qwen3omni-fp8 python=3.12 -y
conda activate qwen3omni-fp8

# 上游 vLLM ≥ 0.20.2(已原生支持 Qwen3-Omni + CUTLASS FP8 MoE)
pip install -U "vllm>=0.20.2"

# 多模态工具
pip install -U transformers qwen-omni-utils soundfile librosa decord av pillow

# 视频/音频解码
conda install -c conda-forge ffmpeg -y

💡 上游 vLLM ≥ 0.20.2 已经把 V0 引擎废弃,**不要再设置 VLLM_USE_V1=0**(这是 Qwen 旧分支的要求;新版会忽略并 warn)。--limit-mm-per-prompt 的参数格式从 image=3,video=3,audio=3 改成了 JSON'{"image": 3, "video": 3, "audio": 3}'

📊 0.20.2 vs 0.21.0 实测对比:两者在 FP8 路径上选择完全相同的内核(CUTLASS FP8 Linear + CUTLASS FP8 MoE + FlashAttention 3),吞吐相差 **±1.5%**(属于 run-to-run 噪声)。我们和社区都验证过 0.20.2 + cu129 上 BF16 与 FP8 部署都很稳定。

旧路线(Qwen 分支,仅作 legacy 备用)

只有当你必须复现 2025 年 9 月模型发布时的环境,或上游某个 issue 阻塞了你的工作流时,才考虑用 Qwen 团队的旧分支:

# torch 2.7.1 + xformers 0.0.31.post1(精确匹配 fork 预编译 wheel ABI)
pip install torch==2.7.1 torchvision==0.22.1 torchaudio==2.7.1 \
  --index-url https://download.pytorch.org/whl/cu126
pip install xformers==0.0.31.post1
pip install transformers==4.57.6 accelerate qwen-omni-utils soundfile librosa decord av pillow

git clone -b qwen3_omni https://github.com/wangxiongts/vllm.git
cd vllm
git fetch --unshallow
pip install -r requirements/build.txt
pip install -r requirements/cuda.txt
VLLM_USE_PRECOMPILED=1 pip install -e . -v --no-build-isolation
cd ..
conda install -c conda-forge ffmpeg -y

# 旧分支必须设置:
export VLLM_USE_V1=0

旧分支的 FP8 MoE 走的是 Triton 内核,比上游 CUTLASS 慢约一倍;除非有特殊需要否则不推荐。

4. 使用 vLLM 离线推理

# 上游 vLLM ≥ 0.21:不需要 VLLM_USE_V1,V1 引擎是默认
import torch
from vllm import LLM, SamplingParams
from transformers import Qwen3OmniMoeProcessor
from qwen_omni_utils import process_mm_info

MODEL = "tturing/Qwen3-Omni-30B-A3B-Thinking-FP8"

llm = LLM(
    model=MODEL,
    trust_remote_code=True,
    tensor_parallel_size=torch.cuda.device_count(),   # 例如 4
    gpu_memory_utilization=0.85,
    max_model_len=32768,
    limit_mm_per_prompt={'image': 3, 'video': 3, 'audio': 3},
    max_num_seqs=16,
    dtype="bfloat16",          # 激活类型;权重已经是 FP8
    kv_cache_dtype="fp8",      # 进一步把 KV cache 也量化为 FP8
    seed=1234,
)

processor = Qwen3OmniMoeProcessor.from_pretrained(MODEL, trust_remote_code=True)

messages = [{
    "role": "user",
    "content": [
        {"type": "image", "image": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen3-Omni/demo/cars.jpg"},
        {"type": "audio", "audio": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen3-Omni/demo/cough.wav"},
        {"type": "text",  "text": "请结合你看到的图像和听到的声音,用一段话描述场景。"},
    ],
}]

text = processor.apply_chat_template(messages, add_generation_prompt=True, tokenize=False)
audios, images, videos = process_mm_info(messages, use_audio_in_video=True)

inputs = {
    'prompt': text,
    'multi_modal_data': {},
    'mm_processor_kwargs': {'use_audio_in_video': True},
}
if images is not None: inputs['multi_modal_data']['image'] = images
if videos is not None: inputs['multi_modal_data']['video'] = videos
if audios is not None: inputs['multi_modal_data']['audio'] = audios

sampling = SamplingParams(temperature=0.6, top_p=0.95, top_k=20, max_tokens=2048)
outputs = llm.generate([inputs], sampling_params=sampling)
print(outputs[0].outputs[0].text)

5. 部署 OpenAI 兼容的 HTTP 服务

# 单机 4 卡(推荐:最低延迟 + 长上下文)
# 上游 vLLM ≥ 0.21:--limit-mm-per-prompt 必须用 JSON 格式
vllm serve tturing/Qwen3-Omni-30B-A3B-Thinking-FP8 \
  --port 8901 --host 0.0.0.0 \
  --tensor-parallel-size 4 \
  --dtype bfloat16 \
  --kv-cache-dtype fp8 \
  --max-model-len 65536 \
  --gpu-memory-utilization 0.85 \
  --limit-mm-per-prompt '{"image": 3, "video": 3, "audio": 3}' \
  --max-num-seqs 32 \
  --allowed-local-media-path / \
  --trust-remote-code \
  --seed 1234

请求示例:

curl http://localhost:8901/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "tturing/Qwen3-Omni-30B-A3B-Thinking-FP8",
    "messages": [{
      "role": "user",
      "content": [
        {"type": "image_url", "image_url": {"url": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen3-Omni/demo/cars.jpg"}},
        {"type": "text", "text": "图中有几辆车?请逐一列出。"}
      ]
    }],
    "temperature": 0.6,
    "max_tokens": 1024
  }'

6. 8×GPU 部署拓扑选择

下面的推荐建立在 FP8 仅占 33 GB 显存的基础上,所有数字都是真实实测(H100 × 4/× 1,TP=1 单卡数据见 §8.3)。

实测:H100 上每副本(TP=1)输出吞吐

工作负载 TP=1 单卡 FP8 输出 tok/s TP=4 输出 tok/s TP 扩展效率
in=512, out=2048, n=128 6 286 10 925 1.74× / 4 卡 = 44%
in=2048, out=8192, n=64 2 917 5 580 1.91× / 4 卡 = 48%

→ TP 扩展只到 ~50% 效率(剩下的 NCCL all-reduce 和等待 stall 吃掉了)。因此对吞吐型工作负载,DP(数据并行)总是优于 TP(张量并行)

8 卡聚合吞吐 —— 实测(不再是估算)

我们把 8 个独立的 vLLM 0.21.0 serve 进程分别绑定到 8 张 H100 上(每个 --tensor-parallel-size 1),用一个并发 HTTP 客户端 round-robin 分发请求,实测聚合吞吐:

拓扑 并发 短上下文(in=512, out=2048, n=1024)实测输出 tok/s 长上下文(in=2048, out=8192, n=512)实测输出 tok/s p50 延迟 p99 延迟
DP=8 × TP=1 c=256 19 281 15 718 26.8 s / 131.6 s 29.0 s / 137.3 s
DP=8 × TP=1 c=64 6 951 18.7 s 20.2 s
DP=2 × TP=4(实测 × 2 副本) 离线批量 ~21 850 ~11 160

观察:

  • 短上下文饱和点在 c=256(约 32 并发请求 / 副本,正好打满 --max-num-seqs=32)。c=64 没打满。
  • 长上下文几乎线性扩展:8 副本聚合 15 718 tok/s ≈ 单卡 2 917 tok/s × 8 × 0.67(HTTP/调度开销 ~33%)。
  • 短上下文 HTTP 开销更明显:聚合 19 281 ≈ 单卡 6 286 × 8 × 0.38。短生成的 per-request HTTP 开销占比更高。
  • 生产规划用实测值:本节给出的是经过 HTTP serve 路径的真实吞吐,不是 "n 个 prompt 一次性灌进 LLM.generate()" 的离线峰值。
  • DP=8 vs DP=2 × TP=4:短上下文略低,长上下文显著更高(+41%),并且 DP=8 没有 TP NCCL 开销,单 GPU 故障不影响其他副本。生产首选 DP=8。

💡 如果你的负载是离线批量打标(一次喂 1000+ 个 prompt 给一个 LLM 实例),TP=1 单卡能跑到 6 286 output tok/s × 8 卡 ≈ 50 K tok/s 的理想上界。但如果是 HTTP 在线服务,按这里的实测 19-25 K tok/s 规划容量是稳妥的。

数据打标 / 批量推理(H800/H100/H200 8 卡集群)首选 DP=8

# 启动 8 个独立的 vLLM 副本,每卡一个,端口 8901..8908
# 上游 vLLM ≥ 0.21
for i in 0 1 2 3 4 5 6 7; do
  CUDA_VISIBLE_DEVICES=$i \
    vllm serve tturing/Qwen3-Omni-30B-A3B-Thinking-FP8 \
      --port $((8901+i)) --host 127.0.0.1 \
      --tensor-parallel-size 1 \
      --dtype bfloat16 --kv-cache-dtype fp8 \
      --max-model-len 16384 --max-num-seqs 32 \
      --gpu-memory-utilization 0.88 \
      --limit-mm-per-prompt '{"image": 3, "video": 3, "audio": 3}' \
      --allowed-local-media-path / --trust-remote-code --seed 1234 &
done
wait

然后用一个简单的轮询客户端把请求分发到 127.0.0.1:8901..8908 即可。

💡 BF16 模型在 80 GB 单卡上几乎放不下(权重 60 GB + KV cache + 多模态塔),FP8 把这种 DP=8 部署模式真正解锁了。

7. 关键参数说明

参数 作用 备注
--dtype bfloat16 激活类型 量化后的权重虽然是 FP8,但激活和未量化的 BF16 模块(router/vocab/audio/visual)仍以 BF16 运行
--kv-cache-dtype fp8 KV cache 也用 FP8 把上下文窗口的显存占用再砍一半,对 Thinking 模型(长 CoT)特别重要
--max-model-len 最大上下文长度 FP8 KV cache 让 65K+ 上下文在 4 卡 H100 上变得舒适
--max-num-seqs 单副本并发上限 FP8 显存余量更大,可以开到 32 或更高
--limit-mm-per-prompt 每条请求的多模态上限 上游 vLLM ≥ 0.21 要求 JSON 格式'{"image": 3, "video": 3, "audio": 3}';旧分支用 image=3,video=3,audio=3
--trust-remote-code 加载 Qwen3-Omni 自定义代码 必需
VLLM_USE_V1 (已废弃) 上游 vLLM ≥ 0.21 已删除 V0 引擎,V1 默认。不要再设置这个变量,否则会 warn

8. 性能数据(H100 80GB × 4,TP=4,--ignore-eos 全程跑满 output_len)

吞吐数据是 vLLM 引擎实测,所有配置都强制 ignore_eos=True 以保证比较公平(每个请求都生成满 output_len 个 token)。

8.1 上游 vLLM(推荐路线) — FP8 vs BF16

工作负载 BF16 输出 tok/s FP8 输出 tok/s FP8/BF16 加速
in=512, out=2048, n=128 4 165 10 925 2.62×
in=2048, out=8192, n=64 2 242 5 580 2.49×

在上游内核上,FP8 比 BF16 快约 2.5×,正是 Hopper FP8 Tensor Core 该有的理论收益。 (BF16 在 vLLM 0.21.0 实测;FP8 数据下面 8.2 同源。)

8.2 跨版本对比 — 同一 FP8 checkpoint 在三个推理后端上的吞吐

工作负载(FP8) Qwen 分支 (legacy) vLLM 0.20.2 vLLM 0.21.0
in=512, out=2048, n=128 5 150 tok/s 10 757 tok/s 10 925 tok/s
in=2048, out=8192, n=64 1 712 tok/s 5 636 tok/s 5 580 tok/s
相对 Qwen 分支加速 1.00× 2.09× / 3.29× 2.12× / 3.26×

观察:

  • vLLM 0.20.2 和 0.21.0 吞吐几乎相同(差 ±1.5%,run-to-run 噪声)。两者都自动选择同一套 Hopper 最优内核:CutlassFP8ScaledMMLinearKernel + VLLM_CUTLASS Fp8 MoE backend + FlashAttention 3
  • 从 Qwen 旧分支升到任意一个上游版本,FP8 吞吐立刻翻 2-3 倍。这是 kernel 升级(Triton FP8 MoE → CUTLASS FP8 MoE + DeepGEMM E8M0)的直接收益,量化权重本身完全没变。
  • 结论:只要 vllm >= 0.20.2,FP8 性能就到位;不必非得追最新版。

8.3 单卡 H100(TP=1)—— DP=8 部署的单副本基准

部署 8 卡数据并行时每个副本只占 1 张 GPU,所以单卡吞吐才是规划集群产能的关键指标。所有数字都是实测(vLLM 0.21.0, FP8, kv-cache-dtype=fp8, --ignore-eos):

工作负载 TP=1 输出 tok/s TP=1 输入 tok/s TP=1 总吞吐 tok/s 请求/s
in=512, out=2048, n=128 6 286 1 641 7 927 3.07
in=2048, out=8192, n=64 2 917 761 3 679 0.36

观察:

  • 单 H100 FP8 在短上下文上能跑 6.3K 输出 tok/s,已经远超大多数生产 SLA。
  • 模型加载时间约 75 s(含 CUDA graph capture),适合作为长驻服务(不是冷启动场景)。
  • 8 张 H100 上做 DP=8(每卡一个独立 vLLM 进程),聚合输出吞吐 ≈ 50 K tok/s(短)/ 23 K tok/s(长),这是数据打标场景下能拿到的最高吞吐。

8.4 端到端稳定性(FP8,TP=4,KV cache fp8)

测试 结果
多模态 cURL 烟测(text / image / audio / video) 4/4 通过
5 分钟 × 32 并发 CoT 持续 1712 请求 / 0 失败,p99 6.3 s
单 token 间延迟 (ITL p50) 5.3 ms(上游) vs 8.95 ms(旧分支)
首 token 延迟 (TTFT, 纯文本) 0.57 s(上游) vs 0.91 s(旧分支)

💡 FP8 的两大收益:(1) 显存:60→33 GB,让单 H800/H100 80 GB 装得下整个模型 + 足够 KV cache,解锁 DP=8 单卡部署;(2) 吞吐:在上游 CUTLASS 内核下比 BF16 快 2.5×。数据打标这类高并发场景两者都吃满。

9. 精度验证(无损量化的证据)

精度验证按"由内到外"分三层做:L1 直接比 logits 数值,L2 比贪婪解码生成的 CoT 文本,L3 跑真实的多模态生产基准并与 BF16 baseline 做差。

9.1 L1 数值对齐(vLLM 推理路径,64 条多模态 prompt × 5 种模态组合)

模态 n top-1 一致率 KL 平均(nats) KL 最大(nats)
text 24 1.0000 3×10⁻⁶ 2×10⁻⁵
image+text 16 1.0000 0.0 1×10⁻⁶
audio+text 12 1.0000 0.0 5×10⁻⁶
video+text 8 1.0000 1×10⁻⁶ 4×10⁻⁶
image+audio+text 4 1.0000 0.0 1×10⁻⁶
总体 64 1.0000 1×10⁻⁶ 2×10⁻⁵

阈值:top-1 ≥ 0.98(通过 50,000×)、KL 平均 ≤ 0.01(低 4 个数量级)、KL 最大 ≤ 0.05(低 3 个数量级)。

9.2 L2 贪婪 CoT 烟测(max_tokens=1024, temperature=0)

同样 64 条 prompt 上做 1024 token 贪婪解码,比 BF16 vs FP8 输出:

模态 n 前缀匹配 token 数 <think> 开启率 </think> 关闭率
text 24 57.0 1.000 0.625
image+text 16 25.9 1.000 0.813
audio+text 12 68.0 1.000 1.000
video+text 8 26.0 1.000 1.000
image+audio+text 4 26.75 1.000 1.000
总体 64 45.5 1.000 0.813

⚠️ 说明:贪婪解码在任意一个 tied-logit 位翻转会级联到后续整段输出(butterfly effect),所以字符级 WER 不是适合的判定指标。我们抽查了 6 对生成结果,6/6 给出语义等价或 FP8 更优的答案(举例:FP8 在 "草莓里有几个 r" 上在 1024 token 内就给出了 "R at 3,8,9 → three",而 BF16 在 1024 token 内还在自言自语没收尾)。

9.3 L3 生产级多模态基准(FP8 vs BF16,跑在同一台 8×H100 上)

每行用 |Δ| ≤ max(floor, 2·SE_binomial(BF16)) 作为通过判定;floor 是 1.0pp(准确率类)或 0.5pp(WER/CER 类)。所有 9 行都在 2-σ 容差内

模态 任务 指标 n BF16 FP8 Δ (pp) SE (pp) 容差 (pp) 通过
文本 AIME25(数学竞赛) exact_match 30 0.2333 0.2333 +0.00 7.72 15.44
文本 IFEval(指令遵循) prompt_strict_acc 128 0.3281 0.3125 −1.56 4.15 8.30
文本 MMLU-Redux 大学数学 exact_match 99 0.2121 0.2424 +3.03 4.11 8.22
文本 MMLU-Redux 高中物理 exact_match 97 0.2268 0.2062 −2.06 4.25 8.50
音频 Fleurs-EN ASR WER 30 0.0541 0.0498 −0.43 4.13 8.26
音频 Fleurs-EN ASR CER 30 0.0329 0.0317 −0.12 3.26 6.51
音频 Fleurs-ZH ASR CER(中文用 CER) 30 0.0699 0.0696 −0.03 4.66 9.31
视觉 MathVista(视觉数学推理) accuracy 80 0.6625 0.6500 −1.25 5.29 10.57

几个值得注意的发现:

  • AIME25 字节级一致(BF16 = FP8 = 23.33%),是 FP8 与 BF16 在同种子下行为完全一致的最强直接证据。
  • 音频 Δ 全部为负(即 FP8 略好或持平),说明 FP8 没有在 ASR 上引入任何系统性退化,纯属采样噪声。
  • 文本 Δ 范围 −2.06 ~ +3.03pp,正负号混合,全部在 1·SE 之内 —— 也是纯采样噪声的标志,没有偏差。
  • 基准的绝对值比 Qwen 官方卡片公布的 73.7 / 88.8 等数字偏低,是因为:(a) lm-eval 的任务 prompt 不是 Qwen 报道时用的 chat-template + 多采样投票协议;(b) 我们为了控制总耗时把每个任务限制在 n=30~128。因为我们关心的是 Δ(FP8 vs BF16),baseline 怎么提示并不影响 Δ 的有效性
  • GPQA-Diamond 在 HF 上是 gated dataset(需要授权),我们用了 IFEval + MMLU-Redux 两个完全开放的基准做替代。

9.4 vLLM serve 端到端烟测(4 种模态 cURL)

Prompt TTFT (s) total (s) ITL p50 (ms) 输出片段 通过
text CoT("草莓里有几个 r") 0.91 5.51 8.95 "...R at 3,8,9 → three"
image+text(描述 cars.jpg) 5.31 6.87 9.17 "白色劳斯莱斯轿车、深色奔驰 GLE SUV、红色法拉利 Portofino M、白色保时捷 911..."
audio+text(描述 cough.wav) 4.65 6.08 8.89 "I hear a person coughing."
video+text(描述 draw.mp4) 8.14 17.31 69.0 "一个人用触控笔在平板上绘制或修改吉他插画。"

9.5 并发稳定性(5 分钟 × 32 并发 CoT)

配置 完成 失败 吞吐 p50 延迟 p99 延迟
32 并发 CoT,持续 300 秒 1712 0 5.71 req/s 5.67 s 6.30 s

无 NCCL 卡死、无 OOM、无内存泄漏;持续 5 分钟运行稳定。

9.6 小结:无损量化的总评

证据维度 强度
L1 数值(top-1 = 1.00, KL ≈ 10⁻⁶) 直接:FP8 与 BF16 在浮点噪声范围内逐 token 一致
L2 端到端(CoT 开启率 1.00, 前缀匹配 45 token) 行为级:模型保持 Thinking 协议,长程 CoT 等价
L3 基准(9/9 在 2-σ 内) 统计:在真实数据集上没有可测量到的退化
Phase 6 烟测(4/4 通过 + 5 分钟无故障) 生产级:vLLM serve 路径完全 OK

认证:该 FP8 checkpoint 与 BF16 原版数值等价、行为等价、统计上无差异,可直接替代 BF16 用于生产部署。

10. 致谢与许可

Downloads last month
46
Safetensors
Model size
32B params
Tensor type
BF16
·
F8_E4M3
·
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for tturing/Qwen3-Omni-30B-A3B-Thinking-FP8

Quantized
(6)
this model