2020CCFBDCI通用音频分类CNN方案(0.90+方案)

Wesley13
• 阅读 728

通用音频分类

  • 赛题介绍
  • 数据简介
  • import的包
  • 音频文件读取
  • 特征构造
  • CNN多分类
  • 提取预测结果并写入csv文件
  • 改进方向
  • 参考文献

博主继续入门了音频分类问题,根据上一个baseline中的改进方向,写了一个基于CNN的baseline,线上分数0.90左右,随便调一下能到0.93

赛题介绍

赛题名:通用音频分类

赛道:训练赛道

背景:随着移动终端的广泛应用以及数据量的不断积累,海量多媒体信息的处理需求日益凸显。作为多媒体信息的重要载体,音频信息处理应用广泛且多样,如自动语音识别、音乐风格识别等。有些声音是独特的,可以立即识别,例如婴儿的笑声或吉他的弹拨声。有些音频背景噪声复杂,很难区分。如果闭上眼睛,您能说出电锯和搅拌机是下面哪种声音?音频分类是音频信息处理领域的一个基本问题,从本质上说,音频分类的性能依赖于音频中的特征提取。传统特征提取算法使用音频特征的统计信息作为分类的依据,使用到的音频特征包括线性预测编码、短时平均能量等。近年来,基于深度学习的音频分类取得了较大进展。基于端到端的特征提取方式,深度学习可以避免繁琐的人工特征设计。音频的多样化给“机器听觉”带来了巨大挑战。如何对音频信息进行有效的分类,从繁芜丛杂的数据集中将具有某种特定形态的音频归属到同一个集合,对于学术研究及工业应用具有重要意义。

任务:基于上述实际需求以及深度学习的进展,本次训练赛旨在构建通用的基于深度学习的自动音频分类系统。通过本赛题建立准确的音频分类模型,希望大家探索更为鲁棒的音频表述方法,以及转移学习、自监督学习等方法在音频分类中的应用。

比赛链接https://www.datafountain.cn/competitions/486

数据简介

数据整理自网上公开数据集(已脱敏),数据集涵盖5类不同音频,该类数据集广泛应用于音频分类的业务场景。

文件类别

文件名

文件内容

训练集音频文件夹

train

训练数据集音频文件

测试集音频文件夹

test

测试数据集音频文件

字段说明

字段说明.xlsx

训练集/测试集字段的具体说明

提交样例

submission.csv

仅有两个字段file_name\label

对数据的进一步说明:train.zip中包含三十个文件夹,每个文件夹的名称是里面所有音频文件的标签,音频文件的格式是wav格式,时间长度都小于等于1s。test.zip中包含所有测试集的音频文件,一共有6835个。

import的包

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 lightgbm as lgb
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
warnings.filterwarnings('ignore')
import wavio
import librosa
import keras
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten,LSTM,TimeDistributed,Bidirectional
from keras.layers import Convolution2D, MaxPooling2D
from keras.utils import to_categorical
from keras.callbacks import LearningRateScheduler
import sklearn

音频文件读取

和之前的baseline一样,音频文件的读取我使用的是os库和librosa库,按顺序读取三十个文件夹,对于每一个文件夹,先读取里面所有的文件名,然后依次用librosa库读取每一个wav文件,具体代码见特征构造章节。

特征构造

baseline选取的特征及其维数如下表所示:

特征名称

特征说明

特征维数

过零率

该特征在语音识别和音乐信息检索中都被大量的使用,对于高冲击声,它通常具有更高的值

(1,44)

光谱质心

它指示声音的“质心”位于何处,并计算为声音中存在的频率的加权平均值

(1,44)

梅尔频率倒谱系数

它是一小组特征(通常10-40),其简明地描述了频谱包络的整体形状

(40,44)

色度频谱

它是音乐音频的一种强大表示,其中整个频谱被投影到12个区间,代表音乐八度音的12个不同的半音(或色度)

(12,44)

谱对比度

详细内容参考文献1

(7,44)

频谱带宽

详细内容参考文献 2

(1,44)

tonnetz

详细内容参考文献3

(6,44)

和之前的baseline不同,我们没有对特征取平均值,因为我们这次使用的是CNN,可以通过卷积提取特征。
所有的特征均可以用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']

训练集

features,labels = np.empty((0,40,44)),np.empty(0)
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)
        #不够长度的信号进行补0
        x = np.pad(sig,(0,22050-sig.shape[0]),'constant')
        #print(x.shape)
        #提取信号的mfcc并进行标准化
        a = librosa.feature.mfcc(x,sr,n_mfcc=40)
        norm_mfccs = sklearn.preprocessing.scale(a,axis=1)
        features = np.append(features,norm_mfccs[None],axis=0)
        labels = np.append(labels,int(i))
        if j < 2:
            print(features.shape)
    print('*****%s*****'%i)

这里我们只写了mfcc的提取,其他的特征提取可以同之前的baseline中的一样来做,用np.concatenate拼接一下array即可,如下:

features = np.empty((0,28,44))
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]
        x,sr = librosa.load(wavpath)
        #不够长度的信号进行补0
        sig = np.pad(x,(0,22050-x.shape[0]),'constant')
        a = librosa.feature.zero_crossing_rate(sig,sr)
        b = librosa.feature.spectral_centroid(sig,sr=sr)[0]
        a = np.vstack((a,b))
        b = librosa.feature.chroma_stft(sig,sr)
        a = np.vstack((a,b))
        b = librosa.feature.spectral_contrast(sig,sr)
        a = np.vstack((a,b))
        b = librosa.feature.spectral_bandwidth(sig,sr)
        a = np.vstack((a,b))
        b = librosa.feature.tonnetz(sig,sr)
        a = np.vstack((a,b))
        norm_a = sklearn.preprocessing.scale(a,axis=1)
        #print(norm_mfccs.shape)
        features = np.append(features,norm_a[None],axis=0)
        if j < 2:
            print(features.shape)
    print('*****%s*****'%i)

训练数据是把上述两段代码获得的特征进行拼接,最终维数是(57886,68,44)
ps:因为博主刚开始写的时候只用了mfcc所以没有把所有特征的提取放一块,大家做的时候可以放在一起提取
测试集

#测试集
X_test = np.empty((0,40,44))
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)
    #不够长度的信号进行补0
    x = np.pad(sig,(0,22050-sig.shape[0]),'constant')
    #print(x.shape)
    #提取信号的mfc并进行标准化
    mfcc = librosa.feature.mfcc(x,sr,n_mfcc=40)
    norm_mfccs = sklearn.preprocessing.scale(mfcc,axis=1)
    X_test = np.append(X_test,norm_mfccs[None],axis=0)
    if j < 2:
        print(X_test.shape)

另外的特征:

X_test = np.empty((0,28,44))
filePath = "D:\\ccf2020\\test"
fl = os.listdir(filePath)
print(len(fl))
for j in range(len(fl)):
    wavpath = filePath + '\\' + fl[j]
    x,sr = librosa.load(wavpath)
    #不够长度的信号进行补0
    sig = np.pad(x,(0,22050-x.shape[0]),'constant')
    a = librosa.feature.zero_crossing_rate(sig,sr)
    b = librosa.feature.spectral_centroid(sig,sr=sr)[0]
    a = np.vstack((a,b))
    b = librosa.feature.chroma_stft(sig,sr)
    a = np.vstack((a,b))
    b = librosa.feature.spectral_contrast(sig,sr)
    a = np.vstack((a,b))
    b = librosa.feature.spectral_bandwidth(sig,sr)
    a = np.vstack((a,b))
    b = librosa.feature.tonnetz(sig,sr)
    a = np.vstack((a,b))
    norm_a = sklearn.preprocessing.scale(a,axis=1)
    #print(norm_mfccs.shape)
    X_test = np.append(X_test,norm_a[None],axis=0)
    if j < 2:
        print(X_test.shape)

拼接上述的两个array,最终测试数据的维数是(6835,68,44)

数据的保存和读取这里不再赘述,不会的小伙伴可以看前面的baseline,保存数据可以方便我们进行操作,避免重复跑特征提取的步骤,比如上面我刚开始用的只是mfcc,后来增加特征的时候我就没有重复计算mfcc,只要把增加的特征拼接上去就行。

CNN多分类

CNN是卷积神经网络,入门可以参考网上的各种介绍或者讲深度学习的书籍,它之所以能够处理音频特征是因为音频的特征本质上是多维时序的,CNN可以通过卷积来提取有效的特征,这样就比我们手工构造特征要有效的多了,本文使用tensorflow的keras构造了一个很简单的CNN(其实是五个,五折交叉验证),最后一层用softmax全连接层进行分类。网络的结构和参数有很大的调整空间,这里交给大家自由发挥。

X_test = X_test.reshape(X_test.shape[0],X_test.shape[1],X_test.shape[2],1)
print(X_test.shape)
nfold = 5
kf = KFold(n_splits=nfold, shuffle=True, random_state=2020)
prediction1 = np.zeros((len(X_test),30 ))
i = 0
for train_index, valid_index in kf.split(features, labels):
    print("\nFold {}".format(i + 1))
    train_x, train_y = features[train_index],labels[train_index]
    val_x, val_y = features[valid_index],labels[valid_index]
    train_x = train_x.reshape(train_x.shape[0],train_x.shape[1],train_x.shape[2],1)
    val_x = val_x.reshape(val_x.shape[0],val_x.shape[1],val_x.shape[2],1)
    print(train_x.shape)
    print(val_x.shape)
    train_y = to_categorical(train_y,30)
    val_y = to_categorical(val_y,30)
    print(train_y.shape)
    print(val_y.shape)
    model = Sequential()
    model.add(Convolution2D(32, (3, 3), activation='relu',input_shape = train_x.shape[1:]))
    model.add(MaxPooling2D((2, 2)))
    model.add(Dropout(0.25)) 
    model.add(Convolution2D(32, (3, 3),  activation='relu'))
    model.add(MaxPooling2D(2, 2))
    model.add(Dropout(0.25))
    model.add(Flatten())
    model.add(Dense(30, activation='softmax'))
    model.compile(optimizer='Adam',
                    loss='categorical_crossentropy',
                    metrics=['accuracy'])
    model.summary(line_length=80)
    history = model.fit(train_x, train_y, epochs=35, batch_size=32, validation_data=(val_x, val_y))
    y1 = model.predict(X_test)
    prediction1 += ((y1)) / nfold
    i += 1

预测结果现在以30维的向量的形式存在prediction1中。

提取预测结果并写入csv文件

这一部分是和之前的baseline一样的,但是为了博客的完整性,我依然放在这里。
由于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)

改进方向

  1. 增加有用的专业特征;
  2. 调整网络结构和参数;
  3. 试试其他网络;
  4. 进行数据增强;
  5. 模型融合等;

参考文献

  1. Jiang, Dan-Ning, Lie Lu, Hong-Jiang Zhang, Jian-Hua Tao, and Lian-Hong Cai. “Music type classification by spectral contrast feature.” In Multimedia and Expo, 2002. ICME’02. Proceedings. 2002 IEEE International Conference on, vol. 1, pp. 113-116. IEEE, 2002.
  2. Klapuri, A., & Davy, M. (Eds.). (2007). Signal processing methods for music transcription, chapter 5. Springer Science & Business Media.
  3. Harte, C., Sandler, M., & Gasser, M. (2006). “Detecting Harmonic Change in Musical Audio.” In Proceedings of the 1st ACM Workshop on Audio and Music Computing Multimedia (pp. 21-26). Santa Barbara, CA, USA: ACM Press. doi:10.1145/1178723.1178727.
点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Easter79 Easter79
2年前
swap空间的增减方法
(1)增大swap空间去激活swap交换区:swapoff v /dev/vg00/lvswap扩展交换lv:lvextend L 10G /dev/vg00/lvswap重新生成swap交换区:mkswap /dev/vg00/lvswap激活新生成的交换区:swapon v /dev/vg00/lvswap
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
2年前
Java获得今日零时零分零秒的时间(Date型)
publicDatezeroTime()throwsParseException{    DatetimenewDate();    SimpleDateFormatsimpnewSimpleDateFormat("yyyyMMdd00:00:00");    SimpleDateFormatsimp2newS
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这