Ускорение графического процессора скалограмм для глубокого обучения

Этот пример показов, как вы можете ускорить скалограмму расчета с помощью графических процессоров. Вычисленные скалограммы используются как входные функции в нейронных сетях глубокой свертки (CNN) для классификации ЭКГ и разговорных цифр.

Для использования графический процессор требуется Parallel Computing Toolbox™. Информацию о том, какие графические процессоры поддерживаются, см. в разделе Поддержка GPU Release (Parallel Computing Toolbox). Раздел аудио этого примера требует, чтобы Audio Toolbox™ использовала audio datastore и преобразованный datastore.

Скалограмма Расчет с использованием графического процессора

Самый эффективный способ вычисления скалограмм на графическом процессоре - использовать cwtfilterbank. Шаги для вычисления скалограмм на графическом процессоре:

  1. Конструкция cwtfilterbank с требуемыми настройками свойств.

  2. Переместите сигнал на графический процессор с помощью gpuArray.

  3. Используйте банк фильтров WT метод для вычисления непрерывного вейвлет (CWT).

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

[y,fs] = audioread('guitartune.wav');
plot(y)
grid on

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

y = single(y);

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

fb = cwtfilterbank('SignalLength',length(y),'SamplingFrequency',fs);

Наконец, переместите сигнал на графический процессор, используя gpuArray и вычислите CWT данных. Постройте график получившейся скалограммы.

[cfs,f] = fb.wt(gpuArray(y));
t = 0:1/fs:(length(y)*1/fs)-1/fs;
imagesc(t,f,abs(cfs))
axis xy
ylabel('Hz')
xlabel('Seconds')

Использование gather для возврата коэффициентов CWT и любых других выходов к центральный процессор.

cfs = gather(cfs);
f = gather(f);

Чтобы продемонстрировать эффективность, полученную при использовании графический процессор, время расчета CWT на GPU и CPU для того же сигнала. Указанное здесь время вычисления графического процессора получено с использованием NVIDIA Titan V с вычислительной способностью 7.0.

ygpu = gpuArray(y);
fgpu = @()fb.wt(ygpu);
Tgpu = gputimeit(fgpu)
Tgpu = 0.2658

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

fcpu = @()fb.wt(y);
Tcpu = timeit(fcpu)
Tcpu = 3.7088
Tcpu/Tgpu
ans = 13.9533

Скалограммы в глубоком обучении

Общим приложением CWT в глубоком обучении является использование скалограммы сигнала в качестве входного «изображения» для глубокого CNN. Это обязательно предписывает расчет нескольких скалограмм, по одному для каждого сигнала в наборах обучения, валидации и тестирования. Хотя графические процессоры часто используются для ускорения обучения и вывода в глубокой сети, также выгодно использовать графические процессоры для ускорения любой редукции данных или предварительной обработки данных, необходимой для повышения устойчивости глубокой сети.

Для иллюстрации этого в следующем разделе применяются вейвлет к классификации электрокардиограммы (ЭКГ) человека. Скалограммы используются с теми же данными, которые обрабатываются в Classify Time Series с использованием Wavelet Analysis и Глубокое Обучение. В этом примере передача обучения с помощью GoogLeNet и SqueezeNet использовалась для классификации форм волны ЭКГ в одну из трех категорий. Описание данных и способы их получения повторяются здесь для удобства.

Описание и загрузка данных ЭКГ

Данные ЭКГ получены от трех групп людей: лиц с сердечной аритмией (АРР), лиц с застойным сердечным отказом (ХСН) и лиц с нормальными синусовыми ритмами (СМП). Всего существует 162 записи ЭКГ из трех баз данных PhysioNet: базы данных аритмии MIT-BIH [2] [3], базы данных нормального синусового ритма MIT-BIH [3] и базы данных застойной сердечной недостаточности BIDMC [1] Более конкретно, 96 записей от лиц с аритмией, 30 записей от лиц с застойным сердечным отказом и 36 записей от лиц с нормальными синусовыми ритмами. Цель состоит в том, чтобы обучить модель различать ARR, CHF и NSR.

Вы можете получить эти данные из репозитория MathWorks GitHub. Чтобы загрузить данные с сайта, нажмите Code и выберите Download ZIP. Сохраните файл physionet_ECG_data-main.zip в папке, в которой у вас есть разрешение на запись. Инструкции для этого примера предполагают, что вы загрузили файл во временную директорию tempdir, в MATLAB. Измените последующие инструкции для распаковки и загрузки данных, если вы решите загрузить данные в папку, отличную от tempdir.

После загрузки данных с GitHub разархивируйте файл во временной директории.

unzip(fullfile(tempdir,'physionet_ECG_data-main.zip'),tempdir)

Unzipping создает папку physionet-ECG_data-main во временной директории. Эта папка содержит текстовый файл README.md и ECGData.zip. The ECGData.zip файл содержит

  • ECGData.mat

  • Modified_physionet_data.txt

  • License.txt

ECGData.mat содержит данные, используемые в этом примере. Текстовый файл, Modified_physionet_data.txt, требуется политикой копирования PhysioNet и предоставляет исходные атрибуты для данных, а также описание шагов предварительной обработки, применяемых к каждой записи ЭКГ.

Разархивирование ECGData.zip в physionet-ECG_data-main. Загрузите файл данных в рабочее рабочее пространство MATLAB.

unzip(fullfile(tempdir,'physionet_ECG_data-main','ECGData.zip'),...
    fullfile(tempdir,'physionet_ECG_data-main'))
load(fullfile(tempdir,'physionet_ECG_data-main','ECGData.mat'))

ECGData массив структур с двумя полями: Data и Labels. The Data поле представляет собой 162 на 65536 матрицу, где каждая строка является дискретизацией записи ЭКГ в 128 герц. Labels - массив ячеек 162 на 1 с диагностическими метками, по одному для каждой строки Data. Три диагностические категории: 'ARR', 'CHF', и 'NSR'. Используйте функцию helper, helperRandomSplit, чтобы разделить данные на наборы обучения и валидации с 80% данных, выделенных для обучения и 20% для валидации. Преобразуйте метки диагностики ЭКГ в категории.

[trainData, validationData, trainLabels, validationLabels] = helperRandomSplit(80,ECGData);
trainLabels = categorical(trainLabels);
validationLabels = categorical(validationLabels);

В trainData 130 записей установите и 32 записи в Data валидации. По проекту обучающие данные содержат 80,25% (130/162) данных. Напомним, что класс ARR представляет 59,26% данных (96/162), класс CHF представляет 18,52% (30/162), и класс NSR представляет 22,22% (36/162). Исследуйте процент каждого класса в наборах обучения и тестов. Проценты в каждом соответствуют общим процентам классов в наборе данных.

Ctrain = countcats(trainLabels)./numel(trainLabels).*100
Ctrain = 3×1

   59.2308
   18.4615
   22.3077

Cvalid = countcats(validationLabels)./numel(validationLabels).*100
Cvalid = 3×1

   59.3750
   18.7500
   21.8750

Скалограммы с глубоким CNN - Данные ЭКГ

Скалограмма, Расчет на графическом процессоре

Вычислите скалограммы для наборов обучения и валидации. Задайте useGPU на true для использования графического процессора и false для вычисления скалограмм на центральном процессоре. Чтобы уменьшить эффект больших входных матриц на CNN и создать больше примеров обучения и валидации, helperECGScalograms разделяет каждую форму волны ЭКГ на четыре неперекрывающихся сегмента по 16384 выборки и вычисляет скалограммы для всех четырех сегментов. Реплицируйте метки так, чтобы они совпадали с расширенным набором данных. В этом случае получите оценку затраченного времени расчета.

frameLength = 16384;
useGPU = true;
tic;
Xtrain = helperECGScalograms(trainData,frameLength,useGPU);
Computing scalograms...
Processed 50 files out of 130
Processed 100 files out of 130
...done
T = toc;
sprintf('Elapsed time is %1.2f seconds',T)
ans = 
'Elapsed time is 4.22 seconds'
trainLabels = repelem(trainLabels,4);

С графическим процессором Titan V 502 скалограммы были вычислены приблизительно за 4,2 секунды. Настройка useGPU на false и повторение вышеописанных расчетов демонстрирует скорость, полученную при помощи графический процессор. В этом случае для вычисления скалограмм с помощью центральный процессор потребовалось 33,3 секунды. Расчет графический процессор было более чем в 7 раз быстрее.

Повторите тот же процесс для данных валидации.

useGPU = true;
Xvalid = helperECGScalograms(validationData,frameLength,useGPU);
Computing scalograms...
...done
validationLabels = repelem(validationLabels,4);

Затем создайте глубокий CNN, чтобы обработать как наборы обучения, так и наборы валидации. Используемая здесь простая сеть не оптимизирована. Этот CNN используется только для иллюстрации сквозного рабочего процесса для случаев, когда скалограммы помещаются в памяти.

sz = size(Xtrain);
specSize = sz(1:2);
imageSize = [specSize 1];
dropoutProb = 0.3;

layers = [
    imageInputLayer(imageSize)
    
    convolution2dLayer(3,12,'Padding','same')
    batchNormalizationLayer
    reluLayer
    
    maxPooling2dLayer(2,'Stride',2)
    
   
    convolution2dLayer(3,20,'Padding','same')
    batchNormalizationLayer
    reluLayer
    
    maxPooling2dLayer(2,'Stride',2)
    
   
    
    convolution2dLayer(3,32,'Padding','same')
    batchNormalizationLayer
    reluLayer
    dropoutLayer(dropoutProb)
    fullyConnectedLayer(3)
    softmaxLayer
    classificationLayer];

Используйте следующие опции обучения.

options = trainingOptions('sgdm',...
    'InitialLearnRate', 1e-4,...
    'LearnRateDropPeriod',18,...
    'MiniBatchSize', 20,...
    'MaxEpochs',25,...
    'L2Regularization',1e-1,...
    'Plots', 'training-progress',...
    'Verbose',false,...
    'Shuffle','every-epoch',...
    'ExecutionEnvironment','auto',...
    'ValidationData',{Xvalid,validationLabels});

Обучите сеть и измерьте валидацию ошибку.

trainNetwork(Xtrain,trainLabels,layers,options);

Несмотря на то, что используемый здесь простой CNN не оптимизирован, точность валидации постоянно находится в области значений от 80 до 90 процентов. Это сопоставимо с точностью валидации, достигнутой с более мощным и оптимизированным SqueezeNet, показанным в Classify Time Series с использованием примера Wavelet Analysis и Глубокое Обучение. Кроме того, это намного более эффективное использование скалограммы, потому что в этом примере скалограммы должны были быть пересмотрены как изображения RGB, совместимые с SqueezeNet, сохранены на диск в соответствующем формате изображения, а затем переданы в глубокую сеть с помощью imageDatastore.

Распознавание разговорных цифр - вычисление графического процессора с использованием Transform Datastore

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

Данные

Клон или скачать бесплатный набор данных Spoken Digit (FSDD), доступный в https://github.com/Jakobovski/free-spoken-digit-dataset. FSDD является открытым набором данных, что означает, что он может расти с течением времени. Этот пример использует версию, зафиксированную на 01/29/2019, которая состоит из 2000 записей английских цифр от 0 до 9, полученных от четырех дикторов. Два носителя в этой версии являются носителями американского английского языка, а два носителя являются носителями английского языка с бельгийским французским и немецким акцентом. Данные отбираются с частотой дискретизации 8000 Гц.

Для других подходов к этому набору данных, включая вейвлет, смотрите Распознавание Разговорных Цифр с Вейвлет и Глубокое Обучение.

Использование audioDatastore управлять доступом к данным и обеспечивать случайное деление записей на обучающие и тестовые наборы. Установите location свойство расположения папки записей FSDD на вашем компьютере, например:

pathToRecordingsFolder = '/home/user/free-spoken-digit-dataset/recordings';
location = pathToRecordingsFolder;

Точечные audioDatastore в это место.

ads = audioDatastore(location);

Функция помощника helpergenLabels создает категориальный массив меток из файлов FSDD. Перечислите классы и количество примеров в каждом классе.

ads.Labels = helpergenLabels(ads);
summary(ads.Labels)
     0      200 
     1      200 
     2      200 
     3      200 
     4      200 
     5      200 
     6      200 
     7      200 
     8      200 
     9      200 

Преобразованный Datastore

Сначала разделите FSDD на обучающие и тестовые наборы. Выделите 80% данных наборов обучающих данных и сохраните 20% для тестового набора.

rng default;
ads = shuffle(ads);
[adsTrain,adsTest] = splitEachLabel(ads,0.8);
countEachLabel(adsTrain)
ans=10×2 table
    Label    Count
    _____    _____

      0       160 
      1       160 
      2       160 
      3       160 
      4       160 
      5       160 
      6       160 
      7       160 
      8       160 
      9       160 

Затем создайте банк фильтров CWT и преобразованные хранилища данных для обучающих и тестовых данных с помощью функции helper, helperDigitScalogram. Преобразованный datastore преобразует каждую запись в сигнал длины 8192, вычисляет скалограмму на графическом процессоре и собирает данные обратно на центральный процессор.

reset(gpuDevice(1))
fb = cwtfilterbank('SignalLength',8192);
adsSCTrain = transform(adsTrain,@(audio,info)helperDigitScalogram(audio,info,fb),'IncludeInfo',true);
adsSCTest = transform(adsTest,@(audio,info)helperDigitScalogram(audio,info,fb),'IncludeInfo',true);

Глубокий CNN

Создайте глубокий CNN, чтобы обучаться с преобразованным datastore, adscTrain. Как и в первом примере, сеть не оптимизирована. Цель точки - показать рабочий процесс с помощью скалограмм, вычисленных на графическом процессоре для данной , которой не помещаютси в память,.

numClasses = 10;

dropoutProb = 0.2;
numF = 12;
layers = [
    imageInputLayer([101 8192 1])

    convolution2dLayer(5,numF,'Padding','same')
    batchNormalizationLayer
    reluLayer

    maxPooling2dLayer(3,'Stride',2,'Padding','same')

    convolution2dLayer(3,2*numF,'Padding','same')
    batchNormalizationLayer
    reluLayer

    maxPooling2dLayer(3,'Stride',2,'Padding','same')

    convolution2dLayer(3,4*numF,'Padding','same')
    batchNormalizationLayer
    reluLayer

    maxPooling2dLayer(3,'Stride',2,'Padding','same')

    convolution2dLayer(3,4*numF,'Padding','same')
    batchNormalizationLayer
    reluLayer
    convolution2dLayer(3,4*numF,'Padding','same')
    batchNormalizationLayer
    reluLayer

    maxPooling2dLayer(2)

    dropoutLayer(dropoutProb)
    fullyConnectedLayer(numClasses)
    softmaxLayer
    classificationLayer('Classes',categories(ads.Labels));
   ]; 

Установите опции обучения для сети.

miniBatchSize = 25;
options = trainingOptions('adam', ...
    'InitialLearnRate',1e-4, ...
    'MaxEpochs',30, ...
    'MiniBatchSize',miniBatchSize, ...
    'Shuffle','every-epoch', ...
    'Plots', 'training-progress',...
    'Verbose',false,...
    'ExecutionEnvironment','gpu');

Обучите сеть.

trainedNet = trainNetwork(adsSCTrain,layers,options);

При этом образце обучение было завершено за 25 минут и 10 секунд. Если вы комментируете вызов gpuArray в helperDigitScalogramи используйте центральный процессор, чтобы получить скалограммы, время обучения значительно увеличивается. При этом наблюдалось увеличение с 25 минут и 10 секунд до 45 минут и 38 секунд.

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

Ypredicted = classify(trainedNet,adsSCTest,'ExecutionEnvironment','CPU');
cnnAccuracy = sum(Ypredicted == adsTest.Labels)/numel(Ypredicted)*100
cnnAccuracy = 96.2500

Время вывода с использованием графический процессор составило приблизительно 22 секунды. Используя центральный процессор, время вывода удвоилось до 45 секунд.

Эффективность обученной сети по тестовым данным близка к 96%. Это сопоставимо с эффективностью в распознавании разговорных цифр с вейвлет и глубоким обучением.

Сводные данные

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

Ссылки

  1. Baim, D. S., В. С. Колуччи, Э. С. Монрэд, Х. С. Смит, Р. Ф. Райт, А. Лэноу, Д. Ф. Готье, Б. Дж. Рэнсил, В. Гроссман и Э. Браунвальд. «Выживание пациентов с тяжёлым застойным сердечным отказом, получавших пероральный милринон». Журнал Американского колледжа кардиологов. Том 7, № 3, 1986, стр. 661-670.

  2. Гольдбергер А. Л., Л. А. Н. Амарал, Л. Гласс, Ж. М. Хаусдорф, П. Ч. Иванов, Р. Г. Марк, Ж. Э. Миетус, Г. Б. Муди, К.-К. Пэн и Х. Э. Стэнли. PhysioBank, PhysioToolkit и PhysioNet: компоненты нового исследовательского ресурса комплексных физиологических сигналов. Циркуляция. Том 101, номер 23: e215-e220. [Тиражные электронные страницы; http://circ.ahajournals.org/content/101/23/e215.full]; 2000 (13 июня). doi: 10.1161/01.CIR.101.23.e215.

  3. Moody, G. B., and R. G. Mark. «The влияния of the MIT-BIH Arrhythmia Database». IEEE Engineering in Medicine and Biology Magazine. Том 20. № 3, май-июнь 2001, с. 45-50. (PMID: 11446209)

function Labels = helpergenLabels(ads)
% This function is only for use in Wavelet Toolbox examples. It may be
% changed or removed in a future release.
tmp = cell(numel(ads.Files),1);
expression = "[0-9]+_";
for nf = 1:numel(ads.Files)
    idx = regexp(ads.Files{nf},expression);
    tmp{nf} = ads.Files{nf}(idx);
end
Labels = categorical(tmp);

end
function X = helperECGScalograms(data,window,useGPU)

disp("Computing scalograms...");
Nsig = size(data,1);
Nsamp = size(data,2);
Nsegment = Nsamp/window;

fb = cwtfilterbank('SignalLength',window,'Voices',10);
Ns = length(fb.Scales);
X = zeros([Ns,window,1,Nsig*Nsegment],'single');
start = 0;
if useGPU
    data = gpuArray(single(data'));
else
    data = single(data');
end
for ii = 1:Nsig
    ts = data(:,ii);
    ts = reshape(ts,window,Nsegment);
    ts = (ts-mean(ts))./max(abs(ts));
    
    for kk = 1:size(ts,2)
        cfs = fb.wt(ts(:,kk));
        X(:,:,1,kk+start) = gather(abs(cfs));
        
    end
    start = start+Nsegment;
    
    if mod(ii,50) == 0
        disp("Processed " + ii + " files out of " + Nsig)
    end
    
end

disp("...done");
data = gather(data);

end
function [x,info] = helperReadSPData(x,info)
% This function is only for use Wavelet Toolbox examples. It may change or
% be removed in a future release.

N = numel(x);
if N > 8192
    x = x(1:8192);
elseif N < 8192
    pad = 8192-N;
    prepad = floor(pad/2);
    postpad = ceil(pad/2);
    x = [zeros(prepad,1) ; x ; zeros(postpad,1)];
end
x = x./max(abs(x));

end
function [dataout,info] = helperDigitScalogram(audioin,info,fb)
audioin = single(audioin);
audioin = gpuArray(audioin);
audioin = helperReadSPData(audioin);
cfs = gather(abs(fb.wt(audioin)));
audioin = gather(audioin);
dataout = {cfs,info.Label};
end