Skip to content

ONNX 优化 / 量化

原版 Style-Bert-VITS2 基于 PyTorch,仅用 CPU 难以实时推理。

HayaKoe 将 BERT 导出为 Q8 量化 ONNX,Synthesizer 导出为 FP32 ONNX 并在 ONNX Runtime 上运行,将 CPU 推理速度 按文本长度提升了 1.5x ~ 3.3x

同时将 1 说话人加载时的 RAM 使用量从 5,122 MB 降至 2,346 MB (-54%)

得益于同一路径,不仅 x86_64,aarch64 (Raspberry Pi 等) 也能用相同代码运行

不足之处

原版 SBV2 (CPU, PyTorch FP32) 有两个不足。

  • 速度 — 文本越长推理时间指数级增长。short (1.7 s 音频) 倍速 1.52x,而 xlong (38.5 s 音频) 推理 35.3 秒·倍速 1.09x,勉强跟上实时。
  • 内存 — 单说话人推理 Peak 内存约 5 GB 以上,负担较重。

分析

先看模型参数分布,全部参数中约 84% 集中在 BERT (DeBERTa-v2-Large-Japanese, 约 329 M),Synthesizer (VITS) 为 63 M 约 16%。

BERT 占模型大部分,因此量化 BERT 预计可以大幅减少内存。在 PyTorch 中仅对 BERT 应用 Q8 Dynamic Quantization (torch.quantization.quantize_dynamic) 进行验证。

配置平均推理时间RAM
PyTorch BERT FP324.796 s+1,698 MB
PyTorch BERT Q84.536 s+368 MB (-78%)

确认 BERT 量化虽不改善速度,但能大幅减少内存使用。

在此基础上通过 转到 ONNX Runtime 进一步确保速度改善。

ONNX Runtime 在加载模型时自动应用 图级优化

  • Kernel fusion — 将连续多个运算合并为一个。例如将 Conv → BatchNorm → Activation 三步合为一步,消除中间结果的内存写入和再读取,减少内存访问从而加速。
  • Constant folding — 将不论输入如何始终产生相同值的运算在加载时预先计算好,推理时使用预计算值加速。
  • 删除不必要节点 — 找出未使用、重复或执行无意义运算的节点并删除。

总之,重构为数学上等价但推理更优化的运算。

Synthesizer 参数量仅 63 M,量化的内存收益有限,且 Flow 层(rational_quadratic_spline)在 FP16 以下数值不稳定,因此排除在量化对象之外。仅导出为 ONNX 以获取图优化收益。

BERT 优化

为确认量化是否影响音质,在同一文本·同一说话人下比较了 FP32 · Q8 · Q4 三种 BERT 配置的输出(Synthesizer 均为 FP32 固定)。

旅の途中で不思議な街に辿り着きました。少し寄り道していきましょう。

(旅途中来到了一个神奇的小镇。稍微绕道走走吧。)

つくよみちゃんBERT dtype = FP32原版 baseline
0:00 / 0:00

FP32 和 Q8 在直接听辨时难以一致地区分。

Q4 在大部分区间与 FP32 · Q8 相似,但在尾部可以听到微小差异。

配置BERT 大小RAM (1 说话人)
FP321,157 MB1,599 MB
Q8497 MB1,079 MB (-33%)
Q4394 MB958 MB (-40%)

判断进一步 Q4 量化获得的内存收益不足以弥补音质差异,决定 默认使用 Q8

Synthesizer 优化

BERT 占参数的 84%,按理快速化 BERT 就能加速整体。

但实际分别测量 BERT 和 Synthesizer 的推理时间后发现,CPU 时间的大部分消耗在 Synthesizer 侧

PyTorch FP32 CPU 下的实测结果(5 次平均)。

文本BERTSynthesizerBERT 占比Synth 占比
short (1.7 s)0.489 s0.885 s36%64%
medium (5.3 s)0.602 s2.504 s19%81%
long (7.8 s)0.690 s3.714 s16%84%
xlong (30 s)1.074 s11.410 s9%91%

文本越长 Synthesizer 占比越大,因为 BERT 对文本长度相对不敏感,而 Synthesizer 时间与要生成的音频长度成正比增长。

实际仅量化 BERT Q8 时整体推理时间仅减少约 5%。

也就是说,要提速必须优化 Synthesizer 部分

Synthesizer 采用 仅 ONNX 转换 而非量化。

  • VITS 的 Flow 层(rational_quadratic_spline)在 FP16 以下因浮点误差导致 assertion error,无法量化。
  • 参数量仅 63 M,量化的内存收益也有限。

通过转换到 ONNX Runtime,将前述图级优化(kernel fusion · constant folding · 删除不必要节点)同样应用到 Synthesizer。

ONNX Runtime + CPUExecutionProvider

BERT 量化和 Synthesizer 图优化都在 ONNX Runtime 上运行。

此外通过 intra-op parallelism 将单个运算分散到多个 CPU 核心,即使只有一个请求也能利用全部 CPU。

改善效果

CPU 性能对比(倍速,同一硬件)

倍速 = 音频时长 / 推理时间(值越大越快)。

配置short (1.7 s)medium (7.6 s)long (10.7 s)xlong (38.5 s)
SBV2 PyTorch FP321.52x2.27x2.16x1.09x
SBV2 ONNX FP321.76x3.09x3.26x2.75x
HayaKoe (Q8 BERT + FP32 ONNX)2.50x3.35x3.33x3.60x

相比 PyTorch FP32 的速度提升为 按文本长度 1.5x ~ 3.3x

内存(1 说话人加载基准)

配置RAM
SBV2 PyTorch FP325,122 MB
SBV2 ONNX FP322,967 MB
HayaKoe (Q8 BERT + FP32 ONNX)2,346 MB (-54%)