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

Читать, слущать книги онлайн бесплатно!

Электронная Литература.

Бесплатная онлайн библиотека.

Читать: Обнаружение скрытых эмоций в голосе - Евгений Столов на бесплатной онлайн библиотеке Э-Лит


Помоги проекту - поделись книгой:

Разбиение на слова

Имея классификацию интервалов, можно попытаться выделить отдельные слова в файле. В основе процедуры "разделение" лежит следующая гипотеза. Слова разделяются последовательностью ШИ. Если ШИ оказался внутри слова, то это интервал между слогами. ШИ предшествующие слову и завершающие его включаются в слово. Заменяя каждый интервал нулем и единицей в зависимости от отнесения его к шуму или информации, получим ступенчатую последовательность Step. Разбиение на слова производится на основе этой последовательности.

Первая проблема, которую нужно решить — найти длины интервалов из нулей (или единиц) в этой ступенчатой последовательности. Таким образом вычисляется истинная длина интервала между отдельными информационным отрезками файла. Фактически мы пытаемся выделить границы слова, поэтому ищем интервалы, состоящие из 1. Такие интервалы назовем сегментами. Выделение сегментов из ступенчатой функции осуществляется с помощью функций 𝑠𝑒𝑙𝑒𝑐𝑡𝑆𝑒𝑔𝑚 и 𝑝𝑎𝑖𝑟𝑠11.

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

def selectSegm(Step):

Marks2Str = [str(X) for X in Step]

Str = ''.join(Marks2Str)

Segms = pairs11(Str)

return Segms

Теперь в этой строке надо найти начало и конец каждого интервала, состоящего из 1.

def pairs11(A):

Out = []

if A[-1] == '1':

A = A + '0'

if A[0] == '1':

End =A.find('10')

Out.append(((0,End+1)))

Beg = End + 1

else:

Beg = 0

while True:

Beg = A.find('01',Beg)

if Beg == -1:

break

else:

End = Beg+1

End = A.find('10',End)

if End == -1:

break

else:

Out.append([Beg+1,End+1])

Beg = End +1

return Out

Вот пример, поясняющий работу этой функции. Исходная последовательность Step = np.int_([1,0,0,1,1,1,0,1,1]) превращается в строку A=’100111011’, а затем находим границы интервалов из 1.

Pairs = selectSegm(А)

print(Pairs)

[(0, 1), [3, 6], [7, 9]]

Следующий шаг является эмпирическим. Дело в том, что среди информационных интервалов, принадлежащих одному слову, может случайно попасть один или несколько интервалов из класса 0. Это приведет к разрыву слова на несколько частей. Чтобы избежать этого явления, вводим эмпирический параметр 𝐿𝑖𝑚, определяющий максимальный возможный разрыв между парами информационных интервалов одного слова. На данном этапе нам известны начала и концы сегментов из единиц. Если конец сегмента отстоит менее чем на 𝐿𝑖𝑚 интервалов от начала следующего сегмента, то оба сегмента объединяются путем замены интервалов между сегментами единицами. Наконец, заменив каждый интервал соответствующим отрезком исходного потока, получаем разбиение этого потока на слова. При частоте стробирования Fr=44100 и длине интервалов Fr/1000 выбор 𝐿𝑖𝑚 осуществляется из промежутка [25,45]. В результате проделанных манипуляций получаем функцию wordBorders(In, SizeFragm), возвращающую список пар из начал и концов «слов». Слово взято в кавычки, поскольку таким образом могут быть выделены как фрагменты слова, произнесенного по слогам, так и целые предложения. Как будет показано ниже, это не имеет принципиального значения.

Описание больших фрагментов файла

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

При втором подходе на входе в систему используют «сырые» данные, исходный речевой файл, а для анализа применяют сложную нейронную сеть, требующую значительных вычислительных ресурсов (на этапе тренировки). Второй подход становится превалирующим в настоящее время, поскольку его применение сводится к выбору подходящей архитектуры сети, не вникая в сущность проблемы. Автор книги придерживается первого подхода, поскольку он предполагает наличие скромных вычислительных ресурсов, и данный раздел посвящён сбору необходимых параметров, связанных с сущностью проблемы. Выше было показано, каким образом исходный файл разбивается на фрагменты, условно названные словами. Теперь определим параметры, связанные с каждым из таких фрагментов.

Энергия сигнала

Если сигнал представлен numpy массивом, то средняя энергия вычисляется с помощью функции

def myEnergy(In):

Ln =len(In)

return sum(In*In)/ln

Частота основного тона

Самые первые «детекторы лжи» на основе анализа голоса использовали именно этот параметр для выделения участков речевого файла, относимых к ложным высказываниям. В их основе лежала здравая идея, согласно которой во время ложного высказывания изменяется напряжение голосовых связок, что и отражается на частоте основного тона. Первые «детекторы» появились вместе с дешёвыми процессорами, встраиваемыми в телефон. Наличие процессора позволяло определить частоту основного тона, таким образом, слушатель на другом конце провода, якобы, мог сразу узнать о ложности какого-либо высказывания. Эффективность таких устройств оказалась исключительно низкой, и от идеи быстро отказались, но сама мысль использовать эти параметры для анализа осталась. Ниже мы объясним тонкости, связанные с измерением этого параметра. В качестве примера рассмотрим цифровой образ слога „ka“.


При произнесении звука „k“ голосовые связки закрыты и напряжены, а поток воздуха из лёгких связки раскрывает. В результате возникает поток повышенного давления воздуха (вспышка), за которой следует поток малого давления, представленный на рисунке в виде шума. При произнесении слога „ka“ голосовой аппарат перестраивается на произнесение звука „A“, для чего требуется время установления. Если посмотреть на участок, относящийся к „A“, то можно заметить, что он имеет почти периодический характер. Частота этого сигнал и есть частота основного тона, или форманта F0.

Форма сигнала для других гласных звуков будет другой, и частота будет зависеть от гласного. Сигнал зависит от напряжения голосовых связок. При прохождении воздушного потока через вибрирующие связки образуются вихри, которые и определяют основную форму сигнала. Дополнительный вклад вносят фильтры, образованные ротовой полостью. Форманта F0 легко находится с помощью стандартной функции fft, если удалось выделить участок файла, относящийся к гласному звуку. Пусть K — номер коэффициента Фурье с максимальной мощностью, Fr частота стробирования сигнала, Len длина интервала, для которого найдены коэффициенты. Тогда F0= K*Fr/Len. Принцип измерения F0 остается прежним для всех гласных.

from scipy.fftpack import fft

from scipy import signal as sgn

[Fr,Dat] = read('Sounds/A.wav')

N =len(Fragm)

Wnd = sgn.windows.hann(N)

Fragm1 — = np.mean(Fragm)

Afft = abs(fft(Fragm1*Wnd))

Ind =np.argmax(Afft)

print('F0=',Ind*Fr/N)

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

Мел-кепстральные коэффициенты

Частота основного тона является разновидностью спектральных характеристик. В настоящее время считается, что наиболее точной сжатой спектральной характеристикой являются мел-кепстральные коэффициенты (MFCC). Идея кепстра достаточно проста. Предположим, что нам нужно краткое описание преобразования Фурье заданного отрезка. Первое, что приходит в голову, применить к этому преобразованию снова преобразование Фурье и оставить лишь несколько коэффициентов. Особенностью преобразования Фурье является то, что в результате мы снова получим исходный отрезок. Чтобы избавиться от такого результата, сначала к преобразованию Фурье применяется логарифм, а уже после этого — обратное преобразование Фурье. Идея оказалась очень плодотворной, поскольку попутно удалось решить и другие задачи. Мел-кепстральные коэффициенты получаются после того, как кепстр применяется не к самому отрезку файла, а к результатам фильтрации исходного отрезка с помощью специальной гребенки фильтров. Для вычисления этих коэффициентов имеется функция в пакете librosa. Эти коэффициенты можно использовать для получения характеристик фрагмента, однако, следует учесть, что это весьма затратная с точки зрения времени операция.

Сравнение кривых

До сих пор мы имели дело с отдельными значениями параметров. Ниже будут рассмотрены параметры, представленные в виде кривых. Возникает вопрос, каким образом сравнивать две кривые, чтобы определить их взаимную близость.

Разложение по ортогональному базису

Пусть заданы две кривые, определенные функциями F(t),G(t). Выбираем ортонормированный базис и оставляем заданное число M коэффициентов. В качестве примера такого разложения можно взять преобразование Фурье или дискретное косинус преобразование. Этот подход хорош, когда совпадают области определения обеих функций. Если же они разнятся (имеют разные длины), то для сравнения понадобятся дополнительные манипуляции с коэффициентами.

Квантили

Существует другой подход, основанный на квантилях, свободный от указанного выше недостатка и требующий минимальных вычислений. С этой целью исходные функции нормируются таким образом, чтобы их область значений заполняла интервал [0,1]. После этого выбирается набор квантилей в качестве вектора характеристик функций. Квантили зависят только от области значений, поэтому зависимость от длины интервалов, на которых определены функции, сводится к минимуму.

def getQuant(Wave,Intervs):

'''Standard quaniles normalized by the maximum of wave

'''

Wave = np.float32(Wave)

Wave /= max(Wave)

Quant = np.quantile(Wave,Intervs)

return np.float32(Quant)

Применим квантили для сравнения формы двух тригонометрических кривых

X = np.arange(0,1,0.01)

F = np.sin(2*np.pi*X)

G = np.cos(2*np.pi*X)

Intervs = np.arange(1,5)/5

print(getQuant(F,Intervs))

print(getQuant(G,Inters))

Ответы

[-0.809017 -0.309017 0.309017 0.809017]

[-0.809017 -0.309017 0.309017 0.809017]

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

Кривые, применяемые для описания фрагмента.

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

Кривая энергии в точке

Согласно теории (Teager), энергия сигнала X в точке n определяется формулой X[n}*X[n]-X[n-1]*X[n+1]. Построенная согласно этой формуле кривая описывает изменение энергии со временем, поэтому служит очень важной характеристикой фрагмента. Используя квантили, получаем сжатое описание этой кривой. Обычно применяют квантили равномерно распределенные по длине так, как это сделано в примере с тригонометрическими функциями.

Огибающая кривая сигнала

Огибающая кривая сигнала Dat строится с помощью функции, зависящей от параметра WinLen. В данном случае выбрано значение 20, но это эмпирическая величина. Визуально огибающая мало зависит от этого параметра, если он берется из интервала [20,50], однако, увеличение WinLen ведет росту времени на вычисление.

WinLen = 20

Beg = 0

End = WinLen

Env1 = []

while End <= len(Dat1):



Поделиться книгой:

На главную
Назад