基于Tacotron模型实现中文语音合成

#基于Tacotron模型实现中文语音合成

1.数据准备

1.1 单个女生语音数据10000句

# https://www.data-baker.com/open_source.html
mkdir tts_tacotron
cd tts_tacotron
wget "https://weixinxcxdb.oss-cn-beijing.aliyuncs.com/gwYinPinKu/BZNSYP.rar"
# 解压之

./BZNSYP
├── PhoneLabeling
├── ProsodyLabeling
└── Wave

1.1.1 标注(ProsodyLabeling/000001-010000.txt)

head ProsodyLabeling/000001-010000.txt

000001	卡尔普#2陪外孙#1玩滑梯#4。
	ka2 er2 pu3 pei2 wai4 sun1 wan2 hua2 ti1
000002	假语村言#2别再#1拥抱我#4。
	jia2 yu3 cun1 yan2 bie2 zai4 yong1 bao4 wo3
000003	宝马#1配挂#1跛骡鞍#3,貂蝉#1怨枕#2董翁榻#4。
	bao2 ma3 pei4 gua4 bo3 luo2 an1 diao1 chan2 yuan4 zhen3 dong3 weng1 ta4
000004	邓小平#2与#1撒切尔#2会晤#4。
	deng4 xiao3 ping2 yu3 sa4 qie4 er3 hui4 wu4
000005	老虎#1幼崽#2与#1宠物犬#1玩耍#4。
	lao2 hu3 you4 zai3 yu2 chong3 wu4 quan3 wan2 shua3

1.1.2 语音(Wave/*.wav)

ls Wave | head
000001.wav
000002.wav
000003.wav
000004.wav
000005.wav
000006.wav
000007.wav
000008.wav
000009.wav
000010.wav

1.2 中文字典素材的准备

# DaCiDian (https://github.com/aishell-foundation/DaCiDian)
这是我在kaldi项目接触到的 一个比较不错的词典 用于分词以及拼音的标注
这里不说准备的过程了 到时候直接用转化好的

2 模型的准备

# tocotron模型的实现(https://github.com/keithito/tacotron)
git clone https://github.com/keithito/tacotron.git

# 这是个默认合成英文的模型,有空可以试试,效果还行作者也提供了训练好的模型测试

2.1 关于中文合成的相关修改

2.1.1 输入特征的相关准备
我们这里的思路是把一个句子 用拼音标注好以后 再通过拼音转声母韵母的字典转成声母韵母的序列
# file: text/symbols.py 追加

_phones_f = open(os.path.split(os.path.realpath(__file__))[0] + '/zh_lang/phones','r');
_phones = []

_line = _phones_f.readline()
while _line:
    _line = _line.strip();
    _phones.append(_line)
    _line = _phones_f.readline()

_phones_f.close();
pinyin_symbols = [_pad, _eos] + _phones;
symbols=pinyin_symbols

# 上述文件会在我上传的代码中 这样符号表就建立好了

多写了个文件把分词的语句转拼音 做合成的时候要用

# file: text/pinyinconvert.py

"""

"""

import os

wordsDict = {}
_align_f = open(os.path.split(os.path.realpath(__file__))[0] + '/zh_lang/align_lexicon.txt','r')
_line = _align_f.readline()
while _line:
    _line = _line.strip();
    _parts = _line.split('\t')
    if len(_parts) == 2 and _parts[0] not in wordsDict:
        wordsDict[_parts[0]] = _parts[1]
    _line = _align_f.readline()


def sentence_to_pinyin(sentence):
    parts = sentence.split(' ')
    text = ""
    for i in range(0, len(parts)):
        word = parts[i]
        if word in wordsDict:
            if len(text) > 0:
                text = text + ' '
            text = text + wordsDict[word]
    return text
2.2.2 数据集的预处理

在dataset中多加入一种新的数据集处理方式

# file: datasets/bznsyp.py

from concurrent.futures import ProcessPoolExecutor
from functools import partial
import numpy as np
import os
from util import audio
from text.pinyinconvert import sentence_to_pinyin
from text.symbols import pinyin_dict


# 声母韵母化bznsyp中的拼音 对一些做特殊处理
def _format_pinyin(value):
    if len(value) == 1:
        return value.lower()

    if value == 'IY1':
        return 'i_1'
    key = value[:-1]
    end = value[-1]
    if key in pinyin_dict:
        fpinyin = pinyin_dict[key]
        if end == '5':
            end = '0'
        fpinyin = fpinyin + '_' + end
        return fpinyin
    elif key[-1] == 'r':
        return _format_pinyin(key[:-1] + end) + ' er_0'
    else :
        print('not',value)





# 声母韵母化拼音
def _convet_pinyin(label):
    parts = label.split(' ')
    #print(parts)
    text = ''
    for value in parts:
        if len(text) > 0:
            text = text + ' '
        text = text + _format_pinyin(value)
    return text


def build_from_path(in_dir, out_dir, num_workers=1, tqdm=lambda x: x):
    executor = ProcessPoolExecutor(max_workers=num_workers)
    futures = []
    #text_dict = _load_dict(os.path.join(in_dir,'text'))
    f = open(os.path.join(in_dir,'ProsodyLabeling/000001-010000.txt'), encoding='utf-8')
    line = f.readline()
    while line:
        parts = line.strip('\n').split('\t')
        line = f.readline()
        name = parts[0]
        label = line.strip()
        text = _convet_pinyin(label)
        wav_path = os.path.join(in_dir,'Wave/' + name + '.wav')
        #print(name,"  ",wav_path);
        futures.append(executor.submit(partial(_process_utterance, out_dir, name, wav_path, text)))
        line = f.readline()
    f.close()
    return [future.result() for future in tqdm(futures)]




def _process_utterance(out_dir, name, wav_path, text):
    wav = audio.load_wav(wav_path)
    spectrogram = audio.spectrogram(wav).astype(np.float32)
    n_frames = spectrogram.shape[1]
    mel_spectrogram = audio.melspectrogram(wav).astype(np.float32)
    spectrogram_filename = 'bznsyp-spec-%s.npy' % name
    mel_filename = 'bznsyp-mel-%s.npy' % name
    np.save(os.path.join(out_dir, spectrogram_filename), spectrogram.T, allow_pickle=False)
    np.save(os.path.join(out_dir, mel_filename), mel_spectrogram.T, allow_pickle=False)
    #text = sentence_to_pinyin(text)
    return (spectrogram_filename, mel_filename, n_frames, text)


file: preprocess.py

# 添加处理函数
def preprocess_bznsyp(args):
  in_dir = '/mnt/ml_data/download/BZNSYP' #数据所在路径 写死省事 记得改成你自己的
  out_dir = os.path.join(args.base_dir, args.output)
  os.makedirs(out_dir, exist_ok=True)
  metadata = bznsyp.build_from_path(in_dir, out_dir, args.num_workers, tqdm=tqdm)
  write_metadata(metadata, out_dir)


# 类似ljspeech 添加相应的到main函数 使之生效

这个样子 代码基本就准备完成了 然后参考README 执行相应的过程来训练

# 数据预处理

python3 preprocess.py --dataset bznsyp 
# 等进度完成

python3 train.py

# 等!!!!!!

# 或者

nohup  python3 train.py &

# 等!!!!!
# 默认~/tacotron/logs-tacotron 每1000步就会保存一次训练的模型

python3 demo_server.py --checkpoint {model_path}

# 1. 访问http://127.0.0.1:9000 
# 2. 先输入 分好词的中文语句 点击convert 转成声母韵母序列 
# 3. 点击 Speak 等一下 就有结果了(有点麻烦不过是为了有再次矫正拼音的机会)
# 4. 可以听到声音了

感谢你的阅读 下面是我改的源码

https://github.com/xiaominfc/tacotron

阅读量: