Qwen3-ASR 课程 · 第 11 课 · 路线第 5 站 · 推理实操

上手推理 —— 在 T4 上跑通 Qwen3-ASR-0.6B

⏱ 约 40 分钟(动手为主)· 环境:单张 T4 16GB,学习用途 · 前置:向量流水线 · 本课目标:三条路线跑通推理 + 逐键读懂 config.json + 一个自检任务清单

理论课全部结束,今天上真机。你的环境很明确:一张 T4 16GB,用来学习——好消息是 0.6B 的推理对 T4 绰绰有余:权重 fp16 只有 1.8GB,16GB 显存扣完还有 11GB+ 的 KV 空间。压测与多实例是工作场景的事(见插入专题),本课只关心把模型跑起来、看清每一行输出。

T4 第一课:它没有 BF16 T4 是 Turing 架构(cc 7.5),不支持 BF16 张量运算(BF16 始于 Ampere)。Qwen3-ASR 的权重是 BF16 存的——所以加载时必须显式指定 float16(存储位数相同,数值转换无损精度)。记住这条,T4 上 80% 的加载报错都源于此。

〇、课前准备(5 分钟)

# 1) 环境信息采集(填进你的笔记)
nvidia-smi                                # 驱动/CUDA 版本;T4 16GB 应可见
python3 --version                         # 建议 3.10+
pip show torch | grep Version             # 建议 torch ≥ 2.1 CUDA 版

# 2) 模型下载(二选一;国内网络优先 ModelScope)
pip install -U huggingface_hub
huggingface-cli download Qwen/Qwen3-ASR-0.6B-hf --local-dir ./Qwen3-ASR-0.6B-hf
# 或 ModelScope:
# pip install modelscope && modelscope download --model Qwen/Qwen3-ASR-0.6B-hf --local_dir ./Qwen3-ASR-0.6B-hf

# 3) 一段测试音频(16kHz 单声道!没有就用麦克风录 30 秒)
sox my_call.wav -r 16000 -c 1 test16k.wav
为什么必须是 16kHz 第 3 课的奈奎斯特定理在这里落地:模型前端按 16kHz 设计(Fbank 128 维、25ms 窗/10ms 移),其他采样率会被拒绝或静默出错。通话录音是 8k 的,先 sox -r 16000 重采样——这是数据准备的固定动作。

一、路线 A:官方 qwen-asr 包(最快跑通)

官方提供了推理包(底层封装 vLLM/transformers),三条命令跑通:

pip install qwen-asr
from qwen_asr import Qwen3ASRModel

model = Qwen3ASRModel.from_pretrained(
    "./Qwen3-ASR-0.6B-hf",
    dtype="float16",          # ← T4 关键:BF16 权重转 fp16
)

result = model.transcribe(
    "test16k.wav",
    context="词表:幂等、回调、Qwen3-ASR",   # 热词/上下文,自由文本
)
print(result.text)            # 转写文本
print(result.language)        # 检出的语言

跑通后做两个实验,亲眼验证第 6 课讲的机制:

如果报错 加载时 BF16 相关报错 → 确认 dtype="float16" 写了;显存 OOM(几乎不可能)→ 检查是否有其他进程占卡;import 失败 → 按包文档核对依赖版本。

二、路线 B:vLLM serve(生产形态,压测的前置)

生产与压测都用服务形态。单实例学习配置:

vllm serve ./Qwen3-ASR-0.6B-hf \
    --dtype float16 \
    --gpu-memory-utilization 0.5 \
    --port 9000

另开一个终端,用 OpenAI 兼容接口打它:

curl http://localhost:9000/v1/audio/transcriptions \
  -F file=@test16k.wav \
  -F model=Qwen3-ASR-0.6B-hf

启动日志里找 "Available KV cache memory" 一行——那是你的实测 KV 池,和下面第五节的估算对照。这条路线跑通后,压测方案(loadtest-plan)里的负载生成器就能直接打它了——但那是工作场景的事,学习阶段跑通即可。

T4 上的 vLLM:已知毛边 vLLM 官方支持 cc ≥ 7.5(含 T4),但历史上有 T4 专属内核兼容 issue(如 fused_add_rms_norm 的 cudaErrorNoKernelImageForDevice,已修复)。若 vLLM 路线折腾超过 30 分钟仍报内核类错误,先退回路线 A/C 完成学习目标,vLLM 环境问题留到压测时再治——不要卡死在一棵树上。

三、路线 C:transformers(读懂代码,微调课的地基)

import torch
from transformers import AutoProcessor, Qwen3ASRForConditionalGeneration

model_id = "./Qwen3-ASR-0.6B-hf"
processor = AutoProcessor.from_pretrained(model_id)
model = Qwen3ASRForConditionalGeneration.from_pretrained(
    model_id, dtype=torch.float16, device_map="cuda",
)

inputs = processor.apply_transcription_request(
    audio="test16k.wav",
    language="Chinese",
    format="wav",
    return_tensors="pt",
).to("cuda")

out = model.generate(**inputs, max_new_tokens=256)
print(processor.batch_decode(out, skip_special_tokens=True)[0])

# 微调课的前菜:训练时只要一行换掉 generate
# labels 由 processor 自动生成:
# inputs = processor.apply_transcription_request(..., output_labels=True)
# loss = model(**inputs).loss

这条路线的价值:你看到的每个对象都对应前七课的知识——processor 切帧出 mel(第 3/5 课),<|audio_pad|> 位置被音频嵌入替换(第 6 课模板),generate 逐词元自回归(第 7 课第 7 站)。

四、逐键读懂 config.json(对照速查卡)

模型目录里的 config.json,现在每个键你都能读懂了(0.6B 的实际数值):

键值(0.6B)你在哪学过
model_type"qwen3_asr"模型家族标识
audio_config.encoder_layers18(d_model 896,14 头)第 2 课注意力堆叠;1.7B 版是 24 层/1024/16 头
audio_config.output_dim1024= LLM 隐藏维:投影器对齐的目标(第 6 课)
text_config.num_hidden_layers28(hidden 1024,FFN 3072)LLM 解码器深度
text_config.num_attention_heads / num_key_value_heads16 / 8GQA:16 查询头共享 8 组 KV(第 6 课新词)
text_config.vocab_size151,936输出层矩阵的列数(第 7 课第 6 站)
audio_token_id151676<|audio_pad|> 占位符:音频嵌入替换处(第 6 课模板)
timestamp_token_id151705ForcedAligner 的时间戳标记
dtypebfloat16存储精度;T4 上运行时转 float16(本课第一课)

音频前端的 128 维 mel、25ms/10ms 帧参数在 preprocessor_config.json——数值与第 3/5 课的手算完全一致,打开对一遍。

五、显存预估器:你的通话在 T4 上占多少

把推理课的知识变成一个可运行的估算器(参数全部来自真实 config,已验证):

# 0.6B 在 T4 16GB 上的推理显存估算(参数来自 config.json 实测值) LAYERS, KVH, HD, B = 28, 8, 128, 2 # text_config:28 层,GQA 8 KV 头,head_dim 128,bf16/fp16 KV_PER_TOKEN = 2 * LAYERS * KVH * HD * B # 每 token 的 KV 字节数 POOL = 0.9 * 16 - 1.8 - 1.5 # T4:util 0.9 x 16GB - fp16 权重 1.8 - 固定开销 ~1.5(估) dur_s = 60 # 改我:通话时长(秒) chars = 150 # 改我:预计转写字数 audio_tok = 12.5 * dur_s # 音频嵌入 12.5 个/秒(AuT 8 倍下采样) text_tok = int(chars * 1.5) # 转写文本约 1.5 token/字 kv_gb = (audio_tok + text_tok) * KV_PER_TOKEN / 1e9 print(f"KV/token = {KV_PER_TOKEN/1024:.0f} KB | 单实例 KV 池 ≈ {POOL:.1f} GB") print(f"{dur_s}s 通话: 音频嵌入 {audio_tok:.0f} 个 + 文本 ~{text_tok} token") print(f"KV 占用 ≈ {kv_gb:.3f} GB | 显存角度可容并发 ≈ {int(POOL/max(kv_gb,0.01))} 路") print("结论:学习场景单路推理,T4 16GB 绰绰有余")
三个数字的来历,一个都不能含糊 12.5 个/秒(mel 100 帧/秒 ÷ 8 倍下采样,第 6 课)、112KB/token(28 层 × 8 KV 头 × 128 维 × 2 字节 × 2,第 7 课线代)、0.5 倍字数的文本 token(粗估, tokenizer 实际切分略有出入)。这三个数往后微调课还会反复出现。

六、课后实战任务清单(可验证)

  1. □ 三条路线至少跑通一条(推荐 A+C),保存转写输出
  2. □ 用自己的一段真实通话(8k 重采样到 16k)转写,人工检查错误模式:错在哪类词?数字?专有名词?
  3. □ 热词实验:context 加/不加领域词表,对比同一段音频的输出
  4. □ 打开 config.json 与 preprocessor_config.json,把 8 个键的值讲给自己听(对照第四节表格)
  5. □ 运行显存预估器,把 dur_s 改成你最长的一段通话,记下 KV 占用

全部打勾 = 第 5 站通关,下一站进入数据准备 + LoRA 微调——在你的 T4 上,让模型学会你业务里的专有名词。

检索练习

本课主读材料

去读(动手前扫一眼)

QwenLM/Qwen3-ASR 官方仓库 README(安装/推理/serve 全流程)与 0.6B-hf 模型卡(transformers 用法与训练配方)

读法:对照本课三路线的代码,重点确认你装的版本与文档一致;参数名有出入时以 --help 与模型卡为准。

我是你的老师,别客气 任何一步卡住:报错信息原样贴回来(含完整 traceback)、或告诉我你卡在哪条命令。环境问题千奇百怪,但几乎都有人踩过。