Пошаговое руководство с полным кодом для Python

Разве не было бы замечательно иметь возможность иметь расшифровки стенограмм наших встреч в Zoom, в которых простым текстом рассказывалось бы, кто и что сказал во время встречи? Этот проект родился из идеи записывать встречи, рассказывающие вам, что сказал каждый человек. У нас есть много инструментов для преобразования речи в текст, но не так много для определения каждого говорящего. Для этого мы создадим голосовой классификатор Python с нуля со всем включенным здесь кодом.

Во-первых, как всегда, нам нужны данные. Эти данные взяты из OpenSLR. Мы будем использовать 30 различных динамиков и 4 образца на каждого динамика для наших обучающих данных, что даст нам 120 образцов. В среднем выборки составляют 15 секунд каждая, то есть примерно одна минута на каждого выступающего для обучения. 30 спикеров - хорошее количество людей для постановки нашей задачи; и поскольку я уже провел этот проект, я могу утверждать, что одной минуты достаточно, чтобы дать нам хорошие результаты.

В дополнение к 120 образцам для обучения нам также нужны данные проверки и тестирования. Мы проверим на 3 образцах и протестируем на 3 образцах. Поскольку у нас 30 динамиков, нам понадобится 90 образцов для проверки и 90 образцов для тестирования. Следовательно, нам потребуется по 10 голосовых клипов из каждого динамика.

Обобщая примеры, мы будем использовать 120 голосовых клипов для обучающих данных, 90 голосовых клипов для данных проверки и 90 голосовых клипов для данных тестирования, всего 300 голосовых клипов.

Когда у нас есть голосовые записи в трех разных папках для обучения, проверки и тестирования, мы можем создавать фреймы данных pandas. Нам нужно импортировать os, чтобы мы могли создать фрейм данных из файлов в наших папках (я работаю на Mac, для других операционных систем процесс может быть немного другим). Моя папка для обучения называется 30_speakers_train.

import os
#list the files
filelist = os.listdir('30_speakers_train') 
#read them into pandas
train_df = pd.DataFrame(filelist)

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

Назовем столбец файла «файл».

# Renaming the column name to file
train_df = train_df.rename(columns={0:'file'})

Иногда создается файл ‘ .DS_Store ’, и нам нужно найти и удалить этот конкретный файл, чтобы продолжить. После этого нам нужно будет сбросить индекс нашего фрейма данных. Вот пример этого:

# Code in case we have to drop the '.DS_Store' and reset the index
train_df[train_df['file']=='.DS_Store']
train_df.drop(16, inplace=True)
train_df = train_df.sample(frac=1).reset_index(drop=True)

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

# We create an empty list where we will append all the speakers ids for each row of our dataframe by slicing the file name since we know the id is the first number before the hash
speaker = []
for i in range(0, len(df)):
    speaker.append(df['file'][i].split('-')[0])
# We now assign the speaker to a new column 
train_df['speaker'] = speaker

Наш фрейм данных должен выглядеть так:

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

Теперь, когда у нас есть фреймы данных, нам нужно написать функцию для извлечения аудио свойств каждого аудиофайла. Для этого мы используем librosa - отличную библиотеку Python для обработки звука. Нам нужно установить librosa через pip и импортировать librosa. Ниже приводится функция, которая анализирует каждый файл в нашей папке и извлекает 5 числовых характеристик из каждого файла, а именно: mfccs, цветность, мел, контраст и тоннетц.

def extract_features(files):
    
    # Sets the name to be the path to where the file is in my computer
    file_name = os.path.join(os.path.abspath('30_speakers_train')+'/'+str(files.file))
# Loads the audio file as a floating point time series and assigns the default sample rate
    # Sample rate is set to 22050 by default
    X, sample_rate = librosa.load(file_name, res_type='kaiser_fast')
# Generate Mel-frequency cepstral coefficients (MFCCs) from a time series 
    mfccs = np.mean(librosa.feature.mfcc(y=X, sr=sample_rate, n_mfcc=40).T,axis=0)
# Generates a Short-time Fourier transform (STFT) to use in the chroma_stft
    stft = np.abs(librosa.stft(X))
# Computes a chromagram from a waveform or power spectrogram.
    chroma = np.mean(librosa.feature.chroma_stft(S=stft, sr=sample_rate).T,axis=0)
# Computes a mel-scaled spectrogram.
    mel = np.mean(librosa.feature.melspectrogram(X, sr=sample_rate).T,axis=0)
# Computes spectral contrast
    contrast = np.mean(librosa.feature.spectral_contrast(S=stft, sr=sample_rate).T,axis=0)
# Computes the tonal centroid features (tonnetz)
    tonnetz = np.mean(librosa.feature.tonnetz(y=librosa.effects.harmonic(X),
    sr=sample_rate).T,axis=0)
return mfccs, chroma, mel, contrast, tonnetz

Каждый mfcc представляет собой массив длиной 40, цветность - 12, мел - 128, контраст - 7, а тоннац - 6. В целом у нас есть 193 числовых функции для каждого голосового клипа из выбранных нами звуковых функций. Не стесняйтесь пробовать другие, если хотите: l извлечение функций ibrosa. Мы применяем эту функцию к каждому отдельному аудиофайлу и сохраняем эту информацию. Обратите внимание, что это займет несколько минут, а может и часов, в зависимости от того, сколько данных вы используете и от вашей вычислительной мощности.

train_features = train_df.apply(extract_features, axis=1)

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

features_train = []
for i in range(0, len(train_features)):
    features_train.append(np.concatenate((
        train_features[i][0],
        train_features[i][1], 
        train_features[i][2], 
        train_features[i][3],
        train_features[i][4]), axis=0))

Теперь мы можем сделать наш X_train набором наших функций:

X_train = np.array(features_train)

Точно так же мы делаем те же шаги сверху с самого начала для наших данных проверки и тестирования. Мы должны получить X_val и X_test.

Теперь мы закончили с нашими данными X. Для Y нам нужны идентификаторы выступающих. Помните, что мы проводим контролируемое обучение, поэтому нам нужны целевые данные. У нас есть это из наших исходных фреймов данных, поэтому мы просто получаем эти значения.

y_train = np.array(train_df['speaker'])
y_val = np.array(val_df['speaker'])

Нам нужно горячее кодирование y, чтобы иметь возможность использовать его в нашей нейронной сети. Вам нужно будет импортировать LabelEncoder из sklearn и to_categorical из keras, который использует Tensorflow .

from sklearn.preprocessing import LabelEncoder
from keras.utils.np_utils import to_categorical
# Hot encoding y
lb = LabelEncoder()
y_train = to_categorical(lb.fit_transform(y_train))
y_val = to_categorical(lb.fit_transform(y_val))

Теперь нам нужно масштабировать наш X:

from sklearn.preprocessing import StandardScaler
ss = StandardScaler()
X_train = ss.fit_transform(X_train)
X_val = ss.transform(X_val)
X_test = ss.transform(X_test)

И, наконец, мы готовы к самому интересному: построению нейронной сети! Мы будем использовать простую нейронную сеть с прямой связью. На входе будут наши 193 функции. Я поигрался с процентами отсева, но вы можете изменить эти значения, если хотите. Использование relu довольно стандартно. Результатом будет softmax, поскольку у нас есть 30 различных классов, и мы используем категориальную кроссэнтропию из-за разных классов.

from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.callbacks import EarlyStopping
# Build a simple dense model with early stopping and softmax for categorical classification, remember we have 30 classes
model = Sequential()
model.add(Dense(193, input_shape=(193,), activation = 'relu'))
model.add(Dropout(0.1))
model.add(Dense(128, activation = 'relu'))
model.add(Dropout(0.25))
model.add(Dense(128, activation = 'relu'))
model.add(Dropout(0.5))
model.add(Dense(30, activation = 'softmax'))
model.compile(loss='categorical_crossentropy', metrics=['accuracy'], optimizer='adam')
early_stop = EarlyStopping(monitor='val_loss', min_delta=0, patience=100, verbose=1, mode='auto')

Теперь мы подгоняем модель с данными обучения и проверки:

history = model.fit(X_train, y_train, batch_size=256, epochs=100, 
                    validation_data=(X_val, y_val),
                    callbacks=[early_stop])

Это код для просмотра графика точности обучения и проверки:

# Check out our train accuracy and validation accuracy over epochs.
train_accuracy = history.history['accuracy']
val_accuracy = history.history['val_accuracy']
# Set figure size.
plt.figure(figsize=(12, 8))
# Generate line plot of training, testing loss over epochs.
plt.plot(train_accuracy, label='Training Accuracy', color='#185fad')
plt.plot(val_accuracy, label='Validation Accuracy', color='orange')
# Set title
plt.title('Training and Validation Accuracy by Epoch', fontsize = 25)
plt.xlabel('Epoch', fontsize = 18)
plt.ylabel('Categorical Crossentropy', fontsize = 18)
plt.xticks(range(0,100,5), range(0,100,5))
plt.legend(fontsize = 18);

Это должно выглядеть примерно так:

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

# We get our predictions from the test data
predictions = model.predict_classes(X_test)
# We transform back our predictions to the speakers ids
predictions = lb.inverse_transform(predictions)
# Finally, we can add those predictions to our original dataframe
test_df['predictions'] = predictions

Теперь наш фрейм данных с тестовыми данными должен выглядеть так:

# Code to see which values we got wrong
test_df[test_df['speaker'] != test_df['predictions']]
# Code to see the numerical accuracy
(1-round(len(test_df[test_df['speaker'] != test_df['predictions']])/len(test_df),3))*100

У вас должно получиться что-то вроде этого:

98,9% верных прогнозов! Мне пришлось запустить нейронную сеть четыре раза, чтобы, наконец, сделать одно из предсказаний неверным. В предыдущих трех запусках нейронная сеть делала все прогнозы верными, но я хотел показать, как найти неверный прогноз. Таким образом, с одной минутой обучающего аудио нейронная сеть почти идеальна для 30 динамиков!

Это доказывает, насколько мощна простая нейронная сеть! Надеюсь, это было полезно!

Вот ссылка на мою записную книжку jupyter со всем кодом. Я также выполнил этот и аналогичные проекты с использованием сверточных нейронных сетей (CNN), вы можете увидеть мой другой пост, объясняющий этот процесс здесь, если вам интересно.

Источники:

Наборы данных: http://www.openslr.org/12/

[1] Дэвид Каспар, Александр Бейли, Патрик Фуллер, Librosa: A Python Audio Library (2019)

[2] Рами С. Альхавальдех, DGR: гендерное распознавание человеческой речи с использованием одномерной обычной нейронной сети (2019)

[3] Кори Беккер, Определение пола голоса с помощью машинного обучения (2016)

[4] Джонатан Балабан, Советы и приемы глубокого обучения (2018)

[5] Юнесс Мансар, Аудиоклассификация: подход сверточной нейронной сети (2018)

[6] Файзан Шайк h, Начало работы с анализом аудиоданных с использованием глубокого обучения (с тематическим исследованием) (2017)

[7] Майк Смейлс, Классификация звуков с использованием глубокого обучения, (2019)

[8] Аакиб Саид, Классификация городских звуков, часть 1, (2016)

[9] Марк Палет Гуаль, Голосовая гендерная идентификация с использованием глубоких нейронных сетей, работающих на FPGA, (2016)

[10] Камил Цемневски, Распознавание речи с нуля с использованием расширенных сверток и CTC в TensorFlow, (2019)

[11] Адмонд Ли, Как создать бота для распознавания речи с помощью Python (2019)

[12] Адриан Ицзе Сюй, Классификация городских звуков с использованием сверточных нейронных сетей с Keras: теория и реализация, (2019)

[13] Саинат Адапа, K aggle freesound audio tagging (2018)

[14] Извлечение признаков Либрозы