exponenta event banner

Распознавание динамиков с помощью x-векторов

Признание спикера отвечает на вопрос «Кто говорит?». Распознавание говорящих обычно делится на две задачи: идентификация говорящих и верификация говорящих. При идентификации говорящего распознается говорящий путем сравнения его речи с закрытым набором шаблонов. При проверке говорящего распознается говорящий путем сравнения вероятности того, что речь принадлежит конкретному говорящему, с заранее определенным порогом. Традиционные методы машинного обучения хорошо выполняют эти задачи в идеальных условиях. Примеры идентификации говорящих с использованием традиционных методов машинного обучения см. в разделах Идентификация говорящих с использованием основного тона и Проверка говорящих с использованием i-векторов. Аудио Toolbox™ обеспечивает ivectorSystem который включает в себя способность обучать i-векторную систему, регистрировать динамики или другие звуковые метки, оценивать систему для порога принятия решения и идентифицировать или проверять динамики или другие звуковые метки.

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

В этом примере разрабатывается система x-векторов. Во-первых, вы обучаете нейронную сеть с временной задержкой (TDNN) выполнять идентификацию динамика. Затем вы тренируете традиционные бэкенды для системы верификации динамиков на основе x-вектора: матрицу проекции LDA и модель PLDA. Затем выполняется проверка динамика с использованием TDNN и уменьшения размерности и оценки бэкэнда. Бэкенд x-векторной системы, или классификатор, является тем же, что разработан для i-векторных систем. Дополнительные сведения о бэкэнд-системе см. в разделе Проверка динамики с использованием i-векторов и ivectorSystem.

В разделе Диаризация динамика с помощью x-векторов используется система x-векторов, обученная в этом примере, для выполнения диаризации динамика. Диаризация спикера отвечает на вопрос «Кто говорил, когда?».

В этом примере можно найти динамические элементы управления настраиваемыми параметрами. Изменение элементов управления не приводит к повторному запуску примера. При изменении элемента управления необходимо повторно запустить пример.

Управление наборами данных

В этом примере используется база данных отслеживания основного тона Грацского технологического университета (PTDB-TUG) [2]. Набор данных состоит из 20 англоязычных носителей, читающих 2342 фонетически насыщенных предложения из корпуса TIMIT. Загрузите и извлеките набор данных. В зависимости от системы загрузка и извлечение набора данных может занять около 1,5 часов.

url = 'https://www2.spsc.tugraz.at/databases/PTDB-TUG/SPEECH_DATA_ZIPPED.zip';
downloadFolder = tempdir;
datasetFolder = fullfile(downloadFolder,'PTDB-TUG');
if ~exist(datasetFolder,'dir')
    disp('Downloading PTDB-TUG (3.9 G) ...')
    unzip(url,datasetFolder)
end

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

ads = audioDatastore([fullfile(datasetFolder,"SPEECH DATA","FEMALE","MIC"),fullfile(datasetFolder,"SPEECH DATA","MALE","MIC")], ...
                     'IncludeSubfolders',true, ...
                     'FileExtensions','.wav');
fileNames = ads.Files;

Прочитайте аудиофайл из обучающего набора данных, прослушайте его, а затем постройте его график.

[audioIn,audioInfo] = read(ads);
fs = audioInfo.SampleRate;
t = (0:size(audioIn,1)-1)/fs;
sound(audioIn,fs)
plot(t,audioIn)
xlabel('Time (s)')
ylabel('Amplitude')
axis([0 t(end) -1 1])
title('Sample Utterance from Training Set')

Имена файлов содержат идентификаторы динамиков. Декодирование имен файлов для установки меток на audioDatastore объект.

speakerIDs = extractBetween(fileNames,'mic_','_');
ads.Labels = categorical(speakerIDs);

Разделите audioDatastore объект на пять наборов:

  • adsTrain - Содержит набор обучения для TDNN и внутреннего классификатора.

  • adsValidation - Содержит набор проверки для оценки хода обучения TDNN.

  • adsTest - Содержит тестовый набор для оценки производительности TDNN для идентификации громкоговорителя.

  • adsEnroll - содержит набор регистрации для оценки компромисса ошибок обнаружения x-векторной системы для проверки говорящего.

  • adsDET - Содержит оценочный набор, используемый для определения компромисса ошибок обнаружения x-векторной системы для проверки динамика.

developmentLabels = categorical(["M01","M02","M03","M04","M06","M07","M08","M09","F01","F02","F03","F04","F06","F07","F08","F09"]);
evaluationLabels = categorical(["M05","M010","F05","F010"]);
adsTrain = subset(ads,ismember(ads.Labels,developmentLabels));
[adsTrain,adsValidation,adsTest] = splitEachLabel(adsTrain,0.8,0.1,0.1);
adsEvaluate = subset(ads,ismember(ads.Labels,evaluationLabels));
[adsEnroll,adsDET] = splitEachLabel(adsEvaluate,3);

Отображение распределения меток результирующего audioDatastore объекты.

countEachLabel(adsTrain)
ans=16×2 table
    Label    Count
    _____    _____

     F01      170 
     F02      170 
     F03      170 
     F04      170 
     F06      170 
     F07      170 
     F08      168 
     F09      170 
     M01      170 
     M02      170 
     M03      170 
     M04      170 
     M06      170 
     M07      170 
     M08      170 
     M09      170 

countEachLabel(adsValidation)
ans=16×2 table
    Label    Count
    _____    _____

     F01      22  
     F02      22  
     F03      22  
     F04      22  
     F06      22  
     F07      22  
     F08      21  
     F09      22  
     M01      22  
     M02      22  
     M03      22  
     M04      22  
     M06      22  
     M07      22  
     M08      22  
     M09      22  

countEachLabel(adsTest)
ans=16×2 table
    Label    Count
    _____    _____

     F01      21  
     F02      21  
     F03      21  
     F04      21  
     F06      21  
     F07      21  
     F08      21  
     F09      21  
     M01      21  
     M02      21  
     M03      21  
     M04      21  
     M06      21  
     M07      21  
     M08      21  
     M09      21  

countEachLabel(adsEnroll)
ans=2×2 table
    Label    Count
    _____    _____

     F05       3  
     M05       3  

countEachLabel(adsDET)
ans=2×2 table
    Label    Count
    _____    _____

     F05      231 
     M05      231 

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

speedUpExample = false;
if speedUpExample
    adsTrain = splitEachLabel(adsTrain,20);
    adsDET = splitEachLabel(adsDET,20);
end

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

Создание audioFeatureExtractor Изобретение относится к медицине и может быть использовано для извлечения 30 MFCC из 30 мс окон Hann с 10 мс перехода. Частота дискретизации набора данных составляет 48 кГц, но вы понизите выборку набора данных до 16 кГц. Проектирование audioFeatureExtractor предполагая желаемую частоту дискретизации, 16 кГц.

desiredFs = 16e3;

windowDuration = 0.03;
hopDuration = 0.005;
windowSamples = round(windowDuration*desiredFs);
hopSamples = round(hopDuration*desiredFs);
overlapSamples = windowSamples - hopSamples;
numCoeffs = 30;
afe = audioFeatureExtractor( ...
    'SampleRate',desiredFs, ...
    'Window',hann(windowSamples,'periodic'), ...
    'OverlapLength',overlapSamples, ...
    ...
    'mfcc',true, ...
    'pitch',false, ...
    'spectralEntropy',false, ...
    'spectralFlux',false);
setExtractorParams(afe,'mfcc','NumCoeffs',numCoeffs)

Выполните пониженную выборку аудиоданных до 16 кГц и извлеките характеристики из наборов данных поезда и проверки. Набор учебных данных используется для определения среднего и стандартного отклонения элементов для выполнения стандартизации элементов. Вспомогательная функция xVectorPreprocessBatch использует параллельный пул по умолчанию при наличии Toolbox™ Parallel Computing.

adsTrain = transform(adsTrain,@(x)resample(x,desiredFs,fs));
[features,YTrain] = xVectorPreprocessBatch(adsTrain,afe);
featuresMAT = cat(1,features{:});
numFeatures = size(featuresMAT,2);
factors = struct('Mean',mean(featuresMAT,1),'STD',std(featuresMAT,1));

XTrain = cellfun(@(x)(x-factors.Mean)./factors.STD,features,'UniformOutput',false);
XTrain = cellfun(@(x)x-mean(x(:)),XTrain,'UniformOutput',false);

adsValidation = transform(adsValidation,@(x)resample(x,desiredFs,fs));
[XValidation,YValidation] = xVectorPreprocessBatch(adsTrain,afe,'Factors',factors);

classes = unique(YTrain);
numClasses = numel(classes);

Модель извлечения x-векторных элементов

В этом примере модель извлечения x-векторной функции [1] реализуется с использованием парадигмы функционального программирования, предоставляемой Deep Learning Toolbox™. Эта парадигма позволяет полностью контролировать дизайн вашей модели глубокого обучения. Учебное пособие по функциональному программированию в инструментарии глубокого обучения см. в разделе Определение функции градиентов модели для пользовательского цикла обучения (инструментарий глубокого обучения). Вспомогательная функция, xvecModel, помещается в текущую папку при открытии этого примера. Отображение содержимого xvecModel функция.

type('xvecModel')
function [Y,state] = xvecModel(X,parameters,state,nvargs)
% This function is only for use in this example. It may be changed or
% removed in a future release.
arguments
    X
    parameters
    state
    nvargs.DoTraining = false
    nvargs.OutputLayer = 'final'
    nvargs.Dropout = 0.2;
end


% LAYER 1 -----------------------------------------------------------------
Y = dlconv(X,parameters.conv1.Weights,parameters.conv1.Bias,'DilationFactor',1);
if nvargs.DoTraining
    [Y,state.batchnorm1.TrainedMean,state.batchnorm1.TrainedVariance] = ...
        batchnorm(Y, ...
        parameters.batchnorm1.Offset, ...
        parameters.batchnorm1.Scale, ...
        state.batchnorm1.TrainedMean, ...
        state.batchnorm1.TrainedVariance);
    Y(rand(size(Y))<nvargs.Dropout) = 0;
else
    Y = batchnorm(Y, ...
        parameters.batchnorm1.Offset, ...
        parameters.batchnorm1.Scale, ...
        state.batchnorm1.TrainedMean, ...
        state.batchnorm1.TrainedVariance);
end
if nvargs.OutputLayer==1
    return
end
Y = relu(Y);
% -------------------------------------------------------------------------


% LAYER 2 -----------------------------------------------------------------
Y = dlconv(Y,parameters.conv2.Weights,parameters.conv2.Bias,'DilationFactor',2);
if nvargs.DoTraining
    [Y,state.batchnorm2.TrainedMean,state.batchnorm2.TrainedVariance] = ...
        batchnorm(Y, ...
        parameters.batchnorm2.Offset, ...
        parameters.batchnorm2.Scale, ...
        state.batchnorm2.TrainedMean, ...
        state.batchnorm2.TrainedVariance);
    Y(rand(size(Y))<nvargs.Dropout) = 0;
else
    Y = batchnorm(Y, ...
        parameters.batchnorm2.Offset, ...
        parameters.batchnorm2.Scale, ...
        state.batchnorm2.TrainedMean, ...
        state.batchnorm2.TrainedVariance);
end
if nvargs.OutputLayer==2
    return
end
Y = relu(Y);
% -------------------------------------------------------------------------


% LAYER 3 -----------------------------------------------------------------
Y = dlconv(Y,parameters.conv3.Weights,parameters.conv3.Bias,'DilationFactor',3);
if nvargs.DoTraining
    [Y,state.batchnorm3.TrainedMean,state.batchnorm3.TrainedVariance] = ...
        batchnorm(Y, ...
        parameters.batchnorm3.Offset, ...
        parameters.batchnorm3.Scale, ...
        state.batchnorm3.TrainedMean, ...
        state.batchnorm3.TrainedVariance);
    Y(rand(size(Y))<nvargs.Dropout) = 0;
else
    Y = batchnorm(Y, ...
        parameters.batchnorm3.Offset, ...
        parameters.batchnorm3.Scale, ...
        state.batchnorm3.TrainedMean, ...
        state.batchnorm3.TrainedVariance);
end
if nvargs.OutputLayer==3
    return
end
Y = relu(Y);
% -------------------------------------------------------------------------


% LAYER 4 -----------------------------------------------------------------
Y = dlconv(Y,parameters.conv4.Weights,parameters.conv4.Bias,'DilationFactor',1);
if nvargs.DoTraining
    [Y,state.batchnorm4.TrainedMean,state.batchnorm4.TrainedVariance] = ...
        batchnorm(Y, ...
        parameters.batchnorm4.Offset, ...
        parameters.batchnorm4.Scale, ...
        state.batchnorm4.TrainedMean, ...
        state.batchnorm4.TrainedVariance);
    Y(rand(size(Y))<nvargs.Dropout) = 0;
else
    Y = batchnorm(Y, ...
        parameters.batchnorm4.Offset, ...
        parameters.batchnorm4.Scale, ...
        state.batchnorm4.TrainedMean, ...
        state.batchnorm4.TrainedVariance);
end
if nvargs.OutputLayer==4
    return
end
Y = relu(Y);
% -------------------------------------------------------------------------


% LAYER 5 -----------------------------------------------------------------
Y = dlconv(Y,parameters.conv5.Weights,parameters.conv5.Bias,'DilationFactor',1);
if nvargs.DoTraining
    [Y,state.batchnorm5.TrainedMean,state.batchnorm5.TrainedVariance] = ...
        batchnorm(Y, ...
        parameters.batchnorm5.Offset, ...
        parameters.batchnorm5.Scale, ...
        state.batchnorm5.TrainedMean, ...
        state.batchnorm5.TrainedVariance);
    Y(rand(size(Y))<nvargs.Dropout) = 0;
else
    Y = batchnorm(Y, ...
        parameters.batchnorm5.Offset, ...
        parameters.batchnorm5.Scale, ...
        state.batchnorm5.TrainedMean, ...
        state.batchnorm5.TrainedVariance);
end
if nvargs.OutputLayer==5
    return
end
Y = relu(Y);
% -------------------------------------------------------------------------


% Layer 6: Statistical pooling --------------------------------------------
if nvargs.DoTraining
    Y = Y + 0.0001*rand(size(Y));
end
Y = cat(2,mean(Y,1),std(Y,[],1));
if nvargs.OutputLayer==6
    return
end
% -------------------------------------------------------------------------

% LAYER 7 -----------------------------------------------------------------
Y = fullyconnect(Y,parameters.fc7.Weights,parameters.fc7.Bias);
if nvargs.DoTraining
    [Y,state.batchnorm7.TrainedMean,state.batchnorm6.TrainedVariance] = ...
        batchnorm(Y, ...
        parameters.batchnorm7.Offset, ...
        parameters.batchnorm7.Scale, ...
        state.batchnorm7.TrainedMean, ...
        state.batchnorm7.TrainedVariance);
     Y(rand(size(Y))<nvargs.Dropout) = 0;
else
        Y = batchnorm(Y, ...
            parameters.batchnorm7.Offset, ...
            parameters.batchnorm7.Scale, ...
            state.batchnorm7.TrainedMean, ...
            state.batchnorm7.TrainedVariance);
end
if nvargs.OutputLayer==7
    return
end
Y = relu(Y);
% -------------------------------------------------------------------------

% LAYER 8 -----------------------------------------------------------------
Y = fullyconnect(Y,parameters.fc8.Weights,parameters.fc8.Bias);
if nvargs.DoTraining
    [Y,state.batchnorm8.TrainedMean,state.batchnorm8.TrainedVariance] = ...
        batchnorm(Y, ...
        parameters.batchnorm8.Offset, ...
        parameters.batchnorm8.Scale, ...
        state.batchnorm8.TrainedMean, ...
        state.batchnorm8.TrainedVariance);
    Y(rand(size(Y))<nvargs.Dropout) = 0;
else
        Y = batchnorm(Y, ...
            parameters.batchnorm8.Offset, ...
            parameters.batchnorm8.Scale, ...
            state.batchnorm8.TrainedMean, ...
            state.batchnorm8.TrainedVariance);
end
if nvargs.OutputLayer==8
    return
end
Y = relu(Y);
% -------------------------------------------------------------------------

% LAYER 9 (softmax)--------------------------------------------------------
Y = fullyconnect(Y,parameters.fc9.Weights,parameters.fc9.Bias);
if nvargs.OutputLayer==9
    return
end
Y = softmax(Y);
% -------------------------------------------------------------------------
end

Инициализируйте структуры, содержащие параметры и состояние модели TDNN, с помощью поддерживающей функции initializexVecModelLayers. [1] определяет количество фильтров между большинством слоев, включая слой встраивания, как 512. Поскольку обучающий набор в этом примере невелик, используйте размер представления 128.

numFilters = 128;
[parameters,state] = initializexVecModelLayers(numFeatures,numFilters,numClasses)
parameters = struct with fields:
         conv1: [1×1 struct]
    batchnorm1: [1×1 struct]
         conv2: [1×1 struct]
    batchnorm2: [1×1 struct]
         conv3: [1×1 struct]
    batchnorm3: [1×1 struct]
         conv4: [1×1 struct]
    batchnorm4: [1×1 struct]
         conv5: [1×1 struct]
    batchnorm5: [1×1 struct]
           fc7: [1×1 struct]
    batchnorm7: [1×1 struct]
           fc8: [1×1 struct]
    batchnorm8: [1×1 struct]
           fc9: [1×1 struct]

state = struct with fields:
    batchnorm1: [1×1 struct]
    batchnorm2: [1×1 struct]
    batchnorm3: [1×1 struct]
    batchnorm4: [1×1 struct]
    batchnorm5: [1×1 struct]
    batchnorm7: [1×1 struct]
    batchnorm8: [1×1 struct]

В таблице представлена архитектура сети, описанная в [1] и реализованная в этом примере. T - общее количество кадров (векторов признаков во времени) в аудиосигнале. N - количество классов (говорящих) в обучающем наборе.

Модель поезда

Использовать arrayDatastore и minibatchqueue (Deep Learning Toolbox) для создания мини-пакетной очереди для данных обучения.

dsXTrain = arrayDatastore(XTrain,'OutputType','same');
dsYTrain = arrayDatastore(YTrain','OutputType','cell');

dsTrain = combine(dsXTrain,dsYTrain);

miniBatchSize = 128;
numOutputs = 2;
mbq = minibatchqueue(dsTrain,numOutputs, ...
    'MiniBatchSize',miniBatchSize, ...
    'MiniBatchFormat',{'SCB','CB'}, ...
    'MiniBatchFcn',@preprocessMiniBatch);

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

numEpochs = 6;

learnRate = 0.001;
gradDecay = 0.5;
sqGradDecay = 0.999;
trailingAvg = [];
trailingAvgSq = [];

LearnRateDropPeriod = 2;
LearnRateDropFactor = 0.1;

ValidationsPerEpoch = 2;

iterationsPerEpoch = floor(numel(XTrain)/miniBatchSize);
iterationsPerValidation = round(iterationsPerEpoch/ValidationsPerEpoch);

Если у вас есть доступ к вычислительному графическому процессору, установите ExecutionEnvironment кому gpu. В противном случае установите ExecutionEnvironment кому cpu.

ExecutionEnvironment = 'gpu';

При выполнении проверки во время обучения выполните предварительную обработку набора проверки для повышения производительности в цикле.

if ValidationsPerEpoch ~= 0
    [XValidation,YValidation] = preprocessMiniBatch(XValidation,{YValidation});
    XValidation = dlarray(XValidation,'SCB');
    if strcmp(ExecutionEnvironment,'gpu')
        XValidation = gpuArray(XValidation);
    end
end

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

Выполните цикл обучения.

pp = progressPlotter(categories(classes));

iteration = 0;
for epoch = 1:numEpochs
    
    % Shuffle mini-batch queue
    shuffle(mbq)
    
    while hasdata(mbq)
        
        % Update iteration counter
        iteration = iteration + 1;
        
        % Get mini-batch from mini-batch queue
        [dlX,Y] = next(mbq);
        
        % Place mini-batch on GPU if requested
        if strcmp(ExecutionEnvironment,'gpu')
            dlX = gpuArray(dlX);
        end

        % Evaluate the model gradients, state, and loss using dlfeval and the modelGradients function
        [gradients,state,loss,predictions] = dlfeval(@modelGradients,dlX,Y,parameters,state);

        % Update the network parameters using the Adam optimizer
        [parameters,trailingAvg,trailingAvgSq] = adamupdate(parameters,gradients, ...
            trailingAvg,trailingAvgSq,iteration,learnRate,gradDecay,sqGradDecay,eps('single'));

        % Update the training progress plot
        updateTrainingProgress(pp,'Epoch',epoch,'Iteration',iteration,'LearnRate',learnRate,'Predictions',predictions,'Targets',Y,'Loss',loss)

        % Update the validation plot
        if ~rem(iteration,iterationsPerValidation)
            
            % Pass validation data through x-vector model
            predictions = xvecModel(XValidation,parameters,state,'DoTraining',false);

            % Update plot
            updateValidation(pp,'Iteration',iteration,'Predictions',predictions,'Targets',YValidation)
        end
    end
    
    % Update learn rate
    if rem(epoch,LearnRateDropPeriod)==0
        learnRate = learnRate*LearnRateDropFactor;
    end
    
end

Оценка модели TDNN

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

  1. Повторная выборка звука до 16 кГц

  2. Извлечение элементов с помощью xVectorPreprocess поддерживающая функция. Функции возвращаются в массивах ячеек, где количество элементов в массиве ячеек равно количеству отдельных речевых сегментов.

  3. Чтобы получить прогнозируемую метку динамика, пропустите каждый сегмент через модель.

  4. Если в звуковом сигнале присутствовало более одного речевого сегмента, усредняйте прогнозы.

  5. Использовать onehotdecode (Deep Learning Toolbox) для преобразования прогноза в метку.

Использовать confusionchart (Deep Learning Toolbox) для оценки производительности системы.

predictedLabels = classes;
predictedLabels(:) = [];

for sample = 1:numel(adsTest.Files)
    [audioIn,xInfo] = read(adsTest);
    audioIn = resample(audioIn,desiredFs,fs);
    f = xVectorPreprocess(audioIn,afe,'Factors',factors,'MinimumDuration',0);
    predictions = zeros(numel(classes),numel(f));
    for segment = 1:numel(f)
        dlX = dlarray(f{segment},'SCB');
        predictions(:,segment) = extractdata(xvecModel(dlX,parameters,state,'DoTraining',false));
    end 
    predictedLabels(sample) = onehotdecode(mean(predictions,2),categories(classes),1);
end
trueLabels = adsTest.Labels;
accuracy = mean(trueLabels==predictedLabels');

figure('Units','normalized','Position',[0.2 0.2 0.6 0.6]);
confusionchart(trueLabels,predictedLabels', ...
    'ColumnSummary','column-normalized', ...
    'RowSummary','row-normalized', ...
    'Title',sprintf('x-vector Speaker Recognition\nAccuracy = %0.2f%%',accuracy*100))

Бэкэнд x-векторной системы поезда

В системе x-vector для верификации динамика только что обученный TDNN используется для вывода уровня встраивания. Выходные данные слоя внедрения (слой 7 в этом примере, после пакетной нормализации и перед активацией) являются «x-векторами» в системе x-векторов.

Бэкэнд (или классификатор) x-векторной системы совпадает с бэкендом i-векторной системы. Для получения подробной информации о алгоритмах см. ivectorSystem и верификация динамика с использованием i-векторов.

Извлеките векторы X из комплекта поездов. Вспомогательная функция, xvecModel, имеет опциональную пару имя-значение 'OutputLayer'. Набор 'OutputLayer' кому 7 для возврата выходных данных седьмого уровня. В [1] в качестве возможных слоев встраивания предлагаются выходные данные либо от слоя 7, либо от слоя 8.

xvecs = zeros(numFilters,numel(YTrain));
for sample = 1:size(YTrain,2)
    dlX = dlarray(XTrain{sample},'SCB');
    
    embedding = xvecModel(dlX,parameters,state,'DoTraining',false,'OutputLayer',7);
    xvecs(:,sample) = extractdata(embedding);
end

Создайте матрицу проекции линейного дискриминантного анализа (LDA), чтобы уменьшить размерность x-векторов до 32. LDA пытается минимизировать внутриклассовую дисперсию и максимизировать дисперсию между динамиками.

numEigenvectors = 32;
projMat = helperTrainProjectionMatrix(xvecs,YTrain,numEigenvectors);

Примените матрицу проекции LDA к x-векторам.

xvecs = projMat*xvecs;

Обучите модель G-PLDA выполнению оценки.

numIterations = 3;
numDimensions = 32;
plda = helperTrainPLDA(xvecs,YTrain,numIterations,numDimensions);

Вычислить x-векторную систему

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

Регистрация динамиков

Извлеките x-векторы из задержанного набора данных, adsEnroll. Установите минимальную длительность аудиосегмента, эквивалентную 15 переходам функций (минимальное число, необходимое для вычисления x-векторов).

minDur = (numel(afe.Window)+14*(numel(afe.Window)-afe.OverlapLength)+1)/desiredFs;

xvecs = zeros(numEigenvectors,numel(adsEnroll.Files));
reset(adsEnroll)
for sample = 1:numel(adsEnroll.Files)
    [audioIn,xInfo] = read(adsEnroll);
    audioIn = resample(audioIn,desiredFs,fs);
    f = xVectorPreprocess(audioIn,afe,'Factors',factors,'MinimumDuration',minDur);
    embeddings = zeros(numFilters,numel(f));
    for segment = 1:numel(f)
        dlX = dlarray(f{segment},'SCB');

        embeddings(:,segment) = extractdata(xvecModel(dlX,parameters,state,'DoTraining',false,'OutputLayer',7));
    end 
    xvecs(:,sample) = mean(projMat*embeddings,2);
end

Создание шаблонных x-векторов для каждого динамика путем усреднения x-векторов отдельных динамиков в файлах регистрации.

labels = adsEnroll.Labels;
uniqueLabels = unique(labels);
atable = cell2table(cell(0,2),'VariableNames',{'xvector','NumSamples'});
for ii = 1:numel(uniqueLabels)
    idx = uniqueLabels(ii)==labels;
    wLocalMean = mean(xvecs(:,idx),2);
    localTable = table({wLocalMean},(sum(idx)), ...
        'VariableNames',{'xvector','NumSamples'}, ...
        'RowNames',string(uniqueLabels(ii)));
    atable = [atable;localTable]; %#ok<AGROW>
end
enrolledLabels = atable
enrolledLabels=2×2 table
              xvector       NumSamples
           _____________    __________

    F05    {32×1 double}        3     
    M05    {32×1 double}        3     

Системы верификации спикеров требуют установить порог, уравновешивающий вероятность ложного принятия (FA) и вероятность ложного отклонения (FR), в соответствии с требованиями вашей заявки. Для определения порогового значения, соответствующего требованиям ОС/ФР, проанализируйте компромисс ошибок обнаружения в системе.

xvecs = zeros(numEigenvectors,numel(adsDET.Files));
reset(adsDET)
for sample = 1:numel(adsDET.Files)
    [audioIn,xInfo] = read(adsDET);
    audioIn = resample(audioIn,desiredFs,fs);
    f = xVectorPreprocess(audioIn,afe,'Factors',factors,'MinimumDuration',minDur);
    embeddings = zeros(numFilters,numel(f));
    for segment = 1:numel(f)
        dlX = dlarray(f{segment},'SCB');
        embeddings(:,segment) = extractdata(xvecModel(dlX,parameters,state,'DoTraining',false,'OutputLayer',7));
    end 
    xvecs(:,sample) = mean(projMat*embeddings,2);
end
labels = adsDET.Labels;
[detTable,scoreRange] = helperDetectionErrorTradeoff(xvecs,labels,enrolledLabels,plda);

Постройте график результатов оценки компромисса ошибок обнаружения как для оценки PLDA, так и для оценки косинусного сходства (CSS).

plot(detTable.PLDA.Threshold,detTable.PLDA.FAR, ...
    detTable.PLDA.Threshold,detTable.PLDA.FRR)
eer = helperEqualErrorRate(detTable.PLDA);
title(sprintf('Speaker Verification\nDetection Error Tradeoff\nPLDA Scoring\nEqual Error Rate = %0.2f',eer));
xlabel('Threshold')
ylabel('Error Rate')
legend({'FAR','FRR'})

plot(detTable.CSS.Threshold,detTable.CSS.FAR, ...
    detTable.CSS.Threshold,detTable.CSS.FRR)
eer = helperEqualErrorRate(detTable.CSS);
title(sprintf('Speaker Verification\nDetection Error Tradeoff\nCosine Similarity Scoring\nEqual Error Rate = %0.2f',eer));
xlabel('Threshold')
ylabel('Error Rate')
legend({'FAR','FRR'})

Ссылки

[1] Снайдер, Дэвид, и др. IEEE 2018 Международная конференция по акустике, обработке речи и сигналов (ICASSP), IEEE, 2018, стр. 5329-33. DOI.org (Crossref), doi:10.1109/ICASSP.2018.8461375.

[2] Лаборатория обработки сигналов и речевой связи. Доступ состоялся 12 декабря 2019 года. https://www.spsc.tugraz.at/databases-and-tools/ptdb-tug-pitch-tracking-database-from-graz-university-of-technology.html.

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

Инициализация параметров уровней TDNN

function [parameters,state] = initializexVecModelLayers(numFeatures,numFilters,numClasses)
% This function is only for use in this example. It may be changed or
% removed in a future release.

% Initialize Layer 1 (1-D Convolutional)
filterSize1                      = 5;
numChannels1                     = numFeatures;
numFilters1                      = numFilters;

numIn1                           = filterSize1*numFilters1;
numOut1                          = filterSize1*numFilters1;

parameters.conv1.Weights         = initializeGlorot([filterSize1,numChannels1,numFilters1],numOut1,numIn1);
parameters.conv1.Bias            = dlarray(zeros([numFilters1,1],'single'));
parameters.batchnorm1.Offset     = dlarray(zeros([numFilters1,1],'single'));
parameters.batchnorm1.Scale      = dlarray(ones([numFilters1,1],'single'));
state.batchnorm1.TrainedMean     = zeros(numFilters1,1,'single');
state.batchnorm1.TrainedVariance = ones(numFilters1,1,'single');


% Initialize Layer 2 (1-D Convolutional)
filterSize2                      = 3;
numChannels2                     = numFilters1;
numFilters2                      = numFilters;

numIn2                           = filterSize2*numFilters2;
numOut2                          = filterSize2*numFilters2;

parameters.conv2.Weights         = initializeGlorot([filterSize2,numChannels2,numFilters2],numOut2,numIn2);
parameters.conv2.Bias            = dlarray(zeros([numFilters2,1],'single'));
parameters.batchnorm2.Offset     = dlarray(zeros([numFilters2,1],'single'));
parameters.batchnorm2.Scale      = dlarray(ones([numFilters2,1],'single'));
state.batchnorm2.TrainedMean     = zeros(numFilters2,1,'single');
state.batchnorm2.TrainedVariance = ones(numFilters2,1,'single');


% Initialize Layer 3 (1-D Convolutional)
filterSize3                      = 3;
numChannels3                     = numFilters2;
numFilters3                      = numFilters;

numIn3                           = filterSize3*numFilters3;
numOut3                          = filterSize3*numFilters3;

parameters.conv3.Weights         = initializeGlorot([filterSize3,numChannels3,numFilters3],numOut3,numIn3);
parameters.conv3.Bias            = dlarray(zeros([numFilters3,1],'single'));
parameters.batchnorm3.Offset     = dlarray(zeros([numFilters3,1],'single'));
parameters.batchnorm3.Scale      = dlarray(ones([numFilters3,1],'single'));
state.batchnorm3.TrainedMean     = zeros(numFilters3,1,'single');
state.batchnorm3.TrainedVariance = ones(numFilters3,1,'single');


% Initialize Layer 4 (1-D Convolutional)
filterSize4                      = 1;
numChannels4                     = numFilters3;
numFilters4                      = numFilters;

numIn4                           = filterSize4*numFilters4;
numOut4                          = filterSize4*numFilters4;

parameters.conv4.Weights         = initializeGlorot([filterSize4,numChannels4,numFilters4],numOut4,numIn4);
parameters.conv4.Bias            = dlarray(zeros([numFilters4,1],'single'));
parameters.batchnorm4.Offset     = dlarray(zeros([numFilters4,1],'single'));
parameters.batchnorm4.Scale      = dlarray(ones([numFilters4,1],'single'));
state.batchnorm4.TrainedMean     = zeros(numFilters4,1,'single');
state.batchnorm4.TrainedVariance = ones(numFilters4,1,'single');


% Initialize Layer 5 (1-D Convolutional)
filterSize5                      = 1;
numChannels5                     = numFilters4;
numFilters5                      = 1500;

numOut5                          = filterSize5*numFilters5;
numIn5                           = filterSize5*numFilters5;

parameters.conv5.Weights         = initializeGlorot([filterSize5,numChannels5,numFilters5],numOut5,numIn5);
parameters.conv5.Bias            = dlarray(zeros([numFilters5,1],'single'));
parameters.batchnorm5.Offset     = dlarray(zeros([numFilters5,1],'single'));
parameters.batchnorm5.Scale      = dlarray(ones([numFilters5,1],'single'));
state.batchnorm5.TrainedMean     = zeros(numFilters5,1,'single');
state.batchnorm5.TrainedVariance = ones(numFilters5,1,'single');


% Initialize Layer 6 (Statistical Pooling)
numIn6                           = numOut5;
numOut6                          = 2*numIn6;


% Initialize Layer 7 (Fully Connected)
numIn7                           = numOut6;
numOut7                          = numFilters;

parameters.fc7.Weights           = initializeGlorot([numFilters,numIn7],numOut7,numIn7);
parameters.fc7.Bias              = dlarray(zeros([numOut7,1],'single'));
parameters.batchnorm7.Offset     = dlarray(zeros([numOut7,1],'single'));
parameters.batchnorm7.Scale      = dlarray(ones([numOut7,1],'single'));
state.batchnorm7.TrainedMean     = zeros(numOut7,1,'single');
state.batchnorm7.TrainedVariance = ones(numOut7,1,'single');


% Initialize Layer 8 (Fully Connected)
numIn8                           = numOut7;
numOut8                          = numFilters;

parameters.fc8.Weights           = initializeGlorot([numOut8,numIn8],numOut8,numIn8);
parameters.fc8.Bias              = dlarray(zeros([numOut8,1],'single'));
parameters.batchnorm8.Offset     = dlarray(zeros([numOut8,1],'single'));
parameters.batchnorm8.Scale      = dlarray(ones([numOut8,1],'single'));
state.batchnorm8.TrainedMean     = zeros(numOut8,1,'single');
state.batchnorm8.TrainedVariance = ones(numOut8,1,'single');


% Initialize Layer 9 (Fully Connected)
numIn9                           = numOut8;
numOut9                          = numClasses;

parameters.fc9.Weights           = initializeGlorot([numOut9,numIn9],numOut9,numIn9);
parameters.fc9.Bias              = dlarray(zeros([numOut9,1],'single'));
end

Инициализация весов с помощью инициализации Glorot

function weights = initializeGlorot(sz,numOut,numIn)
% This function is only for use in this example. It may be changed or
% removed in a future release.
Z = 2*rand(sz,'single') - 1;
bound = sqrt(6 / (numIn + numOut));
weights = bound*Z;
weights = dlarray(weights);
end

Расчет градиентов модели и обновленного состояния

function [gradients,state,loss,Y] = modelGradients(X,target,parameters,state)
% This function is only for use in this example. It may be changed or
% removed in a future release.
[Y,state] = xvecModel(X,parameters,state,'DoTraining',true);
loss = crossentropy(Y,target);
gradients = dlgradient(loss,parameters);
end

Мини-пакет предварительной обработки

function [sequences,labels] = preprocessMiniBatch(sequences,labels)
% This function is only for use in this example. It may be changed or
% removed in a future release.
lengths = cellfun(@(x)size(x,1),sequences);
minLength = min(lengths);
sequences = cellfun(@(x)randomTruncate(x,1,minLength),sequences,'UniformOutput',false);
sequences = cat(3,sequences{:});
        
labels = cat(2,labels{:});
labels = onehotencode(labels,1);
labels(isnan(labels)) = 0;
end

Случайное усечение звуковых сигналов до заданной длины

function y = randomTruncate(x,dim,minLength)
% This function is only for use in this example. It may be changed or
% removed in a future release.
N = size(x,dim);
if N > minLength
    start = randperm(N-minLength,1);
    if dim==1
        y = x(start:start+minLength-1,:);
    elseif dim ==2
        y = x(:,start:start+minLength-1);
    end
else
    y = x;
end
end

Извлечение и нормализация элементов - хранилище данных

function [features,labels] = xVectorPreprocessBatch(ads,afe,nvargs)
% This function is only for use in this example. It may be changed or
% removed in a future release.
    arguments
        ads
        afe
        nvargs.Factors = []
        nvargs.Segment = true;
    end
    if ~isempty(ver('parallel'))
        pool = gcp;
        numpar = numpartitions(ads,pool);
    else
        numpar = 1;
    end
    labels = [];
    features = [];
    parfor ii = 1:numpar
        adsPart = partition(ads,numpar,ii);
        numFiles = numel(adsPart.UnderlyingDatastores{1}.Files);
        localFeatures = cell(numFiles,1);
        localLabels = [];
        for jj = 1:numFiles
            [audioIn,xInfo] = read(adsPart);
            label = xInfo.Label;
            [f,ns] = xVectorPreprocess(audioIn,afe,'Factors',nvargs.Factors,'Segment',nvargs.Segment); %#ok<PFBNS> 
            localFeatures{jj} = f;
            localLabels = [localLabels,repelem(label,ns)];
        end
        features = [features;localFeatures];
        labels = [labels,localLabels];
    end
    features = cat(1,features{:});
    labels = removecats(labels);
end

Извлечение и нормализация элементов

function [features,numSegments] = xVectorPreprocess(audioData,afe,nvargs)
% This function is only for use in this example. It may be changed or
% removed in a future release.
arguments
    audioData
    afe
    nvargs.Factors = []
    nvargs.Segment = true;
    nvargs.MinimumDuration = 1;
end
% Scale
audioData = audioData/max(abs(audioData(:)));

% Protect against NaNs
audioData(isnan(audioData)) = 0;

% Determine regions of speech
mergeDur = 0.5; % seconds
idx = detectSpeech(audioData,afe.SampleRate,'MergeDistance',afe.SampleRate*mergeDur);

% If a region is less than MinimumDuration seconds, drop it.
if nvargs.Segment
    idxToRemove = (idx(:,2)-idx(:,1))<afe.SampleRate*nvargs.MinimumDuration;
    idx(idxToRemove,:) = [];
end

% Extract features
numSegments = size(idx,1);
features = cell(numSegments,1);
for ii = 1:numSegments
    features{ii} = single(extract(afe,audioData(idx(ii,1):idx(ii,2))));
end

% Standardize features
if ~isempty(nvargs.Factors)
    features = cellfun(@(x)(x-nvargs.Factors.Mean)./nvargs.Factors.STD,features,'UniformOutput',false);
end

% Cepstral mean subtraction (for channel noise)
if ~isempty(nvargs.Factors)
    fileMean = mean(cat(1,features{:}),'all');
    features = cellfun(@(x)x - fileMean,features,'UniformOutput',false);
end

if ~nvargs.Segment
    features = cat(1,features{:});
end
end