unit2speech:从离散语音单元到语音的 Tacotron2 + WaveGlow 合成流水线详解)
unilm edgeLMGSLMunit2speech从离散语音单元到语音的 Tacotron2 WaveGlow 合成流水线详解【免费下载链接】unilmLarge-scale Self-supervised Pre-training Across Tasks, Languages, and Modalities项目地址: https://gitcode.com/GitHub_Trending/un/unilm在 edgeLM 的生成式口语语言建模GSLM, Generative Spoken Language Modeling系统中unit2speech 是“语音→单元speech2unit→单元语言模型ulm→语音unit2speech”三组件闭环的最后一个环节它把离散语音单元speech units序列重新合成为可听的语音波形。读完本文你能掌握 unit2speech 推理的完整命令与参数含义、量化单元文件quantized units的格式与来源、Tacotron2 解码器与 WaveGlow 声码器的协作方式以及 FP16 推理、门控终止gate与去偏噪Denoiser等底层实现细节。一、unit2speech 是什么改造版 Tacotron2根据 unit2speech 的 READMEUnit to Speech 模型是一个改造过的 Tacotron2Tacotron2 model that learns to synthesize speech from discrete speech units。它与传统文本转语音TTS的区别在于输入不再是文本字符序列而是上游声学模型Log Mel Filterbank、Modified CPC、HuBERT Base、wav2vec 2.0 Large K-means 量化得到的离散语音单元序列。所有模型均在量化后的 LJSpeech单讲者英语语音数据集22.05 kHz上训练。从 GSLM 总览 README 可以看到unit2speech 与 speech2unit、ulm 共同构成完整系统speech2unit 负责把原始语音量化为离散单元ulm 在单元上做生成式语言建模unit2speech 负责把单元序列还原为语音。因此一条完整的数据流是原始语音 ──(speech2unit)── 离散单元序列 ──(ulm 采样/延续)── 单元序列 ──(unit2speech)── mel 谱 ──(WaveGlow)── 波形官方发布的模型矩阵README 给出了 16 个预训练 checkpoint 的完整矩阵4 种上游特征 × 4 种 K-means 聚类数KM50/100/200/500上游单元特征Upstream Units聚类数选项Log Mel FilterbankKM50 / KM100 / KM200 / KM500Modified CPCKM50 / KM100 / KM200 / KM500HuBERT BaseKM50 / KM100 / KM200 / KM500wav2vec 2.0 LargeKM50 / KM100 / KM200 / KM500每个 checkpoint 文件命名为tts_checkpoint_best.pt存放于对应“上游特征/kmN”子目录下。关键约束README 中特别强调待合成的量化音频必须使用与 unit2speech 模型训练时相同的单元系统即“Log Mel KM100”训练的模型只能吃“Log Mel KM100”的量化单元文件特征类型与聚类数都必须匹配。二、推理前的准备依赖、声码器与单元文件2.1 安装依赖README 给出的安装命令pip install librosa unidecode inflect其中 librosa 用于音频读取/重采样unidecode 与 inflect 是 Tacotron2 文本前端text cleaner的常规依赖——虽然 unit2speech 的输入是单元而非文本但 TacotronInputDataset 仍保留了text_or_code text时走文本清洗的路径因此这三个包需要一并安装。2.2 下载 WaveGlow 声码器除了 unit2speech 模型外还必须下载 WaveGlow checkpoint256 channels 版本它是把 mel 谱还原为波形的声码器vocoder。从源码看加载时读取的是 checkpoint 字典中的model键见 utils.py。2.3 量化单元文件从哪里来--quantized_unit_path指向的量化单元文件不是凭空产生的它由 speech2unit 组件生成。quantize_with_kmeans.py 的写出逻辑明确了文件格式fout.write(f{base_fname}|{pred_str}\n)即每行一条语音文件名 | 单元1 单元2 单元3 ...空格分隔的整数单元 id。这与 unit2speech 侧的解析函数 load_quantized_audio_from_file 严格对应按|拆分行右侧按空格拆分为 int 列表。如果你自己生成单元文件必须遵守这一格式否则推理脚本会直接解析失败。三、推理命令逐行解析README 给出的完整推理命令FAIRSEQ_ROOTpath_to_your_fairseq_repo_root TTS_MODEL_PATHunit2speech_model_file_path QUANTIZED_UNIT_PATHquantized_audio_file_path OUT_DIRdir_to_dump_synthesized_audio_files WAVEGLOW_PATHpath_where_you_have_downloaded_waveglow_checkpoint PYTHONPATH${FAIRSEQ_ROOT}:${FAIRSEQ_ROOT}/examples/textless_nlp/gslm/unit2speech python ${FAIRSEQ_ROOT}/examples/textless_nlp/gslm/unit2speech/synthesize_audio_from_units.py \ --tts_model_path $TTS_MODEL_PATH \ --quantized_unit_path $QUANTIZED_UNIT_PATH \ --out_audio_dir $OUT_DIR \ --waveglow_path $WAVEGLOW_PATH \ --max_decoder_steps 2000各变量的含义变量说明FAIRSEQ_ROOT本仓库根目录unit2speech 代码位于examples/textless_nlp/gslm/unit2speech/下属于 edgeLM 随附的 fairseq 示例TTS_MODEL_PATHunit2speech 的 Tacotron2 checkpointtts_checkpoint_best.ptQUANTIZED_UNIT_PATH文件名\|单元序列格式的量化单元文件OUT_DIR合成 wav 的输出目录WAVEGLOW_PATHWaveGlow 声码器 checkpoint需要注意PYTHONPATH同时加入了仓库根目录和 unit2speech 目录本身——因为源码内部使用from examples.textless_nlp.gslm.unit2speech.xxx import ...这种从 fairseq 仓库根出发的绝对式导入见 synthesize_audio_from_units.py只有把FAIRSEQ_ROOT放进PYTHONPATH才能解析。3.1 命令行参数含 README 未列出的隐藏参数对照 synthesize_audio_from_units.py 的参数解析器完整参数集如下参数类型/默认值说明--quantized_unit_path必填量化单元文件路径名\|单元…格式--tts_model_path必填unit2speech Tacotron2 模型文件路径--waveglow_path必填WaveGlow 声码器 checkpoint 路径--out_audio_dir必填输出 wav 目录输出文件名为{原文件名}.wav--max_decoder_stepsint默认 2000解码器最大步数上限防止门控不触发时无限解码--denoiser_strengthfloat默认 0.1WaveGlow 去偏噪强度README 命令未显式给出可用默认值主流程main函数L59-L89依次做四件事load_quantized_audio_from_file读取所有语音的名称与单元序列load_tacotron加载 TTS 模型并把max_decoder_steps写回 hparamsload_waveglow加载声码器与 Denoiser对每条语音把单元整数序列拼成空格分隔的字符串 →TacotronInputDataset.get_tensor转成模型输入张量 →synthesize_audio合成 →sf.write以 checkpoint 中记录的采样率写 wav。四、源码级原理从单元序列到波形4.1 单元序列如何进入 Tacotron2TacotronInputDataset 根据 checkpoint 中的hparams.text_or_code判断输入类型。对 unit2speech 模型该值为非 text于是走process_code单元字符串按空格切分为 token 列表按hparams.add_sos/hparams.add_eos在首尾插入s//s特殊 token定义于 text.py通过load_code_dictutils.py加载的单元词表把每个单元映射为整数 id。词表第一行固定为_padSOS/EOS 追加在末尾若hparams.collapse_code为真code_to_sequence 会做 run-length 折叠——连续相同单元只保留一次。这对应了“连续帧常落入同一聚类中心”的压缩策略同时把序列长度压短、降低解码步数。这些行为全部由 checkpoint 内嵌的 hparams 决定因此同一套推理代码可以兼容不同训练配置的模型这也是 README 强调“单元系统必须与训练一致”的根本原因code_dict词表与 K-means 中心数必须一致否则单元 id 将指代错误的声学概念。4.2 模型加载FP16 必须 GPUload_tacotron 的关键细节ckpt_dict torch.load(tacotron_model_path) hparams ckpt_dict[hparams] hparams.max_decoder_steps max_decoder_steps sr hparams.sampling_rate model Tacotron2(hparams) model.load_state_dict(ckpt_dict[model_dict]) model model.cuda().eval().half()三点可操作的事实checkpoint 是包含hparams与model_dict两个键的字典超参随 checkpoint 分发无需额外配置模型被显式转为halfFP16并绑定 CUDA因此推理必须使用 GPU输出采样率sr直接取自hparams.sampling_rateLJSpeech 训练即 22.05 kHzwav 按此采样率写出。同目录下还附带了 convert_to_16k.py从文件名可推断其用途是把合成结果转成 16 kHz 便于后续 ASR 评测等处理。load_waveglow中有一个容易被忽略的步骤for k in waveglow.convinv: k.float()即把 WaveGlow 的可逆 1×1 卷积权重转回 float。这是因为逆矩阵求解Invertible1x1Conv 中W.float().inverse()在半精度下数值不稳定仓库已预先处理。4.3 Tacotron2 解码编码器、注意力解码器与门控终止Tacotron2 由嵌入层 编码器 解码器 Postnet 组成编码器Encoder3 层 1-D 卷积ReLU BatchNorm dropout 0.5接双向 LSTM。输入是单元 id 经nn.Embedding(n_symbols, symbols_embedding_dim)得到的嵌入——注意这里n_symbols对应的是单元词表大小而非字符表这是“改造版 Tacotron2”的核心改动之一解码器Decoder经典 Tacotron2 结构——Prenet两层线性dropout 0.5 注意力 LSTMCell 位置感知的 Bahdanau 式注意力LocationLayer对历史注意力权重做卷积 解码 LSTMCell mel 线性投影 门控层gate_layerPostnet5 层 kernel 5 的 1-D 卷积输出残差加到解码器 mel 上forward 中mel_outputs_postnet mel_outputs mel_outputs_postnet。推理时的解码循环Decoder.inference终止条件有二if torch.sigmoid(gate_output.data) self.gate_threshold: has_eos True break elif len(mel_outputs) self.max_decoder_steps: break即门控 sigmoid 超过gate_thresholdhparams 中配置判定“说完了”或达到命令行传入的--max_decoder_steps默认 2000强制停止。这正是 README 命令中显式传--max_decoder_steps 2000的意义——它是长序列的安全阀。若合成结果被截断一句话说到一半戛然而止可以调大该值重试。4.4 WaveGlow 声码器与 Denoisersynthesize_audio 展示了最后两级_, mel, _, ali, has_eos model.inference(inp, lab, ret_has_eosTrue) aud waveglow.infer(mel, sigma0.666) aud_dn denoiser(aud, strengthstrength).squeeze(1)WaveGlowglow.py基于可逆流的神经声码器。WaveGlow.infer先把 mel 谱经ConvTranspose1d(kernel1024, stride256)上采样到音频时间分辨率裁掉卷积伪影后切成分组grouping再从后往前逆序执行各 flow 层每层先做可逆 1×1 卷积逆矩阵在首次调用时缓存为W_inverse再按仿射耦合关系(audio_1 - b) / exp(s)还原音频并按n_early_every/n_early_size提前吐出部分通道early termination。采样噪声缩放固定为sigma0.666。Denoiserwaveglow_denoiser.pyWaveGlow 对静音输入会输出一段固定的“偏置音频”。Denoiser 在构造时用零 mel 输入推一次得到这段偏置的 STFT 谱bias_spec推理时从输出谱中按--denoiser_strength默认 0.1比例减掉并 clamp 到非负再逆变换回波形。作用是消除输出中的低频嗡声/伪影。若觉得声音发闷可以调低 strength觉得底噪残留可以适度调高。最终写盘的是去噪后的aud_dn采样率用load_tacotron返回的srsf.write(f{out_file_path}, aud_dn[0].cpu().float().numpy(), sample_rate)五、实操要点与常见问题排查结合以上源码事实实操时的注意事项可归纳为单元系统与模型必须严格配对上游特征类型logmel / cpc / hubert / w2v2与 K-means 聚类数50/100/200/500任一维度不一致推理虽不报错单元 id 在词表范围内时但合成的语音语义将错乱。量化文件请确保由同一套 speech2unit 配置产出文件格式文件名 | id id id竖线两侧各一个空格是解析脚本的预期形态split(|)后右侧split( )环境必须 CUDA GPU模型.cuda().half()PYTHONPATH缺了仓库根目录会直接ModuleNotFoundError输出被截断调大--max_decoder_steps音质瑕疵低频嗡声调整--denoiser_strength默认 0.1采样率wav 按 checkpoint 内hparams.sampling_rate写出LJSpeech 为 22.05 kHz如需要 16 kHz 可用仓库附带的 convert_to_16k.py 转换。六、在 GSLM 系统中的位置unit2speech 的推理入口单条语音独立、无状态天然适合串联进更大的 speech2speech 管线speech2unit 产出单元、ulm 对单元做采样或延续见 ulm 目录、unit2speech 还原波形再配合 metrics 目录下的 ASR/ABX 指标与 tools 目录下的重合成与“语音 prompt 生成新语音”工具即可完整复现 GSLM 论文的实验闭环。理解本文的 Tacotron2 解码循环、门控终止与 WaveGlow 逆流推理后你就具备了独立排查合成质量问题的源码级能力。【免费下载链接】unilmLarge-scale Self-supervised Pre-training Across Tasks, Languages, and Modalities项目地址: https://gitcode.com/GitHub_Trending/un/unilm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考