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

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

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

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

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

На протяжении всего этого примера вы найдете живые элементы управления настраиваемыми параметрами. Изменение элементов управления не приводит к повторному запуску примера. Если вы меняете элемент управления, необходимо перезапустить пример.

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

Этот пример использует базу данных отслеживания тангажа из Технологического университета Граца (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 и backend классификатора.

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

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

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

  • 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 из окон Hann 30 мс с хмелем 10 мс. Частота дискретизации набора данных составляет 48 кГц, но вы будете понижать частоту набора данных до 16 кГц. Проектируйте audioFeatureExtractor принимая желаемую частоту дискретизации, 16 кГц.

desiredFs = 16e3;

windowDuration = 0.03;
hopDuration = 0.005;
windowSamples = round (windowDuration * designedFs);
hopSamples = round (hopDuration * designedFs);
overlapSamples = windowSamples - hopSamples;
numCoeffs = 30;
afe = audioFeatureExtractor  (...
    'SampleRate', желаемые Fs ,...
    'Window', hann (windowSamples,'periodic'), ...
    'OverlapLength', overlapSamples ,...
    ...
    'mfcc'Правда, ...
    'pitch', ложные ,...
    'spectralEntropy', ложные ,...
    'spectralFlux', ложный);
setExtractorParams (afe,'mfcc','NumCoeffs', numCoeffs)

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

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-векторная модель редукции данных

В этом примере вы реализуете модель извлечения функций [1] с помощью парадигмы функционального программирования, предоставленной Deep Learning Toolbox™. Эта парадигма позволяет полностью контролировать проект вашей модели глубокого обучения. Для руководства, посвященного функциональному программированию в Deep Learning Toolbox, смотрите Define Model Gradients Function for Custom Training Loop (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;
[параметры, состояние] = 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 = этаж (число (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-векторов для верификации динамика TDNN, который вы только что обучили, используется для вывода слоя встраивания. Выходы слоя встраивания (слой 7 в этом примере, после нормализации партии . и перед активацией) являются 'x-векторами' в системе x-векторов.

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

Извлеките x-векторы из train. Вспомогательная функция, 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), согласно требованиям вашего приложения. Чтобы определить порог, соответствующее вашим требованиям 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] Snyder, David, et al. «x-vectors: Robust DNN Embeddings for Speaker Recognition». 2018 IEEE International Conference on Acoustics, Speech and Signal Processing (ICASSP), IEEE, 2018, pp. 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

Редукция данных и нормализация - Datastore

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