ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

EasyMagpieTTS 基于 vLLM-Omni 的两阶段流式 TTS 推理指南

EasyMagpieTTS 基于 vLLM-Omni 的两阶段流式 TTS 推理指南 EasyMagpieTTS 基于 vLLM-Omni 的两阶段流式 TTS 推理指南【免费下载链接】SpeechA scalable generative AI framework built for researchers and developers working on Large Language Models, Multimodal, and Speech AI (Automatic Speech Recognition and Text-to-Speech)项目地址: https://gitcode.com/GitHub_Trending/nem/Speech本文档面向需要在 NeMo Speech 中把 EasyMagpieTTSNemotron-H 骨干 逐码本局部 Transformer 25 fps 谱编码器部署为高吞吐流式语音服务的开发者。文章围绕仓库内 tools/easymagpie_vllm_omni/README.md 展开完整覆盖 NeMo 检查点转换、vLLM-Omni 0.24 服务环境搭建、离线与流式合成、请求时零样本音色克隆、HTTP/WebSocket 服务接入与基准测试并辅以源码级原理讲解。读完你将掌握一条从.nemo权重到可对外服务的端到端推理链路。架构总览两阶段推理流水线EasyMagpieTTS 在 vLLM-Omni 中被拆解为两个可独立部署的阶段整体采用文本 → 堆叠声学码 → 波形的管线阶段职责Stage 0 — EasyMagpie LMEasyMagpie_LM_BackboneNemotron-HEasyMagpie_LM_LT局部 Transformer→ 输出堆叠声学码stacked acoustic codesStage 1 — SpectralCodec-BWE-22kHz有状态的stateful原生 vLLM codec → 解码为 22.05 kHz 波形模型定义与流水线注册位于 tools/easymagpie_vllm_omni/easymagpie_vllm_omni/ 与 tools/easymagpie_vllm_omni/vllm_plugin_easymagpie_omni/部署参数集中在 tools/easymagpie_vllm_omni/deploy/easymagpie.yaml。从源码看两阶段的编排在 pipeline.py 中定义为EASYMAGPIE_PIPELINEStage 0 以model_stageeasymagpie运行 LLM 自回归解码把声学码作为 latent 输出Stage 1 以model_archEasyMagpieCodecForConditionalGeneration、model_subdircodec_native消费这些码并产出波形。由于 EasyMagpie LM 上报的model_type是通用的nemotron_h部署时必须通过pipeline: easymagpie显式指定路由。此外仓库还提供了EASYMAGPIE_LM_PIPELINE对应pipeline: easymagpie_lm它是去掉 codec 的单阶段拓扑用于单独评测 LM 的声学码预测吞吐benchmark_model.py 默认使用该配置。部署配置中的关键字段deploy/easymagpie.yaml 的要点如下pipeline: easymagpie必须显式指定否则路由器无法识别nemotron_h报告类型。async_chunk: true两个阶段间需要流式码传输必须开启。dtypeStage 0 默认float16Stage 1 默认float32——配置注释明确说明 FP16 更快但会引入可感知的 codec 伪影。两阶段通过connectors中的SharedMemoryConnector通信其中codec_chunk_frames: 8表示原生 vLLM 状态页保留解码器的完整因果历史codec_startup_chunk_frames: [6, 6]提供稳态流式开始前的两个启动块作为播放余量。Stage 0 参数max_num_seqs: 32、gpu_memory_utilization: 0.5、enable_chunked_prefill: true、max_num_batched_tokens: 4096、max_model_len: 4096并挂载自定义 workereasymagpie_vllm_omni.runner.EasyMagpieGPUARWorkermamba_ssm_cache_dtype: float32用于 Mamba 选择性状态更新缓存。Stage 1 参数gpu_memory_utilization: 0.35、enforce_eager: true、enable_chunked_prefill: false——codec 块是原子的绝不能在一步中拆分某个请求的状态更新max_num_batched_tokens: 512、max_model_len: 512。注意引擎设置必须扁平地放在 stage 层级嵌套设置不会被解析。第一步将 NeMo 检查点转换为 vLLM 模型目录转换脚本 scripts/convert_to_vllm.py 会把 EasyMagpie LM 转换为自包含的 vLLM-Omni 模型目录同时预计算文本嵌入查找表、保存分词器与可选的命名说话人嵌入。Stage-1 codec 解码器总是被打包而 codec 编码器与参考说话人 Transformer默认不复制因此默认产物只接受已知的speaker_id。转换必须在NeMo 环境中、从仓库根目录执行。当环境里装有指向其他 checkouts 的可编辑 NeMo 安装时把仓库根目录前置到PYTHONPATH很重要PYTHONPATH$PWD python tools/easymagpie_vllm_omni/scripts/convert_to_vllm.py \ --nemo_file /path/to/emptts.nemo \ --codec_model_path /path/to/25fps_spectral_codec.nemo \ --phoneme_tokenizer_path /path/to/bpe_ipa_tokenizer.json \ --outdir tools/easymagpie_vllm_omni/converted_model \ --context_audio /path/to/reference_voice.wav \ --speaker_name eng常用参数说明来自脚本的parse_args参数默认值说明--nemo_file必填EasyMagpieTTS.nemo检查点路径--codec_model_path必填25 fps 谱编码器.nemo路径--outdir必填输出的 vLLM 模型目录--phoneme_tokenizer_path检查点内置覆盖 IPA BPE 音素分词器路径--disable_cas_for_context_text关针对未用 CAS 嵌入训练 context text 的旧检查点--text_tokenizer检查点内置要导出的 HF 文本分词器名称/路径--context_audio/--speaker_name— /default预计算命名说话人嵌入保存为speaker_embeddings/name.pt--bundle-audio-encoders关显式选择打包 codec 编码器与参考说话人 Transformer--max-audio-seconds30.0每条原始参考/用户音频的最大时长超长直接拒绝--context_audio_duration5.0参考音频裁剪时长--dtypefloat32可选bfloat16/float16/float32bf16 匹配参考推理设置--precompute_batch_size1024预计算逐子词文本嵌入的批大小转换产物包含见 convert_to_vllm.py 的 docstringconfig.json—— 扁平 HF 风格配置Nemotron-H 骨干字段 EasyMagpieOmniArch读取的 EasyMagpie 标量model.safetensors index—— 按 vLLMload_weights期望的键布局decoder.*骨干 顶层 TTS 子模块转换的权重文本条件分词器AutoTokenizer.save_pretrained导出用于引擎内按请求 tokenizecontext_textspeaker_embeddings/name.pt可选—— 预计算的说话人编码推理时按speaker_id选择codec_native/—— 转换为有状态 vLLM 模型的因果 codec 解码器codec_encoder.safetensorscodec_encoder.json仅--bundle-audio-encoders时—— 零样本音色克隆与多轮用户历史所需的 codec 编码器与参考说话人 Transformer。原理细节转换时把字符感知子词CAS编码器坍缩为一张subword_id - embedding的预计算查找表CAS 对每个 subword id 完全确定因此在转换期烘焙一次、引擎内不再运行decoder中未被使用的 token 嵌入表被替换为一个宽度为 2 的哑表——模型始终通过inputs_embeds喂入但 vLLM profiling 的_dummy_sampler_run会设置top_k vocab_size - 1并回采索引宽度为 1 会导致越界断言故必须 ≥ 2。此外脚本会校验骨干必须是decoder_typenemotron_h、hidden_dim embedding_dim、nemotron_h_config.hidden_size embedding_dim骨干直接消费inputs_embeds无输入投影、local_transformer_type为ar/autoregressive、默认推理模式为text_input_modestreaming。codec 部分仅支持 GroupFiniteScalarQuantizerFSQ布局且各 FSQ 组需共享同一num_levels_per_group。第二步搭建服务环境服务阶段需要一块 GPU、与vLLM 0.24 / vLLM-Omni 0.24严格匹配的版本以及本包。转换完成后不再需要 NeMocd tools/easymagpie_vllm_omni conda create -n easymagpie-vllm python3.12 -y conda activate easymagpie-vllm pip install -r requirements.txt pip install -e . # 可选为 notebook 安装内核 pip install ipykernel python -m ipykernel install --user \ --name easymagpie-vllm \ --display-name Python (easymagpie-vllm)版本约束写死在 requirements.txt 中vllm0.24.0与vllm-omni0.24.0必须保持相同主次版本此外依赖numpy1.26、PyYAML6.0、safetensors0.8.0、soundfile0.13.1、tokenizers、transformers5.5.3。Mamba 内核缓存提示Mamba 的选择性状态更新内核需要按形状和 GPU 做针对性调优未调优的缓存可能给出次优性能。建议跨多次启动复用同一套 Triton/vLLM 缓存目录让重复运行累积更好的内核如需显式扫描运行python scripts/tune_mamba_ssu.py --model converted_model后重启服务。快速开始离线合成离线合成示例见教程 offline_demo.ipynb其中演示了AsyncOmni的初始化与使用方式先创建单阶段或两阶段的异步引擎再提交 prompt 拿到合成结果。请求时参考音频与用户音频音频标记机制每个源 EasyMagpie NeMo 检查点都包含 codec 编码器与参考说话人 Transformer但默认从转换产物中剔除只有--bundle-audio-encoders才会把它们与总是打包的Stage-1 codec 解码器一起捆绑。没有这个显式选择请求只能按speaker_id选择预计算嵌入。prompt_token_ids中的arch.audio_input_token_id是一个音频标记。每个标记按顺序与multi_modal_data[audio]中的一项配对处理器会拒绝标记/条目数量不匹配的请求。非末尾的标记选择参考条件reference conditioning末尾标记选择用户历史user history。每一项会被编码成两种表示各一次由标记决定插入哪种表示。每一项以(waveform, sample_rate)形式传入必须是单声道且采样率等于arch.codec_input_sample_rate——服务路径刻意不做降混或重采样。每条原始音频限制为arch.max_audio_seconds默认 30 秒超长输入会被拒绝而非截断或分块可通过转换时的--max-audio-seconds修改启动 profiling 上限。第一轮同时带原始参考音频与用户语音时把参考标记放在上下文行之前、用户标记放在最后prompt { prompt_token_ids: ( [0] * task_rows [arch.audio_input_token_id] [0] * len(context_token_ids) [arch.audio_input_token_id] ), multi_modal_data: { audio: [ (reference_waveform, reference_sample_rate), (user_waveform, user_sample_rate), ], }, additional_information: { context_text: [EN], text: response_text, text_prefill_num: arch.text_prefill_num, temperature: 0.7, top_k: 80, reset_codec_on_segment: True, }, }标记放置规则非末尾标记 参考条件末尾标记 用户历史。仅参考合成把末尾用户标记替换为arch.text_prefill_num个零行并提供prefill_text_tokens。已知音色设置speaker_id此时唯一的末尾标记即用户历史。同一 Stage-0 请求的后续轮次只提交末尾标记与新用户波形即可——模型保留先前轮次的原始参考条件。用同一个异步输入生成器以StreamingInput逐轮产出可以保持 Stage-0 的因果/Mamba 状态而一次新的omni.generate(...)调用会开启新请求必须重新提供speaker_id或原始参考条件。每轮回复都应设置reset_codec_on_segmentTrue让 Stage 1 冲刷并重置其响应局部的 codec 状态。多轮前置条件的限制用户历史 prefill 额外要求检查点以use_multiturn_dataset、condition_on_user_speech与用户说话标记训练而仅参考的原始音频不需要这些多轮特性。EasyMagpieOmniArch中相应的校验逻辑config.py会抛出明确错误例如condition_on_user_speech requires use_multiturn_datasetTrue、use_user_speaking_end_token requires use_user_speaking_tokenTrue。另外注意两种服务端点能力的差异OpenAI 兼容的 speech 端点接受请求时参考音频用于零样本 TTS增量式 WebSocket 端点目前只接受已知voice的文本输入——多轮原始用户音频历史请走直接AsyncOmni路径。通过 HTTP 与 WebSocket 提供服务bash ./scripts/run_server.sh ./converted_model 8091脚本 run_server.sh 实际执行的是带 EasyMagpie 插件的vllm serve端口默认 8091可用第二个参数覆盖核心命令为VLLM_PLUGINSeasymagpie_omni vllm serve $MODEL \ --deploy-config $DEPLOY_CONFIG \ --host 0.0.0.0 --port $PORT \ --trust-remote-code --disable-log-stats \ --disable-uvicorn-access-log --uvicorn-log-level warning \ --omni两个服务 APIPOST /v1/audio/speech完整文本输入 已知voice或一条请求时ref_audio。WS /v1/audio/speech/stream增量文本/token 更新 异步 PCM 音频输出。音素文本输入发音控制以enable_phoneme_text_inputtrue转换的检查点接受内联 IPA 片段例如Turn boplɛfteop here。标记只是语法层面的普通片段用导出的文本分词器片段内容用打包的 IPA 分词器与检查点预留的文本 token 区间。延迟流式delayed-stream检查点对于延迟流检查点适配器会把已知的文本主导位置折叠进因果 prefill。当前phoneme_delay3、speech_delay5的模型因此 prefill 四个目标位置纯文本位置 0–2以及携带已知音素 BOS 输入的位置 3。整段文本的 HTTP 请求会自动满足这一点增量式 WebSocket 输入会缓冲初始更新直到至少有phoneme_delay 1个 token 可用。标记字符串与 IPA 片段可以跨input.text消息未闭合的 IPA 片段会在input.done时被拒绝input.tokens仍是精确 token 化绕过通道仅在没有未完成的文本标记或 IPA 片段时被接受。text_prefill_num的计算逻辑见 config.py 的text_prefill_num属性音素流启用且streaming_speech_delay streaming_phonemes_delay时返回streaming_phonemes_delay 1否则为 0锁步/全模式。用 OpenAI 兼容客户端查询 HTTP 端点curl -X POST http://localhost:8091/v1/audio/speech \ -H Content-Type: application/json \ -d {input:This is a TTS service test.,voice:eng,response_format:wav,stream:true,stream_format:audio} \ --output out.wav两种服务 API 的完整示例见教程 server_request.ipynb。零样本 TTS请求时参考音频零样本 TTS 时省略voice改为发送ref_audio——可以是 HTTP(S) URL、base64 data URL 或file://URI。本地文件必须位于服务器的--allowed-local-media-path之下。EasyMagpie不需要ref_text。音频由 HTTP 媒体加载器降混为单声道但不重采样其采样率必须等于转换模型中的codec_input_sample_rate。前提是模型必须用--bundle-audio-encoders转换过。curl -X POST http://localhost:8091/v1/audio/speech \ -H Content-Type: application/json \ -d { input:This voice is conditioned from request-time reference audio., ref_audio:file:///absolute/path/to/reference.wav, response_format:wav, stream:true, stream_format:audio } \ --output out_zero_shot.wav服务端实现位于 serving_adapter.pyEasyMagpieTTSAdapter会校验voice与ref_audio互斥、参考音频格式、以及产物是否已捆绑音频编码器ensure_reference_audio_available未捆绑时提示reconvert with --bundle-audio-encoders然后构造带audio_input_token_id标记与multi_modal_data[audio]的 prompt其中context_text默认[EN]、temperature默认 0.7、top_k默认 80、默认说话人为eng。基准测试仓库在 scripts/ 提供四类基准脚本# 仅评测声学 token 预测不含 codec单阶段 easymagpie_lm 拓扑 python scripts/benchmark_model.py --model ./converted_model -n 128 -c 1 32 \ [--streaming --tokens-per-chunk 5] # 用已知说话人评测服务的 HTTP API python scripts/benchmark_server.py --text-file vctk_subset.txt -n 128 -c 1 32 \ --speaker-id eng # 带一条请求时参考音频的零样本合成评测 python scripts/benchmark_server.py --text-file vctk_subset.txt -n 128 -c 1 32 \ --reference-audio /path/to/reference.wav # 每条请求都扰动参考音频绕过音频缓存 python scripts/benchmark_server.py --text-file vctk_subset.txt -n 128 -c 1 32 \ --reference-audio /path/to/reference.wav \ --randomize-reference-audio # 通过 WebSocket API 评测服务的增量合成 python scripts/benchmark_incremental_server.py --model ./converted_model \ --text-file vctk_subset.txt --tokens-per-chunk 5 -n 128 -c 1 32-c 1 32表示并发数集合-n 128为请求数。从 benchmark_model.py 的 docstring 看--streaming切换两种输入模式默认整段文本一次性提交流式模式下按--tokens-per-chunk个 subword id 分批推送先 prefill 块再每个 chunk 一条StreamingInput消息携带max_tokens len(chunk)让引擎一次自由运行相应帧数最后跟一段自由运行的声学尾巴。脚本报告吞吐、TTFT、ITL均值 p95、EOS 命中率与整体 RTF按 codec 帧率估算而非解码后音频。总结与后续阅读本文覆盖了 EasyMagpieTTS 在 vLLM-Omni 上从权重转换、环境搭建、离线/流式合成到对外服务与基准测试的完整链路。核心要点可归纳为两阶段拓扑Stage 0 自回归预测堆叠声学码Stage 1 有状态 codec 解码出 22.05 kHz 波形二者通过共享内存连接器流式衔接转换期决定能力边界--bundle-audio-encoders是启用请求时原始音频零样本音色克隆与多轮用户历史的唯一开关默认产物仅支持已知speaker_id音频标记配对audio_input_token_id与multi_modal_data[audio]按顺序一一配对非末尾标记 参考条件、末尾标记 用户历史原始音频必须为单声道且采样率匹配codec_input_sample_rate双服务端点HTTP/v1/audio/speech支持完整文本 voice或ref_audio零样本WebSocket/v1/audio/speech/stream支持增量文本与 PCM 流式输出。如需继续深入建议依次阅读离线教程 offline_demo.ipynb、服务端教程 server_request.ipynb、架构配置 config.py、流水线编排 pipeline.py 与服务适配器 serving_adapter.py。【免费下载链接】SpeechA scalable generative AI framework built for researchers and developers working on Large Language Models, Multimodal, and Speech AI (Automatic Speech Recognition and Text-to-Speech)项目地址: https://gitcode.com/GitHub_Trending/nem/Speech创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表