• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

PaddleSpeech-学习笔记第七章声音合成

武飞扬头像
zhangdroid
帮助1

【PaddleSpeech-学习笔记】第七章:声音合成

人类通过听觉获取的信息大概占所有感知信息的20%~30%。
声音储存了丰富的语义时序信息,由专门负责听觉的器官接收信号,产生一系列连锁刺激后,在人类大脑的皮层听区进行处理分析,获取语义和知识。近年来,随着深度学习算法上的进步以及不断丰富的硬件资源条件,文本转语音技术(Text-to-Speech,TTS)在移动、虚拟娱乐等领域得到了广泛的应用。



背景知识

在学习之前,先了解这个技术的发展历程。

定义

文本转语音,又称语音合成(Speech Sysnthesis),是指将一段文本按照一定需求转化成对饮的音频,这种特性决定了输出数据比输入长得多。文本转语音(TTS)是一项包含了语义学,声学,数字信号处理以及机器学习等多项学科的交叉任务。虽然辨识低质量音频文件的内容对人类来说很容易,但这对计算机来说并非易事。

按照不同的应用需求,更广义的语音合成研究包括:语音转换,例如说话人转换、语音到歌唱转换、语音情感转换、口音转换等;唱歌合成,例如歌词到歌唱转换、可是语音合成。

发展历史

第二次工业革命之前,语音的合成主要是机械式的音素合成为主。
1779年,德裔丹麦科学家Kratzenstein建造了人类的声道模型,使其可以产生五个长元音。
1791年,Kempelen添加了唇和舌的模型,使其能够发出辅音和元音。贝尔实验室于20世纪30年代发明了声码器(Vocoder),将语音自动分解为音调和共振,此项技术由Homer Dudley改进为键盘式合成器并于1030年纽约世界博览会展出。

第一台基于计算机的语音合成系统起源于20世纪50年代。1961年,IBM的John Larry Kelly,以及Louis Gerstman使用IBM 704计算机合成语音,成为贝尔实验室最著名的成就之一。1975年,第一代语音合成系统之一——MUSA(MULtichannel Speaking Automation)问世,其由一个独立的硬件和配套的软件组成。1978年发行的第二版本也可以进行无伴奏演唱。90年代的主流是采用MIT和贝尔实验室的系统,结合自然语言处理模型。
学新通

目前的主流方法

主流方法分为四类:

  • 基于统计参数的语音合成
  • 波形拼接语音合成
  • 混合方法
  • 端到端神经网络语音合成
    基于参数的语音合成包含隐马尔科夫模型(Hidden Markov Model,HMM)以及深度学习网络(Deep Neural Network,DNN)。端到端的方法包含了:声学模型 声码器以及“完全”端到端的方法。

基于深度学习的语音合成技术

语音合成基本知识

语音合成的流水线包含了三个主要模块:

  • 文本前端(Text Frontend):通过文本前端模块将原始文本转换成字符/音素
  • 声学模型(Acoustic Model):通过声学模型将字符/音素转换为声学特征,如线性频谱图、mel频谱图、LPC特征等
  • 声码器(Vocoder):通过声码器将声音特征转换为波形。

学新通


实践

示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。

需要的环境和package

设置运行环境和导入相关包

%env CUDA_VISIBLE_DEVICES=0 #默认编号从0开始,一张卡的话就直接用0

import logging
import sys
import warnings
warnings.filterwarnings("ignore", category=DeprecationWarning)

import argparse
import os
from pathlib import Path
import IPython.display as dp
import matplotlib.pyplot as plt
import numpy as np
import paddle
import soundfile as sf
import yaml
from paddlespeech.t2s.frontend.zh_frontend import Frontend
from paddlespeech.t2s.models.fastspeech2 import FastSpeech2
from paddlespeech.t2s.models.fastspeech2 import FastSpeech2Inference
from paddlespeech.t2s.models.parallel_wavegan import PWGGenerator
from paddlespeech.t2s.models.parallel_wavegan import PWGInference
from paddlespeech.t2s.modules.normalizer import ZScore
from yacs.config import CfgNode
学新通

设置预训练模型的路径

#使用Fastspeech2的预训练模型
fastspeech2_config = "download/fastspeech2_nosil_baker_ckpt_0.4/default.yaml"
fastspeech2_checkpoint = "download/fastspeech2_nosil_baker_ckpt_0.4/snapshot_iter_76000.pdz"
fastspeech2_stat = "download/fastspeech2_nosil_baker_ckpt_0.4/speech_stats.npy"

#使用parallelWaveGAN的预训练模型
pwg_config = "download/pwg_baker_ckpt_0.4/pwg_default.yaml"
pwg_checkpoint = "download/pwg_baker_ckpt_0.4/pwg_snapshot_iter_400000.pdz"
pwg_stat = "download/pwg_baker_ckpt_0.4/pwg_stats.npy"
#定义文本前端对象
phones_dict = "download/fastspeech2_nosil_baker_ckpt_0.4/phone_id_map.txt"

#分别答应出两个预训练模型的默认参数
with open(fastspeech2_config) as f:
    fastspeech2_config = CfgNode(yaml.safe_load(f))
with open(pwg_config) as f:
    pwg_config = CfgNode(yaml.safe_load(f))
print("========Config========")
print(fastspeech2_config)
print("---------------------")
print(pwg_config)
学新通

文本前端(Text Frontend)

一个文本前端模块主要包含:

  • 分段(Text Segmentation)
  • 文本正则化(Text Normalization)
  • 分词(Word Segmentation, 主要是在中文句段中)
  • 词性标注(Part-of-Speech,PoS)
  • 韵律预测(Prosody)
  • 字音转换(Grapheme-to-Phoneme,G2P)(Grapheme:语言书写系统的最小有意义单位;Phoneme:区分单词的最小语音单位)
    – 多音字(Polyphone)
    – 变调(Tone Sandhi):“一”、“不”变;三声变调;轻声变调;儿化音;方言
  • ……
    (输入给声学模型之前,还需要把音素序列转换为 id)
    其中最重要的模块是 文本正则化 模块和 字音转换(TTS 中更常用 G2P 代指) 模块。

各模块输出示例:

• Text: 全国一共有112所211高校
• Text Normalization: 全国一共有一百一十二所二一个一个地高校
• Word Segmentation: 全国/一共/有/一百一十二/所/二一个一个地/高校/
• G2P(注意此句中“一”的读音):
quan2 guo2 yi2 gong4 you3 yi4 bai3 yi1 shi2 er4 suo3 er4 yao1 yao1 gao1 xiao4
(可以进一步把声母和韵母分开)
q uan2 g uo2 y i2 g ong4 y ou3 y i4 b ai3 y i1 sh i2 er4 s uo3 er4 y ao1 y ao1 g ao1 x iao4
(把音调和声韵母分开)
q uan g uo y i g ong y ou y i b ai y i sh i er s uo er y ao y ao g ao x iao
0 2 0 2 0 2 0 4 0 3 …
• Prosody (prosodic words #1, prosodic phrases #2, intonation phrases #3, sentence #4):
全国#2一共有#2一百#1一十二所#2二一个一个地#1高校#4
(分词的结果一般是固定的,但是不同人习惯不同,可能有不同的韵律)

文本前端模块的设计需要结合很多专业的语义学知识和经验。人类在读文本的时候可以自然而然地读出正确的发音,但是这些先验知识计算机并不知道。例如,对于一个句子的分词:

我也想过过过儿过过的生活
我也想/过过/过儿/过过的/生活

货拉拉拉不拉拉布拉多
货拉拉/拉不拉/拉布拉多

南京市长江大桥
南京市长/江大桥
南京市/长江大桥

或者是词的变调和儿化音:

你要不要和我们一起出去玩?
你要不(#2)要和我们一(#4)起出去玩(儿)?
不好,我要一个人出去。
不(#4)好,我要一(#2)个人出去。
(以下每个词的所以字都是三声的,请你读一读,体会一下是否每个字都被读成了三声?)
纸老虎,虎骨酒,展览馆,岂有此理,手表厂有五种好产品

又或是多音字,种类情况通常需要先正确分词:

人要行,干一行行一行,一行行行行行;
人要是不行,干一行不行一行,一行不行行行不行。

佟大为妻子产下一女

海水朝朝朝朝朝朝朝落
浮云长长长长长长长消

PaddleSpeech Text-to-Speech的文本前端解决方案:

  • 文本正则
  • G2P
    • 多音字模块:pypinyin/g2pM
    • 变调模块:用分词 规则

构造文本前端对象

传入 phones_dict ,把相应的 phones 转换成 phone_ids

代码如下:

# 传入 `phone_dict` 会把相应的 `phones` 转换成 `phone_ids`
frontend = Frontend(phone_vocab_path = phones_dict)
print("Frontend done")

调用文本前端对象

文本前端对输入数据进行正则化时会进行分句,若 merge_sentences 设置为False,则所有分句的phone_ids构成一个List;若设置为Trueinput_ids["phone_ide"][0]则表示整句的 phone_ids

代码如下:

#输入文本
input = "你好,欢迎来到我的飞桨学习笔记!"
input_ids = frontend.get_input_ids(input, merge_sentences = True, print_info = True)
phone_ids = input_ids["phone_ids"][0]
#打印转换后的张量信息
print("phone_ids:%s"%phone_ids)

打印结果如下:
学新通

传统方法与深度学习方法在文本前端实现上的对比

  串联结构 并联结构
训练效率 线性排序运行,需要前一个模块训练完成后才能开启下一阶段训练,复杂度高,低效 并行度高,训练数据不存在耦合,训练语料不需要一次提取,训练更容易维护
结构特点 分词效果不佳有可能导致:韵律、多音字等生成效果差 各个模型独立,排除木桶效应的问题,模型之间的误差不再传递
结果表现 根据各个模块训练数据和分布的不同,语义表达能力各有差异 借助BERT的优势。字向量是上下文相关的表达,训练数据规模庞大;联合训练下各个模块的训练语料的利用率更高

学新通

声学模型(Acoustic Model)

声学模型将字符/音素转换为声学特征,如线性频谱图、mel频谱图、LPC特征等,声学特征以“帧”为单位,一般一帧是10ms左右,一个音素一般对应5~20帧左右,声学模型需要解决的是“不等长序列间的映射问题”,“不等长”是指,同一个人发不同音素的持续时间不同,同一个人在不同时刻说同一句话的语速可能不同,对应各个音素的持续时间不同,不同人说话的特色不同,对应各个音素的持续时间不同。这是一个困难的“一对多”问题。

#卡尔普陪外孙玩滑梯
000001|baker_corpus|sil 20 k 12 a2 4 er2 10 p 12 u3 12 p 9 ei2 9 uai4 15 s 11 uen1 12 uan2 14 h 10 ua2 11 t 15 i1 16 sil 20

声学模型主要分为两大类:自回归模型非自回归模型
其中自回归模型在t时刻的预测需要依赖t-1时刻的输出作为输入,预测时间长,但是音质相对较好,非自回归模型不存在预测上的依赖关系,预测时间快,音质相对较差。
主流声学模型发展的脉络:

自回归模型 非自回归模型
Tacotron FastSpeech
Tacotron2 SpeedySpeech
TacotronTTS FastPitch
  FastSpeech2
 

在本教程中,我们使用FastSpeech2作为声学模型。
原论文中的FastSpeech2结构图
学新通
PaddleSpeechTTS实现的是FastSpeech2与论文不同的地方在于,我们使用的是phone级别的pitch和energy(与FastPitch类似),这样的合成结果可以更加稳定(真实效果如何笔者没有做过对比)。
学新通

初始化 声学模型 (基于FastSpeech2模型)

with open(phones_dict, "r") as f:
    phn_id = [line.strip().split() for line in f.readlines()]
vocab_size = len(phn_id)
print("vocab_size:", vocab_size)
odim = fastspeech2_config.n_mels
model = FastSpeech2(
    idim=vocab_size, odim=odim, **fastspeech2_config["model"])
# 加载预训练模型参数
model.set_state_dict(paddle.load(fastspeech2_checkpoint)["main_params"])
# 推理阶段不启用 batch norm 和 dropout
model.eval()
stat = np.load(fastspeech2_stat)
# 读取数据预处理阶段数据集的均值和标准差
mu, std = stat
mu, std = paddle.to_tensor(mu), paddle.to_tensor(std)
# 构造归一化的新模型
fastspeech2_normalizer = ZScore(mu, std)
fastspeech2_inference = FastSpeech2Inference(fastspeech2_normalizer, model)
fastspeech2_inference.eval()
print("FastSpeech2 done!")
学新通

调用 声学模型

with paddle.no_grad():
    mel = fastspeech2_inference(phone_ids)
print("shepe of mel (n_frames x n_mels):")
print(mel.shape)
# 绘制声学模型输出的 mel 频谱
fig, ax = plt.subplots(figsize=(16, 6))
im = ax.imshow(mel.T, aspect='auto',origin='lower')
plt.title('Mel Spectrogram')
plt.xlabel('Time')
plt.ylabel('Frequency')
plt.tight_layout()

学新通

声码器(Vocoder)——生成模型

声码器将声学特征转换为波形。声码器需要解决的是“信息缺失的补全问题”。信息缺失是指,在音频波形转换为频谱图的时候,存在相位信息的确实,在频谱图转换为mel频谱图的时候,存在频域压缩导致的信息损失;假设音频的采样率是16KHz,一帧的音频有10ms,也就是说,1s的音频有16000个采样点,而1s中包含100帧,每一帧有160个采样点,声码器的作用就是将一个频谱变成音频波形的160个采样点,所以声码器中一般会包含上采样模块。
与声学模型类似,声码器也分为自回归模型和非自回归模型,更细致的分类如下:

Autoregression Flow GAN VAE Diffusion
WaveNet WaveFlow WaveGAN Wave-VAE WaveGrad
WaveRNN WaveGlow Parallel WaveGAN   DiffWave
LPCNet FloWaveNet MelGAN    
  Parallel WaveNet Style MelGAN    
    Multi Band MelGAN    
    HiFi GAN    

示例中的PaddleSpeechTTS主要展示了百度的 WaveFlow 和一些主流的GAN Vocoder,在本教程中,我们使用 Parallel WaveGAN作为声码器。
学新通
各个GAN Vocoder 的生成器和判别器的Loss的区别如下表格所示:

Model Generator Loss Discriminator Loss
Mel GAN adversial loss
Feature Matching
Multi-Scale Discriminator
Parallel Wave GAN adversial loss
Multi-resolution STFT loss
adversial loss
Multi-Band Mel GAN adversial loss
full band Multi-resolution STFT loss
sub band Multi-resolution STFT loss
Multi-Scale Discriminator
HiFi GAN adversial loss
Feature Matching
Mel-Spectrogram Loss
Multi-Scale Discriminator
Multi-Period Discriminator

初始化 声码器 Parallel WaveGAN

vocoder = PWGGenerator(**pwg_config["generator_params"])
# 模型加载预训练参数
vocoder.set_state_dict(paddle.load(pwg_checkpoint)["generator_params"])
vocoder.remove_weight_norm()
# 推理阶段不启用 batch norm 和 dropout
vocoder.eval()
# 读取数据预处理阶段数据集的均值和标准差
stat = np.load(pwg_stat)
mu, std = stat
mu, std = paddle.to_tensor(mu), paddle.to_tensor(std)
pwg_normalizer = ZScore(mu, std)
# 构建归一化的模型
pwg_inference = PWGInference(pwg_normalizer, vocoder)
pwg_inference.eval()
print("Parallel WaveGAN done!")

调用声码器,并绘制输出的波形图

with paddle.no_grad():
    wav = pwg_inference(mel)
print("shepe of wav (time x n_channels):%s"%wav.shape)

# 绘制声码器输出的波形图
wave_data = wav.numpy().T
time = np.arange(0, wave_data.shape[1]) * (1.0 / fastspeech2_config.fs)
fig, ax = plt.subplots(figsize=(16, 6))
plt.plot(time, wave_data[0])
plt.title('Waveform')
plt.xlabel('Time (seconds)')
plt.ylabel('Amplitude (normed)')
plt.tight_layout()

播放和保存音频

# 播放生成的音频
dp.Audio(wav.numpy().T, rate = fastspeech2_config.fs)

# 保存生成的音频
!mkdir output  # 生成一个文件夹用来存放要保存的内容
sf.write(
		"output/test.wav",
		wav.numpy(),
		samplerate = fastspeech2_config.fs)

下一节讲述如何在Linux或者终端上自训练TTS模型

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgfkigk
系列文章
更多 icon
同类精品
更多 icon
继续加载