exponenta event banner

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

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

Введение

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

Основной тон и MFCC извлекаются из речевых сигналов, записанных для 10 динамиков. Эти функции используются для обучения классификатора К-ближайшего соседа (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')

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 Mel-частоты (MFCC)

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

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

Хотя нет жёсткого стандарта для вычисления MFCC, основные шаги очерчены диаграммой.

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

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

Набор данных

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

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

dataDir = HelperAN4Download;

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

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"

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

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

Отображение хранилища данных и количества динамиков в хранилище данных поезда.

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  

Отображение хранилища данных и количества динамиков в тестовом хранилище данных.

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  

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

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

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

reset(adsTrain)

Извлечение элементов

Извлеките функции основного тона и MFCC из каждого кадра, соответствующего речевому сигналу в хранилище учебных данных. Поддерживающая функция 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

Тангаж и МФКЦ не в одном масштабе. Это приведет к смещению классификатора. Нормализуйте элементы путем вычитания среднего значения и деления стандартного отклонения.

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

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

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

Обучите классификатор и распечатайте точность перекрестной проверки. crossval (Статистика и инструментарий машинного обучения) и 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 - аудио базы данных». Доступ состоялся 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.