使用隐马尔可夫模型进行音乐流派分类的Python实现

crbrave 2019-03-03

使用隐马尔可夫模型进行音乐流派分类的Python实现

音乐类型分类一直是音乐信息检索领域研究的热点问题。在本教程中,我们将尝试使用隐马尔可夫模型对音乐类型进行分类,隐马尔可夫模型非常擅长对时间序列数据进行建模。由于音乐音频文件是时间序列信号,我们希望HMM能够满足我们的需求,给我们一个准确的分类。隐马尔可夫模型是表示观察序列的概率分布的模型。我们假设输出是由隐藏状态生成的。

数据集和特征

我们将使用Marsyas提供的机器学习数据集,这是一个名为GTZAN的开源软件。它是每30秒长的1000个音轨的集合。代表10种类型,每种包含100个音轨。所有音轨都是.au格式的22050Hz单声道16位音频文件。在我们的教程中,我们将使用所有提供的类型(blues, classical, jazz, country, pop, rock, metal, disco, hip-hop, reggae)。对于音乐类型分类,我们将更容易使用WAV文件,因为它们可以通过scipy库轻松读取。因此,我们必须将AU文件转换为WAV格式。可以在此处(http://opihi.cs.uvic.ca/sound/genres.tar.gz)访问机器学习数据集。

对于音频处理,我们需要找到一种简洁地表示歌曲波形的方法。Mel频率倒谱系数(MFCC)是一种很好的方法。MFCC获取信号的功率谱(power spectrum),然后使用Filter banks和离散余弦变换的组合来提取特征。

让我们开始为我们的项目导入必要的Python库。

from python_speech_features import mfcc, logfbank
from scipy.io import wavfile
import numpy as np
import matplotlib.pyplot as plt
from hmmlearn import hmm
from sklearn.metrics import confusion_matrix
import itertools
import os

使用隐马尔可夫模型进行音乐流派分类的Python实现

让我们从机器学习数据集中选择一首歌并提取MFCC和Filter banks 特征。

sampling_freq, audio = wavfile.read("genres/blues/blues.00000.wav")
mfcc_features = mfcc(audio, sampling_freq)
filterbank_features = logfbank(audio, sampling_freq)

使用隐马尔可夫模型进行音乐流派分类的Python实现

我们来看一下提取的特征的形状。Python代码如下:

print ('
MFCC:
Number of windows =', mfcc_features.shape[0])
print ('Length of each feature =', mfcc_features.shape[1])
print ('
Filter bank:
Number of windows =', filterbank_features.shape[0])
print ('Length of each feature =', filterbank_features.shape[1])

使用隐马尔可夫模型进行音乐流派分类的Python实现

使用隐马尔可夫模型进行音乐流派分类的Python实现

现在,我们来看看我们机器学习数据集的一些示例。我们遍历流派文件夹并可视化每个文件夹中第一首歌曲的MFCC特征。Python实现如下:

import glob
import os.path as path
genre_list = [“blues”,”classical”, “jazz”, “country”, “pop”, “rock”, “metal”, “disco”, “hiphop”, “reggae”]
print(len(genre_list))
figure = plt.figure(figsize=(20,3))
for idx ,genre in enumerate(genre_list): 
 example_data_path = ‘genres/’ + genre
 file_paths = glob.glob(path.join(example_data_path, ‘*.wav’))
 sampling_freq, audio = wavfile.read(file_paths[0])
 mfcc_features = mfcc(audio, sampling_freq, nfft=1024)
 print(file_paths[0], mfcc_features.shape[0])
 plt.yscale(‘linear’)
 plt.matshow((mfcc_features.T)[:,:300])
 plt.text(150, -10, genre, horizontalalignment=’center’, fontsize=20)
 
plt.yscale(‘linear’)
plt.show()

使用隐马尔可夫模型进行音乐流派分类的Python实现

使用隐马尔可夫模型进行音乐流派分类的Python实现

MFCC特征为每个文件夹(流派)中的第一首歌曲

建立隐马尔可夫模型

通过包装hmmlearn库提供的模型,我们构建了一个处理HMM训练和预测的Python类。

class HMMTrainer(object):
 def __init__(self, model_name='GaussianHMM', n_components=4, cov_type='diag', n_iter=1000):
 self.model_name = model_name
 self.n_components = n_components
 self.cov_type = cov_type
 self.n_iter = n_iter
 self.models = []
 if self.model_name == 'GaussianHMM':
 self.model = hmm.GaussianHMM(n_components=self.n_components, covariance_type=self.cov_type,n_iter=self.n_iter)
 else:
 raise TypeError('Invalid model type') 
 def train(self, X):
 np.seterr(all='ignore')
 self.models.append(self.model.fit(X))
 # Run the model on input data
 def get_score(self, input_data):
 return self.model.score(input_data)

使用隐马尔可夫模型进行音乐流派分类的Python实现

训练和评估隐马尔可夫模型

为了训练隐马尔可夫模型,我们遍历数据集中的子文件夹,我们迭代子文件夹中的歌曲以提取特征并将其附加到变量。

我们应该存储所有训练过的HMM模型,这样我们就能预测出看不见的歌曲类别。由于HMM是无监督机器学习的生成模型,我们不需要标签来为每个类构建HMM模型。我们明确假设将为

每个类构建单独的HMM模型。

请注意,我们使用4作为components 的数量,这正是HMM模型中隐藏状态的数量。找出最佳状态数:测试不同值,并挑选优化预测的那个值。

hmm_models = []
input_folder = 'genres/'
# Parse the input directory
for dirname in os.listdir(input_folder):
 # Get the name of the subfolder
 subfolder = os.path.join(input_folder, dirname)
 if not os.path.isdir(subfolder):
 continue
 # Extract the label
 label = subfolder[subfolder.rfind('/') + 1:]
 # Initialize variables
 X = np.array([])
 y_words = []
 # Iterate through the audio files (leaving 1 file for testing in each class)
 for filename in [x for x in os.listdir(subfolder) if x.endswith('.wav')][:-1]:
 # Read the input file
 filepath = os.path.join(subfolder, filename)
 sampling_freq, audio = wavfile.read(filepath)
 # Extract MFCC features
 mfcc_features = mfcc(audio, sampling_freq)
 # Append to the variable X
 if len(X) == 0:
 X = mfcc_features
 else:
 X = np.append(X, mfcc_features, axis=0)
 # Append the label
 y_words.append(label)
 print('X.shape =', X.shape)
 # Train and save HMM model
 hmm_trainer = HMMTrainer(n_components=10)
 hmm_trainer.train(X)
 hmm_models.append((hmm_trainer, label))
 hmm_trainer = None

现在是时候评估我们的模型了,我们迭代测试数据集子文件夹,提取特征,然后我们遍历所有

使用隐马尔可夫模型进行音乐流派分类的Python实现

HMM模型并选择得分最高的模型。

input_folder = 'test/'
real_labels = []
pred_labels = []
for dirname in os.listdir(input_folder):
 subfolder = os.path.join(input_folder, dirname)
 if not os.path.isdir(subfolder):
 continue
 # Extract the label
 label_real = subfolder[subfolder.rfind('/') + 1:]
 for filename in [x for x in os.listdir(subfolder) if x.endswith('.wav')][:-1]:
 real_labels.append(label_real)
 filepath = os.path.join(subfolder, filename)
 sampling_freq, audio = wavfile.read(filepath)
 mfcc_features = mfcc(audio, sampling_freq)
 max_score = -9999999999999999999
 output_label = None
 for item in hmm_models:
 hmm_model, label = item
 score = hmm_model.get_score(mfcc_features)
 if score > max_score:
 max_score = score
 output_label = label
pred_labels.append(output_label)

使用隐马尔可夫模型进行音乐流派分类的Python实现

到目前为止,在我们的测试数据集中,我们已经有了每首歌曲的真实标签。由于我们的问题是一个多类分类,评估模型性能的最佳方法是查看混淆矩阵。

我们使用sklearn提供的混淆矩阵,并使用matplotlib库对其进行可视化。

首先,让我们定义一个Python函数来处理绘图。此函数打印并绘制混淆矩阵。可以通过设置`normalize = True`来应用归一化。

def plot_confusion_matrix(cm, classes,
 normalize=False,
 title='Confusion matrix',
 cmap=plt.cm.Blues):
 if normalize:
 cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
 print("Normalized confusion matrix")
 else:
 print('Confusion matrix, without normalization')
 print(cm)
 plt.imshow(cm, interpolation='nearest', cmap=cmap)
 plt.title(title)
 plt.colorbar()
 tick_marks = np.arange(len(classes))
 plt.xticks(tick_marks, classes, rotation=45)
 plt.yticks(tick_marks, classes)
 fmt = '.2f' if normalize else 'd'
 thresh = cm.max() / 2.
 for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
 plt.text(j, i, format(cm[i, j], fmt),
 horizontalalignment="center",
 color="white" if cm[i, j] > thresh else "black")
 plt.tight_layout()
 plt.ylabel('True label')
 plt.xlabel('Predicted label')

使用隐马尔可夫模型进行音乐流派分类的Python实现

是时候计算混淆矩阵并将其可视化了!

cm = confusion_matrix(real, pred)
np.set_printoptions(precision=2)
classes = ["blues","classical", "country", "disco", "hiphop", "jazz", "metal", "pop", "reggae", "rock"]
plt.figure()
plot_confusion_matrix(cm, classes=classes, normalize=True,
 title='Normalized confusion matrix')
plt.show()

使用隐马尔可夫模型进行音乐流派分类的Python实现

使用隐马尔可夫模型进行音乐流派分类的Python实现

对于一个完美的分类器,我们可以预期从左上角到右下角的黑色方块的对角线,以及剩余区域的浅色。正如我们从矩阵图中可以看到的,'Pop'类的HMM模型能够完美地预测歌曲的类别。'Country'和'Hip-hop'也表现不错。然而,最糟糕的模型是'Rock'类别的模型!

为了获得更好的评估,我们始终建议对分类问题使用预先设定和召回指标。sklearn使我们能够轻松地获得有关多类分类的精度和召回率的详细报告。我们只需要提供真实值和预测值以及我们的类名。

from sklearn.metrics import classification_report
print(classification_report(real, pred, target_names=classes))

使用隐马尔可夫模型进行音乐流派分类的Python实现

输出如下:

使用隐马尔可夫模型进行音乐流派分类的Python实现

结论

们的隐马尔可夫模型的性能相对平均,显然,还有很大的改进空间。实际上,隐马尔可夫模型对于时间序列数据是可解释的和强大的,但是,它们需要大量的微调(隐藏状态的数量,输入特征.等)。最好尝试其他方法来对音乐类型进行分类,比如循环神经网络(LSTM)。

相关推荐