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

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

Эскиз системы GMM-UBM показывают:

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

Чтобы мотивировать этот пример, вы сначала выполните верификацию динамика с помощью предварительно обученной универсальной фоновой модели (UBM). Модель была обучена с помощью слова "остановка" от набора данных 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);

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

[~,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)
    [аудиоданные, adsInfo] = читают (adsTest);
    
    fprintf' | Speaker identity: %s\n', представьте (adsInfo.Label) в виде строки),
    
    verificationStatus = helperVerify (аудиоданные, 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} Маркировка Count: 2}> =3);
forEnroll = forEnrollAndTestSet (randi ([1, numel (forEnrollAndTestSet)], numSpeakersToEnroll, 1));
tf = ismember (ads.Labels, forEnroll);
adsEnrollAndValidate = подмножество (объявления, tf);
adsEnroll = splitEachLabel (adsEnrollAndValidate, 2);

adsTest = подмножество (объявления, ismember (ads.Labels, forEnrollAndTestSet));
adsTest = подмножество (adsTest, ~ismember (adsTest.Files, adsEnroll.Files));

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

Читайте из учебного datastore и слушайте файл. Сбросьте datastore.

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

sound(audioData,fs)

reset(adsTrainUBM)

Извлечение признаков

В извлечении признаков конвейерно обрабатывают для этого примера, вас:

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

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

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

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

  5. Примените среднюю нормализацию cepstral

Во-первых, создайте 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- numFeatures.

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

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

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

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

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

Трубопровод извлечения признаков инкапсулируется в функции помощника, helperFeatureExtraction.

Вычислите глобальная переменная показывают коэффициенты нормализации

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

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

Используйте функцию помощника, 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;
альфа = единицы (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.

В бухгалтерских целях преобразуйте массив ячеек GMMs к 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] Начальник П. "Речевые Команды: общедоступный набор данных для распознавания речи однословного", 2017. Доступный от https://storage.googleapis.com/download.tensorflow.org/data/speech_commands_v0.01.tar.gz. Авторское право Google 2017. Речевой Набор данных Команд лицензируется при Приписывании Creative Commons 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.