exponenta event banner

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

Верификация говорящего, или аутентификация, является задачей проверки принадлежности данного речевого сегмента данному говорящему. В системах верификации говорящих существует неизвестный набор всех других говорящих, поэтому вероятность того, что говорящее принадлежит цели верификации, сравнивается с вероятностью того, что оно это делает. Это контрастирует с задачами идентификации говорящих, где вычисляется вероятность каждого говорящего, и эти вероятности сравниваются. И проверка говорящего, и идентификация говорящего могут быть зависимыми от текста или независимыми от текста. В этом примере создается зависящая от текста система проверки говорящего с использованием модели гауссова смешения/универсальной фоновой модели (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 формат. Установите метки хранилища данных для соответствующего идентификатора громкоговорителя.

[~,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);

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

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

Проверка

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

threshold = 0.7;
reset(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,threshold);

    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 набор данных, включающий только слово «stop».

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(ads);
forEnrollAndTestSet = labelCount{:,1}(labelCount{:,2}>=3);
forEnroll = forEnrollAndTestSet(randi([1,numel(forEnrollAndTestSet)],numSpeakersToEnroll,1));
tf = ismember(ads.Labels,forEnroll);
adsEnrollAndValidate = subset(ads,tf);
adsEnroll = splitEachLabel(adsEnrollAndValidate,2);

adsTest = subset(ads,ismember(ads.Labels,forEnrollAndTestSet));
adsTest = subset(adsTest,~ismember(adsTest.Files,adsEnroll.Files));

forUBMTraining = ~(ismember(ads.Files,adsTest.Files) | ismember(ads.Files,adsEnroll.Files));
adsTrainUBM = subset(ads,forUBMTraining);

Чтение из хранилища данных обучения и прослушивание файла. Сбросьте хранилище данных.

[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около-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.

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

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

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;
alpha = ones(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.

Регистрация: максимальная оценка Posteriori (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 в структуру, где поля являются идентификаторами динамика, а значения - структурами 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 resave
    save('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

Log-sum-экспонента

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 Attribution 4.0, доступна здесь: https://creativecommons.org/licenses/by/4.0/legalcode.

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