Raspberry Pi におけるウェーブレットおよび深層学習を使用した信号分類器の展開
この例では、連続ウェーブレット変換 (CWT) と深層畳み込みニューラル ネットワーク (CNN) を使用した人の心電図 (ECG) 信号を分類するワークフローを説明します。また、この例では、Raspberry Pi のターゲット (ARM® ベースのデバイス) で予測用のコードと CNN を生成して展開する方法に関する情報も提供します。
SqueezeNet は、当初 1000 のカテゴリにイメージを分類するために設計された深層 CNN です。例ウェーブレット解析と深層学習を使用した時系列の分類 (Wavelet Toolbox)では、"スカログラム" に基づいて ECG 波形を分類するよう、SqueezeNet に再学習させます。スカログラムは、信号の時間-周波数表現であり、信号の CWT の絶対値です。この例では、再学習させた SqueezeNet を再利用します。
ECG データの説明
この例では、PhysioNet の ECG データを使用します。ECG データは人の 3 つのグループから取得されます。具体的には、心不整脈の患者 (ARR)、鬱血性心不全の患者 (CHF)、および正常洞調律の患者 (NSR) のグループです。このデータ セットには、AAR の患者の記録が 96 個、CHF の患者の記録が 30 個、NSR の患者の記録が 36 個含まれています。次の 3 つの PhysioNet データベースから 162 個の ECG 記録を使用します。MIT-BIH Arrhythmia Database [2][3]、MIT-BIH Normal Sinus Rhythm Database [3]、The BIDMC Congestive Heart Failure Database [1][3]。上記の参照の短縮された ECG データは、GitHub リポジトリからダウンロードできます。
必要条件
NEON 拡張をサポートする ARM プロセッサ
ARM Compute Library version 19.05 (ターゲット ARM ハードウェア上)
コンパイラおよびライブラリの環境変数
サポートされるライブラリのバージョン、および環境変数の設定の詳細については、MATLAB Coder を使用した深層学習の前提条件 (MATLAB Coder)を参照してください。この例は、MATLAB Online™ ではサポートされていません。
生成されたコードの機能
生成された実行可能ファイル内の主な関数 processECG
は、65,536 個の単精度の ECG データ サンプルを入力として使用します。関数は以下を実行します。
ECG データの CWT を受信。
ウェーブレット係数からスカログラムを取得。
スカログラムを寸法が 227×227×3 の RGB イメージに変換。これにより、イメージは SqueezeNet ネットワーク アーキテクチャとの互換性をもちます。
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
コード生成構成オブジェクトの作成
実行可能プログラムの生成用にコード生成構成オブジェクトを作成します。C++ コードの生成を指定します。
cfg = coder.config('exe'); cfg.TargetLang = 'C++';
深層学習コード生成用の構成オブジェクトの設定
coder.ARMNEONConfig
オブジェクトを作成します。Raspberry Pi にある ARM Compute Library と同じバージョンを指定します。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 Support Package for Raspberry Pi のサポート パッケージ関数 raspi
を使用して、Raspberry Pi への接続を作成します。以下のコードで、次を置き換えます。
raspiname
: Raspberry Pi の名前または IP アドレスusername
: 自分のユーザー名password
: 自分のパスワード
r = raspi('raspiname','username','password');
Raspberry Pi 用のコード生成ハードウェア パラメーターの構成
Raspberry Pi 用の coder.Hardware
オブジェクトを作成してコード生成構成オブジェクトに追加します。
hw = coder.hardware('Raspberry Pi');
cfg.Hardware = hw;
Raspberry Pi 上のビルド フォルダーを指定します。
buildDir = '~/remdirECG';
cfg.Hardware.BuildDir = buildDir;
コード実行用 C++ メイン ファイルの提供
C++ メイン ファイルは、入力の ECG データを読み取り、関数 processECG
を呼び出し、ECG データに CNN を使用して前処置と深層学習を実行し、分類確率を表示します。
メイン ファイルをコード生成構成オブジェクトで指定します。main_ecg_raspi.cpp
の生成およびカスタマイズの詳細については、MATLAB コードからのスタンドアロン C/C++ 実行可能ファイルの生成 (MATLAB Coder)を参照してください。
cfg.CustomSource = 'main_ecg_raspi.cpp';
codegen
を使用した C++ ソース コードの生成
関数 codegen
を使用して C++ コードを生成します。codegen
と MATLAB Support Package for Raspberry Pi Hardware を使用する場合、実行可能ファイルは 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
には、代表的な ARR 信号の ECG サンプルが含まれます。実行可能プログラムを実行するのに必要なファイルをコピーするには、putFile
を使用します。これは、MATLAB Support Package for Raspberry Pi Hardware で利用可能です。
r.putFile('input_ecg_raspi.csv', targetDirPath);
視覚的な表現とするために、最初の 1000 個のサンプルをこれらの手順でプロットできます。
input = dlmread('input_ecg_raspi.csv'); plot(input(1:1000)) title('ARR Signal')
Raspberry Pi での実行可能ファイルの実行
MATLAB から Raspberry Pi で実行可能プログラムを実行し、出力を 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 '
参考文献
Baim, D. S., W. S. Colucci, E. S. Monrad, H. S. Smith, R. F. Wright, A. Lanoue, D. F. Gauthier, B. J. Ransil, W. Grossman, and E. Braunwald."Survival of patients with severe congestive heart failure treated with oral milrinone."Journal of the American College of Cardiology.Vol. 7, Number 3, 1986, pp. 661–670.
Goldberger A. L., L. A. N. Amaral, L. Glass, J. M. Hausdorff, P. Ch. Ivanov, R. G. Mark, J. E. Mietus, G. B. Moody, C.-K. Peng, and H. E. Stanley. "PhysioBank, PhysioToolkit,and PhysioNet: Components of a New Research Resource for Complex Physiologic Signals." Circulation. Vol. 101, Number 23: e215–e220. [Circulation Electronic Pages;
http://circ.ahajournals.org/content/101/23/e215.full
]; 2000 (June 13). doi: 10.1161/01.CIR.101.23.e215.Moody, G. B., and R. G. Mark."The impact of the MIT-BIH Arrhythmia Database."IEEE Engineering in Medicine and Biology Magazine.Vol. 20. Number 3, May-June 2001, pp. 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