Учебник по глубокому обучению для классификации аудио с использованием 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 содержит отличные руководства по извлечению функций здесь.
В этом примере я рассчитаю:
- Спектрограмма Mel (MFCCs)
- Спектральная полоса пропускания
- Спектральный центроид
- хроматограмма (chroma stft)
- кратковременное преобразование Фурье (stft)
Первая ось будет идентификатором аудиофайла, представляющим пакет в тензорном потоке. В этом примере вторая ось - это спектральная ширина полосы, центроид и хроматограмма, повторенные, дополненные и соответствующие форме третьей оси (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()
Примечание. Эта модель была слишком простой и не могла предсказать данные вообще (как при однозначной точности).
Затем добавляйте слои, пока ваша модель не начнет соответствовать данным.
Оцените свой модельный поезд и набор для проверки
- Обратите внимание на большие различия в производительности между тренировочным набором и тестовым набором. Если обучающая выборка работает заметно лучше, ее нельзя будет обобщить на невидимые данные.
- Если производительность набора для проверки начинает снижаться, прекратите итерацию.
#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.