音频样本分类案例研究
在实际的数据科学工作中,我们经常会遇到需要对音频样本进行分类的任务。本文将详细介绍如何构建一个音频样本分类器,从数据集的构建、数据增强、数据预处理到模型的构建和评估,为你提供一个完整的操作指南。
1. 构建数据集
我们要处理的数据集包含10个类别,总共400个样本,每个类别有40个样本,每个样本时长为5秒。由于录制和标注样本既耗时又昂贵,我们只能使用现有的数据。
我们使用的音频数据集是ESC - 10,它需要从更大的ESC - 50数据集中提取。以下是提取ESC - 10数据集的具体步骤:
1. 从 https://github.com/karoldvl/ESC - 50/ 下载数据集的ZIP文件并解压,会生成一个名为 ESC - 50 - master 的目录。
2. 使用以下代码构建ESC - 10数据集:
import sys
import os
import shutil
classes = {
"rain": 0,
"rooster": 1,
"crying_baby": 2,
"sea_waves": 3,
"clock_tick": 4,
"sneezing": 5,
"dog": 6,
"crackling_fire": 7,
"helicopter": 8,
"chainsaw": 9,
}
with open("ESC - 50 - master/meta/esc50.csv") as f:
lines = [i[: - 1] for i in f.readlines()]
lines = lines[1:]
os.system("rm -rf ESC - 10")
os.system("mkdir ESC - 10")
os.system("mkdir ESC - 10/audio")
meta = []
for line in lines:
t = line.split(",")
if (t[-3] == 'True'):
meta.append("ESC - 10/audio/%s %d" % (t[0], classes[t[3]]))
src = "ESC - 50 - master/audio/" + t[0]
dst = "ESC - 10/audio/" + t[0]
shutil.copy(src, dst)
with open("ESC - 10/filelist.txt", "w") as f:
for m in meta:
f.write(m + "\n")
运行上述代码后,我们将得到400个时长为5秒的.wav文件,每个类别有40个文件。
2. 数据增强
由于数据集较小,我们需要进行数据增强来扩充数据。数据增强的目标是创建可能来自数据集中类别的新数据样本。对于音频数据,我们可以采用以下四种方法进行增强:
- 时间移位 :类似于将图像左右移动几个像素,我们可以对音频样本进行时间上的移位。
- 添加噪声 :通过向音频信号中添加少量随机噪声来模拟嘈杂环境。
- 音高移位 :使音频的音高升高或降低一定的量。
- 时间伸缩 :延长或压缩音频的时间。
为了方便实现这些增强操作,我们需要安装 librosa 库,使用以下命令进行安装:
sudo pip3 install librosa
以下是增强ESC - 10数据集的代码:
import os
import random
import numpy as np
from scipy.io.wavfile import read, write
import librosa as rosa
N = 8
os.system("rm -rf augmented; mkdir augmented")
os.system("mkdir augmented/train augmented/test")
src_list = [i[: - 1] for i in open("ESC - 10/filelist.txt")]
z = [[] for i in range(10)]
for s in src_list:
_, c = s.split()
z[int(c)].append(s)
train = []
test = []
for i in range(10):
p = z[i]
random.shuffle(p)
test += p[:8]
train += p[8:]
random.shuffle(train)
random.shuffle(test)
def augment_audio(src_list, typ):
flist = []
for i, s in enumerate(src_list):
f, c = s.split()
wav = read(f) # (sample rate, data)
base = os.path.abspath("augmented/%s/%s" % (typ, os.path.basename(f)[: - 4]))
fname = base + ".wav"
write(fname, wav[0], wav[1])
flist.append("%s %s" % (fname, c))
for j in range(19):
d = augment(wav)
fname = base + ("_%04d.wav" % j)
write(fname, wav[0], d.astype(wav[1].dtype))
flist.append("%s %s" % (fname, c))
random.shuffle(flist)
with open("augmented_%s_filelist.txt" % typ, "w") as f:
for z in flist:
f.write("%s\n" % z)
def augment(wav):
sr = wav[0]
d = wav[1].astype("float32")
if (random.random() < 0.5):
s = int(sr / 4.0 * (np.random.random() - 0.5))
d = np.roll(d, s)
if (s < 0):
d[s:] = 0
else:
d[:s] = 0
if (random.random() < 0.5):
d += 0.1 * (d.max() - d.min()) * np.random.random(d.shape[0])
if (random.random() < 0.5):
pf = 20.0 * (np.random.random() - 0.5)
d = rosa.effects.pitch_shift(d, sr, pf)
if (random.random() < 0.5):
rate = 1.0 + (np.random.random() - 0.5)
d = rosa.effects.time_stretch(d, rate)
if (d.shape[0] > wav[1].shape[0]):
d = d[:wav[1].shape[0]]
else:
w = np.zeros(wav[1].shape[0], dtype="float32")
w[:d.shape[0]] = d
d = w.copy()
return d
augment_audio(train, "train")
augment_audio(test, "test")
运行上述代码后,会创建一个新的增强数据目录,包含 train 和 test 子目录,增强后的数据集将是原来的20倍,总共8000个声音文件,其中6400个用于训练,1600个用于测试。
3. 数据预处理
在构建模型之前,我们需要对原始数据进行预处理,将其转换为可以输入到模型中的形式。原始音频样本是长度为220500(44100×5)的向量,这个长度对于我们的处理来说过长。因此,我们可以只保留每个音频文件的前两秒,并每隔100个样本取一个,这样每个音频文件就变成了一个长度为882的向量。
以下是构建预处理后数据集的代码:
import os
import random
import numpy as np
from scipy.io.wavfile import read
sr = 44100 # Hz
N = 2 * sr
w = 100
afiles = [i[: - 1] for i in open("augmented_train_filelist.txt")]
trn = np.zeros((len(afiles), N // w, 1), dtype="int16")
lbl = np.zeros(len(afiles), dtype="uint8")
for i, t in enumerate(afiles):
f, c = t.split()
trn[i, :, 0] = read(f)[1][:N:w]
lbl[i] = int(c)
np.save("esc10_raw_train_audio.npy", trn)
np.save("esc10_raw_train_labels.npy", lbl)
afiles = [i[: - 1] for i in open("augmented_test_filelist.txt")]
tst = np.zeros((len(afiles), N // w, 1), dtype="int16")
lbl = np.zeros(len(afiles), dtype="uint8")
for i, t in enumerate(afiles):
f, c = t.split()
tst[i, :, 0] = read(f)[1][:N:w]
lbl[i] = int(c)
np.save("esc10_raw_test_audio.npy", tst)
np.save("esc10_raw_test_labels.npy", lbl)
运行上述代码后,我们将得到训练集和测试集的NumPy文件,包含处理后的特征向量和对应的标签。
4. 音频特征分类
我们已经准备好了训练集和测试集,接下来可以构建一些模型并评估它们的性能。由于我们有特征向量,我们可以先从经典模型开始,然后尝试构建一维卷积神经网络,看看是否能提高性能。
4.1 使用经典模型
我们可以使用之前在处理乳腺癌数据集时使用过的经典模型,以下是设置代码:
import numpy as np
from sklearn.neighbors import NearestCentroid
from sklearn.neighbors import KNeighborsClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import LinearSVC
x_train = np.load("esc10_raw_train_audio.npy")[:, :, 0]
y_train = np.load("esc10_raw_train_labels.npy")
x_test = np.load("esc10_raw_test_audio.npy")[:, :, 0]
y_test = np.load("esc10_raw_test_labels.npy")
x_train = (x_train.astype('float32') + 32768) / 65536
x_test = (x_test.astype('float32') + 32768) / 65536
def run(x_train, y_train, x_test, y_test, clf):
clf.fit(x_train, y_train)
score = 100.0 * clf.score(x_test, y_test)
print("score = %0.2f%%" % score)
def train(x_train, y_train, x_test, y_test):
print("Nearest Centroid : ", end='')
run(x_train, y_train, x_test, y_test, NearestCentroid())
print("k - NN classifier (k = 3) : ", end='')
run(x_train, y_train, x_test, y_test, KNeighborsClassifier(n_neighbors = 3))
print("k - NN classifier (k = 7) : ", end='')
run(x_train, y_train, x_test, y_test, KNeighborsClassifier(n_neighbors = 7))
print("Naive Bayes (Gaussian) : ", end='')
run(x_train, y_train, x_test, y_test, GaussianNB())
print("Random Forest (trees = 5) : ", end='')
run(x_train, y_train, x_test, y_test, RandomForestClassifier(n_estimators = 5))
print("Random Forest (trees = 50) : ", end='')
run(x_train, y_train, x_test, y_test, RandomForestClassifier(n_estimators = 50))
print("Random Forest (trees = 500) : ", end='')
run(x_train, y_train, x_test, y_test, RandomForestClassifier(n_estimators = 500))
print("Random Forest (trees = 1000) : ", end='')
run(x_train, y_train, x_test, y_test, RandomForestClassifier(n_estimators = 1000))
print("LinearSVM (C = 0.01) : ", end='')
run(x_train, y_train, x_test, y_test, LinearSVC(C = 0.01))
print("LinearSVM (C = 0.1) : ", end='')
run(x_train, y_train, x_test, y_test, LinearSVC(C = 0.1))
print("LinearSVM (C = 1.0) : ", end='')
run(x_train, y_train, x_test, y_test, LinearSVC(C = 1.0))
print("LinearSVM (C = 10.0) : ", end='')
run(x_train, y_train, x_test, y_test, LinearSVC(C = 10.0))
train(x_train, y_train, x_test, y_test)
运行上述代码后,我们可以得到各个经典模型的分类准确率,结果如下表所示:
| 模型名称 | 准确率 |
| ---- | ---- |
| Nearest Centroid | 11.9% |
| k - NN classifier (k = 3) | 12.1% |
| k - NN classifier (k = 7) | 10.5% |
| Naive Bayes (Gaussian) | 28.1% |
| Random Forest (trees = 5) | 22.6% |
| Random Forest (trees = 50) | 30.8% |
| Random Forest (trees = 500) | 32.8% |
| Random Forest (trees = 1000) | 34.4% |
| LinearSVM (C = 0.01) | 16.5% |
| LinearSVM (C = 0.1) | 17.5% |
| LinearSVM (C = 1.0) | 13.4% |
| LinearSVM (C = 10.0) | 10.2% |
从结果可以看出,经典模型的性能普遍较差,很多模型几乎是在随机猜测类别标签。其中表现最好的是具有1000棵树的随机森林模型,但准确率也只有34.4%,远低于我们在大多数情况下可以接受的水平。最近质心、k - NN和线性SVM模型的表现最差,这可能是因为输入的维度较高(882个元素),而训练集中的样本数量相对较少(6400个),导致特征空间过于稀疏,最近邻分类器难以有效利用。
以下是整个流程的mermaid流程图:
graph TD;
A[构建数据集] --> B[数据增强];
B --> C[数据预处理];
C --> D[音频特征分类];
D --> E[使用经典模型];
通过以上步骤,我们完成了一个音频样本分类器的构建过程,从数据集的准备到模型的评估,每个环节都至关重要。在实际应用中,我们可以根据具体情况对模型进行进一步的优化和改进。
音频样本分类案例研究
5. 使用一维卷积神经网络
由于经典模型的表现不佳,我们可以尝试使用一维卷积神经网络(1D CNN)来提高音频分类的性能。一维卷积神经网络在处理序列数据(如音频信号)方面具有很好的效果。
以下是使用Keras构建一维卷积神经网络的示例代码:
import numpy as np
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv1D, MaxPooling1D, Flatten, Dense
from tensorflow.keras.utils import to_categorical
# 加载数据
x_train = np.load("esc10_raw_train_audio.npy")
y_train = np.load("esc10_raw_train_labels.npy")
x_test = np.load("esc10_raw_test_audio.npy")
y_test = np.load("esc10_raw_test_labels.npy")
# 数据预处理
x_train = (x_train.astype('float32') + 32768) / 65536
x_test = (x_test.astype('float32') + 32768) / 65536
y_train = to_categorical(y_train, 10)
y_test = to_categorical(y_test, 10)
# 构建模型
model = Sequential()
model.add(Conv1D(filters=32, kernel_size=3, activation='relu', input_shape=(882, 1)))
model.add(MaxPooling1D(pool_size=2))
model.add(Conv1D(filters=64, kernel_size=3, activation='relu'))
model.add(MaxPooling1D(pool_size=2))
model.add(Flatten())
model.add(Dense(64, activation='relu'))
model.add(Dense(10, activation='softmax'))
# 编译模型
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
# 训练模型
model.fit(x_train, y_train, epochs=10, batch_size=32, validation_data=(x_test, y_test))
# 评估模型
loss, accuracy = model.evaluate(x_test, y_test)
print(f"Test loss: {loss}, Test accuracy: {accuracy}")
在上述代码中,我们首先加载并预处理了训练集和测试集数据。然后,我们使用Keras构建了一个简单的一维卷积神经网络,包含两个卷积层、两个池化层、一个全连接层和一个输出层。接着,我们编译模型并使用训练数据进行训练。最后,我们使用测试数据评估模型的性能。
6. 模型集成
为了进一步提高模型的性能,我们可以尝试将多个模型进行集成。模型集成是指将多个不同的模型组合在一起,利用它们的相对优势来提高整体性能。
以下是一个简单的模型集成示例,我们将之前的随机森林模型和一维卷积神经网络模型进行集成:
from sklearn.ensemble import RandomForestClassifier
import numpy as np
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv1D, MaxPooling1D, Flatten, Dense
from tensorflow.keras.utils import to_categorical
# 加载数据
x_train = np.load("esc10_raw_train_audio.npy")[:, :, 0]
y_train = np.load("esc10_raw_train_labels.npy")
x_test = np.load("esc10_raw_test_audio.npy")[:, :, 0]
y_test = np.load("esc10_raw_test_labels.npy")
# 随机森林模型
rf = RandomForestClassifier(n_estimators=1000)
rf.fit(x_train, y_train)
rf_pred = rf.predict(x_test)
# 一维卷积神经网络模型
x_train_cnn = np.load("esc10_raw_train_audio.npy")
y_train_cnn = np.load("esc10_raw_train_labels.npy")
x_test_cnn = np.load("esc10_raw_test_audio.npy")
y_test_cnn = np.load("esc10_raw_test_labels.npy")
x_train_cnn = (x_train_cnn.astype('float32') + 32768) / 65536
x_test_cnn = (x_test_cnn.astype('float32') + 32768) / 65536
y_train_cnn = to_categorical(y_train_cnn, 10)
y_test_cnn = to_categorical(y_test_cnn, 10)
model = Sequential()
model.add(Conv1D(filters=32, kernel_size=3, activation='relu', input_shape=(882, 1)))
model.add(MaxPooling1D(pool_size=2))
model.add(Conv1D(filters=64, kernel_size=3, activation='relu'))
model.add(MaxPooling1D(pool_size=2))
model.add(Flatten())
model.add(Dense(64, activation='relu'))
model.add(Dense(10, activation='softmax'))
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
model.fit(x_train_cnn, y_train_cnn, epochs=10, batch_size=32, validation_data=(x_test_cnn, y_test_cnn))
cnn_pred_probs = model.predict(x_test_cnn)
cnn_pred = np.argmax(cnn_pred_probs, axis=1)
# 模型集成
ensemble_pred = []
for i in range(len(x_test)):
if rf_pred[i] == cnn_pred[i]:
ensemble_pred.append(rf_pred[i])
else:
# 这里可以根据实际情况进行更复杂的决策,例如根据模型的置信度
ensemble_pred.append(rf_pred[i])
ensemble_pred = np.array(ensemble_pred)
accuracy = np.mean(ensemble_pred == y_test)
print(f"Ensemble accuracy: {accuracy}")
在上述代码中,我们首先训练了一个随机森林模型和一个一维卷积神经网络模型。然后,我们分别使用这两个模型对测试数据进行预测。最后,我们将两个模型的预测结果进行集成,简单地采用了多数表决的方法(当两个模型预测结果相同时采用该结果,不同时暂时采用随机森林的预测结果)。
7. 总结与展望
通过以上的实验,我们可以得出以下结论:
- 经典模型在处理音频分类任务时表现不佳,尤其是在高维输入和相对较少的训练样本情况下,容易受到维度诅咒的影响。
- 一维卷积神经网络在处理音频数据方面具有一定的优势,能够自动提取音频信号中的特征,从而提高分类性能。
- 模型集成可以进一步提高模型的性能,通过结合不同模型的优势,减少单个模型的局限性。
在未来的工作中,我们可以从以下几个方面进行改进和优化:
- 数据方面 :尝试获取更多的音频数据,或者采用更复杂的数据增强方法,进一步扩充数据集,提高模型的泛化能力。
- 模型方面 :探索更复杂的神经网络架构,如二维卷积神经网络(将音频数据转换为图像)、循环神经网络(处理序列数据的能力更强)等。
- 集成方法方面 :研究更复杂的模型集成策略,如加权投票、堆叠等,充分发挥不同模型的优势。
以下是整个音频样本分类流程的详细mermaid流程图:
graph TD;
A[构建数据集] --> B[数据增强];
B --> C[数据预处理];
C --> D[音频特征分类];
D --> E[使用经典模型];
D --> F[使用一维卷积神经网络];
E --> G[模型集成];
F --> G[模型集成];
通过不断地探索和实践,我们有望构建出性能更优的音频样本分类器,应用于各种实际场景中,如语音识别、环境声音监测等。
超级会员免费看
874

被折叠的 条评论
为什么被折叠?



