Main Content

ウェーブレット解析と深層学習を使用した NVIDIA Jetson への信号分類器の展開

この例では、連続ウェーブレット変換 (CWT) と事前学習済みの畳み込みニューラル ネットワーク (CNN) を使用して人間の心電図 (ECG) 信号を分類する CUDA® 実行可能ファイルを生成および展開する方法を説明します。

SqueezeNet は、当初 1000 のカテゴリにイメージを分類するために設計された深層 CNN です。スカログラムを基にした ECG 信号を分類するために CNN のネットワーク アーキテクチャを再利用します。スカログラムは、信号の CWT の絶対値です。ECG 信号を分類するように SqueezeNet を学習させた後、ECG 信号のスカログラムを生成してから CNN を使用して信号を分類する CUDA 実行可能ファイルを作成します。実行可能ファイルと CNN の両方が NVIDIA ハードウェアに展開されます。

この例では、ウェーブレット解析と深層学習を使用した時系列の分類 (Wavelet Toolbox)で使用したのと同じデータを使用します。この例では、GoogLeNet と SqueezeNet による転移学習を使用して、ECG 波形を 3 つのカテゴリのうちの 1 つに分類します。便宜上、ここではデータの概要とこのデータを取得する方法について再度説明します。

ECG データの説明とダウンロード

ECG データは人の 3 つのグループから取得されます。具体的には、心不整脈の患者 (ARR)、鬱血性心不全の患者 (CHF)、および正常洞調律の患者 (NSR) のグループです。次の 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]。具体的には、不整脈の患者の記録は 96 個、鬱血性心不全の患者の記録は 30 個、正常洞調律の患者の記録は 36 個あります。目標は、ARR、CHF、NSR を区別するようにモデルに学習させることです。

このデータは MathWorks の GitHub リポジトリから入手できます。データを Web サイトからダウンロードするには、[Code] をクリックして [Download ZIP] を選択します。書き込み権限のあるフォルダーに、ファイル physionet_ECG_data-main.zip を保存します。この例の手順では、ファイルを一時ディレクトリ (MATLAB の tempdir) にダウンロードしているものと仮定します。tempdir とは異なるフォルダーにデータをダウンロードすることを選択した場合は、データの解凍および読み込みに関する後続の手順を変更してください。

データを GitHub からダウンロードした後、一時ディレクトリでそのファイルを解凍します。

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

解凍すると、一時ディレクトリにフォルダー physionet-ECG_data-main が作成されます。このフォルダーには、テキスト ファイル README.mdECGData.zip が含まれます。ECGData.zip ファイルには次のものが含まれています。

  • ECGData.mat

  • Modified_physionet_data.txt

  • License.txt

ECGData.mat は、この例で使用されるデータを保持します。テキスト ファイル Modified_physionet_data.txt は PhysioNet のコピー ポリシーで必要になり、データのソース属性、および ECG の各記録に適用される前処理手順の説明を提供します。

physionet-ECG_data-main 内にある ECGData.zip を解凍します。データ ファイルを 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 は、2 つのフィールド (Data および Labels) をもつ構造体配列です。Data フィールドは 162 行 65,536 列の行列で、各行は 128 Hz でサンプリングした ECG 記録です。Labels は 162 行 1 列の診断ラベルの cell 配列で、それぞれのラベルが Data の各行に対応します。3 つの診断カテゴリは、'ARR''CHF'、および 'NSR' です。

特徴抽出

データをダウンロードした後、信号のスカログラムを生成しなければなりません。スカログラムは CNN への "入力" イメージです。

各カテゴリのスカログラムを保存するには、最初に tempdir 内に ECG データ ディレクトリ 'data' を作成します。その後、各 ECG カテゴリに由来した 'data' に 3 つのサブディレクトリを作成します。これは、補助関数 helperCreateECGDirectories によって実行されます。helperCreateECGDirectories は、ECGData、ECG データ ディレクトリの名前、親ディレクトリの名前を入力引数として受け入れます。tempdir を書き込み権限のある別のディレクトリと置き換えることができます。この補助関数のソース コードは、この例の最後にあるサポート関数の節で見つけることができます。

parentDir = tempdir;
dataDir = 'data';
helperCreateECGDirectories(ECGData,parentDir,dataDir)

フォルダーを作成した後、ECG 信号のスカログラムを RGB イメージとして作成し、これらのイメージを dataDir 内の適切なサブディレクトリに書き込みます。スカログラムを作成するには、まず CWT フィルター バンクを事前に計算します。フィルター バンクの事前計算は、同じパラメーターを使用して多数の信号の CWT を取得するときに推奨される方法です。補助関数 helperCreateRGBfromTF がこれを実行します。この補助関数のソース コードは、この例の最後にあるサポート関数の節にあります。SqueezeNet アーキテクチャと互換性をもたせるために、各 RGB イメージは 227 x 227 x 3 のサイズの配列になります。

helperCreateRGBfromTF(ECGData,parentDir,dataDir)

学習データと検証データへのデータ セットの分割

スカログラム イメージをイメージ データストアとして読み込みます。関数 imageDatastore は、フォルダー名に基づいてイメージに自動的にラベルを付け、このデータを ImageDatastore オブジェクトとして格納します。イメージ データストアを使用すると、メモリに収まらないデータなどのサイズが大きいイメージ データを格納して、CNN の学習中にイメージをバッチ単位で効率的に読み取ることができます。

allImages = imageDatastore(fullfile(tempdir,dataDir),...
    'IncludeSubfolders',true,...
    'LabelSource','foldernames');

イメージを 2 つのグループ (学習用と検証用) にランダムに分割します。イメージの 80% を学習に使用し、残りを検証に使用します。再現性を得るために、乱数シードを既定値に設定します。

rng default
[imgsTrain,imgsValidation] = splitEachLabel(allImages,0.8,'randomized');
disp(['Number of training images: ',num2str(numel(imgsTrain.Files))]);
Number of training images: 130
disp(['Number of validation images: ',num2str(numel(imgsValidation.Files))]);
Number of validation images: 32

SqueezeNet

SqueezeNet は、イメージを 1000 のカテゴリに分類できる事前学習済みの CNN です。ECG 分類問題に対応するように、SqueezeNet に再学習させる必要があります。再学習を行う前に、いくつかのネットワーク層を変更し、各種の学習オプションを設定します。再学習が完了したら、CNN を .mat ファイルに保存します。CUDA 実行可能ファイルはこの .mat ファイルを使用します。

実験用の試行インデックスと結果ディレクトリを指定します。このディレクトリがなければ作成します。

trial = 1;
ResultDir = 'results';
if ~exist(ResultDir,'dir')
    mkdir(ResultDir)
end
MatFile = fullfile(ResultDir,sprintf('SqueezeNet_Trial%d.mat',trial));

SqeezeNet を読み込みます。層グラフを抽出し、最後の 5 つの層を検査します。

sqz = squeezenet;
lgraph = layerGraph(sqz);
lgraph.Layers(end-4:end)
ans = 
  5×1 Layer array with layers:

     1   'conv10'                            Convolution              1000 1×1×512 convolutions with stride [1  1] and padding [0  0  0  0]
     2   'relu_conv10'                       ReLU                     ReLU
     3   'pool10'                            Global Average Pooling   Global average pooling
     4   'prob'                              Softmax                  softmax
     5   'ClassificationLayer_predictions'   Classification Output    crossentropyex with 'tench' and 999 other classes

ECG 信号の 3 つのクラスを分類するように SqueezeNet に再学習させるには、'conv10' 層を、ECG クラスの数と同じ数のフィルターをもつ新しい畳み込み層で置き換えます。分類層をクラス ラベルがない新しい分類層に置き換えます。

numClasses = numel(categories(imgsTrain.Labels));
new_conv10_WeightLearnRateFactor = 1;
new_conv10_BiasLearnRateFactor = 1;
newConvLayer = convolution2dLayer(1,numClasses,...
        'Name','new_conv10',...
        'WeightLearnRateFactor',new_conv10_WeightLearnRateFactor,...
        'BiasLearnRateFactor',new_conv10_BiasLearnRateFactor);
lgraph = replaceLayer(lgraph,'conv10',newConvLayer);
newClassLayer = classificationLayer('Name','new_classoutput');
lgraph = replaceLayer(lgraph,'ClassificationLayer_predictions',newClassLayer);
lgraph.Layers(end-4:end)
ans = 
  5×1 Layer array with layers:

     1   'new_conv10'        Convolution              3 1×1 convolutions with stride [1  1] and padding [0  0  0  0]
     2   'relu_conv10'       ReLU                     ReLU
     3   'pool10'            Global Average Pooling   Global average pooling
     4   'prob'              Softmax                  softmax
     5   'new_classoutput'   Classification Output    crossentropyex

SqueezeNet で使用する一連の学習オプションを作成します。

OptimSolver = 'sgdm';
MiniBatchSize = 15;
MaxEpochs = 20;
InitialLearnRate = 1e-4;
Momentum = 0.9;
ExecutionEnvironment = 'cpu';

options = trainingOptions(OptimSolver,...
    'MiniBatchSize',MiniBatchSize,...
    'MaxEpochs',MaxEpochs,...
    'InitialLearnRate',InitialLearnRate,...
    'ValidationData',imgsValidation,...
    'ValidationFrequency',10,...
    'ExecutionEnvironment',ExecutionEnvironment,...
    'Momentum',Momentum);

すべてのパラメーターを 1 つの構造体内に保存します。学習済みネットワークと構造体は後で .mat ファイルに保存します。

TrialParameter.new_conv10_WeightLearnRateFactor = new_conv10_WeightLearnRateFactor;
TrialParameter.new_conv10_BiasLearnRateFactor = new_conv10_BiasLearnRateFactor;
TrialParameter.OptimSolver = OptimSolver;
TrialParameter.MiniBatchSize = MiniBatchSize;
TrialParameter.MaxEpochs = MaxEpochs;
TrialParameter.InitialLearnRate = InitialLearnRate;
TrialParameter.Momentum = Momentum;
TrialParameter.ExecutionEnvironment = ExecutionEnvironment;

乱数シードを既定値に設定し、ネットワークを学習させます。学習済みネットワーク、試行パラメーター、学習実行時間、検証イメージが格納されているイメージ データストアを保存します。学習プロセスは、通常、デスクトップ CPU 上で 1 ~ 5 分かかります。以前の試行で学習させた CNN を使用するには、trial をその試行のインデックス番号に設定し、LoadModeltrue に設定します。

LoadModel = false;
if ~LoadModel
    rng default
    tic;
    trainedModel = trainNetwork(imgsTrain,lgraph,options);
    trainingTime = toc;
    fprintf('Total training time: %.2e sec\n',trainingTime);
    save(MatFile,'TrialParameter','trainedModel','trainingTime','imgsValidation');
else
    disp('Load ML model from the file')
    load(MatFile,'trainedModel','imgsValidation');
end
Initializing input data normalization.
|======================================================================================================================|
|  Epoch  |  Iteration  |  Time Elapsed  |  Mini-batch  |  Validation  |  Mini-batch  |  Validation  |  Base Learning  |
|         |             |   (hh:mm:ss)   |   Accuracy   |   Accuracy   |     Loss     |     Loss     |      Rate       |
|======================================================================================================================|
|       1 |           1 |       00:00:02 |       26.67% |       25.00% |       4.1769 |       2.9883 |      1.0000e-04 |
|       2 |          10 |       00:00:12 |       73.33% |       59.38% |       0.9877 |       1.1559 |      1.0000e-04 |
|       3 |          20 |       00:00:21 |       60.00% |       56.25% |       0.9164 |       0.9182 |      1.0000e-04 |
|       4 |          30 |       00:00:31 |       86.67% |       68.75% |       0.6698 |       0.7883 |      1.0000e-04 |
|       5 |          40 |       00:00:40 |       66.67% |       68.75% |       0.9053 |       0.7489 |      1.0000e-04 |
|       7 |          50 |       00:00:50 |       80.00% |       78.13% |       0.5422 |       0.6781 |      1.0000e-04 |
|       8 |          60 |       00:00:59 |      100.00% |       81.25% |       0.4187 |       0.6124 |      1.0000e-04 |
|       9 |          70 |       00:01:08 |       93.33% |       84.38% |       0.3561 |       0.5471 |      1.0000e-04 |
|      10 |          80 |       00:01:18 |       73.33% |       84.38% |       0.5141 |       0.4765 |      1.0000e-04 |
|      12 |          90 |       00:01:27 |       86.67% |       84.38% |       0.4220 |       0.4038 |      1.0000e-04 |
|      13 |         100 |       00:01:36 |       93.33% |       90.63% |       0.1923 |       0.3476 |      1.0000e-04 |
|      14 |         110 |       00:01:46 |      100.00% |       90.63% |       0.1472 |       0.3125 |      1.0000e-04 |
|      15 |         120 |       00:01:55 |      100.00% |       93.75% |       0.0791 |       0.2777 |      1.0000e-04 |
|      17 |         130 |       00:02:04 |       86.67% |       93.75% |       0.2486 |       0.2833 |      1.0000e-04 |
|      18 |         140 |       00:02:14 |      100.00% |       93.75% |       0.0386 |       0.2288 |      1.0000e-04 |
|      19 |         150 |       00:02:23 |      100.00% |       93.75% |       0.0487 |       0.2397 |      1.0000e-04 |
|      20 |         160 |       00:02:32 |      100.00% |       93.75% |       0.0224 |       0.2041 |      1.0000e-04 |
|======================================================================================================================|
Total training time: 1.61e+02 sec

学習済みのネットワークのみを別個の .mat ファイルに保存します。このファイルは CUDA 実行可能ファイルで使用されます。

ModelFile = fullfile(ResultDir,sprintf('SqueezeNet_Trial%d.mat',trial));
OutMatFile = fullfile('ecg_model.mat');

data = load(ModelFile,'trainedModel');
net = data.trainedModel;
save(OutMatFile,'net');

学習済みのネットワークを使用して、検証セットのクラスを予測します。

[YPred, probs] = classify(trainedModel,imgsValidation);
accuracy = mean(YPred==imgsValidation.Labels)
accuracy = 0.9375

検証セットに対する学習済みネットワークのパフォーマンスを混同チャートに要約します。列と行の要約を使用して、各クラスの適合率と再現率を表示します。Figure を保存します。混同チャートの下にあるテーブルに、精度が示されます。混同チャートの右側にあるテーブルに、再現率が示されます。

figure
confusionMat = confusionmat(imgsValidation.Labels,YPred);
confusionchart(imgsValidation.Labels,YPred, ...
    'Title',sprintf('Confusion Matrix on Validation (overall accuracy: %.4f)',accuracy),...
    'ColumnSummary','column-normalized','RowSummary','row-normalized');

AccFigFile = fullfile(ResultDir,sprintf('SqueezeNet_ValidationAccuracy_Trial%d.fig',trial));
saveas(gcf,AccFigFile);

学習済みネットワークのサイズを表示します。

info = whos('trainedModel');
ModelMemSize = info.bytes/1024;
fprintf('Trained network size: %g kB\n',ModelMemSize)
Trained network size: 2981.55 kB

ネットワークがイメージの分類に費やす平均時間を求めます。

NumTestForPredTime = 20;
TrialParameter.NumTestForPredTime = NumTestForPredTime;

fprintf('Test prediction time (number of tests: %d)... ',NumTestForPredTime)
Test prediction time (number of tests: 20)... 
imageSize = trainedModel.Layers(1).InputSize;
PredTime = zeros(NumTestForPredTime,1);
for i = 1:NumTestForPredTime
    x = randn(imageSize);
    tic;
    [YPred, probs] = classify(trainedModel,x,'ExecutionEnvironment',ExecutionEnvironment);
    PredTime(i) = toc;
end
AvgPredTimePerImage = mean(PredTime);
fprintf('Average prediction time (execution environment: %s): %.2e sec \n',...
    ExecutionEnvironment,AvgPredTimePerImage);
Average prediction time (execution environment: cpu): 2.94e-02 sec 

結果を保存します。

if ~LoadModel
    save(MatFile,'accuracy','confusionMat','PredTime','ModelMemSize', ...
        'AvgPredTimePerImage','-append')
end

GPU コード生成 — 関数の定義

信号のスカログラムは、深層 CNN への入力 "イメージ" です。関数 cwt_ecg_jetson_ex を作成します。この関数は、入力信号のスカログラムを計算し、ユーザーが指定した次元でイメージを返します。このイメージは jet(128) カラーマップを使用します。この関数の %#codegen 命令は、この関数がコード生成用であることを指定します。coder.gpu.kernelfun プラグマを使用すると、コード生成時に関数 cwt_ecg_jetson_ex の計算を GPU にマッピングしようとします。

type cwt_ecg_jetson_ex.m
function im = cwt_ecg_jetson_ex(TimeSeriesSignal, ImgSize) %#codegen
% This function is only intended to support wavelet deep learning examples.
% It may change or be removed in a future release.

coder.gpu.kernelfun();

%% Create Scalogram
cfs = cwt(TimeSeriesSignal, 'morse', 1, 'VoicesPerOctave', 12);
cfs = abs(cfs);

%% Image generation
cmapj128 = coder.load('cmapj128');
imx = ind2rgb_custom_ecg_jetson_ex(round(255*rescale(cfs))+1,cmapj128.cmapj128);

% resize to proper size and convert to uint8 data type
im = im2uint8(imresize(imx, ImgSize)); 

end

コード生成用に、エントリポイント関数 model_predict_ecg.m を作成します。この関数は ECG 信号を入力として受け取り、関数 cwt_ecg_jetson_ex を呼び出してスカログラムのイメージを作成します。関数 model_predict_ecg は、ecg_model.mat ファイルに含まれているネットワークを使用して、ECG 信号を分類します。

type model_predict_ecg.m
function PredClassProb = model_predict_ecg(TimeSeriesSignal) %#codegen
% This function is only intended to support wavelet deep learning examples.
% It may change or be removed in a future release.
    coder.gpu.kernelfun();
    
    % parameters
    ModFile = 'ecg_model.mat'; % file that saves neural network model
    ImgSize = [227 227]; % input image size for the ML model
    
    % sanity check signal is a row vector of correct length
    assert(isequal(size(TimeSeriesSignal), [1 65536])) 
    %% cwt transformation for the signal
    im = cwt_ecg_jetson_ex(TimeSeriesSignal, ImgSize);
    
    %% model prediction
    persistent model;
    if isempty(model)
        model = coder.loadDeepLearningNetwork(ModFile, 'mynet');
    end

    PredClassProb = predict(model, im);
    
end

NVIDIA ターゲットに展開できる CUDA 実行可能ファイルを生成するには、カスタムのメイン ファイル (main_ecg_jetson_ex.cu) とヘッダー ファイル (main_ecg_jetson_ex.h) を作成します。メイン ファイルの例を生成し、それをテンプレートとして使用して新しいメイン ファイルとヘッダー ファイルを再作成できます。詳細については、coder.CodeConfig (MATLAB Coder)GenerateExampleMain プロパティを参照してください。メイン ファイルは、MATLAB エントリポイント関数用に生成されたコードを呼び出します。メイン ファイルは最初にテキスト ファイルから ECG 信号を読み取り、そのデータをエントリポイント関数に渡し、予測結果をテキスト ファイル (predClassProb.txt) に書き込みます。GPU での計算効率性を最大化するために、実行可能ファイルは単精度のデータを処理します。

type main_ecg_jetson_ex.cu
//
// File: main_ecg_jetson_ex.cu
//
// This file is only intended to support wavelet deep learning examples.
// It may change or be removed in a future release.
        
//***********************************************************************
// Include Files
#include "rt_nonfinite.h"
#include "model_predict_ecg.h"
#include "main_ecg_jetson_ex.h"
#include "model_predict_ecg_terminate.h"
#include "model_predict_ecg_initialize.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

// Function Definitions

/* Read data from a file*/
int readData_real32_T(const char * const file_in, real32_T data[65536])
{
  FILE* fp1 = fopen(file_in, "r");
  if (fp1 == 0)
  {
    printf("ERROR: Unable to read data from %s\n", file_in);
    exit(0);
  }
  for(int i=0; i<65536; i++)
  {
      fscanf(fp1, "%f", &data[i]);
  }
  fclose(fp1);
  return 0;
}

/* Write data to a file*/
int writeData_real32_T(const char * const file_out, real32_T data[3])
{
  FILE* fp1 = fopen(file_out, "w");
  if (fp1 == 0) 
  {
    printf("ERROR: Unable to write data to %s\n", file_out);
    exit(0);
  }
  for(int i=0; i<3; i++)
  {
    fprintf(fp1, "%f\n", data[i]);
  }
  fclose(fp1);
  return 0;
}

// model predict function
static void main_model_predict_ecg(const char * const file_in, const char * const file_out)
{
  real32_T PredClassProb[3];
  //  real_T b[65536];
  real32_T b[65536];

  // readData_real_T(file_in, b);
  readData_real32_T(file_in, b);
       
  model_predict_ecg(b, PredClassProb);

  writeData_real32_T(file_out, PredClassProb);

}

// main function
int32_T main(int32_T argc, const char * const argv[])
{
  const char * const file_out = "predClassProb.txt";
  // Initialize the application.
  model_predict_ecg_initialize();
  
  // Run prediction function
  main_model_predict_ecg(argv[1], file_out); // argv[1] = file_in

  // Terminate the application.
  model_predict_ecg_terminate();
  return 0;
}
type main_ecg_jetson_ex.h
//
// File: main_ecg_jetson_ex.h
//
// This file is only intended to support wavelet deep learning examples.
// It may change or be removed in a future release.

//
//***********************************************************************
#ifndef MAIN_H
#define MAIN_H

// Include Files
#include <stddef.h>
#include <stdlib.h>
#include "rtwtypes.h"
#include "model_predict_ecg_types.h"

// Function Declarations
extern int32_T main(int32_T argc, const char * const argv[]);

#endif

//
// File trailer for main_ecg_jetson_ex.h
//
// [EOF]
//

GPU コード生成 — ターゲットの指定

ターゲット デバイスに展開できる実行可能ファイルを作成するには、CodeGenMode を 1 に設定します。ローカルで実行してターゲット デバイスにリモートで接続する実行可能ファイルを作成するには、CodeGenMode を 2 に設定します。

main 関数は signalFile で指定されたテキスト ファイルからデータを読み取り、分類結果を resultFile に書き込みます。ExampleIndex を設定して、代表的な ECG 信号を選択します。この信号を使用して、関数 classify に対して実行可能ファイルをテストします。Jetson_BuildDir で、ターゲット上でリモート ビルド プロセスを実行するディレクトリを指定します。指定したビルド ディレクトリがターゲットに存在しない場合は、指定した名前でディレクトリが作成されます。

CodeGenMode = 1;
signalFile = 'signalData.txt';
resultFile = 'predClassProb.txt'; % consistent with "main_ecg_jetson_ex.cu"
Jetson_BuildDir = '~/projectECG';
ExampleIndex = 1; % 1,4: type ARR; 2,5: type CHF; 3,6: type NSR

Function_to_Gen = 'model_predict_ecg';
ModFile = 'ecg_model.mat'; % file that saves neural network model; consistent with "main_ecg_jetson_ex.cu"
ImgSize = [227 227]; % input image size for the ML model

switch ExampleIndex
    case 1 % ARR 7
        SampleSignalIdx = 7;
    case 2 % CHF 97
        SampleSignalIdx = 97;
    case 3 % NSR 132
        SampleSignalIdx = 132;
    case 4 % ARR 31
        SampleSignalIdx = 31;
    case 5 % CHF 101
        SampleSignalIdx = 101;
    case 6 % NSR 131
        SampleSignalIdx = 131;
end
signal_data = single(ECGData.Data(SampleSignalIdx,:));
ECGtype = ECGData.Labels{SampleSignalIdx};

GPU コード生成 — ハードウェアへの接続

NVIDIA ハードウェアと通信するために、関数 jetson を使用してライブ ハードウェア接続オブジェクトを作成します。ライブ ハードウェア接続オブジェクトを作成するには、ターゲット ボードのホスト名または IP アドレス、ユーザー名、およびパスワードを知っている必要があります。

Jetson ハードウェア用のライブ ハードウェア接続オブジェクトを作成します。ハードウェアのライブ オブジェクトの作成中に、ハードウェアのチェック、IO サーバーのインストールのチェック、およびターゲット上の周辺機器情報の収集が行われます。この情報はコマンド ウィンドウに表示されます。

hwobj = jetson('gpucoder-nano-2','ubuntu','ubuntu');
Checking for CUDA availability on the Target...
Checking for 'nvcc' in the target system path...
Checking for cuDNN library availability on the Target...
Checking for TensorRT library availability on the Target...
Checking for prerequisite libraries is complete.
Gathering hardware details...
Checking for third-party library availability on the Target...
Gathering hardware details is complete.
 Board name         : NVIDIA Jetson TX1
 CUDA Version       : 10.0
 cuDNN Version      : 7.3
 TensorRT Version   : 5.0
 GStreamer Version  : 1.14.5
 V4L2 Version       : 1.14.2-1
 SDL Version        : 1.2
 Available Webcams  :  
 Available GPUs     : NVIDIA Tegra X1

関数coder.checkGpuInstall (GPU Coder)を使用して、この例を実行するのに必要なコンパイラおよびライブラリがハードウェア上に適切に設定されていることを検証します。

envCfg = coder.gpuEnvConfig('jetson');
envCfg.DeepLibTarget = 'cudnn';
envCfg.DeepCodegen = 1;
envCfg.HardwareObject = hwobj;
envCfg.Quiet = 1;
coder.checkGpuInstall(envCfg)
ans = struct with fields:
                 gpu: 1
                cuda: 1
               cudnn: 1
            tensorrt: 0
        basiccodegen: 0
       basiccodeexec: 0
         deepcodegen: 1
        deepcodeexec: 0
    tensorrtdatatype: 0
           profiling: 0

GPU コード生成 — コンパイル

コンパイルに必要な GPU コード構成オブジェクトを作成します。関数 coder.hardware を使用して Jetson プラットフォーム用の構成オブジェクトを作成し、それをコード構成オブジェクト cfgHardware プロパティに割り当てます。Jetson TX1 または TX2 ボードには 'NVIDIA Jetson' を使用します。カスタム メイン ファイルは、生成されたコード内でエントリポイント関数を呼び出すラッパーです。このカスタム ファイルは展開済みの実行可能ファイルに必要となります。

関数 coder.DeepLearningConfig (GPU Coder) を使用して CuDNN 深層学習構成オブジェクトを作成し、それを GPU コード構成オブジェクトの DeepLearningConfig プロパティに割り当てます。コード ジェネレーターは、NVIDIA GPU 対応の NVIDIA® CUDA® Deep Neural Network library (cuDNN) を利用します。cuDNN は深層ニューラル ネットワークに対するプリミティブの、GPU で高速化されたライブラリです。

if CodeGenMode == 1
    cfg = coder.gpuConfig('exe');
    cfg.Hardware = coder.hardware('NVIDIA Jetson');
    cfg.Hardware.BuildDir = Jetson_BuildDir;
    cfg.DeepLearningConfig = coder.DeepLearningConfig('cudnn');
    cfg.CustomSource = fullfile('main_ecg_jetson_ex.cu');
elseif CodeGenMode == 2
    cfg = coder.gpuConfig('lib');
    cfg.VerificationMode = 'PIL';
    cfg.Hardware = coder.hardware('NVIDIA Jetson');
    cfg.Hardware.BuildDir = Jetson_BuildDir;
    cfg.DeepLearningConfig = coder.DeepLearningConfig('cudnn');
end

CUDA コードを生成するには、関数 codegen を使用して、GPU コード構成およびエントリポイント関数 model_predict_ecg の入力のサイズと型を渡します。ホスト上でコード生成が完了した後、生成されたファイルがターゲットにコピーされてビルドされます。

codegen('-config ',cfg,Function_to_Gen,'-args',{signal_data},'-report');
Code generation successful: View report

GPU コード生成 — 実行

ターゲットに展開される実行可能ファイルをコンパイルする場合、このサンプルの ECG 信号をテキスト ファイルに書き込みます。ハードウェア オブジェクトの関数 putFile() を使用して、そのテキスト ファイルをターゲット上に配置します。workspaceDir プロパティにはターゲットの codegen フォルダーへのパスが含まれています。

if CodeGenMode == 1
    fid = fopen(signalFile,'w');
    for i = 1:length(signal_data)
        fprintf(fid,'%f\n',signal_data(i));
    end
    fclose(fid);
    hwobj.putFile(signalFile,hwobj.workspaceDir);
end

実行可能ファイルを実行します。

展開済みの実行可能ファイルを実行するときに前の結果ファイルが存在する場合には、そのファイルを削除します。関数 runApplication() を使用してターゲット ハードウェア上の実行可能ファイルを起動してから、関数 getFile() を使用して結果を取得します。関数 runApplication() の呼び出しによって返された直後に結果を取得できるとは限らないため、また通信の遅延を許容できるようにするため、結果取得の最大時間を 90 秒に設定します。関数 evalc を使用してコマンドライン出力を行わないようにします。

if CodeGenMode == 1 % run deployed executable
    maxFetchTime = 90;
    resultFile_hw = fullfile(hwobj.workspaceDir,resultFile);
    if ispc
        resultFile_hw = strrep(resultFile_hw,'\','/');
    end
    
    ta = tic;
    
    hwobj.deleteFile(resultFile_hw)
    hwobj.runApplication(Function_to_Gen,signalFile);
    
    tf = tic;
    success = false;
    while toc(tf) < maxFetchTime
        try
            evalc('hwobj.getFile(resultFile_hw)');
            success = true;
        catch ME
        end
        if success
            break
        end
    end
    fprintf('Fetch time = %.3e sec\n',toc(tf));
    assert(success,'Unable to fetch the prediction')
    PredClassProb = readmatrix(resultFile);
    PredTime = toc(ta);
elseif CodeGenMode == 2 % run PIL executable
    ta = tic;
    eval(sprintf('PredClassProb = %s_pil(signal_data);',Function_to_Gen));
    PredTime = toc(ta);
    eval(sprintf('clear %s_pil;',Function_to_Gen)); % terminate PIL execution
end
### Launching the executable on the target...
Executable launched successfully with process ID 5672.
Displaying the simple runtime log for the executable...

Note: For the complete log, run the following command in the MATLAB command window:
system(hwobj,'cat /home/ubuntu/projectECG/MATLAB_ws/R2021a/C/Users/pkostele/OneDrive_-_MathWorks/Documents/MATLAB/Examples/deeplearning_shared-ex54874305/model_predict_ecg.log')
Fetch time = 9.743e+00 sec

関数 classify を使用して、このサンプルの信号のクラス ラベルを予測します。

ModData = load(ModFile,'net');
im = cwt_ecg_jetson_ex(signal_data,ImgSize);
[ModPred, ModPredProb] = classify(ModData.net,im);
PredCat = categories(ModPred)';

結果を比較します。

PredTableJetson = array2table(PredClassProb(:)','VariableNames',matlab.lang.makeValidName(PredCat));
fprintf('tPred = %.3e sec\nExample ECG Type: %s\n',PredTime,ECGtype)
tPred = 1.288e+01 sec
Example ECG Type: ARR
disp(PredTableJetson)
      ARR        CHF         NSR   
    _______    ________    ________

    0.99872    0.001131    0.000153
PredTableMATLAB = array2table(ModPredProb(:)','VariableNames',matlab.lang.makeValidName(PredCat));
disp(PredTableMATLAB)
      ARR         CHF          NSR    
    _______    _________    __________

    0.99872    0.0011298    0.00015316

ハードウェア接続を終了します。

clear hwobj

まとめ

この例では、CNN を使用して ECG 信号を分類する CUDA 実行可能ファイルを作成して展開する方法を説明しています。ローカルで実行してリモート ターゲットに接続する実行可能ファイルを作成することもできます。ワークフロー全体はこの例で紹介されています。データのダウンロード後に、CWT を使用して ECG 信号から特徴が抽出されます。続いて、スカログラムに基づいて信号を分類するように SqueezeNet に再学習させます。2 つのユーザー定義関数が作成されて、ターゲットの NVIDIA デバイス上でコンパイルされます。実行可能ファイルの結果が MATLAB で比較されます。

参考文献

  1. 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.

  2. 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.

  3. 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)

サポート関数

helperCreateECGDirectories

function helperCreateECGDirectories(ECGData,parentFolder,dataFolder)
% This function is only intended to support wavelet deep learning examples.
% It may change or be removed in a future release.

rootFolder = parentFolder;
localFolder = dataFolder;
mkdir(fullfile(rootFolder,localFolder))

folderLabels = unique(ECGData.Labels);
for i = 1:numel(folderLabels)
    mkdir(fullfile(rootFolder,localFolder,char(folderLabels(i))));
end
end

helperPlotReps

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

folderLabels = unique(ECGData.Labels);

for k=1:3
    ecgType = folderLabels{k};
    ind = find(ismember(ECGData.Labels,ecgType));
    subplot(3,1,k)
    plot(ECGData.Data(ind(1),1:1000));
    grid on
    title(ecgType)
end
end

helperCreateRGBfromTF

function helperCreateRGBfromTF(ECGData,parentFolder, childFolder)
% This function is only intended to support wavelet deep learning examples.
% It may change or be removed in a future release.

imageRoot = fullfile(parentFolder,childFolder);

data = ECGData.Data;
labels = ECGData.Labels;

[~,signalLength] = size(data);

fb = cwtfilterbank('SignalLength',signalLength,'VoicesPerOctave',12);
r = size(data,1);

for ii = 1:r
    cfs = abs(fb.wt(data(ii,:)));
    im = ind2rgb(im2uint8(rescale(cfs)),jet(128));
    
    imgLoc = fullfile(imageRoot,char(labels(ii)));
    imFileName = strcat(char(labels(ii)),'_',num2str(ii),'.jpg');
    imwrite(imresize(im,[227 227]),fullfile(imgLoc,imFileName));
end
end

参考

(Wavelet Toolbox) | (GPU Coder) | (Wavelet Toolbox)

関連するトピック