inflect_micro_v2 / python /README.md
inoryQwQ's picture
三芯片合并:AX620E/AX637 升级 encoder+decoder 全 NPU,新增新一代 SDK;AX650 保持老 SDK
5eee449 verified
|
Raw
History Blame Contribute Delete
5.45 kB

Inflect AX TTS Python SDK

owensong/Inflect-Micro-v2(VITS 系英文 TTS,文本 → 24 kHz 单声道波形)的 Encoder/Decoder 两段式 AXMODEL 推理 SDK,双目标 **AX620E(NPU2)/ AX637(NPU1)**。两套 AXMODEL 接口完全一致(同 shape 契约),SDK 通过模型路径切换目标,代码零改动。

推理流水线

text → [Host: eSpeak 音素化 + symbols 映射 + intersperse(add_blank)]
     → tokens[1,256](int,0 补齐)+ x_lengths[1]
     → encoder.axmodel → m_p/logs_p[1,192,256]、logw[1,1,256]
     → [Host: 取前 x_lengths 帧;w=exp(logw)*length_scale;ceil;generate_path;
        matmul 扩展为 T' 帧;z_p=m_p'+randn(seed)*exp(logs_p')*variation]
     → 按 Tp=512 分块(64 帧重叠 crossfade)→ decoder.axmodel
     → 波形块拼接(尾部裁剪)→ edge fade 5ms + clip → 24kHz mono wav
  • encoder 静态 shape T=256(tokens 以 0 右补齐,x_lengths 填真实长度;单 chunk 上限 255 个 intersperse 后 token,超长文本自动二次拆分)。
  • decoder 静态 shape Tp=512(~5.46 s/块),Host 侧分块 + 64 帧重叠 crossfade,最后一块尾部裁剪(EXPORT_NOTES §5.3)。
  • 噪声注入在 Host 侧:np.random.default_rng(seed)seed 在 SDK 内可复现,但与 PyTorch 参考(torch MT19937)不逐位一致。

模型文件位置

目标 encoder decoder
AX620E models/ax620e/encoder.axmodel models/ax620e/decoder.axmodel
AX637 models/ax637/encoder.axmodel models/ax637/decoder.axmodel(权重 S16 升级版)

(路径相对于交付包根目录 package/;AX637 decoder 为权重 S16 升级版,wav cos 0.9995,详见 ../reports/accuracy_summary.md。)

环境与安装

板端 / 主机(有 NPU 运行时):

pip install -r requirements.txt   # numpy, phonemizer, espeakng-loader, num2words, pyaxengine
  • eSpeak-ng:优先使用系统库(/usr/lib/x86_64-linux-gnu/libespeak-ng.so.1 或 aarch64 对应路径,Debian/Ubuntu:apt install libespeak-ng1);无系统库时 espeakng-loader pip 包自动提供。仅真实文本合成需要;--demo-tokenssynthesize_tokens() 不依赖 eSpeak。
  • 本机(Host)无数值 NPU 时的验证替身:pip install onnxruntime,把 ../model_convert/export/encoder.onnx / ../model_convert/export/decoder.onnx 作为模型路径传入即可(backend 自动切换)。

API

from inflect_ax_tts import InflectTTS

tts = InflectTTS(
    "../models/ax620e/encoder.axmodel",
    "../models/ax620e/decoder.axmodel",
)  # backend="auto": .axmodel -> pyaxengine, .onnx -> onnxruntime

# 文本合成(需要 eSpeak)
sr, wav = tts.synthesize("The quick brown fox.", speed=1.0, variation=0.667, seed=0)
tts.save("The quick brown fox.", "out.wav")

# 免前端:直接给音素 id(未 intersperse,SDK 内部加 blank)
sr, wav = tts.synthesize_tokens([81, 83, 16, 53, 65, 102, 53], seed=0)
  • speed ∈ [0.5, 2.0](length_scale = 1/speed);variation ∈ [0.0, 1.0](噪声幅度,基线 0.667);seed 整数,同参可复现。
  • 返回 (24000, float32 ndarray),幅值 [-1, 1];write_wav() 写 16-bit PCM。

示例

cd package/python

# 板端真实文本(AX620E;AX637 换路径即可)
python example.py \
  --encoder ../models/ax620e/encoder.axmodel \
  --decoder ../models/ax620e/decoder.axmodel \
  --text "The quick brown fox jumps over the lazy dog." \
  --output out.wav

# Host 干跑:无 NPU、无 eSpeak,onnxruntime 替身 + 确定性 dummy tokens
python example.py --demo-tokens \
  --encoder ../model_convert/export/encoder.onnx \
  --decoder ../model_convert/export/decoder.onnx \
  --output demo.wav

Host 前后处理说明

  • 前端(仅 synthesize() 文本路径):normalize_text(数字/缩写/日期/货币展开,num2words)→ eSpeak en-us 音素化(preserve_punctuation + with_stress)→ 音素覆盖表(2 条)→ 逐字符 symbols 映射 → intersperse(0)。与 origin/inference.py 完全一致。
  • 时长/对齐(纯 numpy 整数逻辑):w=exp(logw)*x_mask/speedceilT'=max(sum,1)generate_path(cumsum 区间)→ attn @ m_p/logs_p 扩展。
  • 噪声:z_p = m_p' + randn(seed) * exp(logs_p') * variation
  • decoder 分块:Tp=512、64 帧重叠线性 crossfade;T'<=512 单块路径即 SIMULATE 验证路径。
  • 后处理:每块 edge fade 5 ms、句间停顿(按上一句末标点 0.08~0.28 s)、最终 clip [-1,1]。

测试(Host,onnxruntime 替身)

cd package/python
python tests/test_host_chain.py        # 或 pytest tests/

覆盖:Host 链(expand_priors)与 PyTorch 参考逐位级一致性、encoder padding 语义、decoder 分块合成与 fp32 参考链路数值一致(ORT 层面)、端到端确定性、前端冒烟(espeak 可用时)。

已知差异 / 限制

  • seed 语义与 PyTorch 参考不逐位一致(RNG 不同),仅保证 SDK 内可复现。
  • encoder 输入 dtype:AXMODEL 运行时为 int32(input_processors S64 已折叠进模型),ONNX 为 int64;SDK 按 session 元数据自适应。
  • decoder 多块(T'>512)crossfade 路径未做数值门禁验证(仿真仅覆盖单块路径)。
  • 精度结论(仿真,详见 ../reports/accuracy_summary.md):encoder 双目标 cos ≥ 0.9999;AX637 decoder(权重 S16 升级版)wav cos 0.9995 [PASS];AX620E decoder(基线)wav cos ≈ 0.95,未达 0.99 门禁(权重 S8 量化导致的高频细节损失,本工具链版本不可修复),属量化方案限制,非 SDK 问题。