Учебник по глубокому обучению для классификации аудио с использованием tensorflow

Сверточные нейронные сети

CNN или сверточные нейронные сети - это тип алгоритма глубокого обучения, который действительно хорошо распознает изображения.

Это потому, что они могут изучать шаблоны, которые являются инвариантными и имеют пространственную иерархию (F. Chollet, 2018).

Это означает, что если CNN узнает собаку в левом углу изображения выше, то он сможет идентифицировать собаку на двух других изображениях, которые были перемещены (неизменность перевода ).

Если CNN узнает собаку из левого угла изображения выше, он распознает части исходного изображения на двух других изображениях, потому что он узнал, как выглядят края ее глаза с гетерохромией. , ее волчья морда и форма стильных наушников (пространственная иерархия).

Эти свойства делают CNN незаменимыми помощниками в изучении изображений, потому что реальный мир не всегда выглядит точно так же, как обучающие данные.

Могу я использовать это для аудио?

Да. Вы можете извлекать элементы, которые выглядят как изображения, и формировать их таким образом, чтобы передавать их в CNN.

В этой статье объясняется, как обучить CNN классифицировать виды на основе аудиоинформации.

Данные для этого примера - записи птиц и лягушек из конкурса Kaggle Звуковое обнаружение видов, соединяющихся в тропических лесах.

Для начала загрузите необходимые входы:

import pandas as pd
import os
import librosa
import librosa.display
import matplotlib.pyplot as plt
from sklearn.preprocessing import normalize
import warnings
warnings.filterwarnings('ignore')
import numpy as np
import pickle
import joblib
from sklearn.model_selection import train_test_split
from tensorflow.keras import models, layers
import tensorflow as tf

Затем фрейм данных:

os.chdir('/kaggle/input/rfcx-species-audio-detection')
df = pd.read_csv('train_tp.csv')

Этот набор данных представлен в виде csv-файла с именами аудиофайлов, перечисленных в параметрах record_id, метками в поле sizes_id и началом / концом аудио образца в t_min и t_max:

df.head()

Используйте пакет librosa для загрузки и отображения аудиофайла следующим образом:

sample_num=3 #pick a file to display
#get the filename 
filename=df.recording_id[sample_num]+str('.flac')
#define the beginning time of the signal
tstart = df.t_min[sample_num] 
tend = df.t_max[sample_num] #define the end time of the signal
y,sr=librosa.load('train/'+str(filename)) #load the file
librosa.display.waveplot(y,sr=sr, x_axis='time', color='cyan')

Сложная часть

CNN ожидает изображения:

  • изображение в оттенках серого (1 канал)
  • цветное изображение с тремя каналами: красный, зеленый и синий (RGB)

Поэтому вам нужно сделать ваши звуковые функции похожими на изображение.

  • Выберите 1D для изображения в оттенках серого (одна функция) или 3D для цветного изображения (для представления нескольких функций).
  • Масштабируйте и увеличивайте параметры звука, чтобы каждый «канал» имел одинаковый размер.
#This code was adapted from Nicolas Gervais on https://stackoverflow.com/questions/59241216/padding-numpy-arrays-to-a-specific-size on 1/10/2021
def padding(array, xx, yy):
    """
    :param array: numpy array
    :param xx: desired height
    :param yy: desirex width
    :return: padded array
    """
h = array.shape[0]
    w = array.shape[1]
a = max((xx - h) // 2,0)
    aa = max(0,xx - a - h)
b = max(0,(yy - w) // 2)
    bb = max(yy - b - w,0)
return np.pad(array, pad_width=((a, aa), (b, bb)), mode='constant')

Разве я не могу просто преобразовать свои звуковые функции в трехмерную фигуру, разделив ее на 3 равные части?

В конце концов, это просто числа.

Нет. Это должно иметь внешний смысл. Мусор на входе, мусор на выходе.

Особенности для моделирования

Librosa содержит отличные руководства по извлечению функций здесь.

В этом примере я рассчитаю:

Первая ось будет идентификатором аудиофайла, представляющим пакет в тензорном потоке. В этом примере вторая ось - это спектральная ширина полосы, центроид и хроматограмма, повторенные, дополненные и соответствующие форме третьей оси (stft) и четвертой оси (MFCC).

#The eventual shape of the features
print(X_train.shape,X_test.shape)

Первая ось 1226 - это размер пакета, 128 - высота, 1000 - ширина (устанавливается с помощью max_size в приведенном ниже коде) и 3 - это количество каналов в обучающих данных. Если у меня 1226 аудиофайлов, то размер пакета будет 1226. Если бы мы извлекли только функции для 5 аудиофайлов, изображенных на рисунке dataframe.head (), форма ввода была бы 5x128x1000x3. Вы можете уменьшить размер пакета, если хотите использовать меньше памяти при обучении. В этом примере размер пакета установлен равным количеству аудиофайлов.

def generate_features(y_cut):
    max_size=1000 #my max audio file feature width
    stft = padding(np.abs(librosa.stft(y_cut, n_fft=255, hop_length        = 512)), 128, max_size)
    MFCCs = padding(librosa.feature.mfcc(y_cut, n_fft=n_fft, hop_length=hop_length,n_mfcc=128),128,max_size)
    spec_centroid = librosa.feature.spectral_centroid(y=y_cut, sr=sr)
    chroma_stft = librosa.feature.chroma_stft(y=y_cut, sr=sr)
    spec_bw = librosa.feature.spectral_bandwidth(y=y_cut, sr=sr)
    #Now the padding part
    image = np.array([padding(normalize(spec_bw),1, max_size)]).reshape(1,max_size)
    image = np.append(image,padding(normalize(spec_centroid),1, max_size), axis=0) 
#repeat the padded spec_bw,spec_centroid and chroma stft until they are stft and MFCC-sized
    for i in range(0,9):
        image = np.append(image,padding(normalize(spec_bw),1, max_size), axis=0)
        image = np.append(image, padding(normalize(spec_centroid),1, max_size), axis=0)
        image = np.append(image, padding(normalize(chroma_stft),12, max_size), axis=0)
    image=np.dstack((image,np.abs(stft)))
    image=np.dstack((image,MFCCs))
    return image

Следующие три функции сжимаются, дополняются и повторяются ...

… В следующую ось:

Две последние оси имеют одинаковую форму:

Должен ли я рассчитывать эти точно такие же характеристики?

Нет. Пока вы подкладываете их, чтобы они были одинаковой формы, используйте то, что лучше всего подходит для моделирования.

X=df.drop('species_id',axis=1)
y=df.species_id

Извлечение обучающих, тестовых и проверочных наборов

#Split once to get the test and training set
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=123, stratify=y)
print(X_train.shape,X_test.shape)

#Split twice to get the validation set
X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=0.25, random_state=123)
print(X_train.shape, X_test.shape, X_val.shape, len(y_train), len(y_test), len(y_val))

Рассчитайте эти характеристики для каждого аудиофайла и сохраните как функции и метки:

def get_features(df_in):   
    features=[]     
    labels = [] #empty array to store labels     
    #For each species, determine how many augmentations are needed
    df_in=df_in.reset_index()     
    for i in df_in.species_id.unique():
           print('species_id:',i)    
           #all the file indices with the same species_id     
           filelist = df_in.loc[df_in.species_id == i].index         
    for j in range(0,len(filelist)):             
           filename = df_in.iloc[filelist[j]].recording_id
            +str('.flac') #get the filename   
            #define the beginning time of the signal          
            tstart = df_in.iloc[filelist[j]].t_min             
            tend = df_in.iloc[filelist[j]].t_max #end of signal
            recording_id = df_in.iloc[filelist[j]].recording_id
            species_id = i
            songtype_id = df_in.iloc[filelist[j]].songtype_id   
            #Load the file
            y, sr = librosa.load(filename,sr=28000)  
            #cut the file to signal start and end  
            y_cut=y[int(round(tstart*sr)):int(round(tend*sr))]  
            #generate features & output numpy array          
            data = generate_features(y_cut) 
            features.append(data[np.newaxis,...])    
            labels.append(species_id)     
     output=np.concatenate(features,axis=0)     
     return(np.array(output), labels)
#use get_features to calculate and store the features
test_features, test_labels = get_features(pd.concat([X_test,y_test],axis=1))
train_features, train_labels = get_features_noOS(pd.concat([X_train,y_train],axis=1))

Нормализовать данные и преобразовать их в массив numpy

X_train = np.array((X_train-np.min(X_train))/(np.max(X_train)-np.min(X_train)))
X_test = np.array((X_test-np.min(X_test))/(np.max(X_test)-np.min(X_test)))
X_train = X_train/np.std(X_train)
X_test = X_test/np.std(X_test)
y_train = np.array(y_train)
y_test = np.array(y_test)

Создать CNN

В приведенном ниже примере модели блок 2D сверточного слоя (Conv2D) - это часть, которая изучает инвариантные к трансляции пространственные шаблоны и их пространственные иерархии.

Максимальное объединение слоя уменьшает вдвое размер карт объектов путем уменьшения их разрешения до максимального значения внутри окна. Почему даунсэмпл? Потому что в противном случае это привело бы к огромному количеству параметров, и ваш компьютер взорвался бы, и, в конце концов, модель значительно перестроила бы данные. Этот волшебный слой является причиной того, что CNN может обрабатывать огромные объемы данных в изображениях. Max Pooling - хорошая модель.

Dropout Layer защищает от переобучения, случайным образом устанавливая вес части данных на ноль, а Dense единицы содержат скрытые слои, привязанные к степени свободы, которые модель должна попытаться подогнать под данные. Чем сложнее данные, тем больше степеней свободы требуется модели. Будьте осторожны , чтобы не добавить их кучу и в конечном итоге не уложить данные.

Сглаженный слой сжимает всю информацию карты объектов в один столбец, чтобы передать ее в плотный слой, последний из которых выводит 24 вида, на которые модель должна классифицировать аудиозаписи.

Образец архитектуры модели CNN

В тензорном потоке вы создаете указанную выше модель следующим образом

input_shape=(128,1000,3)
CNNmodel = models.Sequential()
CNNmodel.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=input_shape))
CNNmodel.add(layers.MaxPooling2D((2, 2)))
CNNmodel.add(layers.Dropout(0.2))
CNNmodel.add(layers.Conv2D(64, (3, 3), activation='relu'))
CNNmodel.add(layers.MaxPooling2D((2, 2)))
CNNmodel.add(layers.Dropout(0.2))
CNNmodel.add(layers.Conv2D(64, (3, 3), activation='relu'))
CNNmodel.add(layers.Flatten())
CNNmodel.add(layers.Dense(64, activation='relu'))
CNNmodel.add(layers.Dropout(0.2))
CNNmodel.add(layers.Dense(32, activation='relu'))
CNNmodel.add(layers.Dense(24, activation='softmax'))

Функции активации дают модели возможность добавить модели нелинейности. Здесь используется функция relu, которая обнуляет отрицательные веса. О других функциях активации вы можете прочитать здесь, но для начала лучше с нее. Тип функции активации последнего плотного слоя - softmax, который выводит вероятность для каждого класса.

Скомпилируйте модель

CNNmodel.compile(optimizer='adam',loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),metrics=['accuracy'])

Оптимизатор Адам управляет скоростью обучения за вас, функция потери используется для оценки того, насколько различаются прогнозируемые и фактические данные, и наказывает модель за плохие прогнозы. В этом примере функцией потерь является SparseCategoricalCrossentropy, которая используется когда каждый образец принадлежит одной метке, а не более чем одному, и это не двоичная классификация. Это подходящий выбор, потому что каждый аудиосэмпл принадлежит к одному виду, а их 24.

Подходит для модели

history = CNNmodel.fit(X_train, y_train, epochs=20, validation_data= (X_val, y_val))

Чтобы избежать переобучения, начните с простейшей модели и постепенно поднимайтесь вверх.

Это связано с тем, что, если модель слишком сложна, она точно изучит ваши обучающие данные и не сможет обобщить до невидимых данных.

Попробуй это:

input_shape=(128,1000,3)
CNNmodel = models.Sequential()
CNNmodel.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=input_shape))
CNNmodel.add(layers.MaxPooling2D((2, 2)))
CNNmodel.add(layers.Flatten())
CNNmodel.add(layers.Dense(32, activation='relu'))
CNNmodel.add(layers.Dense(24, activation='softmax'))
CNNmodel.summary()

Примечание. Эта модель была слишком простой и не могла предсказать данные вообще (как при однозначной точности).

Затем добавляйте слои, пока ваша модель не начнет соответствовать данным.

Оцените свой модельный поезд и набор для проверки

  1. Обратите внимание на большие различия в производительности между тренировочным набором и тестовым набором. Если обучающая выборка работает заметно лучше, ее нельзя будет обобщить на невидимые данные.
  2. Если производительность набора для проверки начинает снижаться, прекратите итерацию.
#Adapted from Deep Learning with Python by Francois Chollet, 2018
history_dict=history.history
loss_values=history_dict['loss']
acc_values=history_dict['accuracy']
val_loss_values = history_dict['val_loss']
val_acc_values=history_dict['val_accuracy']
epochs=range(1,21)
fig,(ax1,ax2)=plt.subplots(1,2,figsize=(15,5))
ax1.plot(epochs,loss_values,'bo',label='Training Loss')
ax1.plot(epochs,val_loss_values,'orange', label='Validation Loss')
ax1.set_title('Training and validation loss')
ax1.set_xlabel('Epochs')
ax1.set_ylabel('Loss')
ax1.legend()
ax2.plot(epochs,acc_values,'bo', label='Training accuracy')
ax2.plot(epochs,val_acc_values,'orange',label='Validation accuracy')
ax2.set_title('Training and validation accuracy')
ax2.set_xlabel('Epochs')
ax2.set_ylabel('Accuracy')
ax2.legend()
plt.show()

Заключительные замечания

Теперь вы знаете, как создать CNN для использования в классификации аудио. Начните с простой модели, а затем добавляйте слои, пока не начнете видеть признаки того, что обучающие данные работают лучше, чем тестовые. Добавьте слои Dropout и Max Pooling, чтобы избежать переобучения. Наконец, прекратите итерацию, когда заметите снижение производительности данных проверки по сравнению с данными обучения.

Удачного моделирования!

Источники

Саркар, Дипанджан (2021) Личное сообщение.

Чолле, Ф. Deep Learning with Python (2018), v. 361, New York: Manning.

Гервиас, Николас (2021 г.) Код взят из https://stackoverflow.com/questions/59241216/padding-numpy-arrays-to-a-specific-size , Дата обращения 10.01.2021.

frenzykryger (2021) https://datascience.stackexchange.com/questions/41921/sparse-categorical-crossentropy-vs-categorical-crossentropy-keras-accuracy#:~:text=Use%20sparse % 20categorical% 20crossentropy% 20when, 0,5% 2C% 200,3% 2C% 200,2% 5D , получено 21.02.2021.