unilm edgeLM(GSLM)unit2speech:从离散语音单元到语音的 Tacotron2 + WaveGlow 合成流水线详解
2026/9/13 5:33:29 网站建设 项目流程

unilm edgeLM(GSLM)unit2speech:从离散语音单元到语音的 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 的 README,Unit to Speech 模型是一个改造过的 Tacotron2(Tacotron2 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 / KM500
Modified CPCKM50 / KM100 / KM200 / KM500
HuBERT BaseKM50 / KM100 / KM200 / KM500
wav2vec 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 checkpoint(256 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_ROOT=<path_to_your_fairseq_repo_root> TTS_MODEL_PATH=<unit2speech_model_file_path> QUANTIZED_UNIT_PATH=<quantized_audio_file_path> OUT_DIR=<dir_to_dump_synthesized_audio_files> WAVEGLOW_PATH=<path_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 checkpoint(tts_checkpoint_best.pt
QUANTIZED_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)依次做四件事:

  1. load_quantized_audio_from_file读取所有语音的名称与单元序列;
  2. load_tacotron加载 TTS 模型(并把max_decoder_steps写回 hparams);
  3. load_waveglow加载声码器与 Denoiser;
  4. 对每条语音:把单元整数序列拼成空格分隔的字符串 →TacotronInputDataset.get_tensor转成模型输入张量 →synthesize_audio合成 →sf.write以 checkpoint 中记录的采样率写 wav。

四、源码级原理:从单元序列到波形

4.1 单元序列如何进入 Tacotron2

TacotronInputDataset 根据 checkpoint 中的hparams.text_or_code判断输入类型。对 unit2speech 模型,该值为非 "text",于是走process_code

  1. 单元字符串按空格切分为 token 列表;
  2. hparams.add_sos/hparams.add_eos在首尾插入<s>/</s>特殊 token(定义于 text.py);
  3. 通过load_code_dict(utils.py)加载的单元词表把每个单元映射为整数 id。词表第一行固定为_(pad),SOS/EOS 追加在末尾;
  4. hparams.collapse_code为真,code_to_sequence 会做 run-length 折叠——连续相同单元只保留一次。这对应了“连续帧常落入同一聚类中心”的压缩策略,同时把序列长度压短、降低解码步数。

这些行为全部由 checkpoint 内嵌的 hparams 决定,因此同一套推理代码可以兼容不同训练配置的模型,这也是 README 强调“单元系统必须与训练一致”的根本原因:code_dict词表与 K-means 中心数必须一致,否则单元 id 将指代错误的声学概念。

4.2 模型加载:FP16 + 必须 GPU

load_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 是包含hparamsmodel_dict两个键的字典,超参随 checkpoint 分发,无需额外配置;
  • 模型被显式转为half(FP16)并绑定 CUDA,因此推理必须使用 GPU;输出采样率sr直接取自hparams.sampling_rate(LJSpeech 训练即 22.05 kHz),wav 按此采样率写出。同目录下还附带了 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 组成:

  • 编码器(Encoder):3 层 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_layer)
  • Postnet:5 层 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_threshold(hparams 中配置)判定“说完了”,或达到命令行传入的--max_decoder_steps(默认 2000)强制停止。这正是 README 命令中显式传--max_decoder_steps 2000的意义——它是长序列的安全阀。若合成结果被截断(一句话说到一半戛然而止),可以调大该值重试。

4.4 WaveGlow 声码器与 Denoiser

synthesize_audio 展示了最后两级:

_, mel, _, ali, has_eos = model.inference(inp, lab, ret_has_eos=True) aud = waveglow.infer(mel, sigma=0.666) aud_dn = denoiser(aud, strength=strength).squeeze(1)
  • WaveGlow(glow.py):基于可逆流的神经声码器。WaveGlow.infer先把 mel 谱经ConvTranspose1d(kernel=1024, stride=256)上采样到音频时间分辨率,裁掉卷积伪影后切成分组(grouping),再从后往前逆序执行各 flow 层:每层先做可逆 1×1 卷积(逆矩阵在首次调用时缓存为W_inverse),再按仿射耦合关系(audio_1 - b) / exp(s)还原音频,并按n_early_every/n_early_size提前吐出部分通道(early termination)。采样噪声缩放固定为sigma=0.666
  • Denoiser(waveglow_denoiser.py):WaveGlow 对静音输入会输出一段固定的“偏置音频”。Denoiser 在构造时用零 mel 输入推一次得到这段偏置的 STFT 谱(bias_spec),推理时从输出谱中按--denoiser_strength(默认 0.1)比例减掉并 clamp 到非负,再逆变换回波形。作用是消除输出中的低频嗡声/伪影。若觉得声音发闷可以调低 strength,觉得底噪残留可以适度调高。

最终写盘的是去噪后的aud_dn,采样率用load_tacotron返回的sr

sf.write(f"{out_file_path}", aud_dn[0].cpu().float().numpy(), sample_rate)

五、实操要点与常见问题排查

结合以上源码事实,实操时的注意事项可归纳为:

  1. 单元系统与模型必须严格配对:上游特征类型(logmel / cpc / hubert / w2v2)与 K-means 聚类数(50/100/200/500)任一维度不一致,推理虽不报错(单元 id 在词表范围内时),但合成的语音语义将错乱。量化文件请确保由同一套 speech2unit 配置产出;
  2. 文件格式文件名 | id id id,竖线两侧各一个空格是解析脚本的预期形态(split("|")后右侧split(" "));
  3. 环境:必须 CUDA GPU(模型.cuda().half());PYTHONPATH缺了仓库根目录会直接ModuleNotFoundError
  4. 输出被截断:调大--max_decoder_steps
  5. 音质瑕疵(低频嗡声):调整--denoiser_strength(默认 0.1);
  6. 采样率: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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询