加入CSDN的昇思MindSpore社区昇思MindSpore社区-CSDN社区云
1. 前言
作为一名深度学习领域的开发者,我对音频生成方向的前沿模型一直保持关注。DeepMind提出的WaveNet凭借其直接对原始音频波形建模的能力,成为了该领域的标志性工作。
近期,我注意到华为开源自研AI框架昇思MindSpore在音频生成领域的应用案例。为了验证国产框架对复杂生成模型的支持能力,并探索WaveNet在实际音乐生成任务中的表现,我参考官方社区教程,基于MindSpore复现了这一模型。本文将详细记录复现过程中的技术细节与思考。
参考来源:WaveNet原论文《WaveNet: A generative model for raw audio》
2. 环境搭建
复现的第一步永远是配环境。我使用的是昇思大模型的Notebook环境(不得不说,云环境省去了很多本地配置驱动的烦恼)。
2.1 检查版本
打开Notebook,我先看了一眼预置环境:
!pip show mindspore
输出显示是旧版本。查看案例要求,明确指出需要 MindSpore 2.7.1。
2.2 升级安装
这里我直接照搬了教程里的安装命令,不过为了稳妥,我先卸载了旧版本:
# 卸载旧版本,避免冲突
!pip uninstall mindspore -y
# 安装指定版本
%env MINDSPORE_VERSION=2.7.1
!pip install mindspore==2.7.1 -i https://repo.mindspore.cn/pypi/simple --trusted-host repo.mindspore.cn --extra-index-url https://repo.huaweicloud.com/repository/pypi/simple
2.3 安装音频处理依赖
WaveNet需要专门的音频处理库,这一步不能省:
!pip install librosa
!pip install soundfile
!pip install nnmnkwii
体验点:安装过程比预想的快,镜像源速度很给力。安装完后再次check版本,确认无误。这一步顺利通过
3. 代码复现:见证生成时刻
环境搞定后,就是激动人心的跑代码环节了。
3.1 导包与数据准备
导入mindspore和相关音频处理模块。这一步没啥好说的,直接运行。
import mindspore as ms
import mindspore.dataset as ds
import numpy as np
from pathlib import Path
import librosa
from nnmnkwii import preprocessing as pre
import soundfile as sf
接下来是数据预处理。WaveNet使用μ率压扩变换将原始音频量化到256个值:
# 加载音频并进行μ率量化
audio, _ = librosa.load("music.wav", sr=16000, mono=True)
wav_quantized = pre.mulaw_quantize(audio, 256)
这里我稍微停顿了一下。μ率变换是音频处理中的经典技术,可以将16位音频(65536种取值)压缩到256种,大大降低了预测难度。nnmnkwii库封装得很好,一行代码搞定。
3.2 构建数据集
WaveNet的训练数据构建有点特殊。训练时,网络一次性预测多个样本,输入长度 = 感受野 + 输出长度 - 1。
class WaveDataset:
def __init__(self, dataset_file, receptive_feild, output_length, ...):
self.item_length = receptive_feild + output_length
# ... 加载数据并切片
def __getitem__(self, index):
data_slice = self.data[num_audio][pos_start: pos_end]
onehot = self.get_onehot(data_slice[: -1]) # 输入
target = data_slice[-self.item_length + 1:] # 标签
return onehot.astype(np.float32), target.astype(np.int32)
数据集加载成功后,控制台打印出数据集大小,悬着的心放下了。
3.3 构建网络
WaveNet的核心是残差单元,包含扩张卷积和门控激活:
from mindspore import nn, mint
import math
class ResidualConv1dGLU(nn.Cell):
def __init__(self, residual_channels, gate_channels, kernel_size,
skip_out_channels, dilation=1, dropout=0.05):
super(ResidualConv1dGLU, self).__init__()
padding = (kernel_size - 1) * dilation
self.conv = mint.nn.Conv1d(residual_channels, gate_channels, kernel_size,
padding=padding, dilation=dilation)
gate_out_channels = gate_channels // 2
self.conv1x1_out = mint.nn.Conv1d(gate_out_channels, residual_channels, 1)
self.conv1x1_skip = mint.nn.Conv1d(gate_out_channels, skip_out_channels, 1)
def construct(self, x):
residual = x
x = self.conv(x)
x = x[:, :, :residual.shape[-1]] # 保持因果性
a, b = mint.chunk(x, chunks=2, dim=1)
x = mint.mul(mint.tanh(a), mint.sigmoid(b)) # 门控激活
s = self.conv1x1_skip(x) # 跳跃连接
x = self.conv1x1_out(x)
x = mint.mul(mint.add(x, residual), math.sqrt(0.5)) # 残差连接
return x, s
体验点:
MindSpore的mint模块提供了类似PyTorch的函数式API,写起来非常顺手。门控激活用tanh和sigmoid的组合,一行代码搞定。扩张卷积通过dilation参数控制,感受野呈指数增长。
接下来是WaveNet主体:
class WaveNet(nn.Cell):
def __init__(self, out_channels=256, layers=24, blocks=4, ...):
super().__init__()
self.first_conv = mint.nn.Conv1d(out_channels, residual_channels, 1)
conv_layers = []
for layer in range(layers):
dilation = 2 ** (layer % (layers // blocks)) # 扩张系数
conv = ResidualConv1dGLU(..., dilation=dilation)
conv_layers.append(conv)
self.conv_layers = nn.CellList(conv_layers)
# 计算感受野
self.receptive_field = 1
for _ in range(blocks):
additional_scope = 2
for _ in range(layers // blocks):
self.receptive_field += additional_scope
additional_scope *= 2
print("receptive field:", self.receptive_field)
运行这段代码,控制台输出:
receptive field: 4095
感受野达到4095个样本点,对于16kHz采样率来说约0.26秒,足够捕捉音频的局部结构了。
3.4 模型训练
MindSpore使用函数式自动微分,训练代码写起来很清晰:
from mindspore import ops
from mindspore.amp import all_finite
def train_loop(model, dataset, loss_fn, optimizer, logger):
def forward_fn(data, label):
logits = model(data)
loss = loss_fn(logits, label)
return loss, logits
grad_fn = ops.value_and_grad(forward_fn, None, optimizer.parameters, has_aux=True)
def train_step(data, label):
(loss, logits), grads = grad_fn(data, label)
if all_finite(grads):
optimizer(grads)
return loss
model.set_train()
for batch, (data, label) in enumerate(dataset.create_tuple_iterator()):
loss = train_step(data, label)
if batch % 20 == 0:
print(f"loss: {loss.asnumpy():.3f}\t\t{batch}/{size}")
开始训练:
model = WaveNet(out_channels=256, layers=24, blocks=4)
loss_fn = nn.CrossEntropyLoss()
optimizer = nn.Adam(model.trainable_params(), learning_rate=0.001)
for t in range(epochs):
print(f"Epoch {t+1}")
train_loop(model, dataset, loss_fn, optimizer, loss_recoder)
ms.save_checkpoint(model, f"./wavenet_{t}.ckpt")
体验点:
训练开始后,Loss从最初的4.x逐渐下降。大概跑了几个epoch后,Loss稳定在2.x左右。MindSpore的自动微分机制运行稳定,没有出现梯度爆炸的问题。
交叉熵损失随着迭代次数稳步下降,网络学习正常。
3.5 音乐生成
训练完成后,最激动人心的环节来了——让模型生成音乐!
import random
from tqdm import tqdm
def gen_music(model, gen_time_length, head_location, head_length=1024):
# 加载预测头(一小段音频作为起始)
head_file = np.load(head_location)["arr_0"]
random_start = random.randint(0, len(head_file) - head_length)
head = head_file[random_start: random_start + head_length]
total_length = int(gen_time_length * 16000 * 60)
for _ in tqdm(range(total_length), ncols=60):
current_input = head[-head_length:]
pred = pred_one(model, current_input).asnumpy()
head = np.append(head, pred) # 将预测结果追加到序列末尾
return head
def pred_one(model, x):
onehot = np.eye(256)[x].transpose()
input_tensor = ms.Tensor(onehot).astype(ms.float32)
input_tensor = mint.unsqueeze(input_tensor, 0)
pred = model(input_tensor)
pred_sample = pred[0, :, -1]
return mint.argmax(pred_sample)
加载模型并生成:
model = WaveNet(out_channels=256, layers=24, blocks=4)
ms.load_checkpoint("wavenet_1.ckpt", model)
model.set_train(False)
output = gen_music(model, gen_time_length=1/6, head_location="./pred_head.npz")
output = pre.inv_mulaw_quantize(output, 256) # μ率反变换
sf.write("gen.wav", output, 16000, subtype='PCM_24')
print("generated!")
体验点:
生成过程是逐样本进行的,所以速度不算快。生成10秒音频大概需要几分钟。听到生成的音频那一刻,虽然质量还达不到商业水准,但能清晰听到学习到的音色和节奏特征,这种"AI在创作"的感觉很奇妙。
仔细听生成的音频,虽然有些噪声,但整体节奏和音色特征都被学习到了。这就是WaveNet强大的生成能力,而MindSpore完美地复现了这一能力。
4. 总结
- API设计友好:MindSpore的
mint模块提供了类似PyTorch的函数式API,从PyTorch迁移过来的开发者几乎没有学习成本。 - 自动微分稳定:在训练这种深层生成模型时,MindSpore的自动微分机制表现稳定,没有出现梯度异常。
- 生成效果可验证:虽然WaveNet的生成质量受限于训练数据和模型规模,但Loss曲线和生成音频都证明了复现的正确性。
结论:官方这个案例完全可复现,且体验良好。如果你也想在国产框架上探索音频生成领域,MindSpore是一个非常值得尝试的选择!
