Верификация спикера с использованием Смешанной гауссовской модели

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

Рисунок системы GMM-UBM показан:

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

Чтобы мотивировать этот пример, вы сначала выполните верификацию динамика с помощью предварительно обученной универсальной фоновой модели (UBM). Модель обучалась с использованием слова «stop» из набора данных Google Speech Commands [1].

Файл MAT, speakerVerficationExampleData.mat, включает UBM, сконфигурированную audioFeatureExtractor объект и коэффициенты нормализации, используемые для нормализации функций.

load speakerVerificationExampleData.mat ubm afe normFactors

Зарегистрироваться

Если вы хотите проверить себя, задайте enrollYourself на true. Вам будет предложено несколько раз записать слова «стоп». Говорите «стоп» только один раз на приглашение. Увеличение количества записей должно повысить точность верификации.

enrollYourself = false;
if enrollYourself
    numToRecord = 5;
    ID = 'self';
    helperAddUser (afe.SampleRate, numToRecord, ID);
end

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

ads = audioDatastore(pwd);

Файлы, включенные в этот пример, состоят из слова «stop», на котором пять раз говорят три разных динамика: BFn (1), BHm (3) и RPalanim (1). Имена файлов указаны в SpeakerID_RecordingNumber формате. Установите метки datastore на соответствующий идентификатор динамика.

[~,fileName] = cellfun(@(x)fileparts(x),ads.Files,'UniformOutput',false);
fileName = split(fileName,'_');
speaker = strcat(fileName(:,1));
ads.Labels = categorical(speaker);

Используйте все файлы, кроме одного, от оратора, который вы регистрируетесь в процессе регистрации. Остальные файлы используются для тестирования системы.

if enrollYourself
    enrollLabel = ID;
else
    enrollLabel = 'BHm';
end

forEnrollment = ads.Labels==enrollLabel;
forEnrollment(find(forEnrollment==1,1)) = false;
adsEnroll = subset(ads,forEnrollment);
adsTest = subset(ads,~forEnrollment);

Зарегистрируйте выбранный динамик, используя максимально апостериорную (MAP) адаптацию. Подробную информацию об алгоритме регистрации можно найти позже в примере.

speakerGMM = helperEnroll(ubm,afe,normFactors,adsEnroll);

Верификация

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

threshold = 0.7;
сброс (adsTest)
while hasdata (adsTest)
    fprintf ('Identity to confirm: %s\n', enrollLabel)
    [audioData, adsInfo] = read (adsTest);
    
    fprintf (' | Speaker identity: %s\n', string (adsInfo.Label))
    
    verificationStatus = helperVerify (audioData, afe, normFactors, speakerGMM, ubm, порог);

    if verificationStatus
        fprintf (' | Confirmed.\n');
    else
        fprintf (' | Imposter!\n');
    end
end
Identity to confirm: BHm
 | Speaker identity: BFn
 | Imposter!
Identity to confirm: BHm
 | Speaker identity: BHm
 | Confirmed.
Identity to confirm: BHm
 | Speaker identity: RPalanim
 | Imposter!

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

Создайте универсальную фоновую модель

UBM, используемый в этом примере, обучается с помощью [1]. Загрузите и извлеките набор данных.

url = 'https://storage.googleapis.com/download.tensorflow.org/data/speech_commands_v0.01.tar.gz';

downloadFolder = tempdir;
datasetFolder = fullfile(downloadFolder,'google_speech');

if ~exist(datasetFolder,'dir')
    disp('Downloading Google speech commands data set (1.9 GB)...')
    untar(url,datasetFolder)
end

Создайте audioDatastore это указывает на набор данных. Используйте имена папок в качестве меток. Имена папок указывают на слова, сказанные в наборе данных.

ads = audioDatastore(datasetFolder,"Includesubfolders",true,'LabelSource','folderNames');

subset набор данных, включающий только слово «стоп».

ads = subset(ads,ads.Labels==categorical("stop"));

Установите метки на уникальные идентификаторы динамиков, закодированные в именах файлов. Идентификаторы динамиков иногда начинаются с числа: добавьте 'a' всем идентификаторам, чтобы сделать имена более удобными для переменных.

[~,fileName] = cellfun(@(x)fileparts(x),ads.Files,'UniformOutput',false);
fileName = split(fileName,'_');
speaker = strcat('a',fileName(:,1));
ads.Labels = categorical(speaker);

Создайте три хранилища данных: один для регистрации, один для оценки системы верификации и один для обучения UBM. Регистрируйте ораторов, которые имеют по крайней мере три высказывания. Для каждого из ораторов поместите два слова в набор для зачисления. Остальные попадут в тестовый набор. Тестовый набор состоит из высказываний всех дикторов, которые имеют три или более высказываний в наборе данных. Набор обучающих данных UBM состоит из остальных высказываний.

numSpeakersToEnroll = 10;
labelCount = countEachLabel (объявления);
forEnrollAndTestSet = labelCount {:, 1} (labelCount {:, 2} > = 3);
forEnroll = forEnrollAndTestSet (randi ([1, numel (forEnrollAndTestSet)], numSpeakersToEnroll, 1));
tf = ismember (ads.Labels, forEnroll);
adsEnrollAndValidate = подмножество (объявления, tf);
adsEnroll = splitEachLabel (adsEnrollAndValidate, 2);

adsTest = subset (ads, ismember (ads.Labels, forEnrollAndTestSet));
adsTest = subset (adsTest, ~ ismember (adsTest.Files, файлы));

forUBMTraining = ~ (ismember (ads.Files, файлы) | ismember (ads.Files, файлы));
adsTrainUBM = подмножество (объявления, forUBMTraining);

Прочтите из обучающего datastore и прослушайте файл. Сбросьте datastore.

[audioData,audioInfo] = read(adsTrainUBM);
fs = audioInfo.SampleRate;

sound(audioData,fs)

reset(adsTrainUBM)

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

В редукцию данных конвейере для этого примера вы:

  1. Нормализуйте аудио

  2. Использование detectSpeech чтобы удалить непрошенные области из аудио

  3. Извлечение функций из аудио

  4. Нормализуйте функции

  5. Примените среднюю кепстральную нормализацию

Во-первых, создайте audioFeatureExtractor объект для извлечения MFCC. Задайте длительность 40 мс и скачок 10 мс для систем координат.

windowDuration = 0.04;
hopDuration = 0.01;
windowSamples = round(windowDuration*fs);
hopSamples = round(hopDuration*fs);
overlapSamples = windowSamples - hopSamples;

afe = audioFeatureExtractor( ...
    'SampleRate',fs, ...
    'Window',hann(windowSamples,'periodic'), ...
    'OverlapLength',overlapSamples, ...
    ...
    'mfcc',true);

Нормализуйте аудио.

audioData = audioData./max(abs(audioData));

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

detectSpeech(audioData,fs);

Функции detectSpeech снова. На этот раз вернём индексы речевой области и с помощью них уберем из аудиоклипа непиковые области.

idx = detectSpeech(audioData,fs);
audioData = audioData(idx(1,1):idx(1,2));

Функции extract на audioFeatureExtractor объект для извлечения функций из аудио данных. Размер выходных данных extract является numHops-by- numFeatures.

features = extract(afe,audioData);
[numHops,numFeatures] = size(features)
numHops = 66
numFeatures = 13

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

features = (features' - normFactors.Mean) ./ normFactors.Variance;

Примените локальную среднюю кепстральную нормализацию.

features = features - mean(features,'all');

Трубопровод редукции данных инкапсулирован в вспомогательную функцию helperFeatureExtraction.

Вычисление коэффициентов нормализации глобальных функций

Извлеките все функции из набора данных. Если у вас есть Parallel Computing Toolbox™, определите оптимальное количество разделов для набора данных и распределите расчеты между доступными работниками. Если у вас нет Parallel Computing Toolbox™, используйте один раздел.

featuresAll = {};
if ~isempty(ver('parallel'))
    numPar = 18;
else
    numPar = 1;
end

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

parfor ii = 1:numPar
    adsPart = partition(ads,numPar,ii);
    featuresPart = cell(0,numel(adsPart.Files));
    for iii = 1:numel(adsPart.Files)
        audioData = read(adsPart);
        featuresPart{iii} = helperFeatureExtraction(audioData,afe,[]);
    end
    featuresAll = [featuresAll,featuresPart];
end
Starting parallel pool (parpool) using the 'local' profile ...
Connected to the parallel pool (number of workers: 6).
allFeatures = cat(2,featuresAll{:});

Вычислите среднее значение и отклонение каждой функции.

normFactors.Mean = mean(allFeatures,2,'omitnan');
normFactors.STD = std(allFeatures,[],2,'omitnan');

Инициализация GMM

Универсальная фоновая модель является Гауссовой смешанной моделью. Определите количество компонентов в смеси. [2] предлагает более 512 для текстово-независимых систем. Веса компонентов начинают распределяться равномерно.

numComponents =32;
alpha = таковые (1,numComponents )/numComponents;

Используйте случайную инициализацию для mu и sigma каждого компонента GMM. Создайте структуру для хранения необходимой информации UBM.

mu = randn(numFeatures,numComponents);
sigma = rand(numFeatures,numComponents);
ubm = struct('ComponentProportion',alpha,'mu',mu,'sigma',sigma);

Обучите UBM, используя максимизацию ожиданий

Подбор GMM к набору обучающих данных для создания UBM. Используйте алгоритм максимизации ожиданий.

Алгоритм максимизации ожиданий рекурсивен. Во-первых, задайте критерии остановки.

maxIter = 20;
targetLogLikelihood = 0;
tol = 0.5;
pastL = -inf; % initialization of previous log-likelihood

В цикле обучите UBM с помощью алгоритма максимизации ожиданий.

tic
for iter = 1:maxIter
    
    % EXPECTATION
    N = zeros(1,numComponents);
    F = zeros(numFeatures,numComponents);
    S = zeros(numFeatures,numComponents);
    L = 0;
    parfor ii = 1:numPar
        adsPart = partition(adsTrainUBM,numPar,ii);
        while hasdata(adsPart)
            audioData = read(adsPart);
            
            % Extract features
            features = helperFeatureExtraction(audioData,afe,normFactors);
 
            % Compute a posteriori log-likelihood
            logLikelihood = helperGMMLogLikelihood(features,ubm);

            % Compute a posteriori normalized probability
            logLikelihoodSum = helperLogSumExp(logLikelihood);
            gamma = exp(logLikelihood - logLikelihoodSum)';
            
            % Compute Baum-Welch statistics
            n = sum(gamma,1);
            f = features * gamma;
            s = (features.*features) * gamma;
            
            % Update the sufficient statistics over utterances
            N = N + n;
            F = F + f;
            S = S + s;
            
            % Update the log-likelihood
            L = L + sum(logLikelihoodSum);
        end
    end
    
    % Print current log-likelihood and stop if it meets criteria.
    L = L/numel(adsTrainUBM.Files);
    fprintf('\tIteration %d, Log-likelihood = %0.3f\n',iter,L)
    if L > targetLogLikelihood || abs(pastL - L) < tol
        break
    else
        pastL = L;
    end
    
    % MAXIMIZATION
    N = max(N,eps);
    ubm.ComponentProportion = max(N/sum(N),eps);
    ubm.ComponentProportion = ubm.ComponentProportion/sum(ubm.ComponentProportion);
    ubm.mu = bsxfun(@rdivide,F,N);
    ubm.sigma = max(bsxfun(@rdivide,S,N) - ubm.mu.^2,eps);
end
	Iteration 1, Log-likelihood = -826.174
	Iteration 2, Log-likelihood = -538.546
	Iteration 3, Log-likelihood = -522.670
	Iteration 4, Log-likelihood = -517.458
	Iteration 5, Log-likelihood = -514.852
	Iteration 6, Log-likelihood = -513.068
	Iteration 7, Log-likelihood = -511.644
	Iteration 8, Log-likelihood = -510.588
	Iteration 9, Log-likelihood = -509.788
	Iteration 10, Log-likelihood = -509.135
	Iteration 11, Log-likelihood = -508.529
	Iteration 12, Log-likelihood = -508.032
fprintf('UBM training completed in %0.2f seconds.\n',toc)
UBM training completed in 32.31 seconds.

Регистрация: Максимальная оценка апостериори (MAP)

Как только у вас есть универсальная фоновая модель, можно зарегистрировать динамики и адаптировать UBM к динамикам. [2] предполагает коэффициент релевантности адаптации 16. Коэффициент релевантности управляет тем, сколько нужно переместить каждый компонент UBM в динамик GMM.

relevanceFactor = 16;

speakers = unique(adsEnroll.Labels);
numSpeakers = numel(speakers);

gmmCellArray = cell(numSpeakers,1);
tic
parfor ii = 1:numSpeakers
    % Subset the datastore to the speaker you are adapting.
    adsTrainSubset = subset(adsEnroll,adsEnroll.Labels==speakers(ii));
    
    N = zeros(1,numComponents);
    F = zeros(numFeatures,numComponents);
    S = zeros(numFeatures,numComponents);
    while hasdata(adsTrainSubset)
        audioData = read(adsTrainSubset);
        features = helperFeatureExtraction(audioData,afe,normFactors);
        [n,f,s,l] = helperExpectation(features,ubm);
        N = N + n;
        F = F + f;
        S = S + s;
    end
    
    % Determine the maximum likelihood
    gmm = helperMaximization(N,F,S);
    
    % Determine adaption coefficient
    alpha = N ./ (N + relevanceFactor);
    
    % Adapt the means
    gmm.mu = alpha.*gmm.mu + (1-alpha).*ubm.mu;
    
    % Adapt the variances
    gmm.sigma = alpha.*(S./N) + (1-alpha).*(ubm.sigma + ubm.mu.^2) - gmm.mu.^2;
    gmm.sigma = max(gmm.sigma,eps);
    
    % Adapt the weights
    gmm.ComponentProportion = alpha.*(N/sum(N)) + (1-alpha).*ubm.ComponentProportion;
    gmm.ComponentProportion = gmm.ComponentProportion./sum(gmm.ComponentProportion);

    gmmCellArray{ii} = gmm;
end
fprintf('Enrollment completed in %0.2f seconds.\n',toc)
Enrollment completed in 0.27 seconds.

В целях бухгалтерии преобразуйте массив ячеек GMM в struct с полями, которые являются идентификаторами динамика и значениями, являющимися структурами GMM.

for i = 1:numel(gmmCellArray)
    enrolledGMMs.(string(speakers(i))) = gmmCellArray{i};
end

Оценка

Скорость ложного отклонения динамика

Скорость ложного отклонения (FRR) динамика является частотой неправильного отклонения данного динамика. Используйте известный набор динамиков, чтобы определить частоту ложного отклонения динамиков для набора порогов.

speakers = unique(adsEnroll.Labels);
numSpeakers = numel(speakers);
llr = cell(numSpeakers,1);
tic
parfor speakerIdx = 1:numSpeakers
    localGMM = enrolledGMMs.(string(speakers(speakerIdx))); 
    adsTestSubset = subset(adsTest,adsTest.Labels==speakers(speakerIdx));
    llrPerSpeaker = zeros(numel(adsTestSubset.Files),1);
    for fileIdx = 1:numel(adsTestSubset.Files)
        audioData = read(adsTestSubset);
        [x,numFrames] = helperFeatureExtraction(audioData,afe,normFactors);
        
        logLikelihood = helperGMMLogLikelihood(x,localGMM);
        Lspeaker = helperLogSumExp(logLikelihood);
        
        logLikelihood = helperGMMLogLikelihood(x,ubm);
        Lubm = helperLogSumExp(logLikelihood);
        
        llrPerSpeaker(fileIdx) = mean(movmedian(Lspeaker - Lubm,3));
    end
    llr{speakerIdx} = llrPerSpeaker;
end
fprintf('False rejection rate computed in %0.2f seconds.\n',toc)
False rejection rate computed in 0.20 seconds.

Постройте график частоты ложного отклонения как функции от порога.

llr = cat(1,llr{:});

thresholds = -0.5:0.01:2.5;
FRR = mean(llr<thresholds);

plot(thresholds,FRR*100)
title('False Rejection Rate (FRR)')
xlabel('Threshold')
ylabel('Incorrectly Rejected (%)')
grid on

Ложная приемка спикера

Коэффициент ложного принятия (FAR) является частотой того, что высказывания, не принадлежащие зарегистрированному оратору, неправильно принимаются как принадлежащие зарегистрированному оратору. Используйте известный набор динамиков, чтобы определить FAR динамика для набора порогов. Используйте тот же набор порогов, который используется для определения FRR.

speakersTest = unique(adsTest.Labels);
llr = cell(numSpeakers,1);
tic
parfor speakerIdx = 1:numel(speakers)
    localGMM = enrolledGMMs.(string(speakers(speakerIdx)));
    adsTestSubset = subset(adsTest,adsTest.Labels~=speakers(speakerIdx));
    llrPerSpeaker = zeros(numel(adsTestSubset.Files),1);
    for fileIdx = 1:numel(adsTestSubset.Files)
        audioData = read(adsTestSubset);
        [x,numFrames] = helperFeatureExtraction(audioData,afe,normFactors);
        
        logLikelihood = helperGMMLogLikelihood(x,localGMM);
        Lspeaker = helperLogSumExp(logLikelihood);
        
        logLikelihood = helperGMMLogLikelihood(x,ubm);
        Lubm = helperLogSumExp(logLikelihood);
        
        llrPerSpeaker(fileIdx) = mean(movmedian(Lspeaker - Lubm,3));
    end
    llr{speakerIdx} = llrPerSpeaker;
end
fprintf('FAR computed in %0.2f seconds.\n',toc)
FAR computed in 22.64 seconds.

Постройте график FAR как функцию от порога.

llr = cat(1,llr{:});

FAR = mean(llr>thresholds);

plot(thresholds,FAR*100)
title('False Acceptance Rate (FAR)')
xlabel('Threshold')
ylabel('Incorrectly Rejected (%)')
grid on

Сравнение ошибок обнаружения (DET)

Когда вы перемещаете порог в системе верификации динамика, вы сходитесь между FAR и FRR. Это называется компромиссом ошибок обнаружения (DET) и обычно сообщается для двоичных задач классификации.

x1 = FAR*100;
y1 = FRR*100;
plot(x1,y1)
grid on
xlabel('False Acceptance Rate (%)')
ylabel('False Rejection Rate (%)')
title('Detection Error Tradeoff (DET) Curve')

Равная вероятность ошибок (EER)

Чтобы сравнить несколько систем, вам нужна одна метрика, которая объединяет характеристики FAR и FRR. Для этого вы определяете равную частоту ошибок (EER), которая является порогом, где совпадают кривые FAR и FRR. На практике порог EER может быть не лучшим выбором. Для примера, если верификация динамика используется как часть подхода с множественной аутентификацией для передачи проводов, FAR, скорее всего, будет взвешиваться больше, чем FRR.

[~,EERThresholdIdx] = min(abs(FAR - FRR));
EERThreshold = thresholds(EERThresholdIdx);
EER = mean([FAR(EERThresholdIdx),FRR(EERThresholdIdx)]);
plot(thresholds,FAR,'k', ...
     thresholds,FRR,'b', ...
     EERThreshold,EER,'ro','MarkerFaceColor','r')
title(sprintf('Equal Error Rate = %0.2f, Threshold = %0.2f',EER,EERThreshold))
xlabel('Threshold')
ylabel('Error Rate')
legend('False Acceptance Rate (FAR)','False Rejection Rate (FRR)','Equal Error Rate (EER)')
grid on

Если вы изменили параметры обучения UBM, рассмотрите пересохранение файла MAT с новой универсальной фоновой моделью, audioFeatureExtractor, и нормальные факторы.

resave = false;
if повторно спасти
    сохранить'speakerVerificationExampleData.mat','ubm','afe','normFactors')
end

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

Добавление пользователя к набору данных

function helperAddUser(fs,numToRecord,ID)
% Create an audio device reader to read from your audio device
deviceReader = audioDeviceReader('SampleRate',fs);

% Initialize variables
numRecordings = 1;
audioIn = [];

% Record the requested number
while numRecordings <= numToRecord
    fprintf('Say "stop" once (recording %i of %i) ...',numRecordings,numToRecord)
    tic
    while toc<2
        audioIn = [audioIn;deviceReader()];
    end
    fprintf('complete.\n')
    idx = detectSpeech(audioIn,fs);
    if isempty(idx)
        fprintf('Speech not detected. Try again.\n')
    else
        audiowrite(sprintf('%s_%i.flac',ID,numRecordings),audioIn,fs)
        numRecordings = numRecordings+1;
    end
    pause(0.2)
    audioIn = [];
end

% Release the device
release(deviceReader)
end

Зарегистрироваться

function speakerGMM = helperEnroll(ubm,afe,normFactors,adsEnroll)
% Initialization
numComponents = numel(ubm.ComponentProportion);
numFeatures = size(ubm.mu,1);
N = zeros(1,numComponents);
F = zeros(numFeatures,numComponents);
S = zeros(numFeatures,numComponents);
NumFrames = 0;

while hasdata(adsEnroll)
    % Read from the enrollment datastore
    audioData = read(adsEnroll);

    % 1. Extract the features and apply feature normalization
    [features,numFrames] = helperFeatureExtraction(audioData,afe,normFactors);
    
    % 2. Calculate the a posteriori probability. Use it to determine the
    % sufficient statistics (the count, and the first and second moments)
    [n,f,s] = helperExpectation(features,ubm);
    
    % 3. Update the sufficient statistics
    N = N + n;
    F = F + f;
    S = S + s;
    NumFrames = NumFrames + numFrames;
end
% Create the Gaussian mixture model that maximizes the expectation
speakerGMM = helperMaximization(N,F,S);

% Adapt the UBM to create the speaker model. Use a relevance factor of 16,
% as proposed in [2]
relevanceFactor = 16;

% Determine adaption coefficient
alpha = N ./ (N + relevanceFactor);

% Adapt the means
speakerGMM.mu = alpha.*speakerGMM.mu + (1-alpha).*ubm.mu;

% Adapt the variances
speakerGMM.sigma = alpha.*(S./N) + (1-alpha).*(ubm.sigma + ubm.mu.^2) - speakerGMM.mu.^2;
speakerGMM.sigma = max(speakerGMM.sigma,eps);

% Adapt the weights
speakerGMM.ComponentProportion = alpha.*(N/sum(N)) + (1-alpha).*ubm.ComponentProportion;
speakerGMM.ComponentProportion = speakerGMM.ComponentProportion./sum(speakerGMM.ComponentProportion);
end

Проверить

function verificationStatus = helperVerify(audioData,afe,normFactors,speakerGMM,ubm,threshold)
    % Extract features
    x = helperFeatureExtraction(audioData,afe,normFactors);
    
    % Determine the log-likelihood the audio came from the GMM adapted to
    % the speaker
    post = helperGMMLogLikelihood(x,speakerGMM);
    Lspeaker = helperLogSumExp(post);
    
    % Determine the log-likelihood the audio came form the GMM fit to all
    % speakers
    post = helperGMMLogLikelihood(x,ubm);
    Lubm = helperLogSumExp(post);
    
    % Calculate the ratio for all frames. Apply a moving median filter
    % to remove outliers, and then take the mean across the frames
    llr = mean(movmedian(Lspeaker - Lubm,3));

    if llr > threshold
        verificationStatus = true;
    else
        verificationStatus = false;
    end
end

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

function [features,numFrames] = helperFeatureExtraction(audioData,afe,normFactors)
    % Normalize
    audioData = audioData/max(abs(audioData(:)));
    
    % Protect against NaNs
    audioData(isnan(audioData)) = 0;
    
    % Isolate speech segment
    % The dataset used in this example has one word per audioData, if more
    % than one is speech section is detected, just use the longest
    % detected.
    idx = detectSpeech(audioData,afe.SampleRate);
    if size(idx,1)>1
        [~,seg] = max(idx(:,2) - idx(:,1));
    else
        seg = 1;
    end
    audioData = audioData(idx(seg,1):idx(seg,2));
    
    % Feature extraction
    features = extract(afe,audioData);

    % Feature normalization
    if ~isempty(normFactors)
        features = (features-normFactors.Mean')./normFactors.STD';
    end
    features = features';
    
    % Cepstral mean subtraction (for channel noise)
    if ~isempty(normFactors)
        features = features - mean(features,'all');
    end
    
    numFrames = size(features,2);
end

Логарифмическая сумма-экспонента

function y = helperLogSumExp(x)
% Calculate the log-sum-exponent while avoiding overflow
a = max(x,[],1);
y = a + sum(exp(bsxfun(@minus,x,a)),1);
end

Ожидание

function [N,F,S,L] = helperExpectation(features,gmm)

post = helperGMMLogLikelihood(features,gmm);

% Sum the likelihood over the frames
L = helperLogSumExp(post);

% Compute the sufficient statistics
gamma = exp(post-L)';

N = sum(gamma,1);
F = features * gamma;
S = (features.*features) * gamma;
L = sum(L);
end

Максимизация

function gmm = helperMaximization(N,F,S)
    N = max(N,eps);
    gmm.ComponentProportion = max(N/sum(N),eps);
    gmm.mu = bsxfun(@rdivide,F,N);
    gmm.sigma = max(bsxfun(@rdivide,S,N) - gmm.mu.^2,eps);
end

Логарифмическая правдоподобность многокомпонентной смеси Гауссова

function L = helperGMMLogLikelihood(x,gmm)
    xMinusMu = repmat(x,1,1,numel(gmm.ComponentProportion)) - permute(gmm.mu,[1,3,2]);
    permuteSigma = permute(gmm.sigma,[1,3,2]);
    
    Lunweighted = -0.5*(sum(log(permuteSigma),1) + sum(bsxfun(@times,xMinusMu,(bsxfun(@rdivide,xMinusMu,permuteSigma))),1) + size(gmm.mu,1)*log(2*pi));

    temp = squeeze(permute(Lunweighted,[1,3,2]));
    if size(temp,1)==1
        % If there is only one frame, the trailing singleton dimension was
        % removed in the permute. This accounts for that edge case
        temp = temp';
    end
    L = bsxfun(@plus,temp,log(gmm.ComponentProportion)');
end

Ссылки

[1] Warden P. «Speech Commands: A public dataset for single-word speech recognition», 2017. Доступно из https://storage.googleapis.com/download.tensorflow.org/data/speech_commands_v0.01.tar.gz. Копирайт Google 2017. Набор данных Speech Commands лицензирован по лицензии Creative Commons Attribution 4.0, доступной здесь: https://creativecommons.org/licenses/by/4.0/legalcode.

[2] Рейнольдс, Дуглас А., Томас Ф. Кватьери и Роберт Б. Данн. «Динамик Верификации использования адаптированных Смешанных гауссовских моделей». Цифровая обработка сигналов 10, № 1-3 (2000): 19-41. https://doi.org/10.1006/dspr.1999.0361.