使用隐马尔可夫模型进行音乐流派分类的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
让我们从机器学习数据集中选择一首歌并提取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代码如下:
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])
现在,我们来看看我们机器学习数据集的一些示例。我们遍历流派文件夹并可视化每个文件夹中第一首歌曲的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()
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)
训练和评估隐马尔可夫模型
为了训练隐马尔可夫模型,我们遍历数据集中的子文件夹,我们迭代子文件夹中的歌曲以提取特征并将其附加到变量。
我们应该存储所有训练过的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
现在是时候评估我们的模型了,我们迭代测试数据集子文件夹,提取特征,然后我们遍历所有
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)
到目前为止,在我们的测试数据集中,我们已经有了每首歌曲的真实标签。由于我们的问题是一个多类分类,评估模型性能的最佳方法是查看混淆矩阵。
我们使用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')
是时候计算混淆矩阵并将其可视化了!
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()
对于一个完美的分类器,我们可以预期从左上角到右下角的黑色方块的对角线,以及剩余区域的浅色。正如我们从矩阵图中可以看到的,'Pop'类的HMM模型能够完美地预测歌曲的类别。'Country'和'Hip-hop'也表现不错。然而,最糟糕的模型是'Rock'类别的模型!
为了获得更好的评估,我们始终建议对分类问题使用预先设定和召回指标。sklearn使我们能够轻松地获得有关多类分类的精度和召回率的详细报告。我们只需要提供真实值和预测值以及我们的类名。
from sklearn.metrics import classification_report print(classification_report(real, pred, target_names=classes))
输出如下:
结论
们的隐马尔可夫模型的性能相对平均,显然,还有很大的改进空间。实际上,隐马尔可夫模型对于时间序列数据是可解释的和强大的,但是,它们需要大量的微调(隐藏状态的数量,输入特征.等)。最好尝试其他方法来对音乐类型进行分类,比如循环神经网络(LSTM)。