WaveNet 代码解析 —— ops.py
文章目录
简介
本项目一个基于 WaveNet 生成神经网络体系结构的语音合成项目,它是使用 TensorFlow 实现的(项目地址)。
WaveNet 神经网络体系结构能直接生成原始音频波形,在文本到语音和一般音频生成方面显示了出色的结果(详情请参阅 WaveNet 的详细介绍)。
由于 WaveNet 项目较大,代码较多。为了方便学习与整理,将按照工程文件的结构依次介绍。
本文将介绍项目中的 ops.py 文件:基础操作函数集。
代码解析
变量解析
以下变量为 ops.py 脚本的全局变量:
# optimizer_factory 保存了优化器的三种方案:adam, sgd, rmsporp
# 分别对应了 tensorflow 中 optimizer 的几种设置方法
optimizer_factory = {'adam': create_adam_optimizer,
'sgd': create_sgd_optimizer,
'rmsprop': create_rmsprop_optimizer}
函数解析
create_adam_optimizer
下面这段代码的主要任务是:创建 adam 优化器
# learning_rate : 学习速率
# epsilon : 数值稳定性的小常数,作为精度
def create_adam_optimizer(learning_rate, momentum):
# 实现 Adam 算法的优化器
return tf.train.AdamOptimizer(learning_rate=learning_rate,
epsilon=1e-4)
create_sgd_optimizer
下面这段代码的主要任务是:创建 sgd 优化器
# learning_rate : 学习速率
# momentum : 动量
def create_sgd_optimizer(learning_rate, momentum):
# 实现动量算法的优化器
return tf.train.MomentumOptimizer(learning_rate=learning_rate,
momentum=momentum)
create_rmsprop_optimizer
下面这段代码的主要任务是:创建 RMSProp 优化器
# learning_rate : 学习速率
# momentum : 动量
# epsilon : 数值稳定性的小常数,作为精度
def create_rmsprop_optimizer(learning_rate, momentum):
# 实现RMSProp算法的优化器
return tf.train.RMSPropOptimizer(learning_rate=learning_rate,
momentum=momentum,
epsilon=1e-5)
time_to_batch
下面这段代码的主要任务是:时序数据划分成 dilation 批,便于膨胀卷积运算
def time_to_batch(value, dilation, name=None):
# 创建 time_to_batch 变量命名空间
with tf.name_scope('time_to_batch'):
# 获取 value 的形状
shape = tf.shape(value)
# 为了让 value 的数据量恰好为 dilation 的整数倍,计算需要填补的元素个数
pad_elements = dilation - 1 - (shape[1] + dilation - 1) % dilation
# 在 value 的数据维度上填补 pad_elements 个0
padded = tf.pad(value, [[0, 0], [0, pad_elements], [0, 0]])
# 将输入的数据转换格式,使每 dilation 个数据一组
reshaped = tf.reshape(padded, [-1, dilation, shape[2]])
# 交换维度,使数据转换成易于卷积的格式
transposed = tf.transpose(reshaped, perm=[1, 0, 2])
# 返回数据,确保数据的格式与需要的个数相符
return tf.reshape(transposed, [shape[0] * dilation, -1, shape[2]])
batch_to_time
下面这段代码的主要任务是:将 dilation 批数据还原为时序数据,为后续的网络层做准备
def batch_to_time(value, dilation, name=None):
# 创建 batch_to_time 变量命名空间
with tf.name_scope('batch_to_time'):
# 获取 value 的形状
shape = tf.shape(value)
# 确保 value 的形状是所需的形状
prepared = tf.reshape(value, [dilation, -1, shape[2]])
# 交换维度,将数据还原到每组 dilation 个
transposed = tf.transpose(prepared, perm=[1, 0, 2])
# 还原数据,将数据平铺,并返回
return tf.reshape(transposed,
[tf.div(shape[0], dilation), -1, shape[2]])
causal_conv
下面这段代码的主要任务是:对当前层根据膨胀系数的大小分情况进行卷积,并将卷积的结果返回
def causal_conv(value, filter_, dilation, name='causal_conv'):
with tf.name_scope(name):
# 取过滤器宽度
filter_width = tf.shape(filter_)[0]
# 若膨胀系数大于1
if dilation > 1:
# 将时序数据转换为便于直接卷积的 dilation 批数据
transformed = time_to_batch(value, dilation)
# 开始卷积
conv = tf.nn.conv1d(transformed, filter_, stride=1,
padding='VALID')
# 将卷积后的数据还原成时序数据
restored = batch_to_time(conv, dilation)
# 若膨胀系数为1
else:
# 直接进行卷积
restored = tf.nn.conv1d(value, filter_, stride=1, padding='VALID')
# 计算数据应有的输出宽度
out_width = tf.shape(value)[1] - (filter_width - 1) * dilation
# 删除多余的元素,并返回
result = tf.slice(restored,
[0, 0, 0],
[-1, out_width, -1])
return result
mu_law_encode
下面这段代码的主要任务是:使用数字转换波形振幅
def mu_law_encode(audio, quantization_channels):
# 创建 encode 变量命名空间
with tf.name_scope('encode'):
# 量化振幅值的数量
mu = tf.to_float(quantization_channels - 1)
# 限制最大值,避免大振幅的出现
safe_audio_abs = tf.minimum(tf.abs(audio), 1.0)
# 执行 mu-law 扩展变换
magnitude = tf.log1p(mu * safe_audio_abs) / tf.log1p(mu)
signal = tf.sign(audio) * magnitude
# 将信号量化到指定的数值
return tf.to_int32((signal + 1) / 2 * mu + 0.5)
mu_law_decode
下面这段代码的主要任务是:从量化值恢复波形
def mu_law_decode(output, quantization_channels):
# 创建 decode 变量命名空间
with tf.name_scope('decode'):
# 量化振幅值的数量
mu = quantization_channels - 1
# 将值映射回[- 1,1]
signal = 2 * (tf.to_float(output) / mu) - 1
# 进行 mu-law 逆变换,并返回
magnitude = (1 / mu) * ((1 + mu)**abs(signal) - 1)
return tf.sign(signal) * magnitude
本文还在持续更新中!
欢迎各位大佬交流讨论!