我在本科的期末项目中想构建一个神经网络,它将从一个wav文件中提取前13个MFCC系数,并从一组说话人中识别出音频文件中的说话人是谁。
我想让你注意以下几点:
- 我的音频文件是文本独立的,因此它们的长度和词汇各不相同
- 我用大约35个音频文件训练了机器,这些文件来自10个说话人(第一个说话人有大约15个文件,第二个有10个,第三个和第四个各有5个)
我定义了:
X=mfcc(sound_voice)
Y=zero_array + 1在第i个位置(其中第i个位置对于第一个说话人是0,对于第二个是1,对于第三个是2…)
然后训练了机器,接着检查了机器对一些文件的输出…
这就是我所做的…但不幸的是,结果看起来完全是随机的…
你能帮我理解为什么吗?
这是我的Python代码 –
from sklearn.neural_network import MLPClassifierimport python_speech_featuresimport scipy.io.wavfile as wavimport numpy as npfrom os import listdirfrom os.path import isfile, joinfrom random import shuffleimport matplotlib.pyplot as pltfrom tqdm import tqdmwinner = [] # 这个数组记录我们在测试神经网络时有多少次命中当我们测试NN时for TestNum in tqdm(range(5)): # 在每一轮中,我们用X,Y构建NN,之后我们检查50个构建NN后的 X = [] Y = [] onlyfiles = [f for f in listdir("FinalAudios/") if isfile(join("FinalAudios/", f))] # 目录中的文件 names = [] # 说话人的名字 for file in onlyfiles: # 对于每个wav声音文件 # 不需要理解代码 if " " not in file.split("_")[0]: names.append(file.split("_")[0]) else: names.append(file.split("_")[0].split(" ")[0]) names = list(dict.fromkeys(names)) # 说话人的名字 vector_names = [] # 每个名字的向量 i = 0 vector_for_each_name = [0] * len(names) for name in names: vector_for_each_name[i] += 1 vector_names.append(np.array(vector_for_each_name)) vector_for_each_name[i] -= 1 i += 1 for f in onlyfiles: if " " not in f.split("_")[0]: f_speaker = f.split("_")[0] else: f_speaker = f.split("_")[0].split(" ")[0] (rate, sig) = wav.read("FinalAudios/" + f) # 读取文件 try: mfcc_feat = python_speech_features.mfcc(sig, rate, winlen=0.2, nfft=512) # MFCC系数 for index in range(len(mfcc_feat)): # 将每个MFCC系数添加到X中,这意味着如果有50000个系数, # X将是[第一个系数,第二个...第50000个系数],Y将是[f_speaker_vector] * 50000 X.append(np.array(mfcc_feat[index])) Y.append(np.array(vector_names[names.index(f_speaker)])) except IndexError: pass Z = list(zip(X, Y)) shuffle(Z) # 我们打乱X,Y以在测试级别上进行随机化 X, Y = zip(*Z) X = list(X) Y = list(Y) X = np.asarray(X) Y = np.asarray(Y) Y_test = Y[:50] # 选择50个用于测试,其余用于训练 X_test = X[:50] X = X[50:] Y = Y[50:] clf = MLPClassifier(solver='lbfgs', alpha=1e-2, hidden_layer_sizes=(5, 3), random_state=2) # 创建神经网络 clf.fit(X, Y) # 训练它 for sample in range(len(X_test)): # 如果我们正确预测,添加1到winner数组中,否则添加0,然后在最后绘制它 if list(clf.predict([X[sample]])[0]) == list(Y_test[sample]): winner.append(1) else: winner.append(0)# 绘制winnerplot_x = []plot_y = []for i in range(1, len(winner)): plot_y.append(sum(winner[0:i])*1.0/len(winner[0:i])) plot_x.append(i)plt.plot(plot_x, plot_y)plt.xlabel('x轴')# 命名y轴plt.ylabel('y轴')# 为图表命名标题plt.title('我的第一张图表!')# 显示图表的函数plt.show()
这是包含代码和音频文件的zip文件: https://ufile.io/eggjm1gw
回答:
你的代码中存在多个问题,一次性解决这些问题几乎是不可能的,但我们可以尝试。主要有两个大问题:
- 目前你试图用非常少的训练示例来训练你的神经网络,每个说话人可能只有一个示例(!)。任何机器学习算法都不可能从中学到任何东西。
- 更糟糕的是,你只向ANN输入每个录音的前25毫秒的MFCC(25来自
python_speech_features
的winlen参数)。在这些录音中,前25毫秒几乎是相同的。即使你有每个说话人1万个录音,用这种方法你也无法取得任何进展。
我会给你具体的建议,但不会做所有的编码——毕竟这是你的作业。
- 使用所有MFCC,而不仅仅是前25毫秒。许多这些应该被跳过,因为没有语音活动。通常应该有一个VOD(语音活动检测器)告诉你应该取哪些,但在这个练习中,我建议初学者先跳过它(你需要先学习基础知识)。
- 不要使用字典。不仅它无法处理每个说话人的多个MFCC向量,而且对于你的任务来说,它还是一个非常低效的数据结构。使用
numpy
数组,它们速度更快,内存使用更有效。有大量的教程,包括scikit-learn
,展示了如何在这种情况下使用numpy
。本质上,你创建两个数组:一个用于训练数据,另一个用于标签。例如:如果说话人omersk“产生”50000个MFCC向量,你将得到(50000, 13)
的训练数组。对应的标签数组将是50000
,带有一个常量值(id),对应于说话人(假设omersk是0,lucas是1,以此类推)。我建议使用更长的窗口(或许是200毫秒,试验一下!)来减少方差。
别忘了将你的数据分为训练、验证和测试。你将有足够的数据。此外,对于这个练习,我建议注意不要为任何单个说话人输入过多的数据——或者采取措施确保算法不会有偏见。
之后,当你进行预测时,你将再次计算说话人的MFCC。对于10秒的录音,200毫秒的窗口和100毫秒的重叠,你将得到99个MFCC向量,形状为(99, 13)
。模型应该在99个向量中的每一个上运行,每个向量产生一个概率。当你对其求和(并规范化,使其看起来好一些)并取最高值时,你将得到最可能的说话人。
还有许多其他通常会考虑的事情,但在这种情况下(作业),我建议专注于掌握基础知识。
编辑:我决定尝试根据你的想法创建模型,但修复了基础问题。这不是完全干净的Python代码,因为它是从我运行的Jupyter笔记本中改编的。
import python_speech_featuresimport scipy.io.wavfile as wavimport numpy as npimport globimport osfrom collections import defaultdictfrom sklearn.neural_network import MLPClassifierfrom sklearn import preprocessingfrom sklearn.model_selection import cross_validatefrom sklearn.ensemble import RandomForestClassifieraudio_files_path = glob.glob('audio/*.wav')win_len = 0.04 # 单位为秒step = win_len / 2nfft = 2048mfccs_all_speakers = []names = []data = []for path in audio_files_path: fs, audio = wav.read(path) if audio.size > 0: mfcc = python_speech_features.mfcc(audio, samplerate=fs, winlen=win_len, winstep=step, nfft=nfft, appendEnergy=False) filename = os.path.splitext(os.path.basename(path))[0] speaker = filename[:filename.find('_')] data.append({'filename': filename, 'speaker': speaker, 'samples': mfcc.shape[0], 'mfcc': mfcc}) else: print(f'由于文件大小为0,跳过{path}')speaker_sample_size = defaultdict(int)for entry in data: speaker_sample_size[entry['speaker']] += entry['samples']person_with_fewest_samples = min(speaker_sample_size, key=speaker_sample_size.get)print(person_with_fewest_samples)max_accepted_samples = int(speaker_sample_size[person_with_fewest_samples] * 0.8)print(max_accepted_samples)training_idx = []test_idx = []accumulated_size = defaultdict(int)for entry in data: if entry['speaker'] not in accumulated_size: training_idx.append(entry['filename']) accumulated_size[entry['speaker']] += entry['samples'] elif accumulated_size[entry['speaker']] < max_accepted_samples: accumulated_size[entry['speaker']] += entry['samples'] training_idx.append(entry['filename'])X_train = []label_train = []X_test = []label_test = []for entry in data: if entry['filename'] in training_idx: X_train.append(entry['mfcc']) label_train.extend([entry['speaker']] * entry['mfcc'].shape[0]) else: X_test.append(entry['mfcc']) label_test.extend([entry['speaker']] * entry['mfcc'].shape[0])X_train = np.concatenate(X_train, axis=0)X_test = np.concatenate(X_test, axis=0)assert (X_train.shape[0] == len(label_train))assert (X_test.shape[0] == len(label_test))print(f'训练:{X_train.shape}')print(f'测试:{X_test.shape}')le = preprocessing.LabelEncoder()y_train = le.fit_transform(label_train)y_test = le.transform(label_test)clf = MLPClassifier(solver='lbfgs', alpha=1e-2, hidden_layer_sizes=(5, 3), random_state=42, max_iter=1000)cv_results = cross_validate(clf, X_train, y_train, cv=4)print(cv_results){'fit_time': array([3.33842635, 4.25872731, 4.73704267, 5.9454329 ]), 'score_time': array([0.00125694, 0.00073504, 0.00074005, 0.00078583]), 'test_score': array([0.40380048, 0.52969121, 0.48448687, 0.46043165])}
test_score
并不出色。有很多可以改进的地方(首先是算法的选择),但基础知识已经到位。注意我是如何获取训练样本的。不是随机的,我只考虑整体的录音。你不能将来自某个录音的样本同时放入training
和test
中,因为test
应该是新的。
你的代码中哪里出了问题?我认为很多地方。你取了200毫秒的样本,但fft
非常短。python_speech_features
可能会抱怨你fft
应该比你处理的帧更长。
我把测试模型的工作留给你。它不会很好,但这是一个开始。