Идентификация динамика с использованием тангажа и MFCC

Этот пример демонстрирует подход машинного обучения для идентификации людей на основе функций, извлеченных из записанной речи. Функциями, используемыми для обучения классификатора, являются тангаж озвученных сегментов речи и коэффициенты mel frequency cepstrum (MFCC). Это идентификация динамика с закрытой установкой: аудио тестируемого динамика сравнивается со всеми доступными моделями динамика (конечный набор) и возвращается ближайшее соответствие.

Введение

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

Тангаж и MFCC извлекаются из речевых сигналов, записанных для 10 динамиков. Эти функции используются для обучения классификатора K-ближайших соседей (KNN). Затем новые речевые сигналы, которые нужно классифицировать, проходят через ту же редукцию данных. Обученный классификатор KNN предсказывает, какой из 10 динамиков является ближайшим соответствием.

Функции, используемые для классификации

В этом разделе рассматриваются тангаж и MFCC, две функции, которые используются для классификации динамиков.

Тангаж

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

В качестве примера звонкой и негласной речи рассмотрим представление слова «два» во временной области (/T UW/). Согласный/T/( негласная речь) выглядит как шум, в то время как гласный/UW/( голосовая речь) характеризуется сильной основной частотой.

[audioIn, fs] = audioread('Counting-16-44p1-mono-15secs.wav');
twoStart = 110e3;
twoStop = 135e3;
audioIn = audioIn(twoStart:twoStop);
timeVector = linspace((twoStart/fs),(twoStop/fs),numel(audioIn));

sound(audioIn,fs)

figure
plot(timeVector,audioIn)
axis([(twoStart/fs) (twoStop/fs) -1 1])
ylabel('Amplitude')
xlabel('Time (s)')
title('Utterance - Two')

Речевой сигнал имеет динамический характер и изменяется с течением времени. Принято, что речевые сигналы являются стационарными по коротким временным шкалам, и их обработка осуществляется в окнах 20-40 мс. Этот пример использует окно 30 мс с перекрытием 25 мс. Используйте pitch функция, чтобы увидеть, как тангаж изменяется с течением времени.

windowLength = round(0.03*fs);
overlapLength = round(0.025*fs);

f0 = pitch(audioIn,fs,'WindowLength',windowLength,'OverlapLength',overlapLength,'Range',[50,250]);

figure
subplot(2,1,1)
plot(timeVector,audioIn)
axis([(110e3/fs) (135e3/fs) -1 1])
ylabel('Amplitude')
xlabel('Time (s)')
title('Utterance - Two')

subplot(2,1,2)
timeVectorPitch = linspace((twoStart/fs),(twoStop/fs),numel(f0));
plot(timeVectorPitch,f0,'*')
axis([(110e3/fs) (135e3/fs) min(f0) max(f0)])
ylabel('Pitch (Hz)')
xlabel('Time (s)')
title('Pitch Contour')

The pitch функция оценивает значение тангажа для каждой системы координат. Однако тангаж характерен только для источника в областях голосовой речи. Самым простым методом различения тишины и речи является анализ краткосрочной степени. Если степень в системе координат выше заданного порога, вы объявляете систему координат речевым.

pwrThreshold = -20;
[segments,~] = buffer(audioIn,windowLength,overlapLength,'nodelay');
pwr = pow2db(var(segments));
isSpeech = (pwr > pwrThreshold);

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

zcrThreshold = 300;
zeroLoc = (audioIn==0);
crossedZero = logical([0;diff(sign(audioIn))]);
crossedZero(zeroLoc) = false;
[crossedZeroBuffered,~] = buffer(crossedZero,windowLength,overlapLength,'nodelay');
zcr = (sum(crossedZeroBuffered,1)*fs)/(2*windowLength);
isVoiced = (zcr < zcrThreshold);

Объедините isSpeech и isVoiced для определения, содержит ли система координат голосовую речь.

voicedSpeech = isSpeech & isVoiced;

Удалите области, которые не соответствуют озвученной речи, из оценки тангажа и графика.

f0(~voicedSpeech) = NaN;

figure
subplot(2,1,1)
plot(timeVector,audioIn)
axis([(110e3/fs) (135e3/fs) -1 1])
axis tight
ylabel('Amplitude')
xlabel('Time (s)')
title('Utterance - Two')

subplot(2,1,2)
plot(timeVectorPitch,f0,'*')
axis([(110e3/fs) (135e3/fs) min(f0) max(f0)])
ylabel('Pitch (Hz)')
xlabel('Time (s)')
title('Pitch Contour')

Коэффициенты Мел-частоты Cepstrum (MFCC)

MFCC являются популярными функциями, извлеченными из речевых сигналов для использования в задачах распознавания. В модели речи «источник-фильтр» MFCC понимается как представление фильтра (голосового тракта). Частотная характеристика голосового тракта относительно гладка, в то время как источник голосовой речи может быть смоделирован как импульсный train. Результатом является то, что голосовой тракт может быть оценен спектральной огибающей речевого сегмента.

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

Несмотря на отсутствие жесткого стандарта для вычисления MFCC, основные шаги описаны на схеме.

mel filterbank линейно разделяет первые 10 треугольных фильтров и логарифмически разделяет оставшиеся фильтры. Отдельные полосы взвешены для четной энергии. График представляет типичный mel filterbank.

Этот пример использует mfcc вычислить MFCC для каждого файла.

Набор данных

Этот пример использует базу данных переписи населения (также известную как AN4 Database) из группы распознавания робастной речи CMU [1]. Набор данных содержит записи мужских и женских субъектов, говорящих слова и цифры. Функция helper в этом разделе загружает ее для вас и преобразует необработанные файлы в FLAC. Речевые файлы разделяются на подкаталоги на основе меток, соответствующих динамикам. Если вы не можете загрузить его, можно загрузить таблицу функций из HelperAN4TrainingFeatures.mat и переходите непосредственно к разделу «Обучение классификатора». Функции были извлечены из того же набора данных.

Загрузите и извлечите файлы речи для 10 динамиков (5 женских и 5 мужских) во временную директорию с помощью HelperAN4Download функция.

dataDir = HelperAN4Download;

Создайте audioDatastore объект для управления этой базой данных для обучения. datastore позволяет собирать необходимые файлы формата файла и читать их.

ads = audioDatastore(dataDir,'IncludeSubfolders',true, ...
    'FileExtensions','.flac', ...
    'LabelSource','foldernames')
ads = 
  audioDatastore with properties:

                       Files: {
                              ' ...\scrawfor\AppData\Local\Temp\an4\wav\flacData\fejs\an36-fejs-b.flac';
                              ' ...\scrawfor\AppData\Local\Temp\an4\wav\flacData\fejs\an37-fejs-b.flac';
                              ' ...\scrawfor\AppData\Local\Temp\an4\wav\flacData\fejs\an38-fejs-b.flac'
                               ... and 122 more
                              }
                     Folders: {
                              'C:\Users\scrawfor\AppData\Local\Temp\an4\wav\flacData'
                              }
                      Labels: [fejs; fejs; fejs ... and 122 more categorical]
    AlternateFileSystemRoots: {}
              OutputDataType: 'double'
      SupportedOutputFormats: ["wav"    "flac"    "ogg"    "mp4"    "m4a"]
         DefaultOutputFormat: "wav"

The splitEachLabel функция audioDatastore разделяет datastore на два или более хранилищ данных. Получившиеся хранилища данных имеют заданную долю аудио файлов от каждой метки. В этом примере datastore разделяется на две части. 80% данных для каждой метки используется для обучения, а оставшиеся 20% используются для проверки. The countEachLabel метод audioDatastore используется для подсчета количества аудио файлов на метку. В этом примере метка идентифицирует динамик.

[adsTrain, adsTest] = splitEachLabel(ads,0.8);

Отобразите datastore и количество динамиков в train.

adsTrain
adsTrain = 
  audioDatastore with properties:

                       Files: {
                              ' ...\scrawfor\AppData\Local\Temp\an4\wav\flacData\fejs\an36-fejs-b.flac';
                              ' ...\scrawfor\AppData\Local\Temp\an4\wav\flacData\fejs\an37-fejs-b.flac';
                              ' ...\scrawfor\AppData\Local\Temp\an4\wav\flacData\fejs\an38-fejs-b.flac'
                               ... and 94 more
                              }
                     Folders: {
                              'C:\Users\scrawfor\AppData\Local\Temp\an4\wav\flacData'
                              }
                      Labels: [fejs; fejs; fejs ... and 94 more categorical]
    AlternateFileSystemRoots: {}
              OutputDataType: 'double'
      SupportedOutputFormats: ["wav"    "flac"    "ogg"    "mp4"    "m4a"]
         DefaultOutputFormat: "wav"

trainDatastoreCount = countEachLabel(adsTrain)
trainDatastoreCount=10×2 table
    Label    Count
    _____    _____

    fejs      10  
    fmjd      10  
    fsrb      10  
    ftmj      10  
    fwxs      10  
    mcen      10  
    mrcb      10  
    msjm      10  
    msjr      10  
    msmn       7  

Отобразите datastore и количество динамиков в тестовом datastore.

adsTest
adsTest = 
  audioDatastore with properties:

                       Files: {
                              ' ...\scrawfor\AppData\Local\Temp\an4\wav\flacData\fejs\cen6-fejs-b.flac';
                              ' ...\scrawfor\AppData\Local\Temp\an4\wav\flacData\fejs\cen7-fejs-b.flac';
                              ' ...\scrawfor\AppData\Local\Temp\an4\wav\flacData\fejs\cen8-fejs-b.flac'
                               ... and 25 more
                              }
                     Folders: {
                              'C:\Users\scrawfor\AppData\Local\Temp\an4\wav\flacData'
                              }
                      Labels: [fejs; fejs; fejs ... and 25 more categorical]
    AlternateFileSystemRoots: {}
              OutputDataType: 'double'
      SupportedOutputFormats: ["wav"    "flac"    "ogg"    "mp4"    "m4a"]
         DefaultOutputFormat: "wav"

testDatastoreCount = countEachLabel(adsTest)
testDatastoreCount=10×2 table
    Label    Count
    _____    _____

    fejs       3  
    fmjd       3  
    fsrb       3  
    ftmj       3  
    fwxs       2  
    mcen       3  
    mrcb       3  
    msjm       3  
    msjr       3  
    msmn       2  

Чтобы предворительно просмотреть содержимое datastore, прочтите образец файла и воспроизведите его с помощью аудио устройства по умолчанию.

[sampleTrain, dsInfo] = read(adsTrain);
sound(sampleTrain,dsInfo.SampleRate)

Чтение из train datastore перемещает указатель на чтение так, чтобы можно было выполнить итерацию через базу данных. Сбросьте train datastore, чтобы вернуть указатель на чтение в начало для следующей редукции данных.

reset(adsTrain)

Редукция данных

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

fs = dsInfo.SampleRate;
windowLength = round(0.03*fs);
overlapLength = round(0.025*fs);

features = [];
labels = [];
while hasdata(adsTrain)
    [audioIn,dsInfo] = read(adsTrain);
    
    melC = mfcc(audioIn,fs,'Window',hamming(windowLength,'periodic'),'OverlapLength',overlapLength);
    f0 = pitch(audioIn,fs,'WindowLength',windowLength,'OverlapLength',overlapLength);
    feat = [melC,f0];
    
    voicedSpeech = isVoicedSpeech(audioIn,fs,windowLength,overlapLength);
    
    feat(~voicedSpeech,:) = [];
    label = repelem(dsInfo.Label,size(feat,1));
    
    features = [features;feat];
    labels = [labels,label];
end

Тангаж и MFCC не имеют одинаковых шкал. Это приведет к смещению классификатора. Нормализуйте функции путем вычитания среднего и деления стандартного отклонения.

M = mean(features,1);
S = std(features,[],1);
features = (features-M)./S;

Обучение классификатора

Теперь, когда вы собрали функции для всех 10 динамиков, можно обучить классификатор на их основе. В этом примере вы используете классификатор K-ближайших соседей (KNN). KNN является классификационным методом, естественно подходящим для многоклассовой классификации. Гиперпараметры для ближайшего соседнего классификатора включают количество ближайших соседей, метрику расстояния, используемую для вычисления расстояния до соседей, и вес метрики расстояния. Гиперпараметры выбираются для оптимизации точности валидации и эффективности на тестовом наборе. В этом примере количество соседей устанавливается равным 5, и метрика для выбранного расстояния является квадратным-обратным взвешенным Евклидовым расстоянием. Для получения дополнительной информации о классификаторе см. fitcknn (Statistics and Machine Learning Toolbox).

Обучите классификатор и распечатайте точность перекрестной проверки. crossval (Statistics and Machine Learning Toolbox) и kfoldLoss (Statistics and Machine Learning Toolbox) используются для вычисления точности перекрестной валидации классификатора KNN.

Задайте все опции классификатора и обучите классификатор.

trainedClassifier = fitcknn( ...
    features, ...
    labels, ...
    'Distance','euclidean', ...
    'NumNeighbors',5, ...
    'DistanceWeight','squaredinverse', ...
    'Standardize',false, ...
    'ClassNames',unique(labels));

Выполните перекрестную валидацию.

k = 5;
group = labels;
c = cvpartition(group,'KFold',k); % 5-fold stratified cross validation
partitionedModel = crossval(trainedClassifier,'CVPartition',c);

Вычислите точность валидации.

validationAccuracy = 1 - kfoldLoss(partitionedModel,'LossFun','ClassifError');
fprintf('\nValidation accuracy = %.2f%%\n', validationAccuracy*100);
Validation accuracy = 97.64%

Визуализируйте график неточностей.

validationPredictions = kfoldPredict(partitionedModel);
figure
cm = confusionchart(labels,validationPredictions,'title','Validation Accuracy');
cm.ColumnSummary = 'column-normalized';
cm.RowSummary = 'row-normalized';

Вы также можете использовать Приложение Classification Learner (Statistics and Machine Learning Toolbox), чтобы попробовать и сравнить различные классификаторы с вашей таблицей функций.

Проверка классификатора

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

Чтение файлов, извлечение функций из тестового набора и их нормализация.

features = [];
labels = [];
numVectorsPerFile = [];
while hasdata(adsTest)
    [audioIn,dsInfo] = read(adsTest);
    
    melC = mfcc(audioIn,fs,'Window',hamming(windowLength,'periodic'),'OverlapLength',overlapLength);
    f0 = pitch(audioIn,fs,'WindowLength',windowLength,'OverlapLength',overlapLength);
    feat = [melC,f0];
    
    voicedSpeech = isVoicedSpeech(audioIn,fs,windowLength,overlapLength);
    
    feat(~voicedSpeech,:) = [];
    numVec = size(feat,1);
    
    label = repelem(dsInfo.Label,numVec);
    
    numVectorsPerFile = [numVectorsPerFile,numVec];
    features = [features;feat];
    labels = [labels,label];
end
features = (features-M)./S;

Спрогнозируйте метку (динамик) для каждой системы координат путем вызова predict на trainedClassifier.

prediction = predict(trainedClassifier,features);
prediction = categorical(string(prediction));

Визуализируйте график неточностей.

figure('Units','normalized','Position',[0.4 0.4 0.4 0.4])
cm = confusionchart(labels,prediction,'title','Test Accuracy (Per Frame)');
cm.ColumnSummary = 'column-normalized';
cm.RowSummary = 'row-normalized';

Для данного файла делаются предсказания для каждой системы координат. Определите режим предсказаний для каждого файла и затем постройте график неточностей.

r2 = prediction(1:numel(adsTest.Files));
idx = 1;
for ii = 1:numel(adsTest.Files)
    r2(ii) = mode(prediction(idx:idx+numVectorsPerFile(ii)-1));
    idx = idx + numVectorsPerFile(ii);
end

figure('Units','normalized','Position',[0.4 0.4 0.4 0.4])
cm = confusionchart(adsTest.Labels,r2,'title','Test Accuracy (Per File)');
cm.ColumnSummary = 'column-normalized';
cm.RowSummary = 'row-normalized';

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

Эксперимент повторяли с использованием внутреннего набора данных. Набор данных состоит из 20 дикторов, каждый из которых говорит несколько предложений из гарвардского списка предложений [2]. Для 20 динамиков точность валидации 89%.

Вспомогательные функции

function voicedSpeech = isVoicedSpeech(x,fs,windowLength,overlapLength)

pwrThreshold = -40;
[segments,~] = buffer(x,windowLength,overlapLength,'nodelay');
pwr = pow2db(var(segments));
isSpeech = (pwr > pwrThreshold);

zcrThreshold = 1000;
zeroLoc = (x==0);
crossedZero = logical([0;diff(sign(x))]);
crossedZero(zeroLoc) = false;
[crossedZeroBuffered,~] = buffer(crossedZero,windowLength,overlapLength,'nodelay');
zcr = (sum(crossedZeroBuffered,1)*fs)/(2*windowLength);
isVoiced = (zcr < zcrThreshold);

voicedSpeech = isSpeech & isVoiced;

end

Ссылки

[1] «CMU Sphinx Group - Audio Databases». Доступ к 19 декабря 2019 года. http://www.speech.cs.cmu.edu/databases/an4/.

[2] «Гарвардские предложения». Википедия, 27 августа 2019 года. Википедия, https://en.wikipedia.org/w/index.php?title=Harvard_sentences&oldid=912785385.