目录
摘要
本周我阅读了一篇关于WaveRNN的论文,它是谷歌提出的语音合成算法,应用在手机,嵌入式等资源比较少的系统。WaveRNN 采用了三种先进的计算方法去提高生成语音的质量同事保证模型很小。其次,对LSTM的反向传播进行了手动推导,对LSTM的认识更加深刻,最后自己手写代码实现了LSTM的前向传播过程。
ABSTRACT
This week, I read a paper on WaveRNN, a speech synthesis algorithm proposed by Google, designed for systems with limited resources such as mobile phones and embedded devices. WaveRNN employs three advanced computational techniques to enhance the quality of generated speech while ensuring a compact model size. Additionally, I manually derived the backpropagation for LSTM, deepening my understanding of the LSTM (Long Short-Term Memory) architecture. Finally, I wrote code to implement the forward propagation process of LSTM on my own.
一、文献阅读
1、论文题目
1、题目:Efficient Neural Audio Synthesis
2、链接:https://arxiv.org/pdf/1802.08435.pdf
2、摘要
论文主要内容是语音合成的新技术,WaveRNN, 谷歌最新提出的语音合成算法,可应用在手机,嵌入式等资源比较少的系统。WaveRNN 采用了三种先进的计算方法去提高生成语音的质量同事保证模型很小。
The main content of the paper is the new technology in speech synthesis, WaveRNN, Google's latest proposed algorithm for speech synthesis that can be applied to systems with limited resources, such as smartphones and embedded systems. WaveRNN employs three advanced computational methods to enhance the quality of generated speech while ensuring that the model remains small in size.
3、创新点
1. 该文提出了一个单层的RNN 网络架构,使用了两个softmax layer, 实现了当前最佳语音生成水准, 可以实时生成24kHz 16-bit的语音。
2. 这篇文章采用了 weight pruning technique, 实现了96% sparsity.
3. 这篇文章提出了高并行度生成语音算法,在语音生成中,把很长的序列生成折叠成若干个短的序列,每个短序列同时生成,进而提高了长序列的生成速度 。
4、文章解读
对于序列生成模型来说, 他的生成速度可以用下面的公式表达。
T(u) 就是生成某句子 u 发音需要的时间。一共有|u|个sample, 这里的sample 和digital audio 里的sample是一个意思。不了解的同学可以看我以前的简介博客。 |u| 会非常大,对于高品质声音。比如高保真声音就是24K 个sample, 每个sample 对应16 bit.
上面公式里还有求和表达式中的N, 这个用来代表神经网络的层数,number of layers。 这个可能很大,如果神经网络的层数很多。 c() 代表每一层的计算时间,如果网络很宽,或者网络的kernel 很多,计算时间也会很长。 而d(
)代表硬件执行程序的overhead 时间, 包含了调用程序,提取对应参数之类的时间。 要想语音生成的快, 上面的每个参数都要尽量的小。
一、WaveRNN 架构
先看图说话,Fig. 1 中的WaveRNN 有两层softmax layer. coarse 8 代表粗略的8bit. 这个翻译太直译,很拙劣。可以理解成most important 8 bits, 即 8 MSB。 R 这一层是一个GRU layer, 它首先会用来生成coarse 8 bit, coarse 8bit 生成后会当做输入 去生成 fine 8 bits.
我们先来看看公式, 如上图所示。 分别代表 t-1 时刻 coarse 8 bit 和 fine 8 bit 的输出。 注意观察上图中的softmax 计算, 就可以知道网络的输出就是
和
。其中计算
的时候有一个*, 用来表示masked matrix。masked matrix 主要是在计算coarse 8 bit 的时候使用,因为 x_t 的输入中有个c_t, 这个还没有生成, 所以需要用masked matrx, 使这部分变成0, 来计算。 其中 coarse and fine parts 都在 [0,255]之间, 对应了softmax 的256 个分类。Coarse parts 和fine parts 合起来就是对应声音的16 bit.
二、WaveRNN 稀疏化
WaveRNN使用的是裁剪法, 在训练中将网络里较小的值,裁剪成0. 具体说来就是首先随机生成一个正常的矩阵 然后每训练500步, 就将weight matrix 里k 个最小的值变成0. 这个k, 是根据稀疏程度的要求确定的。 逐渐会越来越大, 直到满足的稀疏度的要求。 这里WaveRNN 进一步提出了用block sparse 的方式来表达这个稀疏矩阵,据说可以相应的减少神经网络的精度损失。
这个创新点有点不好理解, 我尽力解释的让大家理解。上文介绍的WaveRNN 减少了N和d()的大小, sparse WaveRNN减少了c(
)的计算,最后要减少的是|u|的大小。一个比较简单的方法就是生成8bit量化的声音, 但是这样声音的保真性损失很大。 这篇文章提出了子规模生成过程, 可以并行的线性减少生成声音所需的时间,可以用下图公式表示。
Step 1: 首先 把一个尺度为L的张量折叠成L/B 大小的B 个张量。 就是把数列折叠成了8 份, 每一份都是16 这么长的张量。 这里叫子规模生成的原因是本来生成的数字是1,2,3,4,5,6 这些。但是折叠以后生成的是1,9,17,25 这样的数组, 相当于把原数列降采样,即sub-scale sampling.
Step 2: 解释了声音生成过程,以及相对的依赖关系(conditioning)。这里假设每个子张量的生成需要较小的前瞻性。首先生成的是第八行。这列数组。 这列数组会先生成, 然后生成的是第七行数组, 依次生成。 但是第七行数组的生成不仅考虑第八行和第七行当前时间已经生成的数组,还要看第八行横轴未来生成的数组。 按照原论文, 这里主要解释第五行红色数字,76 的生成。 76 的生成, 需要考虑当前时刻之前的所有数字, 即第八行, 第七行, 第六行, 第五行, 1-9列蓝色标注的数值,还要考虑未来, 10-13列产生的数值, 即前瞻性为F=4.
Step 3: 解释了并行生成的过程, 就是红色标注的37, 76, 107 是可以同时并列生成。他的前瞻性都是4, 如step 2 中的解释。
Step 4: 最后一步就是把生成的矩阵,再展开变成原来的数列, 1,2,3,4,5.。这样的话就实现了高度并行快速生成语音的目的。
按照上图的解释,如果想生成 24kHz 的声音, 每一个子规模的生成速度就是24/16= 1.5kHz, 如果B = 16.
三、实验结果
上图对比的是WaveRNN-896 和表格中其他网络的表现对比。 首先, WaveRNN-896的表现和 WaveNet-512 效果差不多,说明达到了state-of-the-art result. 接下来用16个子规模, subscale WR 1024, 生成的声音依旧效果很好, 这说明subscale的方法效果很好, 而且基于这算法,可以实现10x 的采样速度。
这个结果图是保证参数个数不变的情况下, 提高网络的稀疏度,也就是逐渐增大网络模型的大小。 这个实验结果表明, 直到稀疏度98%的情况下,都是网络模型越大,语音生成的效果最好。 这里, NLL代表negative log likelihood, 它的值越小越好。 绿色的224和蓝色384是两个基准, 没有使用block sparse. 而红色和紫色分别使用了block sparse 的稀疏表达方式。
这里的NLL, 就是negative Log-likelihood, MOS 是mean opinion scores. MOS 是打分, 从 1 到 5。 1 表示非常不好, 3 表示普通, 5 表示非常好。 这个表格就是说明WaveRNN 可以表现的和WaveNet 差不多好, 但是速度会快很多。
四、结论
文章介绍了WaveRNN,这是一个用于顺序建模高保真音频的简单而强大的循环网络,并在GPU上展示了该模型的高性能实现。文章实验结果表明,与具有相同参数数量的小而密集模型相比,大型稀疏模型具有更好的质量,并且编写了高性能的块稀疏矩阵-向量乘积运算,以证明采样时间与参数数量成正比。最后介绍了子尺度依赖方案,该方案允许顺序模型在保持原始模型输出质量的同时,在每个步骤中生成多个样本。
二、LSTM反向传播
对于上述讲到的前向传播过程以上面的LSTM单元结构为例得出以下相关公式
三、LSTM代码实现
1)nn.lstm()的输入为(序列长度,batch,输入维数)
2)使用batch_first=True,把输入变成(batch,序列长度,输入维度),这里序列长度指的是一句话的单词数目,而且batch_first=True会改变输出的维度顺序。
3)x是单词的索引列表,size为len(x)。
4)embedding之后,x的size为(len(x), n_dim)。
5)unsqueeze之后,x的size为(1, len(x), n_dim),其中1在下一行程序的lstm中被当做是batchsize,len(x)被当做序列长度。
6)lstm的隐藏层输出,x的size为(1, len(x), n_hidden),因为定义lstm网络时用了batch_first=True,所以1在第一维,如果batch_first=False,则len(x)会在第一维。
7)squeeze之后,x的size为(len(x), n_hidden),在下一行的linear层中,len(x)被当做是batchsize。
8)linear层之后,x的size为(len(x), n_tag)。
9)y的size为(len(x), n_tag)。
import torch
import torch.nn.functional as F
from torch import nn
class LSTM(nn.Module):
def __init__(self, n_word, n_dim, n_hidden, n_tag):
super(LSTM, self).__init__()
self.word_embedding = nn.Embedding(n_word, n_dim)
self.lstm = nn.LSTM(n_dim, n_hidden, batch_first=True)
self.linear = nn.Linear(n_hidden, n_tag)
def forward(self, x):
x = self.word_embedding(x)
x = x.unsqueeze(0)
x, _ = self.lstm(x)
x = x.squeeze(0)
x = self.linear(x)
y = F.log_softmax(x, dim=1)
return y
总结
我通过手动推导对LSTM的反向传播,加深了对LSTM结构的理解,并亲自编写代码实现了LSTM的前向传播过程。这一过程不仅加深了我对LSTM的理解,还锻炼了我在深度学习领域的实际操作能力。下周我将继续对GRU进行学习。