利器:TTS Frontend 中英Text-to-Phoneme Converter,附代码

点击下方标题,迅速定位到你感兴趣的内容


前言

Github:NLP相关Paper笔记和代码复现
说明:讲解时会对相关文章资料进行思想、结构、优缺点,内容进行提炼和记录,相关引用会标明出处,引用之处如有侵权,烦请告知删除。
转载请注明:DengBoCong

NLP的语音合成中,有一种关键技术是将文字拆解成音素,再去语音库里匹配相同音素的语音片段,来实现文字转换语音。音素是给定语言的语音,如果与另一个音素交换,则会改变单词的含义,同时,音素是绝对的,并不是特定于任何语言,但只能参考特定语言讨论音素。由于音素的特性,非常适合用于语音合成领域。

音素(phone),是语音中的最小的单位,依据音节里的发音动作来分析,一个动作构成一个音素。音素分为元音、辅音两大类。

说白了,音素其实就是人在说话时,能发出最最最最短小、简洁的不能再分割的发音,不同的音素就是不同的短发音,可以组成不同的长发音,再组成词句形成语言。
在这里插入图片描述

本篇文章就来讲讲中文和英文中,如何将文本转换为音素序列。

相关知识

众所周知,语音合成系统通常包含前端和后端两个模块。前端模块主要是对输入文本进行分析,提取后端模块所需要的语言学信息。前端模块一般包含文本正则化、分词、词性预测、多音字消歧、韵律预测等子模块。后端模块根据前端分析结果,通过一定的方法生成语音波形。后端模块一般分为基于统计参数建模的语音合成(Statistical Parameter Speech Synthesis,SPSS,以下简称参数合成),以及基于单元挑选和波形拼接的语音合成(以下简称拼接合成)两条技术主线。

而“端到端”架构的语音合成系统的出现,能够直接从字符文本合成语音,打破了各个传统组件之间的壁垒,使得我们可以从<文本,声谱>配对的数据集上,完全随机从头开始训练。最具代表性的端到端语音合成系统,就是2017年初,Google 提出的端到端的语音合成系统——Tacotron

从通俗一点的角度来讲,语音合成过程,需要处理两部分内容,分别是文本(Text)处理和音频(speech)处理,如下图是端到端的语音合成系统整体技术架构选型。我们文章要讲的就是属于TTS Frontend范畴的Text-to-Phoneme。
在这里插入图片描述

文本规范化

对文本进行预处理,主要是去掉无用字符,全半角字符转化等,对于中文而言,有时候普通话文本中会出现简略词、日期、公式、号码等文本信息,这就需要通过文本规范化,对这些文本块进行处理以正确发音,比如:

  • “小明体重是 128 斤”中的“128”应该规范为“一百二十八”,而“G128 次列车”中的“128” 应该规范为“一 二 八”
  • “2016-05-15”、“2016 年 5 月 15 号”、“2016/05/15”可以统一为一致的发音

对于英文而言,也需要将年份、货币、数字、字母等文本信息,转换为完整单词,比如:

  • 类别为年份(NYER): 2011 → twenty eleven
  • 类别为货币(MONEY): £100 → one hundred pounds
  • 类别为非单词,需要拟音(ASWD): IKEA → apply letter-to-sound
  • 类别为数字(NUM) : 100 NUM → one hundred
  • 类别为字母(LSEQ) : DVD → dee vee dee

这些文本规范化预处理不放在深度学习模型去学习,而是通过各种规则的正则表达式进行转换,所以涉及到的代码工作量还是比较大的,所以我将写好的代码更新至GitHub项目中,方便需要者使用(TensorFlow和PyTorch版本同步更新):NLP相关Paper笔记和代码复现,这里粘贴一个作为举例,方便大家知道是啥。

def _clean_number(text: str):
    """
    对句子中的数字相关进行统一单词转换
    :param text: 单个句子文本
    :return: 转换后的句子文本
    """
    comma_number_re = re.compile(r"([0-9][0-9\,]+[0-9])")
    decimal_number_re = re.compile(r"([0-9]+\.[0-9]+)")
    pounds_re = re.compile(r"£([0-9\,]*[0-9]+)")
    dollars_re = re.compile(r"\$([0-9\.\,]*[0-9]+)")
    ordinal_re = re.compile(r"[0-9]+(st|nd|rd|th)")
    number_re = re.compile(r"[0-9]+")

    text = re.sub(comma_number_re, lambda m: m.group(1).replace(',', ''), text)
    text = re.sub(pounds_re, r"\1 pounds", text)
    text = re.sub(dollars_re, _dollars_to_word, text)
    text = re.sub(decimal_number_re, lambda m: m.group(1).replace('.', ' point '), text)
    text = re.sub(ordinal_re, lambda m: inflect.engine().number_to_words(m.group(0)), text)
    text = re.sub(number_re, _number_to_word, text)

    return text

英文Text-to-Phoneme

我们先来讲讲英文的Text-to-Phoneme,想要将英文单词转换成音素,想必需要了解Arpabet,下面是WiKi上的解释:

ARPABET(也称为ARPAbet)是高级研究计划局(ARPA)在1970年代语音理解研究项目中开发的一组语音转录代码。 它代表具有不同ASCII字符序列的通用美国英语的音素和同音素。

可以理解为通过字母组合定义发音规则,英文发音是由元音辅音等组成的,同时,在元音后紧接着用数字表示压力, 辅助符号在1和2个字母的代码中相同, 在2个字母的符号中,段之间用空格隔开,大概如下这样:
在这里插入图片描述

我们将文本转换为ARPABET并不需要我们了解很多语言学的东西,我们只需要选择现有合适的发音字典就可以了,使用比较多的还是CMU的发音词典,官方网站,想了解原理的可以看这篇论文。可以先在官网体验一下是啥效果,如下:
在这里插入图片描述
自行从官网下载发音字典(也可以去我的github下载,链接在文章顶部),音素集,39个音素,如下:

		Phoneme Example Translation
        ------- ------- -----------
        AA	odd     AA D
        AE	at	AE T
        AH	hut	HH AH T
        AO	ought	AO T
        AW	cow	K AW
        AY	hide	HH AY D
        B 	be	B IY
        CH	cheese	CH IY Z
        D 	dee	D IY
        DH	thee	DH IY
        EH	Ed	EH D
        ER	hurt	HH ER T
        EY	ate	EY T
        F 	fee	F IY
        G 	green	G R IY N
        HH	he	HH IY
        IH	it	IH T
        IY	eat	IY T
        JH	gee	JH IY
        K 	key	K IY
        L 	lee	L IY
        M 	me	M IY
        N 	knee	N IY
        NG	ping	P IH NG
        OW	oat	OW T
        OY	toy	T OY
        P 	pee	P IY
        R 	read	R IY D
        S 	sea	S IY
        SH	she	SH IY
        T 	tea	T IY
        TH	theta	TH EY T AH
        UH	hood	HH UH D
        UW	two	T UW
        V 	vee	V IY
        W 	we	W IY
        Y 	yield	Y IY L D
        Z 	zee	Z IY
        ZH	seizure	S IY ZH ER

有了音素集之后就可以使用脚本将文本转换为音素了,我的转换脚本如下:(完整代码同GitHub可找到)

def text_to_phonemes_converter(text: str, cmu_dict_path: str):
    """
    将句子按照CMU音素字典进行分词切分
    :param text: 单个句子文本
    :param cmu_dict_path: cmu音素字典路径
    :return: 按照音素分词好的数组
    """
    _, symbols_set = get_phoneme_dict_symbols()

    alt_re = re.compile(r'\([0-9]+\)')
    cmu_dict = {}
    text = _clean_text(text)
    text = re.sub(r"([?.!,])", r" \1", text)

    # 文件是从官网下载的,所以文件编码格式要用latin-1
    with open(cmu_dict_path, 'r', encoding='latin-1') as cmu_file:
        for line in cmu_file:
            if len(line) and (line[0] >= "A" and line[0] <= "Z" or line[0] == "'"):
                parts = line.split('  ')
                word = re.sub(alt_re, '', parts[0])

                # 这里要将非cmu音素的干扰排除
                pronunciation = " "
                temps = parts[1].strip().split(' ')
                for temp in temps:
                    if temp not in symbols_set:
                        pronunciation = None
                        break
                if pronunciation:
                    pronunciation = ' '.join(temps)
                    if word in cmu_dict:
                        cmu_dict[word].append(pronunciation)
                    else:
                        cmu_dict[word] = [pronunciation]

    cmu_result = []
    for word in text.split(' '):
        # 因为同一个单词,它的发音音素可能不一样,所以存在多个
        # 音素分词,我这里就单纯的取第一个,后面再改进和优化
        cmu_word = cmu_dict.get(word.upper(), [word])[0]
        if cmu_word != word:
            cmu_result.append("{" + cmu_word + "}")
        else:
            cmu_result.append(cmu_word)

    return " ".join(cmu_result)

中文Text-to-Phoneme

对于中文,其实我们再熟悉不过了,中文的音素其实就是汉语拼音的最小单元,包括声母,韵母,但是其中还会有一些整体认读音节,更详细的拼音标注文本分析,可以参考MTTS文本分析。同一个字在不同分词情况下的发音不同,所以导致数量级比较大,比较典型的可以参见清华大学的thchs30中文数据集,里面提供了分词好的音素字典,如下:

SIL sil
<SPOKEN_NOISE> sil
啊 aa a1
啊 aa a2
啊 aa a4
啊 aa a5
啊啊啊 aa a2 aa a2 aa a2
啊啊啊 aa a5 aa a5 aa a5
阿 aa a1
阿 ee e1
阿尔 aa a1 ee er3
阿根廷 aa a1 g en1 t ing2
阿九 aa a1 j iu3
阿克 aa a1 k e4
阿拉伯数字 aa a1 l a1 b o2 sh u4 z iy4
阿拉法特 aa a1 l a1 f a3 t e4

对应脚本在这里,不过其实汉子转拼音还有更方便的方式,就是使用Python 的拼音库 PyPinyin,如下:

from pypinyin import pinyin
print(pinyin('朝阳'))
# [['zhāo'], ['yáng']]

PyPinyin可以用于汉字注音、排序、检索等等场合,是基于 hotto/pinyin 这个库开发的,一些站点链接如下:

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 程序猿惹谁了 设计师:白松林 返回首页