keras实现声音二分类

基于深度学习的声音分类

前言

今天花了一天时间来复现哭声识别,从网上找了很多资料发现整合起来还是比较困难的。这里我做一下笔记方便后面的学习,希望对大家也有一定的帮助。

基础知识

音频与我们生活有着十分联系。 我们的大脑不断处理和理解音频数据,并为您提供有关环境的信息。 一个简单的例子就是你每天与人交谈。 这个演讲被另一个人看出来进行讨论。 即使你认为自己处于一个安静的环境中,你也会听到更微妙的声音,比如树叶的沙沙声或雨水的飞溅。 这是您与音频连接的程度。所以你能以某种方式抓住你周围的音频,做一些有建设性的事情吗? 当然是! 有一些设备可以帮助您捕获这些声音并以计算机可读格式表示。 这些格式是:
在这里插入图片描述

  • wav(波形音频文件)格式

  • mp3(MPEG-1 Audio Layer 3)格式

  • WMA(Windows Media Audio)格式

    音频处理的是目前深度学习应用做火热的方向之一,虽然我们讨论过音频数据可用于分析。 但是音频处理的潜在应用是什么? 在这里,我将列出其中的一些:

  • 根据音频功能索引音乐集

  • 推荐用于广播频道的音乐

  • 相似性搜索音频文件(又名Shazam)

  • 语音处理和合成 - 为会话代理生成人工语音

当我们对音频数据进行采样时,我们需要更多的数据点来表示整个数据,并且采样率应该尽可能高。另一方面,如果我们在频域中表示音频数据,则需要更少的计算空间。
在这里插入图片描述

实战

​ 传统的语音识别技术,主要在隐马尔可夫模型和高斯混合模型两大”神器“的加持之下,取得了不错的成绩。但是深度学习算法后来者居上,节省了原先耗费在特征提取上的时间,甚至可以直接进行端到端的语音识别任务,大有燎原之势。本文主要借助 python 的音频处理库 librosa 和非常适合小白使用的深度学习库 keras。通过调用他们的 api ,我们可以快速地实现语音分类任务。

1.准备数据集

​ 我们通过一个实际的案例进行实战,首先我在github上下载了哭声数据集https://github.com/gveres/donateacry-corpus/tree/master/donateacry-android-upload-bucket,然后又使用了一些其他的城市声音数据集,整体加起来有1000个。在此数据基础上做了一些工作。数据集截图如下,baby目录下存放了457个音频数据,others下存放了大致相同的数据。

在这里插入图片描述

2.排序

通过自己编写的排序代码对文件夹下的文件进行统一命名。代码如下:

import sys

import os
 

 
def rename(pic_path):
    piclist=os.listdir(pic_path)
    i=1
    print("ok")
    for pic in piclist:
        if pic.endswith(".wav"):
            old_path=os.path.join(os.path.abspath(pic_path),pic)
            new_path=os.path.join(os.path.abspath(pic_path),str(1000+(int(i)))+'.wav')#这里对两个目录下文件进行排序,第一个我设置成1001这种,第二个others目录下设置成了9001这种。
 
            os.renames(old_path,new_path)
            print ("把原命名格式:"+old_path+u"转换为新命名格式:"+new_path)
            i=i+1
    
    
 


if __name__ == '__main__':

        rename("./data/babytest/")

在这里插入图片描述

3.添加数据标签

首先大家要下载来的音频文件保存在一个固定的文件夹中,比如取名为“data”。我们通过函数os.listdir,获取“./data/”文件夹中所有的音频的类别,这些标签就是我们需要分类的目标。

# 加载标签

def load_label(label_path):
  label = os.listdir(label_path)
  return label

4.提取mfcc系数

​ mfcc 系数,全称“Mel Frequency Cepstrum Coefficient”,音译为:梅尔频率倒谱系数,是模仿人类听觉特性而提取的特征参数,主要用于特征提取和降维处理。就像主成分分析方法(PCA),可以将高维度的数据压缩到低维,从而起到减小计算量以及过滤噪声的目的。拿我们这次的音频为例,我们选取了 5000 多个采样点 ,经过提取 mfcc 系数,得到 20 * 11 的矩阵,大大减小了计算量。

​ 首先,第一个函数 librosa.load用于读取音频文件,path 为音频路径,sr 为采样率(也就是一秒钟采样点的个数),设置为None,就按音频本身的采样率进行读取。mono 为双声道,我们读取的音频都是双声道的(注意,有些是单声道),所以也要设置为True。其次,我们并不需要这么高的采样率,所以就每三个选取一个采样点,y=y[::3]。

如何提取 mfcc 参数呢?传统的语音识别预处理,要经过 分帧>>加窗>>快速傅里叶变换 等一系列操作,才能提取 mfcc 参数。但是呢,我们可以调用 librosa.feature.mfcc方法,快速提取 mfcc 系数,毕竟我们只是简单地熟悉下语音处理的流程。这里要注意的是,由于我们拿到的音频文件,持续时间都不尽相同,所以提取到的 mfcc 大小是不相同的。但是神经网络要求待处理的矩阵大小要相同,所以这里我们用到了铺平操作。我们 mfcc 系数默认提取 20 帧,对于每一帧来说,如果帧长小于 11,我们就用 0 填满不满足要求的帧;如果帧长大于 11,我们就只选取前 11 个参数。我们用到的函数numpy.pad(array, pad_width, mode),其中 array 是我们需要填充的矩阵,pad_width是各个维度上首尾填充的个数。举个例子,假定我们设置的 pad_width 是((0,0), (0,2)),而待处理的 mfcc 系数是 20 * 11 的矩阵。我们把 mfcc 系数看成 20 行 11 列的矩阵,进行 pad 操作,第一个(0,0)对行进行操作,表示每一行最前面和最后面增加的数个数为零,也就相当于总共增加了 0 列。第二个(0,2)对列操作,表示每一列最前面增加的数为 0 个,但最后面要增加两个数,也就相当于总共增加了 2 行。mode 设置为 ‘constant’,表明填充的是常数,且默认为 0 。

# 提取 mfcc 参数
def wav2mfcc(path, max_pad_size=11):
  y, sr = librosa.load(path=path, sr=None, mono=1)
  y = y[::3]  #每三个点选用一个
  audio_mac = librosa.feature.mfcc(y=y, sr=16000)
  y_shape = audio_mac.shape[1]
  if y_shape < max_pad_size:
      pad_size = max_pad_size - y_shape
      audio_mac = np.pad(audio_mac, ((0, 0), (0, pad_size)), mode='constant')
  else:
      audio_mac = audio_mac[:, :max_pad_size]
  return audio_mac

5.数据预保存

​ 首先建立两个列表,分别用来存储 mfcc 系数和相应的标签(也就是 baby,other)。然后每提取到一个 mfcc 参数就把它添加到 mfcc_vectors 中,并且在 target 中存储它的标签名。当我们把2个文件夹所有的音频文件 全部处理完毕后,我们要把数据存储用 npy(numpy 矩阵的存储格式) 格式存储起来。这样方便程序下次的快速启动,不用预先花太多时间加载数据。

def save_data_to_array(label_path, max_pad_size=11):
    mfcc_vectors = []
    target = []
    labels = load_label(label_path=label_path)
    for i, label in enumerate(labels):
        path = label_path + '/' + label
        wavfiles = [path + '/' + file for file in os.listdir(path)]
        for wavfile in wavfiles:
            wav = wav2mfcc(wavfile, max_pad_size=max_pad_size)
            mfcc_vectors.append(wav)
            target.append(i)
    np.save(DATA, mfcc_vectors)
    np.save(TARGET, target)

6.训练模型

将之前的处理代码整合起来,总结一下:首先主函数调用save_data_to_array("./data/", max_pad_size=11) 将当前目录中data下的两个数据集保存成npy格式。接着我们要使用get_train_test()为神经网络准备训练集和测试集了。我们借助 sklearn 中的train_test_split,把数据集分为训练集和验证集。其中训练集占 6 成,测试集占 4 成。shuffle 是指随机打乱数据集,以获得无序的数据集。之后改变 mfcc 系数的 shape,使它变成二维矩阵且第二个维度大小为 220。其次,我们要用到 keras 的One-hot 编码。举个例子,原先的标签为‘baby’,‘others’,,经过编码,凡是对应标签,就编码成 1,反之编码成 0。这里我们使用了很简单了CNN模型进行训练。

import os
import keras
import librosa
import numpy as np
import matplotlib.pyplot as plt
from keras import Sequential
from keras.utils import to_categorical
from keras.layers import Dense
from sklearn.model_selection import train_test_split

DATA = 'data.npy'
TARGET = 'target.npy'


# 加载标签
def load_label(label_path):
  label = os.listdir(label_path)
  return label


# 提取 mfcc 参数
def wav2mfcc(path, max_pad_size=11):
  y, sr = librosa.load(path=path, sr=None, mono=1)
  y = y[::3]  #每三个点选用一个
  audio_mac = librosa.feature.mfcc(y=y, sr=16000)
  y_shape = audio_mac.shape[1]
  if y_shape < max_pad_size:
      pad_size = max_pad_size - y_shape
      audio_mac = np.pad(audio_mac, ((0, 0), (0, pad_size)), mode='constant')
  else:
      audio_mac = audio_mac[:, :max_pad_size]
  return audio_mac


# 存储处理过的数据,方便下一次的使用
def save_data_to_array(label_path, max_pad_size=11):
  mfcc_vectors = []
  target = []
  labels = load_label(label_path=label_path)
  for i, label in enumerate(labels):
      path = label_path + '/' + label
      wavfiles = [path + '/' + file for file in os.listdir(path)]
      for wavfile in wavfiles:
          wav = wav2mfcc(wavfile, max_pad_size=max_pad_size)
          mfcc_vectors.append(wav)
          target.append(i)
  np.save(DATA, mfcc_vectors)
  np.save(TARGET, target)
  # return mfcc_vectors, target


# 获取训练集与测试集
def get_train_test(split_ratio=.6, random_state=42):
  X = np.load(DATA)
  y = np.load(TARGET)
  assert X.shape[0] == y.shape[0]
  X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=(1 - split_ratio), random_state=random_state,
                                                      shuffle=True)
  return X_train, X_test, y_train, y_test


def main():
 
  x_train, x_test, y_train, y_test = get_train_test()
  x_train = x_train.reshape(-1, 220)
  x_test = x_test.reshape(-1, 220)
  y_train_hot = to_categorical(y_train)
  y_test_hot = to_categorical(y_test)
  model = Sequential()
  model.add(Dense(64, activation='relu', input_shape=(220,)))
  model.add(Dense(64, activation='relu'))
  model.add(Dense(64, activation='relu'))
  model.add(Dense(2, activation='softmax'))

  model.compile(loss=keras.losses.categorical_crossentropy,
                optimizer=keras.optimizers.RMSprop(),
                metrics=['accuracy'])
  history = model.fit(x_train, y_train_hot, batch_size=100, epochs=100, verbose=1,
                      validation_data=(x_test, y_test_hot))
  plot_history(history)


def save():
  label_path = './data/'
  save_data_to_array(label_path, max_pad_size=11)


def plot_history(history):
  plt.plot(history.history['acc'],label='train')
  plt.plot(history.history['val_acc'],label='validation')
  plt.legend()
  plt.show()


if __name__ == "__main__":
  save_data_to_array("./data/", max_pad_size=11) 
  main()

在这里插入图片描述

7. 验证

import wave
import numpy as np
import os
from keras.models import load_model

# 提取 mfcc 参数
def wav2mfcc(path, max_pad_size=11):
  y, sr = librosa.load(path=path, sr=None, mono=1)
  y = y[::3]  #每三个点选用一个
  audio_mac = librosa.feature.mfcc(y=y, sr=16000)
  y_shape = audio_mac.shape[1]
  if y_shape < max_pad_size:
      pad_size = max_pad_size - y_shape
      audio_mac = np.pad(audio_mac, ((0, 0), (0, pad_size)), mode='constant')
  else:
      audio_mac = audio_mac[:, :max_pad_size]
  return audio_mac




if __name__ == '__main__':
    # 构建模型
    model = load_model('classaud.h5') # 加载训练模型
    wavs=[]
    wavs.append(wav2mfcc("./7816.wav",11))
    X=np.array(wavs)
    X= X.reshape(-1, 220)
    print(X.shape)
    result=model.predict(X[0:1])[0] # 
    print("识别结果",result)
    #  因为在训练的时候,标签集的名字 为:  0:seven   1:stop    0 和 1 是下标
    name = ["baby crying","other"] # 创建一个跟训练时一样的标签集
    ind=0 # 结果中最大的一个数
    for i in range(len(result)):
        if result[i] > result[ind]:
            ind=1
    print("识别的语音结果是:",name[ind])


在这里插入图片描述

原创文章 68 获赞 134 访问量 5万+

猜你喜欢

转载自blog.csdn.net/liupeng19970119/article/details/103229798