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

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

9acf28b5169705015320ed6ba48f1ff2

Можно сказать, что звук — это физическое представление звука, частота которого варьируется от 20 Гц до 20 кГц. Эти звуки доступны во многих форматах, позволяющих компьютеру их анализировать, таких как mp3, wma, wav.

Для работы со звуковым сигналом его необходимо оцифровать, то есть преобразовать звуковую волну в ряд чисел. Это делается путем измерения амплитуды звука через фиксированные интервалы. Каждое из этих измерений называется выборкой, а частота выборки называется количеством выборок в секунду. Например, типичная частота дискретизации составляет около 44 100 выборок в секунду. Это означает, что 10-секундный видеоклип будет содержать 441 000 сэмплов.

5fe62671625de8452c34905d3347bc15

Путем аудиосемплирования из аудиозаписей можно извлечь достаточно большое количество различных признаков, что помогает в дальнейшем аудиоанализе. Среди этих характеристик можно выделить, например, мел-кепстральный коэффициент (MFCC), спектр, спектрограмму, спектральный центроид (Spectral Centroid), спектральный спад (Spectral Rolloff) и др.

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

pip install librosa

Или

conda install -c conda-forge librosa

И здесь я хочу сделать небольшое отступление. Я работаю в блокноте Jupyter, и часто бывает так, что вы вводите эти волшебные команды, и… ничего не работает. А потом начинается, гуглить, пробовать разные варианты, которые обещают решить проблему с установкой, пытаться заново все переустановить, создать новую виртуальную среду, и все равно ничего не получается. Именно с этой библиотекой у меня было много проблем, в разное время появлялись разные ошибки и ничего не работало. Но потом волшебным образом Librosa все-таки установилась, более того, я даже не знаю точно, что мне помогло.

Но мы предполагаем, что команда

.pip install librosa –-user оказался волшебным.

Теперь вы можете импортировать библиотеку и начать работать.

import librosa

Мы будем классифицировать аудиофайлы в соответствии с набором данных, содержащим коллекцию аудиофайлов в 10 различных жанрах. В каждом жанре по 100 аудиофайлов продолжительностью 3 и 30 секунд.

Загрузите аудио и извлеките важные функции

Использование функции librosa.load() мы можем воспроизводить определенные звуки из нашего набора данных.

import os # библиотека для работы с файлами
dir="***/datasets/Data/genres_original" #задаем директорию с данными
file = dir+'/blues/blues.00000.wav'
signal, sr = librosa.load(file, sr = 22050) # загружаем файл

На выходе мы получаем два объекта, первый — это цифровое представление нашего аудиосигнала (в виде временного ряда), второй — соответствующая частота дискретизации, с которой он был извлечен. По умолчанию передискретизация составляет 22050 Гц.Как упоминалось выше, частота дискретизации — это количество аудиовыборок, передаваемых в секунду, измеряемое в Гц или кГц.

print(signal.shape, sr)
(661794,) 22050

Давайте посмотрим на наш звуковой сигнал

print(signal)
[ 0.00732422  0.01660156  0.00762939 ... -0.05560303 -0.06106567
 -0.06417847]

Принятый звуковой сигнал можно представить в виде звуковой волны с помощью функции librosa.display.waveshow()

import matplotlib.pyplot as plt
import librosa.display as ld
plt.figure(figsize=(12,4))
ld.waveshow(signal, sr=sr)
c5cbbaec422b70d8da10e2ee701f5f6d

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

ЧИТАТЬ   Посол ответил на обвинения США в «непрофессионализме» ВКС России в Сирии

А с помощью функции IPyhon.display() мы получим плеер в записной книжке, где сможем воспроизвести аудиофайл.

import IPython
display(IPython.display.Audio(signal, rate = sr))
fe5f6c5e8c230b8852baaa14cb55a264

Звук можно перевести из временной области в частотную с помощью быстрого преобразования Фурье, так что мы получим спектр сигнала. Для этого в librosa есть функция librosa.stft() с такими опциями, как n_fft — длина сигнала окна после заполнения нулями и hop_length — размер кадра или размер быстрого преобразования Фурье.

n_fft = 2048
ft = np.abs(librosa.stft(signal[:n_fft], hop_length = n_fft+1))
plt.plot(ft)
plt.title('Spectrum')
plt.xlabel('Frequency Bin')
plt.ylabel('Amplitude')
a208c1f02c6753e9c9a7b220495698ef

Спектрограмма это визуальный способ представить уровень или интенсивность сигнала на разных частотах, присутствующих в волновых формах. То есть он показывает интенсивность частот во времени. Это дает представление времени по оси x, частоты по оси y, а соответствующие амплитуды представлены цветом.

Использование функции librosa.display.specshow() Вы можете посмотреть на спектрограмму звукового сигнала:

X = librosa.stft(signal)
s = librosa.amplitude_to_db(abs(X))
ld.specshow(s, sr=sr, x_axis="time", y_axis="linear")
plt.colorbar()
a62d643f1f5fd1ac766c94dfd181033f

Мел-кепстральные коэффициенты (МФСС) являются одной из наиболее важных функций обработки звука. Процесс расчета этих коэффициентов учитывает ряд характеристик слухового анализатора человека, имитирующих характеристики человеческого голоса. Это связано с тем, что звуки, издаваемые человеком, определяются формой голосового тракта, включая язык, зубы и т. д.

Мел-кепстральные коэффициенты можно найти с помощью функции librosa.feature.mfcc()

mfccs = librosa.feature.mfcc(y=signal, sr=sr, n_mfcc = 40, hop_length=512)
mfccs
4037c54995539ad0a7d7a2fb6d51f9da
mfccs.shape
(40, 1293)

n_mfcc — количество коэффициентов MFCC

hop_length — Размер рамки

Размер матрицы меловых коэффициентов рассчитывается как

[n_mfcc, len(signal)//hop_length+1]

То есть если n_mels = 40, hop_length = 512ТАК

len(signal)//hop_length+1 = 661794//512+1 = 1292+1 = 1293.

Также можно построить спектрограмму мела с помощью функции librosa.feature.melspectrogram(). Меловая спектрограмма представляет собой спектрограмму, преобразованную в меловую шкалу.

melspectrum = librosa.feature.melspectrogram(y=signal, sr = sr,
                                        	hop_length =512, n_mels = 40)
636ad64a0c6fb3638d0fa74e3243bf6a

Спектральный центроид является хорошим индикатором яркости звука, широко используемым в качестве автоматической меры музыкального тембра. То есть центроид указывает, где находится центр масс звука. В блюзовых композициях частоты распределены равномерно, в металлических спектроид лежит ближе к концу спектра. Librosa использует функцию librosa.feature.spectral_centroid()

cent = librosa.feature.spectral_centroid(y=signal, sr=sr)
plt.figure(figsize=(15,5))
plt.semilogy(cent.T, label="Spectral centroid")
plt.ylabel('Hz')
plt.legend()
c120939da8a614e114e04e33ea7147b4

array([[1936.83283904, 1820.36294357, 1780.31673025, ..., 2770.21094705, 2661.92181327, 2604.75205139]])

Спектральное затухание представляет собой частоту, ниже которой находится определенный процент общей спектральной энергии. librosa использует функцию librosa.feature.spectral_rolloff()

rolloff = librosa.feature.spectral_rolloff(y=signal, sr=sr)
plt.figure(figsize=(15,5))
plt.semilogy(rolloff.T, label="Roll-off frequency")
plt.ylabel('Hz')
plt.legend()
0cdcb81e29e89a840a1b9fe48c4bf638

array([[4005.17578125, 3520.67871094, 3348.41308594, ..., 5792.43164062, 5577.09960938, 5361.76757812]])

Скорость пересечения нуля — частота смены знака сигнала, т.е. частота, с которой сигнал меняется с положительного на отрицательный и наоборот. Например, для металла и рока этот параметр обычно выше, чем для других жанров, из-за большого количества барабанов. librosa использует функцию librosa.feature.zero_crossing_rate()

zrate=librosa.feature.zero_crossing_rate(signal)
plt.figure(figsize=(14,5))
plt.semilogy(zrate.T, label="Fraction")
plt.ylabel('Fraction per Frame')
plt.legend()
7f45f0ab4a60e80766545c11a85a8465

array([[0.03808594, 0.06054688, 0.07861328, ..., 0.14550781, 0.13623047, 0.10058594]])

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

ЧИТАТЬ   Кудерметова проиграла Швентек и не вышла в финал мадридского турнира

Например, вы также можете выбрать средние значения и стандартные отклонения мелкепстральных коэффициентов:

mfcc_mean = np.mean(librosa.feature.mfcc(y=signal, sr=sr), axis=1)
mfcc_std = = np.std(librosa.feature.mfcc(y=signal, sr=sr), axis=1)

средние значения и стандартные отклонения спектрального барицентра

cent_mean = np.mean(cent)
cent_std = np.std(cent)

средние значения и стандартные отклонения спектрального распада и т.д.

roloff_mean = np.mean(roloff)
croloff_std = np.std(roloff)

Потом все эти значения можно записать в датафрейм и работать с ними

df = pd.DataFrame(audio_data)
df['labels'] = labels

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

audio_files = []
labels = []
labelind = -1
for label in os.listdir(dir):
	labelind +=1
	label_path = os.path.join(dir, label)
	for audio_file in os.listdir(label_path):
    	audio_file_path = os.path.join(label_path, audio_file)
    	audio_files.append(audio_file_path)
    	labels.append(labelind)

Теперь давайте создадим функцию, которая будет принимать на вход аудиофайл, а затем получать средние значения коэффициентов для этого файла.

def preprocess_audio(audio_file_path):
	audio, sr = librosa.load(audio_file_path)
	mfcc_mean = np.mean(librosa.feature.mfcc(y=audio, sr=sr),   axis=1)
	return abs(mfcc_mean)

Получить список audio_data числовые значения для всех аудиофайлов:

audio_data =[]
for audio_file in audio_files:
	mfccs_mean = preprocess_audio(audio_file)
	audio_data.append(mfccs_mean)

Создадим таблицы из характеристик аудиофайлов и их тегов

audio_data = np.array(audio_data)
labels = np.array(labels)

Таким образом, можно объединить все характеристики аудиофайлов в единый фрейм данных и затем работать с ним.

Моделирование

В наборе данных, с которым я работаю, уже есть CSV-файл с информацией о мелкепстральных коэффициентах, спектральных центроидах и т. д. Давайте скачаем его и посмотрим.

df = pd.read_csv(f'{dir}/features_3_sec.csv')
df
c5e37152fc0a115f8fe36d21999810a6

Наш фрейм данных имеет 2 столбца (filename И length), которые нам больше не понадобятся, поэтому мы их удалим.

df = df.iloc[0:, 2:]

Теперь определим вектор с обучающими данными (X) и вектор соответствующих меток (y). Обучающие данные — важная особенность аудиоданных, всего 57 значений. Разберемся с метками классов.

df['label'].unique()
array(['blues', 'classical', 'country', 'disco', 'hiphop', 'jazz',
       'metal', 'pop', 'reggae', 'rock'], dtype=object)

Они имеют категориальное представление, поэтому мы преобразуем их в числовое представление с помощью метода LabelEncoder().

class_list=df.iloc[:,-1] # создаем список классов
convertor = preprocessing.LabelEncoder()
y=convertor.fit_transform(class_list) # конвертируем признаки

Теперь давайте перейдем к данным для обучения X. Удалим столбец с метками из нашего фрейма данных.

X = df.loc[:, df.columns !='label']

Мы нормализуем наш целевой вектор, используя метод StandardScaler()

from sklearn import preprocessing
cols = X.columns
scaler = preprocessing.StandardScaler()
np_scaled = scaler.fit_transform(X)
X = pd.DataFrame(np_scaled, columns = cols)

Разделите выборку на тестовую и обучающую

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.3, random_state=42 )

Давайте использовать классические алгоритмы машинного обучения: алгоритм Байеса, логистическая регрессия, k-ближайших соседей, машины опорных векторов, ансамблевые методы: случайный лес, XGBoost (деревья с градиентным усилением) и многослойная персптронная модель. MLPClassifier.

Импортируйте необходимые модули:

from sklearn.naive_bayes import GaussianNB
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import SVC
from sklearn.neural_network import MLPClassifier
from xgboost import XGBClassifier
from sklearn.metrics import confusion_matrix, accuracy_score, roc_auc_score, roc_curve

Создадим дополнительную функцию для работы с алгоритмами обучения:

def model_assess(model, title = "Default"):
	model.fit(X_train, y_train)
	preds = model.predict(X_test)
	print('Accuracy', title, ':', round(accuracy_score(y_test, preds), 5), '\n')

Мы построим модели и оценим точность.

# алгорит Баейса
nb = GaussianNB()
model_assess(nb, "Naive Bayes")
# алгоритм k-ближайших соседей
knn = KNeighborsClassifier(n_neighbors=10)
model_assess(knn, "KNN")
# метод опорных векторов
svm = SVC(decision_function_shape="ovo")
model_assess(svm, "Support Vector Machine")
# логистическая регрессия
lg = LogisticRegression(random_state=0, solver="lbfgs", multi_class="multinomial")
model_assess(lg, "Logistic Regression")
# случайный лес
rforest = RandomForestClassifier(n_estimators=1000, max_depth=10, random_state=0)
model_assess(rforest, "Random Forest")
# многослойный персептрон
nn = MLPClassifier(solver="lbfgs", alpha=1e-5, hidden_layer_sizes=(5000, 10), random_state=1)
model_assess(nn, "Neural Nets")
# деревья с градиентным бустингом
xgb = XGBClassifier(n_estimators=1000)
model_assess(xgb, "XGBClassifier")
3577dd58cad9a05098eebd0b4ea1a4e1

Мы видим, что самый низкий показатель точности 0,52 у алгоритма Байеса, а самый высокий 0,9 у алгоритма XGBClassifier.

Рекомендации по аудиопесням

А теперь попробуем порекомендовать пользователю аудиокомпозиции по методу cosine_similarity() из библиотеки scikit-learn. Этот метод вычисляет косинусное сходство между двумя ненулевыми векторами и основан на косинусе угла между ними, что дает значение от -1 до 1. Значение -1 означает, что векторы противоположны, 0 представляет ортогональные векторы, а значение 1 означает похожие векторы.

ЧИТАТЬ   Loymax завершил внедрение рекомендательной платформы в торговой сети Красный Яр

Для этого возьмите csv файл, прочитайте его и удалите лишние столбцы.

df1 = pd.read_csv(f'{dir}/features_30_sec.csv',index_col=0)
labels = df1[['label']]
df1 = df1.drop(columns=['length','label'])

Затем мы переводим наш фрейм данных в матрицу 1000×57 и вычисляем косинусное сходство между векторами.

from sklearn import preprocessing
from sklearn.metrics.pairwise import cosine_similarity
scaled=preprocessing.scale(df1)
similarity = cosine_similarity(scaled)

И теперь мы представляем полученную матрицу сходства в виде кадра данных:

similarity_labels = pd.DataFrame(similarity)
similarity_names = similarity_labels.set_index(labels.index)
similatity_names.columns = labels.index
6f2cc343cfafb1d8b81f585cbc589a99

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

name="rock.00087.wav"
series = pd.DataFrame(similarity_names[name].sort_values(ascending = False))
series = series.loc[(series[name]>0.90)]
series = series.drop(name)
print("\n*******\nSimilar songs to ", name)
print(series.head(5))
8ef8b6357b981416e6381013b7a29e6a

Получаем названия рекомендуемых композиций и их косинусное сходство.

В будущем было бы интересно получать изображения из аудиофайлов в виде, например, их спектрограмм или мел-кепстральных спектрограмм, а затем классифицировать аудиофайлы с помощью нейросетевых алгоритмов. Так мы сможем работать со звуком, не используя классический подход преобразования данных, а будем работать с визуальным представлением звука. Классифицируйте аудиофайлы с помощью библиотеки Librosa.

И напоследок хочу порекомендовать вам бесплатный урок от моих коллег из OTUS. На уроке преподаватели OTUS расскажут о рекомендательных системах, основанных на контентной фильтрации. А затем вы примените изученные подходы на практике для создания рекомендательной системы для интернет-магазина.

Source

От admin