Время вейвлета, рассеиваясь для классификации сигналов ECG

В этом примере показано, как классифицировать человеческую электрокардиограмму (ECG) сигналы с помощью времени вейвлета, рассеявшись и классификатора машины опорных векторов (SVM). В рассеивании вейвлета данные распространены через серию вейвлета, преобразовывает, нелинейность и усреднение, чтобы произвести представления низкого отклонения временных рядов. Время вейвлета, рассеивая выражения сигнализирует, что представления, нечувствительные к, переключают входной сигнал на нижний регистр, не жертвуя классом discriminability. У вас должны быть Wavelet Toolbox™ и Statistics and Machine Learning Toolbox™, чтобы запустить этот пример. Данные, используемые в этом примере, общедоступны от PhysioNet. Можно найти, что подход глубокого обучения к этой проблеме классификации в этом примере Классифицирует Временные ряды Используя Анализ Вейвлета и Глубокое обучение и подход машинного обучения в этой Классификации Сигналов в качестве примера, использующей Основанные на вейвлете Функции и Машины опорных векторов.

Описание данных

Этот пример использует данные о ECG, полученные из трех групп или классов, людей: люди с сердечной аритмией, люди с застойной сердечной недостаточностью и люди с нормальными ритмами пазухи. Пример использует 162 записи ECG от трех Физиосетевых баз данных: База данных Аритмии MIT-BIH [3] [5], MIT-BIH Нормальная База данных Ритма Пазухи [3] и База данных Застойной сердечной недостаточности BIDMC [2] [3]. Всего, существует 96 записей от людей с аритмией, 30 записей от людей с застойной сердечной недостаточностью и 36 записей от людей с нормальными ритмами пазухи. Цель состоит в том, чтобы обучить классификатор различать аритмию (ARR), застойная сердечная недостаточность (CHF), и нормальный ритм пазухи (NSR).

Загрузите данные

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

Файл physionet_ECG_data-main.zip содержит

  • ECGData.zip

  • README.md

и ECGData.zip содержит

  • ECGData.mat

  • Modified_physionet_data.txt

  • License.txt.

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

Загрузите файлы

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

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

После того, как вы разархивировали файл ECGData.zip, загружаете данные в MATLAB.

load(fullfile(tempdir,'ECGData','ECGData.mat'))

ECGData массив структур с двумя полями: Data и Labelsданные 162 65536 матрица, где каждая строка является записью ECG, произведенной на уровне 128 герц. Каждые временные ряды ECG имеют общую длительность 512 секунд. Labels 162 1 массив ячеек диагностических меток, один для каждой строки Data. Три диагностических категории: 'ARR' (аритмия), 'швейцарский франк' (застойная сердечная недостаточность) и 'NSR' (нормальный ритм пазухи).

Создайте обучение и тестовые данные

Случайным образом разделите данные в два набора - наборы тестовых данных и обучение. Функция помощника helperRandomSplit выполняет случайное разделение. helperRandomSplit принимает желаемый процент разделения для обучающих данных и ECGData. helperRandomSplit функционируйте выходные параметры два набора данных наряду с набором меток для каждого. Каждая строка trainData и testData сигнал ECG. Каждый элемент trainLabels и testLabels содержит метку класса для соответствующей строки матриц данных. В этом примере мы случайным образом присваиваем 70%-й процент данных в каждом классе к набору обучающих данных. Остающиеся 30% протянуты для тестирования (предсказания) и присвоены набору тестов.

percent_train = 70;
[trainData,testData,trainLabels,testLabels] = ...
    helperRandomSplit(percent_train,ECGData);

В trainData существует 113 записей установите и 49 записей в testData. Проектом обучающие данные содержит 69,75% (113/162) данных. Вспомните, что класс ARR представляет 59,26% данных (96/162), класс швейцарского франка представляет 18,52% (30/162), и класс NSR представляет 22,22% (36/162). Исследуйте процент каждого класса в наборах обучающих данных и наборах тестов. Проценты в каждом сопоставимы с полными процентами класса в наборе данных.

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

   59.2920
   18.5841
   22.1239

Ctest = countcats(categorical(testLabels))./numel(testLabels).*100
Ctest = 3×1

   59.1837
   18.3673
   22.4490

Постройте выборки

Постройте первую несколько тысяч выборок четырех случайным образом выбранных записей от ECGData. Функция помощника helperPlotRandomRecords делает это. helperPlotRandomRecords принимает ECGData и случайный seed, как введено. Начальный seed установлен в 14 так, чтобы по крайней мере одна запись от каждого класса была построена. Можно выполнить helperPlotRandomRecords с ECGData как единственный входной параметр так много раз, как вы хотите получить смысл множества форм волны ECG, сопоставленных с каждым классом. Можно найти исходный код для этого и всех функций помощника в разделе Supporting Functions в конце этого примера.

helperPlotRandomRecords(ECGData,14)

Время вейвлета, рассеиваясь

Основные параметры, чтобы задать в сети рассеивания времени вейвлета являются шкалой независимого от времени, количество вейвлета преобразовывает, и количество вейвлетов на октаву в каждом из наборов фильтров вейвлета. Во многих приложениях каскад двух наборов фильтров достаточен, чтобы достигнуть хорошей эффективности. В этом примере мы создаем сеть рассеивания времени вейвлета с наборами фильтров по умолчанию: 8 вейвлетов на октаву в первом наборе фильтров и 1 вейвлет на октаву во втором наборе фильтров. Шкала инвариантности установлена в 150 секунд.

N = size(ECGData.Data,2);
sn = waveletScattering('SignalLength',N, 'InvarianceScale',150,'SamplingFrequency',128);

Можно визуализировать вейвлет, просачивается эти два набора фильтров со следующим.

[fb,f,filterparams] = filterbank(sn);
figure
subplot(211)
plot(f,fb{2}.psift)
xlim([0 128])
grid on
title('1st Filter Bank Wavelet Filters');
subplot(212)
plot(f,fb{3}.psift)
xlim([0 128])
grid on
title('2nd Filter Bank Wavelet Filters');
xlabel('Hz');

Чтобы продемонстрировать шкалу инвариантности, получите обратное преобразование Фурье масштабирующейся функции и сосредоточьте его в 0 секунд вовремя. Две вертикальных черных линии отмечают-75 и 75 вторых контуров. Кроме того, постройте действительные и мнимые части самой грубой шкалы (самая низкая частота) вейвлет от первого набора фильтров. Обратите внимание на то, что вейвлет самой грубой шкалы не превышает инвариантную шкалу, определенную поддержкой времени масштабирующейся функции. Это - важное свойство времени вейвлета, рассеиваясь.

figure;
phi = ifftshift(ifft(fb{1}.phift));
psiL1 = ifftshift(ifft(fb{2}.psift(:,end)));
t = (-2^15:2^15-1).*1/128;
scalplt = plot(t,phi);
hold on
grid on
ylim([-1.5e-4 1.6e-4]);
plot([-75 -75],[-1.5e-4 1.6e-4],'k--');
plot([75 75],[-1.5e-4 1.6e-4],'k--');
xlabel('Seconds'); ylabel('Amplitude');
wavplt = plot(t,[real(psiL1) imag(psiL1)]);
legend([scalplt wavplt(1) wavplt(2)],{'Scaling Function','Wavelet-Real Part','Wavelet-Imaginary Part'});
title({'Scaling Function';'Coarsest-Scale Wavelet First Filter Bank'})
hold off

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

scat_features_train = featureMatrix(sn,trainData');

Выход featureMatrix в этом случае 409 16 113. Каждая страница тензора, scat_features_train, рассеивающееся преобразование одного сигнала. Рассеивание вейвлета преобразовывает, критически прорежен вовремя на основе полосы пропускания масштабирующейся функции. В этом случае это заканчивается в 16 раз окна для каждого из 409 рассеивающихся путей.

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

Nwin = size(scat_features_train,2);
scat_features_train = permute(scat_features_train,[2 3 1]);
scat_features_train = reshape(scat_features_train,...
    size(scat_features_train,1)*size(scat_features_train,2),[]);

Повторите процесс для тестовых данных. Первоначально, scat_features_test 409 16 49, потому что существует 49 форм волны ECG в наборе обучающих данных. После изменения для классификатора SVM матрица функции 784 416.

scat_features_test = featureMatrix(sn,testData');
scat_features_test = permute(scat_features_test,[2 3 1]);
scat_features_test = reshape(scat_features_test,...
    size(scat_features_test,1)*size(scat_features_test,2),[]);

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

[sequence_labels_train,sequence_labels_test] = createSequenceLabels(Nwin,trainLabels,testLabels);

Перекрестная проверка

Для классификации выполняются два исследования. Первое использование все рассеивающиеся данные и подгонки мультикласс SVM с квадратичным ядром. Всего там 2 592 рассеивающихся последовательности в наборе данных в целом, 16 для каждого из 162 сигналов. Коэффициент ошибок или потеря, оценивается с помощью 5-кратной перекрестной проверки.

scat_features = [scat_features_train; scat_features_test];
allLabels_scat = [sequence_labels_train; sequence_labels_test];
rng(1);
template = templateSVM(...
    'KernelFunction', 'polynomial', ...
    'PolynomialOrder', 2, ...
    'KernelScale', 'auto', ...
    'BoxConstraint', 1, ...
    'Standardize', true);
classificationSVM = fitcecoc(...
    scat_features, ...
    allLabels_scat, ...
    'Learners', template, ...
    'Coding', 'onevsone', ...
    'ClassNames', {'ARR';'CHF';'NSR'});
kfoldmodel = crossval(classificationSVM, 'KFold', 5);

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

predLabels = kfoldPredict(kfoldmodel);
loss = kfoldLoss(kfoldmodel)*100;
confmatCV = confusionmat(allLabels_scat,predLabels)
confmatCV = 3×3

        1535           0           1
           1         479           0
           0           0         576

fprintf('Accuracy is %2.2f percent.\n',100-loss);
Accuracy is 99.92 percent.

Точность составляет 99,88%, который довольно хорош, но фактические результаты лучше полагают, что здесь каждый раз окно классифицируется отдельно. Существует 16 отдельных классификаций для каждого сигнала. Используйте голосование простого большинства, чтобы получить предсказание единого класса для каждого представления рассеивания.

classes = categorical({'ARR','CHF','NSR'});
[ClassVotes,ClassCounts] = helperMajorityVote(predLabels,[trainLabels; testLabels],classes);

Определите фактическую точность перекрестной проверки на основе режима предсказаний класса для каждого набора рассеивающихся окон времени. Если режим не уникален для данного набора, helperMajorityVote возвращает ошибку классификации, обозначенную 'NoUniqueMode'. Это приводит к дополнительному столбцу в матрице беспорядка, которая в этом случае является всеми нулями, потому что уникальный режим существует для каждого набора рассеивающихся предсказаний.

CVaccuracy = sum(eq(ClassVotes,categorical([trainLabels; testLabels])))/162*100;
fprintf('True cross-validation accuracy is %2.2f percent.\n',CVaccuracy);
True cross-validation accuracy is 100.00 percent.
MVconfmatCV = confusionmat(categorical([trainLabels; testLabels]),ClassVotes);
MVconfmatCV
MVconfmatCV = 4×4

    96     0     0     0
     0    30     0     0
     0     0    36     0
     0     0     0     0

Рассеивание правильно классифицировало все сигналы на перекрестную подтвержденную модель. Если вы исследуете ClassCounts, вы видите что 2 неправильно классифицированных окна времени в confmatCV относятся к 2 сигналам, где 15 из 16 рассеивающихся окон были классифицированы правильно.

Классификация SVM

Для следующего анализа мы соответствуем мультиклассу квадратичный SVM к обучающим данным (только 70%) и затем используем ту модель, чтобы сделать предсказания на 30% данных требуемыми, тестируя. В наборе тестов существует 49 записей данных. Используйте решение большинством голосов на отдельных окнах рассеивания.

model = fitcecoc(...
     scat_features_train, ...
     sequence_labels_train, ...
     'Learners', template, ...
     'Coding', 'onevsone', ...
     'ClassNames', {'ARR','CHF','NSR'});
predLabels = predict(model,scat_features_test);
[TestVotes,TestCounts] = helperMajorityVote(predLabels,testLabels,classes);
testaccuracy = sum(eq(TestVotes,categorical(testLabels)))/numel(testLabels)*100;
fprintf('The test accuracy is %2.2f percent. \n',testaccuracy);
The test accuracy is 97.96 percent. 
confusionchart(categorical(testLabels),TestVotes)

Точность классификации на тестовом наборе данных составляет приблизительно 98%. Матрица беспорядка показывает, что запись за один швейцарский франк неправильно классифицируется как ARR. Все 48 других сигналов правильно классифицируются.

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

Этот пример использовал время вейвлета, рассеиваясь и классификатор SVM, чтобы классифицировать формы волны ECG в один из трех диагностических классов. Рассеивание вейвлета оказалось экстрактором мощной функции, который потребовал, чтобы только минимальный набор заданных пользователями параметров дал к набору устойчивых функций классификации. Сравните это с Классификацией Сигналов в качестве примера, использующей Основанные на вейвлете Функции и Машины опорных векторов, которые потребовали, чтобы существенное количество экспертных знаний изготовило вручную функции, чтобы использовать в классификации. Со временем вейвлета, рассеиваясь, вы только обязаны задавать шкалу инвариантности времени, количество наборов фильтров (или вейвлет преобразовывает), и количество вейвлетов на октаву. Комбинация рассеивания вейвлета преобразовывает, и классификатор SVM дал к 100%-й классификации на перекрестной подтвержденной и 98%-й правильной классификации модели при применении SVM к рассеивающимся преобразованиям набора тестов затяжки.

Ссылки

  1. Anden, J., Mallat, S. 2014. Глубоко рассеивая спектр, Транзакции IEEE на Обработке сигналов, 62, 16, стр 4114-4128.

  2. Baim DS, WS Colucci, ES Monrad, Смит ХС, Мастер RF, Lanoue A, Готье ДФ, Ransil BJ, Гроссман В, Браунвальд E. Выживание пациентов с тяжелой застойной сердечной недостаточностью отнеслось с устным milrinone. J американский Колледж Кардиологии 1 986 мартов; 7 (3):661-670.

  3. Голдбергер ЭЛ, LAN Amaral, Стекло L, Гаусдорф ДЖМ, Иванов PCh, Марк РГ, Mietus JE, Капризный Гбайт, Пенг C-K, Стэнли ХЭ. PhysioBank, PhysioToolkit и PhysioNet: Компоненты Нового Ресурса Исследования для Комплексных Физиологических Сигналов. Циркуляция. Издание 101, № 23, 13 июня 2000, стр e215-e220. http://circ.ahajournals.org/content/101/23/e215.full

  4. Mallat, S., 2012. Рассеивание инварианта группы. Коммуникации в Чистой и Прикладной математике, 65, 10, стр 1331-1398.

  5. Капризный Гбайт, Марк РГ. Удар Базы данных Аритмии MIT-BIH. Инженер IEEE в Медиане и Biol 20 (3):45-50 (мочь-июнь 2001). (PMID: 11446209)

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

Графики helperPlotRandomRecords четыре сигнала ECG, случайным образом выбранные из ECGData.

function helperPlotRandomRecords(ECGData,randomSeed)
% This function is only intended to support the XpwWaveletMLExample. It may
% change or be removed in a future release.

if nargin==2
    rng(randomSeed)
end

M = size(ECGData.Data,1);
idxsel = randperm(M,4);
for numplot = 1:4
    subplot(2,2,numplot)
    plot(ECGData.Data(idxsel(numplot),1:3000))
    ylabel('Volts')
    if numplot > 2
        xlabel('Samples')
    end
    title(ECGData.Labels{idxsel(numplot)})
end

end

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

function [ClassVotes,ClassCounts] = helperMajorityVote(predLabels,origLabels,classes)
% This function is in support of ECGWaveletTimeScatteringExample. It may
% change or be removed in a future release.

% Make categorical arrays if the labels are not already categorical
predLabels = categorical(predLabels);
origLabels = categorical(origLabels);
% Expects both predLabels and origLabels to be categorical vectors
Npred = numel(predLabels);
Norig = numel(origLabels);
Nwin = Npred/Norig;
predLabels = reshape(predLabels,Nwin,Norig);
ClassCounts = countcats(predLabels);
[mxcount,idx] = max(ClassCounts);
ClassVotes = classes(idx);
% Check for any ties in the maximum values and ensure they are marked as
% error if the mode occurs more than once
modecnt = modecount(ClassCounts,mxcount);
ClassVotes(modecnt>1) = categorical({'error'});
ClassVotes = ClassVotes(:);

%-------------------------------------------------------------------------
function modecnt = modecount(ClassCounts,mxcount)
modecnt = Inf(size(ClassCounts,2),1);
for nc = 1:size(ClassCounts,2)
    modecnt(nc) = histc(ClassCounts(:,nc),mxcount(nc));
end

end

% EOF
end

Смотрите также

Похожие темы