メインコンテンツ

このページの内容は最新ではありません。最新版の英語を参照するには、ここをクリックします。

長短期記憶ネットワークを使用した ECG 信号の分類

この例では、PhysioNet 2017 Challenge からの心拍心電図 (ECG) データを深層学習と信号処理を使用して分類する方法を示します。特に、この例では長短期記憶ネットワークと時間-周波数解析を使用します。

GPU および Parallel Computing Toolbox™ を使用してこのワークフローの再現と高速化を行う例については、長短期記憶ネットワークを GPU 高速化と組み合わせて使用した ECG 信号の分類を参照してください。

はじめに

ECG は、人の心臓の電気的活動を一定期間記録します。医師は ECG を使用して、患者の心拍が正常か正常でないかどうかを視覚的に検出します。

心房細動 (AFib) は、心臓の上部室、心房が下部室、心室と連携せずに脈打っているときに発生する不規則な心拍の一種です。

この例では、PhysioNet 2017 Challenge [1]、[2]、[3] からの ECG データを使用します。これは、https://physionet.org/challenge/2017/ で入手できます。データは、300 Hz でサンプリングされ、専門家の手によって次の 4 つの別々のクラスに分けられた、一連の ECG 信号で構成されています。正常 (N)、AFib (A)、その他の律動 (O)、およびノイズを含む録音 (~)。この例では、深層学習を使用して分類プロセスを自動化する方法を説明します。手順として、AFib の兆候を示す信号から正常な ECG 信号を識別できるバイナリ分類器を調査します。

長短期記憶 (LSTM) ネットワークは、シーケンスおよび時系列のデータの学習に適した再帰型ニューラル ネットワーク (RNN) の一種です。LSTM ネットワークは、シーケンスのタイム ステップ間の長期的な依存関係を学習できます。LSTM 層 (lstmLayer (Deep Learning Toolbox)) では順方向の時間系列を確認でき、双方向の LSTM 層 (bilstmLayer (Deep Learning Toolbox)) では順方向と逆方向の両方の時間系列を確認できます。この例では、双方向の LSTM 層を使用します。

この例では、人工知能 (AI) の問題を解く際にデータ中心の手法を使用することの利点を示します。生データを使用して LSTM ネットワークに学習させる最初の試行では、標準以下の結果しか得られません。抽出した特徴を使用して同じモデル アーキテクチャに学習させると、分類性能が大幅に改善されます。

学習プロセスを高速化するには、GPU を使用するマシン上でこの例を実行します。マシンに GPU と Parallel Computing Toolbox™ がある場合は、MATLAB® は学習に GPU を自動で使用します。それ以外では CPU を使用します。

データの読み込みおよび確認

ReadPhysionetData スクリプトを実行して PhysioNet Web サイトからデータをダウンロードし、適切な形式の ECG 信号を含む MAT ファイル (PhysionetData.mat) を生成します。データのダウンロードには数分かかる場合があります。PhysionetData.mat が現在のフォルダーに既に存在していない場合のみスクリプトを実行する条件ステートメントを使用します。

if ~isfile('PhysionetData.mat')
    ReadPhysionetData         
end
load PhysionetData

読み込み操作ではワークスペースに 2 つの変数、SignalsLabels が追加されます。Signals は ECG 信号を保持する cell 配列です。Labels は対応する信号のグラウンド トゥルース ラベルを保持する categorical 配列です。

Signals(1:5)'
ans=1×5 cell array
    {1×9000 double}    {1×9000 double}    {1×18000 double}    {1×9000 double}    {1×18000 double}

Labels(1:5)'
ans = 1×5 categorical
     N      N      N      A      A 

関数 summary を使用して、データに含まれる AFib 信号と正常な信号の数を確認します。

summary(Labels)
Labels: 5788×1 categorical

     A                 738 
     N                5050 
     <undefined>         0 

信号長のヒストグラムを生成します。ほとんどの信号は 9000 サンプルの長さです。

L = cellfun(@length,Signals);
h = histogram(L);
xticks(0:3000:18000);
xticklabels(0:3000:18000);
title('Signal Lengths')
xlabel('Length')
ylabel('Count')

各クラスから 1 つの信号のセグメントを可視化します。通常の心拍は規則的に起こりますが、AFib 心拍は不規則な間隔で起こります。さらに AFib 心拍信号は P 波が欠落することがよくあります。P 波は、正常な心拍信号では QRS 群の前に脈を打ちます。正常な信号のプロットでは P 波と QRS 群が示されます。

normal = Signals{1};
aFib = Signals{4};

tiledlayout("flow")
nexttile
plot(normal)
title("Normal Rhythm")
xlim([4000,5200])
ylabel("Amplitude (mV)")
text(4330,150,"P",HorizontalAlignment="center")
text(4370,850,"QRS",HorizontalAlignment="center")

nexttile
plot(aFib)
title("Atrial Fibrillation")
xlim([4000,5200])
xlabel("Samples")
ylabel("Amplitude (mV)")

学習用データの準備

学習中に、関数 trainnet はデータをミニバッチに分割します。関数は、その後、同じミニバッチ内の信号にパディングや切り捨てを行い、すべて同じ長さになるようにします。パディングや切り捨てをしすぎると、ネットワークのパフォーマンスにマイナスの影響が出ることがあります。これは、ネットワークが追加または削除された情報に基づいて信号を誤って解釈することがあるためです。

過度なパディングや切り捨てを防ぐため、helperSegmentSignals 関数を ECG 信号に適用してすべてが 9000 サンプルの長さになるようにします。helperSegmentSignals 関数は、この例の最後で定義されています。関数は 9000 サンプル未満の信号を無視します。信号が 9000 サンプルを超える場合、helperSegmentSignals はできるだけ多くの 9000 サンプルのセグメントに分割し、残っているサンプルは無視します。たとえば、18500 サンプルの信号は、2 つの 9000 サンプルの信号になり、残りの 500 サンプルは無視されます。

[Signals,Labels] = helperSegmentSignals(Signals,Labels);

配列 Signals の最初の 5 つの要素を表示し、各エントリが 9000 サンプルの長さになっていることを確認します。

Signals(1:5)'
ans=1×5 cell array
    {1×9000 double}    {1×9000 double}    {1×9000 double}    {1×9000 double}    {1×9000 double}

1 番目の試行: 生の信号データを使用した分類器の学習

分類器を設計するには、前のセクションで生成した生の信号を使用します。分類器に学習させるための学習セットと新しいデータに対して分類器の精度をテストするためのテスト セットに信号を分割します。

関数 summary を使用して、AFib 信号と通常の信号の比率が約 1:7 であることを示します。

summary(Labels)
Labels: 5655×1 categorical

     A                 718 
     N                4937 
     <undefined>         0 

約 7/8 の信号が正常であるため、分類器は単純にすべての信号を正常として分類することで高い精度が達成できる、と学習することがあります。このバイアスを回避するには、データセットの AFib 信号を複製することで AFib データを拡張して、正常な信号と AFib 信号とを同じ数にします。一般にオーバーサンプリングと呼ばれるこの複製は、深層学習で使用されるデータ拡張の 1 つの形式です。

信号をそれらのクラスに従って分割します。

afibX = Signals(Labels=="A");
afibY = Labels(Labels=="A");

normalX = Signals(Labels=="N");
normalY = Labels(Labels=="N");

次に、splitlabels を使用して、各クラスからターゲットを学習セット、検証セット、およびテスト セットにランダムに分割します。

rng("default")

indA = splitlabels(afibY,[0.8 0.1],"randomized");
indN = splitlabels(normalY,[0.8 0.1],"randomized");

XTrainA = afibX(indA{1});
YTrainA = afibY(indA{1});
XTrainN = normalX(indN{1});
YTrainN = normalY(indN{1});

XValidA = afibX(indA{2});
YValidA = afibY(indA{2});
XValidN = normalX(indN{2});
YValidN = normalY(indN{2});

XTestA = afibX(indA{3});
YTestA = afibY(indA{3});
XTestN = normalX(indN{3});
YTestN = normalY(indN{3});

このデータセットは不均衡です。AFib 信号と通常の信号の数をほぼ同じにするには、AFib 信号を 7 回繰り返します。検証データとテスト データに必ず実際の分布を反映させるため、学習データにのみオーバーサンプリングを適用します。

XTrain = [repmat(XTrainA,7,1); XTrainN];
YTrain = [repmat(YTrainA,7,1); YTrainN];

XValid = [XValidA; XValidN];
YValid = [YValidA; YValidN];

XTest = [XTestA; XTestN];
YTest = [YTestA; YTestN];

これにより、学習セットにおける正常な信号と AFib 信号の分布がほぼ等しく釣り合います。

summary(YTrain)
YTrain: 7968×1 categorical

     A                4018 
     N                3950 
     <undefined>         0 

LSTM ネットワーク アーキテクチャの定義

LSTM ネットワークは、シーケンス データのタイム ステップ間の長期的な依存関係を学習できます。この例では、双方向の LSTM 層 bilstmLayer を使用し、シーケンスを順方向および逆方向の両方で確認します。

入力信号がそれぞれ 1 次元であるため、入力サイズがサイズ 1 のシーケンスになるように指定します。出力サイズが 50 の双方向 LSTM 層を指定し、シーケンスの最後の要素を出力します。このコマンドは双方向の LSTM 層に対し、入力時系列の 50 の特徴へのマッピングを指示し、その後全結合層への出力を準備します。最後に、サイズが 2 の全結合層を含めることによって 2 個のクラスを指定し、その後にソフトマックス層を配置します。

layers = [ ...
    sequenceInputLayer(1)
    bilstmLayer(50,OutputMode="last")
    fullyConnectedLayer(2)
    softmaxLayer]
layers = 
  4×1 Layer array with layers:

     1   ''   Sequence Input    Sequence input with 1 dimensions
     2   ''   BiLSTM            BiLSTM with 50 hidden units
     3   ''   Fully Connected   2 fully connected layer
     4   ''   Softmax           softmax

次に、分類器の学習オプションを指定します。オプションの中から選択するには、経験的解析が必要です。実験を実行してさまざまな学習オプションの構成を調べるには、実験マネージャー (Deep Learning Toolbox)アプリを使用できます。

  • Adam オプティマイザーを使用して学習させます。Adam は、LSTM などの RNN を使用した既定のモーメンタム項付き確率的勾配降下法 (SGDM) ソルバーよりも優れたパフォーマンスを発揮します。

  • ミニバッチ サイズ 200 を使用して 50 エポック学習させます。

  • 学習を安定させるために、勾配しきい値を 1 に設定します。

  • すべてのエポックでデータをシャッフルします。

  • 検証データを指定します。

  • 学習データには、チャネルとタイム ステップにそれぞれ対応する行と列を含むシーケンスがあるため、入力データ形式 'CTB' (チャネル、時間、バッチ) を指定します。

  • 学習の進行状況をプロットで表示し、学習中のネットワークの精度を監視します。

  • 詳細出力を無効にします。

options = trainingOptions("adam", ...
    MaxEpochs=50, ...
    MiniBatchSize=200, ...
    GradientThreshold=1, ...
    Shuffle="every-epoch", ...
    InitialLearnRate=1e-3, ...
    plots="training-progress", ...
    Metrics="accuracy", ...
    InputDataFormats="CTB", ...
    ValidationData={XValid,YValid}, ...
    OutputNetwork="best-validation", ...
    Verbose=false);

LSTM ネットワークの学習

trainnet (Deep Learning Toolbox)を使用し、指定した学習オプションと層のアーキテクチャで LSTM ネットワークに学習させます。分類には、クロスエントロピー損失を使用します。既定では、関数 trainnet は利用可能な GPU がある場合にそれを使用します。GPU での学習には、Parallel Computing Toolbox™ ライセンスとサポートされている GPU デバイスが必要です。サポートされているデバイスの詳細については、GPU 計算の要件 (Parallel Computing Toolbox)を参照してください。そうでない場合、関数 trainnet は CPU を使用します。実行環境を指定するには、ExecutionEnvironment 学習オプションを使用します。学習セットが大きいため、学習プロセスには数分かかる場合があります。既定では、検証データを指定した場合、trainnet 関数は検証損失が最も低いネットワークを返します。

net = trainnet(XTrain,YTrain,layers,"crossentropy",options);

学習の進行状況プロットの一番上のサブプロットには、学習精度、つまり各ミニバッチの分類精度が表示されます。学習が正常に進行すると、この値は通常 100% へと増加します。一番下のサブプロットには、学習損失、つまり各ミニバッチのクロスエントロピー損失が表示されます。学習が正常に進行すると、この値は通常ゼロへと減少します。

学習が収束しない場合、プロットは上方または下方の特定の方向に向かわず、値と値の間で振動する場合があります。この振動は学習精度が向上せず、学習損失が減少していないことを意味します。この状況は学習の最初から発生することもありますし、学習精度においてまずいくらか改善された後でプロットが停滞してしまうこともあります。多くの場合、学習オプションを変更するとネットワークが収束できるようになります。MiniBatchSize が減少したり、InitialLearnRate が減少したりすると、学習時間が長くなることがありますが、ネットワークの学習を改善できます。

ここでは、学習精度は非常に高いものの、検証精度はそれほど改善されていません。これは過適合を示している可能性があります。つまり、モデルが汎化されず、代わりに学習データセットに過剰に適合している可能性があります。これには、冗長な情報や無関係な情報が学習データに大量に含まれている、分類のための真に重要な要因をネットワークが学習していないなど、さまざまな原因が考えられます。

学習精度とテスト精度の可視化

学習精度を計算します。これは学習した信号に対する分類器の精度を表します。まず、学習データを分類します。

複数の観測値を使用して予測を行うには、関数minibatchpredict (Deep Learning Toolbox)を使用します。予測スコアをラベルに変換するには、関数 scores2label を使用します。関数 minibatchpredict は利用可能な GPU がある場合に自動的にそれを使用します。そうでない場合、関数は CPU を使用します。

classNames = categories(YTrain);
scores = minibatchpredict(net,XTrain,InputDataFormats="CTB");
trainPred = scores2label(scores,classNames);

分類問題において、混同行列は真の値が既知である一連のデータに対する分類パフォーマンスの可視化に使用されます。ターゲット クラスは信号のグラウンド トゥルース ラベルで、出力クラスはネットワークによって信号に割り当てられるラベルです。座標軸のラベルは AFib (A) と正常 (N) のクラス ラベルを表します。

confusionchart コマンドを使用して、テスト データ予測に対する全体の分類精度を計算します。真陽性率と偽陽性率を行要約に表示するため、"row-normalized" として RowSummary を指定します。また、陽性の予測値と偽発見率を列要約に表示するため、"column-normalized" として ColumnSummary を指定します。

LSTMAccuracy = sum(trainPred == YTrain)/numel(YTrain)*100
LSTMAccuracy = 
87.8514
figure
confusionchart(YTrain,trainPred,ColumnSummary="column-normalized",...
              RowSummary="row-normalized",title="Confusion Chart for LSTM");

テスト データを同じネットワークで分類します。

scores = minibatchpredict(net,XTest,InputDataFormats="CTB");
testPred = scores2label(scores,classNames);

テスト精度を計算して、混同行列で分類性能を可視化します。

LSTMAccuracy = sum(testPred == YTest)/numel(YTest)*100
LSTMAccuracy = 
75.0883
figure
confusionchart(YTest,testPred,ColumnSummary="column-normalized",...
              RowSummary="row-normalized",Title="Confusion Chart for LSTM");

2 番目の試行: 特徴抽出によるパフォーマンスの改善

データからの特徴抽出によって、分類器のパフォーマンスを向上させることができます。抽出する特徴を決定するために、この例では、スペクトログラムなどの時間-周波数イメージを計算するアプローチを適応させて、それらを使用して畳み込みニューラル ネットワーク (CNN) に学習させます [4]、[5]。

各タイプの信号のスペクトログラムを可視化します。

fs = 300;

figure
tiledlayout("flow")
nexttile
pspectrum(normal,fs,"spectrogram",TimeResolution=0.5)
title("Normal Signal")

nexttile
pspectrum(aFib,fs,"spectrogram",TimeResolution=0.5)
title("AFib Signal")

この例では、CNN の代わりに LSTM を使用するため、1 次元信号に作用するようにアプローチを変換することが重要です。時間-周波数 (TF) モーメントはスペクトログラムから情報を抽出します。各モーメントは 1 次元の特徴として使用し、LSTM に入力することができます。

時間領域の次の 2 つの TF モーメントを調査します。

  • 瞬時周波数 (instfreq)

  • スペクトル エントロピー (pentropy)

関数 instfreq は、パワー スペクトログラムの最初のモーメントとして信号の時間依存周波数を推定します。関数は時間枠上の短時間フーリエ変換を使用してスペクトログラムを計算します。この例では、関数は 255 個の時間枠を使用します。関数の時間出力は、時間枠の中心に対応します。

各タイプの信号の瞬時周波数を可視化します。

[instFreqA,tA] = instfreq(aFib,fs);
[instFreqN,tN] = instfreq(normal,fs);

figure
tiledlayout("flow")
nexttile
plot(tN,instFreqN)
title("Normal Signal")
xlabel("Time (s)")
ylabel("Instantaneous Frequency")

nexttile
plot(tA,instFreqA)
title("AFib Signal")
xlabel("Time (s)")
ylabel("Instantaneous Frequency")

cellfun を使用して、学習セットとテスト セットの各セルに関数 instfreq を適用します。

instfreqTrain = cellfun(@(x)instfreq(x,fs)',XTrain,UniformOutput=false);
instfreqTest = cellfun(@(x)instfreq(x,fs)',XTest,UniformOutput=false);
instfreqValid = cellfun(@(x)instfreq(x,fs)',XValid,UniformOutput=false);

スペクトル エントロピーは、信号のスペクトルがどの程度スパイキーでフラットであるかを計測します。正弦波の和などのスパイキー スペクトルの信号はスペクトル エントロピーが低くなります。ホワイト ノイズなどのフラット スペクトルの信号はスペクトル エントロピーが高くなります。spectralEntropy 関数は、パワー スペクトログラムに基づいてスペクトル エントロピーを推定します。

[s,f,tA2] = pspectrum(aFib,fs,"spectrogram");
pentropyA = spectralEntropy(s,f,Scaled=true);
[s,f,tN2] = pspectrum(normal,fs,"spectrogram");
pentropyN = spectralEntropy(s,f,Scaled=true);

各タイプの信号のスペクトル エントロピーを可視化します。

figure
tiledlayout("flow")
nexttile
plot(tN2,pentropyN)
title("Normal Signal")
ylabel("Spectral Entropy")

nexttile
plot(tA2,pentropyA)
title("AFib Signal")
xlabel("Time (s)")
ylabel("Spectral Entropy")

cellfun を使用して、学習セット、テスト セット、および検証セットの各セルに helperComputeSpecEntropy 関数を適用します。

pentropyTrain = cellfun(@(x)helperComputeSpecEntropy(x,fs)',XTrain,UniformOutput=false);
pentropyTest = cellfun(@(x)helperComputeSpecEntropy(x,fs)',XTest,UniformOutput=false);
pentropyValid = cellfun(@(x)helperComputeSpecEntropy(x,fs)',XValid,UniformOutput=false);

新しい学習セットとテスト セットの各セルが 2 次元、つまり 2 つの特徴をもつように特徴を連結します。

XTrain2 = cellfun(@(x,y)[x;y],instfreqTrain,pentropyTrain,UniformOutput=false);
XTest2 = cellfun(@(x,y)[x;y],instfreqTest,pentropyTest,UniformOutput=false);
XValid2 = cellfun(@(x,y)[x;y],instfreqValid,pentropyValid,UniformOutput=false);

新しい入力の形式を可視化します。各セルには既に、1 つの 9000 サンプル長の信号は含まれまていません。ここでは 2 つの 255 サンプル長の特徴が含まれます。

XTrain2(1:5)'
ans=1×5 cell array
    {2×255 double}    {2×255 double}    {2×255 double}    {2×255 double}    {2×255 double}

データの標準化

瞬時周波数とスペクトル エントロピーの平均値には、ほぼ 1 桁の差があります。さらに、瞬時周波数の平均値は LSTM には高すぎるため効果的に学習することができない可能性があります。平均値が大きくて値の範囲が広いデータに対してネットワークが近似される場合、入力量が大きいとネットワークの学習と収束の速度が低下します [6]。

mean(instFreqN)
ans = 
5.5551
mean(pentropyN)
ans = 
0.6324

学習セットの平均値と標準偏差を使用して、学習セット、テスト セット、および検証セットを標準化します。標準化、つまり z スコアは学習中のネットワーク性能を向上させる一般的な方法です。

XV = [XTrain2{:}];
mu = mean(XV,2);
sg = std(XV,[],2);

XTrainSD = XTrain2;
XTrainSD = cellfun(@(x)(x-mu)./sg,XTrainSD,UniformOutput=false);
XValidSD = XValid2;
XValidSD = cellfun(@(x)(x-mu)./sg,XValidSD,UniformOutput=false);
XTestSD = XTest2;
XTestSD = cellfun(@(x)(x-mu)./sg,XTestSD,UniformOutput=false);

標準化した瞬時周波数とスペクトル エントロピーの平均値を表示します。

instFreqNSD = XTrainSD{1}(1,:);
pentropyNSD = XTrainSD{1}(2,:);

mean(instFreqNSD)
ans = 
0.1545
mean(pentropyNSD)
ans = 
0.1936

LSTM ネットワーク アーキテクチャの変更

信号にはそれぞれ 2 つの次元があるため、入力シーケンス サイズに 2 を指定してネットワーク アーキテクチャを変更する必要があります。隠れユニットが 50 個の双方向 LSTM 層を指定して、シーケンスの最後の要素を出力します。サイズが 2 の全結合層を含めることによって 2 個のクラスを指定し、その後にソフトマックス層を配置します。

layers = [ ...
    sequenceInputLayer(2)
    bilstmLayer(50,OutputMode="last")
    fullyConnectedLayer(2)
    softmaxLayer]
layers = 
  4×1 Layer array with layers:

     1   ''   Sequence Input    Sequence input with 2 dimensions
     2   ''   BiLSTM            BiLSTM with 50 hidden units
     3   ''   Fully Connected   2 fully connected layer
     4   ''   Softmax           softmax

学習オプションを指定します。50 エポックではなく 150 エポックで学習させ、異なる検証データを指定することを除き、学習オプションは最初のネットワークの学習に使用したものと同じです。

options = trainingOptions("adam", ...
    MaxEpochs=150, ...
    MiniBatchSize=200, ...
    GradientThreshold=1, ...
    Shuffle="every-epoch", ...
    InitialLearnRate=1e-3, ...
    plots="training-progress", ...
    Metrics="accuracy", ...
    InputDataFormats="CTB", ...
    ValidationData={XValidSD,YValid}, ...
    OutputNetwork="best-validation", ...
    Verbose=false);

時間-周波数の特徴を使用した LSTM ネットワークの学習

trainnet を使用し、指定した学習オプションと層のアーキテクチャで LSTM ネットワークに学習させます。

net2 = trainnet(XTrainSD,YTrain,layers,"crossentropy",options);

TF モーメントが生のシーケンスより短いため、学習に必要な時間が短くなっています。

学習精度とテスト精度の可視化

更新した LSTM ネットワークを使用して学習データを分類します。混同行列として分類性能を可視化します。

scores = minibatchpredict(net2,XTrainSD,InputDataFormats="CTB");
trainPred2 = scores2label(scores,classNames);
LSTMAccuracy = sum(trainPred2 == YTrain)/numel(YTrain)*100
LSTMAccuracy = 
96.1094
figure
confusionchart(YTrain,trainPred2,ColumnSummary="column-normalized",...
              RowSummary="row-normalized",Title="Confusion Chart for LSTM");

テスト データを更新したネットワークで分類します。混同行列をプロットして、テスト精度を調べます。

scores = minibatchpredict(net2,XTestSD,InputDataFormats="CTB");
testPred2 = scores2label(scores,classNames);
LSTMAccuracy = sum(testPred2 == YTest)/numel(YTest)*100
LSTMAccuracy = 
96.6431
figure
confusionchart(YTest,testPred2,ColumnSummary="column-normalized",...
              RowSummary="row-normalized",Title="Confusion Chart for LSTM");

まとめ

この例では、分類器をビルドし、LSTM ネットワークを使用して ECG 信号の心房細動を検出する方法を示します。手順では、オーバーサンプリングを使用して、大半が健康な患者で構成される母集団の中で異常な状態を検出しようとするときに発生する分類バイアスを回避します。生の信号データを使用した LSTM ネットワークの学習では良い分類精度は得られません。各信号で 2 つの時間-周波数モーメントの特徴を使用してネットワークに学習させると、分類性能は大幅に改善されて学習時間も短くなります。

参考文献

[1] AF Classification from a Short Single Lead ECG Recording: the PhysioNet/Computing in Cardiology Challenge, 2017. https://physionet.org/challenge/2017/

[2] Clifford, Gari, Chengyu Liu, Benjamin Moody, Li-wei H. Lehman, Ikaro Silva, Qiao Li, Alistair Johnson, and Roger G. Mark. "AF Classification from a Short Single Lead ECG Recording: The PhysioNet Computing in Cardiology Challenge 2017." Computing in Cardiology (Rennes: IEEE). Vol. 44, 2017, pp. 1–4.

[3] 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, No. 23, 13 June 2000, pp. e215–e220. http://circ.ahajournals.org/content/101/23/e215.full

[4] Pons, Jordi, Thomas Lidy, and Xavier Serra. "Experimenting with Musically Motivated Convolutional Neural Networks". 14th International Workshop on Content-Based Multimedia Indexing (CBMI). June 2016.

[5] Wang, D. "Deep learning reinvents the hearing aid," IEEE Spectrum, Vol. 54, No. 3, March 2017, pp. 32–37. doi: 10.1109/MSPEC.2017.7864754.

[6] Brownlee, Jason. How to Scale Data for Long Short-Term Memory Networks in Python. 7 July 2017. https://machinelearningmastery.com/how-to-scale-data-for-long-short-term-memory-networks-in-python/.

付録 — 補助関数

helperSegmentSignals — この関数は、ECG 信号の cell 配列 (signalsIn) とラベルの cell 配列 (labelsIn) を受け取ります。過度のパディングや切り捨てを回避するために、関数は各信号をセグメント化します。この際、9000 サンプル未満の信号は無視されます。残りの信号は可能な限り多くの 9000 サンプルのセグメントに分割され、残ったサンプルはすべて無視されます。たとえば、18500 サンプルの信号は、2 つの 9000 サンプルの信号になり、残りの 500 サンプルは無視されます。関数が信号を複数の 9000 サンプル セグメントに分割する場合、関数は各セグメントに元の信号のラベルを付けます。

function [signalsOut,labelsOut] = helperSegmentSignals(signalsIn,labelsIn)
% This functions is only for use in this example. It might change or be removed in a future release.

% Specify the length of each segmented signal.
targetLength = 9000;

signalsOut = {};
labelsOut = {};

for idx = 1:numel(signalsIn)
    
    % Extract signal and label from cell array.
    x = signalsIn{idx};
    y = labelsIn(idx);
    
    % Skip signals shorter than 9000 samples.
    if length(x) < targetLength
        continue;
    end

    % Partition signal into frames of length 9000.
    x = framesig(x,targetLength);
    
    % Compute the number of targetLength-sample chunks in the signal.
    numSigs = size(x,2);

    % Repeat the label numSigs times.
    y = repmat(y,[numSigs,1]);

    % Vertically concatenate into cell arrays.
    signalsOut = [signalsOut; mat2cell(x.',ones(numSigs,1))]; %#ok<AGROW>
    labelsOut = [labelsOut; cellstr(y)]; %#ok<AGROW>
end

% Convert labels to categorical.
labelsOut = categorical(labelsOut);

end

helperComputeSpecEntropy — この関数はスペクトル エントロピーを計算します。

function [specentropy,t] = helperComputeSpecEntropy(signal,fs)
    [s,f,t] = pspectrum(signal,fs,"spectrogram");
    specentropy = spectralEntropy(s,f,Scaled=true);
end

参考

関数

トピック