想在本机或自有显卡上跑 SparkAudio 1.0,顺序比热情更重要:先在下载完权重后确认文件体积,再按体积粗算显存区间,然后核对驱动与推理框架版本,接着用一段固定长度的短音频实测峰值,最后才去调批大小。把“装完就爆显存”这类问题挡在下载阶段,比装好之后再回头删环境要省事得多。下面每一步都给出可直接执行的命令和判据,环境不同时数值需要自己确认。
本地跑 SparkAudio 1.0 的关键不是先装框架,而是先用文件体积换出显存区间。看目录体积和权重精度,按 fp16 两个字节一参数估出底数,再留出权重之外 2GB 以上的余量给缓存和 workspace;驱动用 nvidia-smi 看 CUDA 上限,框架要求去仓库的依赖文件和安装说明里核对。粗算只是起点,必须用一段固定文本的短音频实测显存峰值,再逐档定批大小。显存不足时先降批大小和音频长度,不要靠频繁换页硬撑。
先确认模型文件与权重的实际体积
下载完成后不要急着加载,先让文件系统告诉你事实。目录总体积决定显存估算的起点,文件后缀决定精度和加载方式:
cd /path/to/SparkAudio
ls -lh
du -sh .
du -h `--max-depth`=1 . | sort -h
find . -type f \( -name "*.safetensors" -o -name "*.bin" -o -name "*.pt" \
-o -name "*.ckpt" -o -name "*.gguf" -o -name "*.onnx" \) -exec ls -lh {} +
重点看三件事。第一是格式:.safetensors 和 .bin 通常是 PyTorch 权重,.gguf 走 llama.cpp 一类量化推理,.onnx 需要 onnxruntime,三者对显存和框架的要求完全不同。第二是精度:翻一下 config.json 里的 torch_dtype(float32 / float16 / bfloat16),以及文件名或配置里是否出现 int8、int4、q4、q8 这类量化标记。第三是组件构成:语音合成模型通常拆成文本前端、语言模型主干或扩散声学模型、声码器、说话人编码器几部分,各自的权重单独成文件。
把每个组件标记为“必须进显存”和“可以留在 CPU”。声码器和说话人编码器往往音短、算力需求集中,放在 CPU 上只损失一部分速度但能省下一块显存;主干模型如果量化过,显存需求会明显低于同参数量的 fp16 版本。这一步做完,你手上应该有一个数字:需要常驻显存的权重总字节数。
按权重体积粗算显存占用区间
换算关系很直接:参数量乘以每参数字节数。fp32 是 4 字节,fp16 和 bf16 是 2 字节,int8 约 1 字节,int4 约 0.5 字节。因此一个 3GB 的 bf16 权重文件大致对应 15 亿参数级别,以 fp16 加载时需要 3GB 左右显存;如果目标环境是 fp32,同一份权重就要翻倍。用 文件体积 × (目标精度字节数 ÷ 文件精度字节数) 做一次转换,就能得到权重常驻部分的估算值。
权重之外还有几块容易被忽略的开销:CUDA context 和运行时本身;声学模型自回归解码时的 KV cache,随生成 token 数增长;声码器一次性处理整段音频,音频越长峰值越高;cuDNN 和算子库的工作区。这些不写在文件里,只能靠留余量。实践中建议在权重估算值之上先留 2GB 到 4GB,如果把声码器和说话人编码器也放 GPU,余量按比例再加。
一个保守但并不激进的起点是:按权重体积的 1.5 到 2 倍规划显存,或者“权重 + 2GB”取较大值。这不是精确值,只是保证第一次跑不因余量不足失败;真实峰值由下一步的实测决定。如果显卡显存低于这个区间,就先考虑量化权重或把部分组件挪到 CPU,而不是强行加载后反复重试。
确认推理框架与设备驱动版本匹配
驱动能支持的 CUDA 上限由 nvidia-smi 给出,注意这里的 CUDA Version 是驱动兼容的最高运行时版本,不是当前安装的 CUDA 工具链版本:
nvidia-smi
nvidia-smi `--query-gpu`=name,driver_version,memory.total `--format`=csv
nvcc `--version` # 若系统装了 CUDA 工具链
框架对版本的要求写在几个固定位置,逐个核对比凭印象装更可靠:仓库根目录的 requirements.txt、pyproject.toml 或 setup.py;安装说明里给出的pip 命令中 torch 的 wheel 后缀(如 cu118、cu121、cu124);如果提供 Dockerfile,看基础镜像的 tag。装上之后立刻验证:
python -c "import torch;print(torch.__version__, torch.version.cuda, torch.cuda.is_available(), torch.cuda.get_device_name(0))"
三个值要一起看:torch 版本、编译时链接的 CUDA 版本、是否真的识别到 GPU。is_available() 返回 False 时,多半是 wheel 与驱动不匹配或驱动过旧,先解决这一步再谈显存。若显卡显存偏小或暂时不想折腾驱动,可以先退到 CPU 跑通流程:把设备参数改成 `--device` cpu 或配置里的 device: cpu,权重用 float32 加载(CPU 上 fp16 支持有限),速度会慢很多,但能验证模型文件完整、代码路径正确。部分实现里设备是写死 .cuda() 的,需要先在推理入口搜一下 device 参数是否存在。
跑一次短音频测端到端耗时与显存峰值
估算之后必须实测,而且测试条件要固定:一句固定文本(例如 20 字左右的中文,明确写死在脚本里),固定参考音频和固定生成上限(如 max_new_tokens 或最大音频时长),否则两次结果没有可比性。
CPU 侧耗时和内存可以用 time 命令看,GPU 侧峰值需要在推理脚本里打点:
import time, torch
torch.cuda.reset_peak_memory_stats()
torch.cuda.synchronize()
t0 = time.perf_counter()
# 此处调用你的推理函数,输入固定文本
out = infer(TEXT)
torch.cuda.synchronize()
print("elapsed_s", round(time.perf_counter() - t0, 3))
print("peak_alloc_GB", round(torch.cuda.max_memory_allocated()/1024**3, 2))
print("peak_reserved_GB", round(torch.cuda.max_memory_reserved()/1024**3, 2))
如果不方便改脚本,另开一个终端采样整卡占用也可以:
nvidia-smi `--query-gpu`=timestamp,memory.used,utilization.gpu \
`--format`=csv -l 1 -f gpu_peak.csv
记录方式建议:同一句文本连跑 3 到 5 次,第一次通常包含加载和算子编译,明显偏慢偏高,取后续稳定值;把加载耗时和单次推理耗时分开记;把 max_memory_reserved 当成“显存实际被占用的上限”,max_memory_allocated 是张量本身的用量,两者差值就是碎片和缓存的余量。实测峰值如果明显高于前面的估算,多半是音频长度或解码步数超预期,先检查生成上限而不是怀疑估算公式。
在显存打满之前定下批大小
最后一步是把能稳定跑的组合固定成配置。批大小从小到大逐档试,每次只改一个变量:
- 批大小:从 1 开始,依次试 2、4、8;很多语音合成实现推理时批大小为 1 更常见,并发靠服务层排队实现。
- 生成长度:
max_new_tokens或最大音频秒数,同样按档递增。 - 精度:bf16 / fp16 / 量化权重,逐项确认实际生效。
- 组件位置:声码器、说话人编码器放 GPU 还是 CPU。
触发显存不足时,把报错原样记下来,不要只写“OOM”三个字。CUDA out of memory. Tried to allocate ... MiB (GPU 0; ... GiB total capacity; ... GiB already allocated; ... GiB reserved ...) 这段里的 Tried to allocate 和 reserved 数值,能直接告诉你差多少、是碎片还是真的不够。把报错时间点和当时的批大小、音频长度一起记进笔记,后面复现才有依据。
确定下来的配置建议写成一份能直接复制的命令,并顺手保存版本快照:
pip freeze > env_snapshot.txt
nvidia-smi > gpu_snapshot.txt
python infer.py `--device` cuda `--dtype` bf16 `--batch-size` 1 \
`--max-new-tokens` 512 `--text` "固定的一句测试文本"
如果环境变量层面需要限制,可以用 PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:128 缓解碎片,或 torch.cuda.set_per_process_memory_fraction 给进程设上限,避免占满整卡影响其他任务。这类设置只是让分配更可控,不改变模型本身需要的显存。真正要提升吞吐时,优先考虑把不常驻的组件移出显存、缩短单次音频长度,或换更大显存的卡。