TTS模型共存的可扩展框架
通过一组简单的抽象,不同架构的模型可以被集成,并针对特定硬件加速器进行优化。
像Alexa这样的语音智能体通常拥有多种不同的语音合成器,它们在表现力、个性、语言和说话风格等属性上各不相同。支撑这些不同应用的机器学习模型可能具有完全不同的架构,而将这些架构集成到单一语音服务中可能是一个耗时且充满挑战的过程。
为了使这个过程更容易、更快速,某机构的文本转语音(TTS)小组开发了一个通用的模型集成框架,该框架允许以快速且可扩展的方式定制生产环境中的语音模型。
模型的多样性
最先进的语音模型通常使用两个大型神经网络从文本输入合成语音。
第一个网络称为声学模型,它以文本为输入,生成一个梅尔频谱图,这是一种随时间变化、表示语音音高和能量等声学参数的图像。第二个网络称为声码器,它以梅尔频谱图为输入,并生成最终的输出——语音的音频波形。
虽然我们已经发布了一个支持多种说话风格的通用声码器架构,但我们仍然使用不同的声学模型架构来生成这种多样化的说话风格。
最常见的声学模型架构依赖于注意力机制,该机制学习输入文本的哪些元素与输出频谱图的当前时间片(或“帧”)最相关。通过这种机制,网络隐式地模拟了文本不同块的语音时长。同一个模型还使用了“教师强制”技术,即将先前生成的语音帧作为输入来产生下一帧。虽然这种架构可以生成富有表现力和自然的语音,但它容易出现可懂度错误,如含糊不清、遗漏或重复单词,并且错误很容易从一帧累积到下一帧。
更现代的架构通过显式地建模文本块的时长并并行生成语音帧来解决这些问题,这比依赖先前生成的帧作为输入更高效、更稳定。为了对齐文本和语音序列,模型只需根据外部时长模型指定的语音帧数,对文本块的编码(其表示向量)进行“上采样”或重复。
在不同场景(例如Alexa问答、儿童讲故事、智能家居自动化)中使用的复杂TTS模型的持续演进,催生了一个能够处理所有这些模型的可扩展框架的需求。
集成的挑战
要将声学模型集成到生产环境中,需要一个组件,它接收输入文本话语并返回梅尔频谱图。第一个困难是,语音通常是按顺序生成的块,而不是一次性合成的。为了最大限度地减少延迟,框架应尽快返回数据。一个将整个模型包装在代码中并用单个函数调用处理所有内容的朴素解决方案将慢得无法接受。
另一个挑战是调整模型以适应各种硬件加速器。例如,为了从高性能的AWS Inferentia运行时中受益,需要确保所有张量都具有固定大小(在模型编译阶段一次性设置)。这意味着需要添加逻辑,将较长的话语分割成适合特定输入大小的较小块(取决于模型);添加确保正确填充的逻辑;并决定哪些功能应由模型直接处理,哪些应由集成层处理。
当希望在通用GPU上运行同一个模型时,可能不需要这些更改,如果框架能够以一种简单的方式在不同上下文之间来回切换,那将会很有用。因此,将TTS模型解耦为一组更专门的、能够执行所有必要逻辑的集成组件。
集成组件
集成层将模型封装在一组能够将输入话语转换为梅尔频谱图的组件中。由于模型通常分两个阶段运行——预处理数据和按需生成数据——使用两种类型的组件会很方便:
- SequenceBlock:接收一个输入张量并返回一个转换后的张量(输入可以是应用另一个SequenceBlock的结果)。
- StreamableBlock:按需生成数据(例如,帧)。作为输入,它接收另一个StreamableBlock的结果(块可以形成流水线)和/或由SequenceBlock生成的数据。
这些简单的抽象为创建声学模型的变体提供了极大的灵活性。以下是一个示例:
一个使用SequenceBlock和StreamableBlock抽象构建的声学模型示例。
该声学模型包括:
- 两个编码器(SequenceBlocks):将输入的文本嵌入转换为一维表示张量,一个用于编码文本,一个用于预测时长。
- 一个上采样器(一个StreamableBlock):它接收编码器的结果作为输入,根据编码器返回的数据创建中间的、与语音长度相等的序列。
- 一个解码器(一个StreamableBlock):它生成梅尔频谱图帧。
整个模型被封装在一个专门的StreamableBlock中,称为StreamablePipeline,它恰好包含一个SequenceBlock和一个StreamableBlock:
- SequenceBlockContainer 是一个专门的SequenceBlock,由一组能够运行神经网络编码器的嵌套SequenceBlock组成。
- StreamableStack 是专门的StreamableBlock,它解码来自上采样器的输出并创建梅尔频谱图帧。
该集成框架确保所有组件按正确顺序运行,并且根据组件的特定版本,它允许使用各种硬件加速器。
集成层
声学模型作为一个插件提供,称之为“addon”。一个addon由导出的神经网络组成,每个网络都表示为一组命名的符号和参数(编码器、解码器等),以及配置数据。其中一个名为“stack”的配置属性,指定了应如何连接集成组件以构建一个工作的集成层。以下是描述上述架构的stack属性代码:
'stack'=[
{'type' : 'StreamablePipeline',
'sequence_block' : {'type' : 'Encoders'},
'streamable_block' :
{'type': 'StreamableStack',
'stack' : [
{'type' : 'Upsampler'},
{'type' : 'Decoder'}
]}
}
]
这个定义将创建一个由以下部分组成的集成层:
- 一个StreamablePipeline,包含:
- addon中指定的所有编码器(框架将自动创建所有必需的组件);
- 一个StreamableStack,包含:
- 一个上采样器,为解码器生成中间数据;
- addon中指定的解码器,生成最终的帧。
JSON格式允许轻松进行更改。例如,可以创建一个专门的组件,在特定硬件加速器上并行运行所有序列块,并将其命名为CustomizedEncoders。在这种情况下,配置规范中唯一的变化就是将名称“Encoders”替换为“CustomizedEncoders”。
使用带有额外诊断或数字信号处理效果的组件运行实验也非常简单。一个新组件的唯一要求是扩展两个通用抽象之一;除此之外,没有其他限制。根据框架设计,甚至将一个StreamableBlock替换为整个嵌套的序列到序列堆栈也完全可以。
该框架已投入生产。它是最近成功集成最先进TTS架构(无需注意力机制)和遗留模型的关键支柱。
致谢:Daniel KorzekwaFINISHED