Разверните классификатор сигнала Используя вейвлеты и глубокое обучение на Raspberry Pi

Этот пример показывает рабочий процесс, чтобы классифицировать человеческую электрокардиограмму (ECG) сигналы с помощью Непрерывного преобразования вейвлета (CWT) и глубокой сверточной нейронной сети (CNN). Этот пример также предоставляет информацию о том, как сгенерировать и развернуть код и CNN для предсказания на цели Raspberry Pi (устройство ARM®-based).

SqueezeNet является глубоким CNN, первоначально спроектированным, чтобы классифицировать изображения на 1 000 категорий. В примере Классифицируют Временные ряды Используя Анализ Вейвлета и Глубокое обучение (Wavelet Toolbox), SqueezeNet переобучен, чтобы классифицировать формы волны ECG на основе их scalograms. scalogram является представлением частоты времени сигнала и является абсолютным значением CWT сигнала. Мы снова используем переобученный SqueezeNet в этом примере.

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

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

Необходимые условия

Для поддерживаемых версий библиотек и для получения информации о подготовке переменных окружения, смотрите Необходимые условия для Глубокого обучения для MATLAB Coder (MATLAB Coder). Этот пример не поддерживается в MATLAB Online™.

Функциональность сгенерированного кода

Базовая функция в сгенерированном исполняемом файле, processECG, использование 65 536 выборок данных о ECG с одинарной точностью, как введено. Функция:

  1. Берет CWT данных о ECG.

  2. Получает scalogram из коэффициентов вейвлета.

  3. Преобразует scalogram в волшебника RGB размерности 227 227 3. Это делает изображение совместимым с архитектурой сети SqueezeNet.

  4. Выполняет предсказание, чтобы классифицировать использование изображений SqueezeNet.

type processECG
function [YPred] = processECG(input)
% processECG function - converts 1D ECG to image and predicts the syndrome
% of heart disease
%
% This function is only intended to support the example:
% Signal Classification Code Generation Using Wavelets and
% Deep Learning on Raspberry Pi. It may change or be removed in a
% future release.

% Copyright 2020 The MathWorks, Inc.

    % colourmap for image transformation
    persistent net jetdata;
    if(isempty(jetdata))
        jetdata = colourmap(128,class(input));
    end

    % Squeezenet trained network
    if(isempty(net))
        net = coder.loadDeepLearningNetwork('trainedNet.mat');
    end

    % Wavelet Transformation & Image conversion
    cfs = ecg_to_Image(input);
    image = ind2rgb(im2uint8(rescale(cfs)),single(jetdata));
    image = im2uint8(imresize(image,[227,227]));

    % figure
    if isempty(coder.target)        
        imshow(image);
    end

    % Prediction
    [YPred] = predict(net,image);


    %% ECG to image conversion
    function cfs = ecg_to_Image(input)

        %Wavelet Transformation
        persistent filterBank
        [~,siglen] = size(input);
        if isempty(filterBank)
            filterBank = cwtfilterbank('SignalLength',siglen,'VoicesPerOctave',6);
        end
        %CWT conversion
        cfs = abs(filterBank.wt(input));
    end


    %% Colourmap
    function J = colourmap(m,class)

        n = ceil(m/4);
        u = [(1:1:n)/n ones(1,n-1) (n:-1:1)/n]';
        g = ceil(n/2) - (mod(m,4)==1) + (1:length(u))';
        r = g + n;
        b = g - n;
        r1 = r(r<=128);
        g1 = g(g<=128);
        b1 = b(b >0);
        J = zeros(m,3);
        J(r1,1) = u(1:length(r1));
        J(g1,2) = u(1:length(g1));
        J(b1,3) = u(end-length(b1)+1:end);
        feval = str2func(class);
        J = feval(J);
    end
end

Создайте объект настройки генерации кода

Создайте объект настройки генерации кода для генерации исполняемой программы. Задайте генерацию Кода С++.

cfg = coder.config('exe');
cfg.TargetLang = 'C++';

Настройте объект настройки для генерации кода глубокого обучения

Создайте coder.ARMNEONConfig объект. Задайте ту же версию библиотеки ARM Compute как та на Raspberry Pi. Задайте архитектуру Raspberry Pi.

dlcfg = coder.DeepLearningConfig('arm-compute');
dlcfg.ArmComputeVersion = '19.05';
dlcfg.ArmArchitecture = 'armv7';

Присоедините объект настройки глубокого обучения к объекту настройки генерации кода

Установите DeepLearningConfig свойство настройки генерации кода возражает против объекта настройки глубокого обучения. Сделайте Исходные Комментарии MATLAB видимыми в объекте настройки во время генерации кода.

cfg.DeepLearningConfig = dlcfg;
cfg.MATLABSourceComments = 1;

Создайте связь с Raspberry Pi

Используйте Пакет поддержки MATLAB для функции Пакета поддержки Raspberry Pi, raspi, создать связь с Raspberry Pi. В следующем коде, замене:

  • 'raspiname' с именем вашего Raspberry Pi

  • 'pi' с вашим именем пользователя

  • 'password' с вашим паролем

r = raspi('172.18.76.69','pi','raspberry');

Сконфигурируйте аппаратные параметры генерации кода для Raspberry Pi

Создайте coder.Hardware объект для Raspberry Pi и присоединения это к объекту настройки генерации кода.

hw = coder.hardware('Raspberry Pi');
cfg.Hardware = hw;

Задайте папку сборки на Raspberry Pi.

buildDir = '~/remdirECG';
cfg.Hardware.BuildDir = buildDir;

Предоставьте C++ основной файл для выполнения кода

C++ основной файл считывает данные о входе ECG, вызывает processECG функция, чтобы выполнить предварительную обработку и глубокое обучение с помощью CNN на данных о ECG и отображений вероятность классификации.

Задайте основной файл в объекте настройки генерации кода. Узнать больше о генерации и настройке main_ecg_raspi.cpp, означайте генерацию Автономных Исполняемых файлов C/C++ из кода MATLAB (MATLAB Coder).

cfg.CustomSource = 'main_ecg_raspi.cpp';

Сгенерируйте исходный код С++ Используя codegen

Используйте codegen функция, чтобы сгенерировать Код С++. Когда codegen используется с Пакетом поддержки MATLAB для Оборудования Raspberry Pi, исполняемый файл основан на плате Raspberry Pi.

Убедитесь, что установили переменные окружения ARM_COMPUTELIB и LD_LIBRARY_PATH на Raspberry Pi. Смотрите необходимые условия для глубокого обучения для MATLAB Coder (MATLAB Coder).

codegen -config cfg processECG -args {ones(1,65536,'single')} -d arm_compute
 Deploying code. This may take a few minutes. 

Выберите сгенерированную исполняемую директорию

Чтобы протестировать сгенерированный код на Raspberry Pi, скопируйте сигнал входа ECG в директорию сгенерированного кода. Можно найти эту директорию вручную или при помощи raspi.utils.getRemoteBuildDirectory API. Эта функция перечисляет директории двоичных файлов, которые сгенерированы при помощи codegen.

applicationDirPaths = raspi.utils.getRemoteBuildDirectory('applicationName','processECG')
applicationDirPaths=1×4 cell array
    {1×1 struct}    {1×1 struct}    {1×1 struct}    {1×1 struct}

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

directoryUnknown = true;

if directoryUnknown
    targetDirPath = helperFindTargetDir(applicationDirPaths);
else   
    targetDirPath = applicationDirPaths{1}.directory;
end

Скопируйте входной файл в Raspberry Pi

Текстовый файл input_ecg_raspi.csv содержит выборки ECG представительного сигнала ARR. Чтобы скопировать файл, требуемый запускать исполняемую программу, используйте putFile, который доступен с Пакетом поддержки MATLAB для Оборудования Raspberry Pi.

r.putFile('input_ecg_raspi.csv', targetDirPath);

Для графического представления первые 1 000 выборок могут быть построены при помощи этих шагов.

input = dlmread('input_ecg_raspi.csv');
plot(input(1:1000))
title('ARR Signal')

Запустите исполняемый файл на Raspberry Pi

Запустите исполняемую программу на Raspberry Pi из MATLAB и направьте выход назад к MATLAB. Входное имя файла передается как параметр командной строки для исполняемого файла.

exeName = 'processECG.elf';           % executable name
fileName = 'input_ecg_raspi.csv';     % Input ECG file that is pushed to target
command = ['cd ' targetDirPath ';./' exeName ' ' fileName];
output = system(r,command)
output = 
    'Predicted Values on the Target Hardware
     ARR            CHF            NSR
     0.806078	0.193609	0.000313103
     '

Ссылки

  1. Baim, D. S. В. С. Колуччи, Э. С. Монрэд, Х. С. Смит, Р. Ф. Райт, А. Лэноу, Д. Ф. Готье, Б. Дж. Рэнсил, В. Гроссман и Э. Бронвалд. "Выживание пациентов с тяжелой застойной сердечной недостаточностью отнеслось с устным milrinone". Журнал американского Колледжа Кардиологии. Издание 7, Номер 3, 1986, стр 661–670.

  2. Голдбергер А. Л., Л. А. Н. Амарал, L. Стекло, Дж. М. Гаусдорф, P. Ch. Иванов, Р. Г. Марк, Дж. Э. Митус, Г. Б. Муди, C.-K. Пенг и Х. Э. Стэнли. "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. Капризный, G. B. и Р. Г. Марк. "Удар Базы данных Аритмии MIT-BIH". Разработка IEEE в Журнале Медицины и Биологии. Издание 20. Номер 3, мочь-июнь 2001, стр 45–50. (PMID: 11446209)

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

helperFindTargetDir

function targetDir = helperFindTargetDir(dirPaths)
%
% This function is only intended to support wavelet deep learning examples.
% It may change or be removed in a future release.

% find pwd
p = pwd;
if ispc
    % replace blank spaces with underscores
    p = strrep(p,' ','_');
    
    % split path into component folders
    pSplit = regexp(p,filesep,'split');
    
    % Since Windows uses colons, remove any colons that occur
    for k=1:numel(pSplit)
        pSplit{k} = erase(pSplit{k},':');
    end
    
    % now build the path using Linux file separation
    pLinux = '';
    for k=1:numel(pSplit)-1
        pLinux = [pLinux,pSplit{k},'/'];
    end
    pLinux = [pLinux,pSplit{end}];
else
    pLinux = p;
end

targetDir = '';
for k=1:numel(dirPaths)
    d = strfind(dirPaths{k}.directory,pLinux);
    if ~isempty(d)
        targetDir = dirPaths{k}.directory;
        break
    end
end

if numel(targetDir) == 0
    disp('Target directory not found.');
end
end