语音与音乐信号处理轻松入门(基于Python与PyTorch)

为了对音乐风格进行分类,需要提取音频的一些特征,在本案例中提取了MFCC、频谱质心等特征。Torchaudio中提供了相应的处理功能,不过为了保持本书的一贯性仍然使用了librosa进行处理。所有音频统一取前128帧的特征并组合成一个33维的特征向量,最后将提取的特征和标签数据保存为npy文件。特征提取阶段的代码如下:

第12章/prepare_data.

import librosa
import re
import numpy as np

root_dir = 'E:/Download/Genres/' #GTZAN数据集根目录
feature_file = 'features.npy' #特征数据文件
label_file = 'labels.npy' #标签数据文件

数据集中所有音乐风格分类

genre_list = ['blues', #布鲁斯
'classical', #古典音乐
'country', #乡村音乐
'disco', #迪斯科
'hiphop', #嘻哈
'jazz', #爵士乐
'metal', #金属乐
'pop', #流行音乐
'reggae', #雷盖乐
'rock'] #摇滚乐

训练用音频文件清单

def make_list(start_num, end_num):
#start_num:起始编号;end_num:截止编号
filelist = []
for i in range(len(genre_list)):
genre = genre_list[i]
dir = root_dir + genre + '/'
for id in range(start_num, end_num):
name = dir + genre + '.' + f'{id:05d}'+'.wav'
filelist.append(name)
return filelist

生成独热代码

def onehot(target):
code = np.zeros((target.shape[0], len(genre_list)))
for i, str in enumerate(target):
index = genre_list.index(str)
code[i, index] = 1
return code

提取音频特征

def get_features(file_list):
hop_length = 512
total = len(file_list) #文件数量
data = np.zeros((total, 128, 33), dtype=np.float64)
target = []

for i, file in enumerate(file_list):

加载文件并提取mfcc、频谱质心等特征

y, sr = librosa.load(file, sr=22050)
mfcc = librosa.feature.mfcc(
y=y, sr=sr, hop_length=hop_length, n_mfcc=13
)
centroid = librosa.feature.spectral_centroid(
y=y, sr=sr, hop_length=hop_length
)
chroma = librosa.feature.chroma_stft(
y=y, sr=sr, hop_length=hop_length
)
contrast = librosa.feature.spectral_contrast(
y=y, sr=sr, hop_length=hop_length
)

data[i, :, 0:13] = mfcc.T[0:128, :]
data[i, :, 13:14] = centroid.T[0:128, :]
data[i, :, 14:26] = chroma.T[0:128, :]
data[i, :, 26:33] = contrast.T[0:128, :]

提取音乐风格名称

genre = re.split("[ /]", file)[3]
target.append(genre)

输出处理结果

print('Feature extraction for No. %i of %i files finished.'
% (i + 1, total))
return data, np.expand_dims(np.asarray(target), axis=1)

if name == "main":

生成音频文件清单(每种风格前70个)

filelist = make_list(0, 70)
print('Total files: ', len(filelist))

提取音频特征并保存为npy文件

features, target = get_features(filelist)
labels = onehot(target)
with open(feature_file, "wb") as f:
np.save(f, features)
with open(label_file, "wb") as f:
np.save(f, labels)

print('Feature extraction finished!')

上述代码运行后将生成features.npy和labels.npy两个数据文件供调用。由于每种音乐风格仅提取了前70个文件的特征,因此特征数据的维度为(700,128,33)。

12.3.3 模型及训练
接下来进入模型设计阶段。本案例采用LSTM模型,因而结构比较简单。训练的代码也并不复杂,每批样本数设为35个,这样批次共20批(700/35=20),考虑到处理速度,优先使用GPU进行训练。有关模型和训练过程的代码如下:

第12章/train.py

import os
import sys
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

LSTM模型定义

class LSTM(nn.Module):
#模型初始化函数
def init(self, input_size, hidden_size, output_size, num_layers):
super(LSTM, self).init()
self.input_size = input_size
self.hidden_size = hidden_size
self.num_layers = num_layers

self.lstm = nn.LSTM(self.input_size, self.hidden_size,
self.num_layers) #LSTM层
self.fc = nn.Linear(self.hidden_size, output_size) #全连接层

前向计算函数

def forward(self, input, hidden=None):
out, hidden = self.lstm(input, hidden)
logits = self.fc(out[-1])
predict = F.log_softmax(logits, dim=1)
return predict, hidden

计算准确率的函数

def cal_accuracy(self, predictions, labels, batch_size):
#获取最大值的索引值
predict = torch.max(predictions.data, 1)[1]
#预测准确次数
correct_num = predict.eq(labels.data).sum()
#计算准确率并返回
accuracy = correct_num / batch_size

return accuracy.item()

训练函数

def train(features, labels, model, criterion, optimizer, epochs,
num_batches, batch_size, byCuda):

使用GPU时的转换

if byCuda:
model = model.cuda()
criterion = criterion.cuda()

训练循环

for epoch in range(epochs):

初始化

losses = 0.0
accuracy = 0.0
hidden_state = None

处理一个批次

for i in range(num_batches):

提取一批次数据

feature = features[i * batch_size: (i + 1) * batch_size, ]
label = labels[i * batch_size: (i + 1) * batch_size, ]

根据模型和损失函数要求对数据进行调整

feature = feature.permute(1, 0, 2) #转置函数
label = torch.max(label, 1)[1] #NLLLoss函数要求格式

张量转移到GPU上

if by_cuda:
feature = feature.cuda()
label = label.cuda()

梯度清零

model.zero_grad()

前向传播

pred, _ = model(feature, hidden_state)

计算损失函数值

loss = criterion(pred, label)

反向传播

loss.backward()

对网络进行优化

optimizer.step()

累计损失函数值并计算准确率

losses += loss.item()
accuracy += model.cal_accuracy(pred, label, batch_size)

输出此轮训练结果

print('Epoch: %d | NLLoss: %.4f | Accuracy rate: %.2f'
% (epoch+1, losses / num_batches, 100.0 * accuracy / num_batches))

if name == "main":

feature_file = 'features.npy' #特征数据文件
label_file = 'labels.npy' #标签数据文件

检查是否存在npy文件

if (os.path.isfile(feature_file) and os.path.isfile(label_file)):
print('Loading npy files')
feature_data = np.load(feature_file)
label_data = np.load(label_file)
else:
print('Preprocessed files does not exist, please check!')
sys.exit()

转换成张量并输出shape

features = torch.from_numpy(feature_data).type(torch.Tensor)
labels = torch.from_numpy(label_data).type(torch.LongTensor)
print('Features shape: ' + str(feature_data.shape))
print('Labels shape: ' + str(label_data.shape))

epochs = 200 #训练轮数
batch_size = 35 #每批样本数
num_batches = int(features.shape[0] / batch_size) #700÷35=20批

构建LSTM模型并输出模型参数

print('Building LSTM model ...')
model = LSTM(33, 128, 10, 2)
criterion = nn.NLLLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
print(model.parameters)

检查是否有Cuda

by_cuda = torch.cuda.is_available()
if by_cuda:
print('Training on GPU ...')
else:
print('Training on CPU ...')

训练过程

train(features, labels, model, criterion,
optimizer, epochs, num_batches, batch_size, by_cuda)

上述代码先检查是否有处理好的特征数据和标签数据文件,确认无误后开始构建模型,其间将输出如图12-19所示的信息。

posted @ 2025-10-06 10:27  tupjzhao  阅读(140)  评论(0)    收藏  举报