赛题名:通用音频分类
赛道:训练赛道
背景:随着移动终端的广泛应用以及数据量的不断积累,海量多媒体信息的处理需求日益凸显。作为多媒体信息的重要载体,音频信息处理应用广泛且多样,如自动语音识别、音乐风格识别等。有些声音是独特的,可以立即识别,例如婴儿的笑声或吉他的弹拨声。有些音频背景噪声复杂,很难区分。如果闭上眼睛,您能说出电锯和搅拌机是下面哪种声音?音频分类是音频信息处理领域的一个基本问题,从本质上说,音频分类的性能依赖于音频中的特征提取。传统特征提取算法使用音频特征的统计信息作为分类的依据,使用到的音频特征包括线性预测编码、短时平均能量等。近年来,基于深度学习的音频分类取得了较大进展。基于端到端的特征提取方式,深度学习可以避免繁琐的人工特征设计。音频的多样化给“机器听觉”带来了巨大挑战。如何对音频信息进行有效的分类,从繁芜丛杂的数据集中将具有某种特定形态的音频归属到同一个集合,对于学术研究及工业应用具有重要意义。
任务:基于上述实际需求以及深度学习的进展,本次训练赛旨在构建通用的基于深度学习的自动音频分类系统。通过本赛题建立准确的音频分类模型,希望大家探索更为鲁棒的音频表述方法,以及转移学习、自监督学习等方法在音频分类中的应用。
比赛链接:https://www.datafountain.cn/competitions/486
数据整理自网上公开数据集(已脱敏),数据集涵盖5类不同音频,该类数据集广泛应用于音频分类的业务场景。
文件类别 | 文件名 | 文件内容 |
---|---|---|
训练集音频文件夹 | train | 训练数据集音频文件 |
测试集音频文件夹 | test | 测试数据集音频文件 |
字段说明 | 字段说明.xlsx | 训练集/测试集字段的具体说明 |
提交样例 | submission.csv | 仅有两个字段file_name\label |
对数据的进一步说明:train.zip中包含三十个文件夹,每个文件夹的名称是里面所有音频文件的标签,音频文件的格式是wav格式,时间长度都小于等于1s。test.zip中包含所有测试集的音频文件,一共有6835个。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import datetime
from tqdm import tqdm
from sklearn.preprocessing import LabelEncoder
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2
from sklearn.preprocessing import MinMaxScaler
import xgboost as xgb
import lightgbm as lgb
from catboost import CatBoostRegressor
import warnings
from sklearn.model_selection import StratifiedKFold, KFold
from sklearn.metrics import accuracy_score, f1_score, roc_auc_score, log_loss
import time
import os
import librosa
import pickle
warnings.filterwarnings('ignore')
音频文件的读取我使用的是os库和librosa库,按顺序读取三十个文件夹,对于每一个文件夹,先读取里面所有的文件名,然后依次用librosa库读取每一个wav文件,具体代码见特征构造章节。librosa是一个处理音频文件的python库,可以直接pip安装,里面有很多功能,包括读写wav文件,提取音频特征等等,具体示例和教程的请见官网。
baseline选取的特征及其维数如下表所示:
特征名称 | 特征说明 | 特征维数 |
---|---|---|
平均过零率 | 该特征在语音识别和音乐信息检索中都被大量的使用,对于高冲击声,它通常具有更高的值 | 1 |
平均光谱质心 | 它指示声音的“质心”位于何处,并计算为声音中存在的频率的加权平均值 | 1 |
梅尔频率倒谱系数 | 它是一小组特征(通常10-40),其简明地描述了频谱包络的整体形状 | 40 |
色度频谱 | 它是音乐音频的一种强大表示,其中整个频谱被投影到12个区间,代表音乐八度音的12个不同的半音(或色度) | 12 |
谱对比度 | 详细内容参考文献1 | 7 |
频谱带宽 | 详细内容参考文献 2 | 1 |
tonnetz | 详细内容参考文献3 | 6 |
所有的特征均可以用librosa自带的函数来计算,具体的音频文件读取和特征提取代码如下,注意路径要根据自己的情况进行修改:
三十个文件夹名称
filename_list = ['bed', 'bird', 'cat', 'dog', 'down', 'eight', 'five', 'four', 'go', 'happy', 'house', 'left', 'marvin', 'nine', 'no', 'off', 'on', 'one', 'right', 'seven', 'sheila', 'six', 'stop', 'three', 'tree', 'two', 'up', 'wow', 'yes', 'zero']
训练集
y=[]
X_train = []
for i in range(30):
filePath = "D:\\ccf2020\\%s" % filename_list[i]
fl = os.listdir(filePath)
print(len(fl))
for j in range(len(fl)):
wavpath = filePath + '\\' + fl[j]
sig,sr = librosa.load(wavpath)
y.append(int(i))
#print(len(sig))
temp = []
#过零率
a = librosa.feature.zero_crossing_rate(sig,sr)
b = np.mean(a)
temp.append(b)
#光谱质心
a = librosa.feature.spectral_centroid(sig,sr=sr)[0]
b = np.mean(a)
temp.append(b)
#MFCC
a = librosa.feature.mfcc(sig,sr,n_mfcc=40)
b = np.mean(a,axis = 1)
for k in range(len(b)):
temp.append(b[k])
#色度频谱
a = librosa.feature.chroma_stft(sig,sr)
b = np.mean(a,axis = 1)
for k in range(len(b)):
temp.append(b[k])
#谱对比度
a = librosa.feature.spectral_contrast(sig,sr)
b = np.mean(a,axis = 1)
for k in range(len(b)):
temp.append(b[k])
#频谱带宽
a = librosa.feature.spectral_bandwidth(sig,sr)
b = np.mean(a,axis=1)
temp.append(b)
#tonnetz
a = librosa.feature.tonnetz(sig,sr)
b = np.mean(a,axis = 1)
for k in range(len(b)):
temp.append(b[k])
X_train.append(temp)
print('*****%s*****'%i)
测试集
#测试集
X_test = []
filePath = "D:\\ccf2020\\test"
fl = os.listdir(filePath)
print(len(fl))
for j in range(len(fl)):
wavpath = filePath + '\\' + fl[j]
sig,sr = librosa.load(wavpath)
#print(len(sig))
temp = []
#过零率
a = librosa.feature.zero_crossing_rate(sig,sr)
b = np.mean(a)
temp.append(b)
#光谱质心
a = librosa.feature.spectral_centroid(sig,sr=sr)[0]
b = np.mean(a)
temp.append(b)
#MFCC
a = librosa.feature.mfcc(sig,sr,n_mfcc=40)
b = np.mean(a,axis = 1)
for k in range(len(b)):
temp.append(b[k])
#色度频谱
a = librosa.feature.chroma_stft(sig,sr)
b = np.mean(a,axis = 1)
for k in range(len(b)):
temp.append(b[k])
#谱对比度
a = librosa.feature.spectral_contrast(sig,sr)
b = np.mean(a,axis = 1)
for k in range(len(b)):
temp.append(b[k])
#频谱带宽
a = librosa.feature.spectral_bandwidth(sig,sr)
b = np.mean(a,axis=1)
temp.append(b)
#tonnetz
a = librosa.feature.tonnetz(sig,sr)
b = np.mean(a,axis = 1)
for k in range(len(b)):
temp.append(b[k])
X_test.append(temp)
将list转为array,输出维度进行检查,保存成txt文件方便后面使用
X_train_c = np.array(X_train)
print(X_train_c.shape)
y1_train_c = np.array(y)
print(y1_train_c.shape)
X_test_c = np.array(X_test)
print(X_test_c.shape)
fileHandle = open ( 'traindata_v2.txt', 'wb+' )
pickle.dump(X_train_c, fileHandle)
fileHandle.close()
fileHandle = open ( 'ydata_v2.txt', 'wb+' )
pickle.dump(y1_train_c, fileHandle)
fileHandle.close()
fileHandle = open ( 'testdata_v2.txt', 'wb+' )
pickle.dump(X_test_c, fileHandle)
fileHandle.close()
再顺便讲一下如何使用保存的txt文件,还是用pickle包读取,获得的变量和保存时候的变量是一样的:
import pickle
fileHandle = open ( 'traindata_v2.txt' ,'rb')
X_train_c = pickle.load(fileHandle)
fileHandle.close()
fileHandle = open ( 'ydata_v2.txt' ,'rb')
y1_train_c = pickle.load(fileHandle)
fileHandle.close()
fileHandle = open ( 'testdata_v2.txt' ,'rb')
X_test_c = pickle.load(fileHandle)
fileHandle.close()
使用lgb和LR等模型进行多分类已经有很多博客里面有讲,比如:多分类模型评测,这里就不在赘述了,直接上代码,baseline使用五折交叉验证和lgb模型:
nfold = 5
kf = KFold(n_splits=nfold, shuffle=True, random_state=2020)
oof = np.zeros((len(X_train_c), ))
prediction1 = np.zeros((len(X_test_c),30 ))
ITERATIONS = 100000
EARLY_STOP = 100
VERBOSE = 500
i = 0
for train_index, valid_index in kf.split(X_train_c, y1_train_c):
print("\nFold {}".format(i + 1))
X_train, label_train = X_train_c[train_index],y1_train_c[train_index]
X_valid, label_valid = X_train_c[valid_index],y1_train_c[valid_index]
params = {
'num_leaves': 16,
'min_data_in_leaf': 16,
'objective': 'multiclass', #定义的目标函数
'is_unbalance' : True,
'max_depth': 8,
'learning_rate': 0.08,
'boosting': 'gbdt',
'feature_fraction': 0.98,
'bagging_freq': 4,
'bagging_fraction': 0.8,
'lambda_l1': 0.1, #l1正则
'lambda_l2': 0.2, #l2正则
'num_class':30,
"random_state": 2020, #随机数种子,可以防止每次运行的结果不一致
}
trn_data = lgb.Dataset(X_train, label_train)
val_data = lgb.Dataset(X_valid, label_valid)
clf = lgb.train(params,
trn_data,
30000,
valid_sets=[trn_data, val_data],
verbose_eval=VERBOSE,
early_stopping_rounds=EARLY_STOP)
y1 = clf.predict(X_test_c)
prediction1 += ((y1)) / nfold
i += 1
预测结果现在以30维的向量的形式存在prediction1中。
由于os读取文件夹下的所有文件获得的list是按字母序排序的,而给我们的提交文件中的file_name字段不是字母序排序的,因此我们需要对其进行处理,即改变我们预测结果的顺序使其与提交文件中的顺序相同。另外,还需要选取预测向量中最大的类作为预测结果。
y_pred=[list(x).index(max(x)) for x in prediction1]
sub = pd.read_csv('submission.csv')
cnt = 0
result = [0 for i in range(6835)]
for i in range(6835):
ss = sub.iloc[i]['file_name']
for j in range(6835):
if fl[j] == ss:
result[i] = y_pred[j]
cnt = cnt+1
print(cnt)
result1 = []
for i in range(len(result)):
result1.append(filename_list[result[i]])
print(result1[0:10])
df = pd.DataFrame({
'file_name':sub['file_name'],'label':result1})
now = time.strftime("%Y%m%d_%H%M%S",time.localtime(time.time()))
fname="submit_"+now+r".csv"
df.to_csv(fname, index=False)