이 페이지의 최신 내용은 아직 번역되지 않았습니다. 최신 내용은 영문으로 볼 수 있습니다.

딥러닝을 사용한 파형 분할

이 예제에서는 순환 딥러닝 신경망과 시간-주파수 분석을 사용하여 사람의 심전도(ECG) 신호를 분할하는 방법을 다룹니다.

소개

심장의 전기 활동은 기준 신호에서 벗어난 일련의 진폭으로 측정할 수 있습니다. 단일 정상 심박 주기의 경우 심전도 신호를 다음 심박 모폴로지로 나눌 수 있습니다 [1].

  • P파 — 심방 탈분극을 나타내는 QRS 복합파 이전의 작은 편향

  • QRS 복합파 — 심박의 최대 진폭 부분

  • T파 — 심실 재분극을 나타내는 QRS 복합파 이후의 작은 편향

심전도 파형의 이 영역을 분할하여 사람 심장의 전반적인 상태 및 이상 유무를 가늠하는 데 유용한 측정 기준을 마련할 수 있습니다 [2]. 심전도 신호의 각 영역에 주석을 지정할 때 신호 처리 및 머신러닝 방법을 사용하여 자동화할 수 있다면, 이를 직접 손으로 하는 것은 번거롭고 오랜 시간이 소요되는 작업일 것입니다.

이 예제에서는 공개적으로 사용 가능한 QT Database의 심전도 신호를 사용합니다 [3] [4]. 이 데이터는 총 105명의 환자로부터 얻은 약 15분 분량의 심전도 기록으로 구성되었습니다. 검사자가 각 기록을 얻기 위해 환자의 가슴에 서로 다른 위치에 두 전극을 설치했기 때문에 2채널 신호를 얻었습니다. 이 데이터베이스는 자동화된 전문가 시스템에서 생성한 신호 영역 레이블을 제공합니다 [2]. 이 예제는 딥러닝을 통해 샘플이 위치한 영역에 따라 개개의 샘플에 레이블을 지정하는 것을 목적으로 합니다. 신호의 전 범위에서 관심 영역에 레이블을 지정하는 이 과정은 파형 분할이라고도 합니다.

신호 영역을 분류하도록 심층 신경망을 훈련시키기 위해 LSTM(장단기 기억) 신경망을 사용할 수 있습니다. 이 예제에서는 신호 전처리 기법 및 시간-주파수 분석을 활용하여 LSTM 분할 성능을 향상하는 방법을 보여줍니다. 특히 이 예제에서는 푸리에 싱크로스퀴즈 변환을 사용하여 심전도 신호의 비정상적 동작을 나타냅니다.

데이터 다운로드 및 준비

먼저 GitHub Repository에서 데이터를 다운로드합니다. 모든 브라우저에는 다운로드 디렉터리가 있으며, 기본 설정에서 이를 확인할 수 있습니다. 이 디렉터리에서 QT_Database-master.zip 파일을 찾은 다음 쓰기 권한이 있는 폴더로 옮깁니다. 이 예제에서는 임시 디렉터리에 파일을 저장했다고 가정합니다. 그 위치는 MATLAB®의 tempdir 명령으로 확인할 수 있습니다. tempdir이 아닌 폴더에 데이터가 있을 경우 이후 지침에서 디렉터리 이름을 이에 맞게 변경하십시오. 먼저 데이터 파일의 압축을 풉니다.

unzip(fullfile(tempdir,'QT_Database-master.zip'),tempdir)

압축을 풀면 임시 디렉터리에 QT_Database-master 폴더가 생성됩니다. 이 폴더에는 텍스트 파일 README.md와 다음 파일이 들어 있습니다.

  • QTData.mat

  • Modified_physionet_data.txt

  • License.txt

QTData.mat에는 이 예제에서 사용되는 데이터가 있습니다. Modified_physionet_data.txt 파일은 데이터의 출처를 밝히고 각 원시 심전도 기록에 적용된 연산에 대해 설명합니다.

load(fullfile(tempdir,'QT_Database-master','QTData.mat'))
QTData
QTData = 
  labeledSignalSet with properties:

             Source: {105×1 cell}
         NumMembers: 105
    TimeInformation: "sampleRate"
         SampleRate: 250
             Labels: [105×2 table]
        Description: ""

 Use labelDefinitionsHierarchy to see a list of labels and sublabels.
 Use setLabelValue to add data to the set.

QTData는 원본 심전도 신호 및 이에 대응하는 파형 레이블을 단일 객체에 담고 있는 labeledSignalSet입니다. 105개의 2채널 심전도 신호는 Source 속성에 담겨 있습니다. Labels 속성에는 파형 레이블의 테이블이 있습니다. 각 채널은 자동화된 전문가 시스템에 의해 개별적으로 레이블이 지정되었으며, 총 210개 심전도 신호에 대해 개별적으로 처리됩니다. 파형 레이블은 신호의 각 샘플을 다음 클래스 중 하나에 속한 것으로 지정합니다. P, QRS, T 및 N/A. 값이 N/A라면 P파, QRS 복합파 또는 T파 바깥의 샘플에 해당합니다.

head 명령을 사용하여 QTDataLabels 속성에 있는 테이블의 처음 몇 개 행을 검사합니다.

head(QTData.Labels)
ans=8×2 table
                 WaveformLabels_Chan1    WaveformLabels_Chan2
                 ____________________    ____________________

    Member{1}      [225000×2 table]        [225000×2 table]  
    Member{2}      [225000×2 table]        [225000×2 table]  
    Member{3}      [225000×2 table]        [225000×2 table]  
    Member{4}      [225000×2 table]        [225000×2 table]  
    Member{5}      [225000×2 table]        [225000×2 table]  
    Member{6}      [225000×2 table]        [225000×2 table]  
    Member{7}      [225000×2 table]        [225000×2 table]  
    Member{8}      [225000×2 table]        [225000×2 table]  

각 테이블 행은 환자에 해당하고 각 테이블 열은 채널에 해당합니다. getSignal 함수를 사용하여 첫 번째 환자에 대한 신호 데이터를 추출합니다. getLabelValues 함수를 사용하여 첫 번째 채널에 대한 레이블을 추출합니다. displayWaveformLabels 헬퍼 함수를 사용하여 첫 1,000개 샘플에 대한 레이블을 시각화합니다.

patientID = 1;
signalVals = getSignal(QTData,patientID);
labelVals = getLabelValues(QTData,patientID,'WaveformLabels_Chan1');

displayWaveformLabels(signalVals(1,1:1000),labelVals.Value(1:1000)) 

신호가 빠르게 바뀌는 150번째 샘플 주위의 레이블 값을 살펴봅니다. 이 영역은 QRS 복합파가 끝나고 N/A 샘플로 전환되는 구간을 나타냅니다.

val = labelVals.Value(145:155)
val = 11×1 categorical array
     QRS 
     QRS 
     QRS 
     QRS 
     QRS 
     QRS 
     QRS 
     QRS 
     n/a 
     n/a 
     n/a 

일반적인 머신러닝 분류 절차는 다음과 같습니다.

  1. 데이터베이스를 훈련 데이터셋 및 테스트 데이터셋으로 나눕니다.

  2. 훈련 데이터셋을 사용하여 신경망을 훈련시킵니다.

  3. 훈련된 신경망을 사용하여 테스트 데이터셋에 대해 예측을 수행합니다.

데이터의 70%로 신경망을 훈련시키고 나머지 30%로 테스트합니다. 편향을 방지하기 위해 훈련 세트 및 테스트 세트의 전 범위에서 같은 환자의 어떤 데이터도 공유되지 않습니다.

재현 가능한 결과를 얻기 위해 난수 생성기를 재설정합니다. labeledSignalSetNumMembers 속성을 사용하여 환자 수를 추출합니다. dividerand 함수를 사용하여 환자를 섞고, subset 함수를 사용하여 데이터를 훈련 및 테스트 labeledSignalSet로 나눕니다.

rng default
[trainInd,~,testInd] = dividerand(QTData.NumMembers,0.7,0,0.3);

trainQT = subset(QTData,trainInd);
testQT = subset(QTData,testInd);

신경망에 시계열 데이터를 입력하려면 헬퍼 함수 resizeSignals를 사용하여 데이터를 행렬의 셀 배열로 정리하십시오. 또한 이 헬퍼 함수는 메모리를 과도하게 사용하지 않도록 데이터를 5000개 샘플 크기의 청크로 나눕니다.

[signalsTrain,labelsTrain] = resizeSignals(trainQT);
[signalsTest,labelsTest] = resizeSignals(testQT);

LSTM 신경망에 직접 원시 심전도 신호 입력하기

우선, 훈련 데이터셋의 원시 심전도 신호를 사용하여 신경망을 훈련시킵니다.

훈련에 앞서 신경망 아키텍처를 정의합니다. 1차원 시계열을 받을 수 있도록 sequenceInputLayer의 크기를 1로 지정하십시오. 신호의 각 샘플에 대해 분류하기 위해 'sequence' 출력 모드를 사용해 LSTM 계층을 지정합니다. 최적의 성능을 위해 200개의 숨겨진 노드를 사용합니다. 각각의 파형 클래스에 하나씩 할당할 수 있도록 fullyConnectedLayer의 출력 크기를 4로 지정합니다. softmaxLayerclassificationLayer를 추가하여 추정된 레이블을 출력합니다.

layers = [ ...
    sequenceInputLayer(1)
    lstmLayer(200,'OutputMode','sequence')
    fullyConnectedLayer(4)
    softmaxLayer
    classificationLayer];

우수한 신경망 성능을 보장할 수 있도록 훈련 과정에 대한 옵션을 선택합니다. 각 파라미터에 대한 설명은 trainingOptions 문서를 참조하십시오.

options = trainingOptions('adam', ...
    'MaxEpochs',10, ...
    'MiniBatchSize',50, ...
    'InitialLearnRate',0.01, ...
    'LearnRateDropPeriod',3, ...
    'LearnRateSchedule','piecewise', ...
    'GradientThreshold',1, ...
    'Plots','training-progress',...
    'Verbose',0);

신경망 훈련시키기

trainNetwork 명령을 사용하여 LSTM 신경망을 훈련시킵니다. 데이터셋이 크기 때문에 이 과정은 시간이 꽤 걸릴 수 있습니다. 시스템에 GPU와 Parallel Computing Toolbox™가 있는 경우 MATLAB은 자동으로 훈련에 GPU를 사용합니다. GPU가 없으면 CPU를 사용합니다.

아래 Figure의 훈련 정확도 및 손실 서브플롯은 모든 반복 과정에 걸쳐 훈련 진행 상황을 추적합니다. 신경망은 원시 신호 데이터를 사용하여 약 70%의 샘플에 대해 P파, QRS 복합파, T파 또는 N/A인지 여부를 올바르게 분류합니다.

net = trainNetwork(signalsTrain,labelsTrain,layers,options);

테스트 데이터 분류하기

훈련된 LSTM 신경망 및 classify 명령을 사용하여 테스트 데이터를 분류합니다. 훈련 옵션과 일치하도록 미니 배치 크기를 50으로 지정합니다.

predTest = classify(net,signalsTest,'MiniBatchSize',50);

정오분류표는 분류 성능을 시각화하는 직관적이며 유용한 수단입니다. confusionchart 명령을 사용하여 테스트 데이터 예측값에 대한 전체적인 분류 정확도를 계산합니다. 각 입력값에 대해, categorical형 레이블로 구성된 셀형 배열을 행 벡터로 변환합니다. 각 클래스에 대해 샘플의 백분율로 결과를 표시하기 위해 열 정규화된 디스플레이를 지정합니다.

confusionchart([predTest{:}],[labelsTest{:}],'Normalization','column-normalized');

원시 심전도 신호를 신경망에 대한 입값력으로 사용한 경우 P파 샘플의 약 35%, QRS 복합파 샘플의 60%, T파 샘플의 60%만 올바르게 분류했습니다. 성능을 높이려면 입력 전에 심전도 신호 특성에 관한 지식(예를 들어 환자의 호흡 동작에 의한 기준선 변동)을 딥러닝 신경망에 적용합니다.

기준선 변동 및 고주파 잡음을 제거하기 위해 필터링 방법 적용하기

세 심박 모폴로지는 서로 다른 주파수 대역에 있습니다. QRS 복합파의 스펙트럼은 대개 중앙 주파수가 약 10~25Hz이며, 그 성분은 40Hz 아래에 있습니다. P파와 T파는 훨씬 더 낮은 주파수에서 발생합니다. P파 성분이 20Hz 미만, T파 성분이 10Hz 미만입니다 [5].

기준선 변동은 환자의 호흡 동작으로 인해 발생하는 저주파(< 0.5Hz) 진동입니다. 이 진동은 심박 모폴로지와 상관없으며 유의미한 정보를 제공하지 않습니다 [6].

기준선 변동 및 모든 고주파 잡음을 제거하려면 [0.5, 40]Hz의 통과대역 주파수 범위로 대역통과 필터를 설계하십시오. 이 성분을 제거하면 신경망이 불필요한 특징을 학습하지 않으므로 LSTM 훈련이 향상됩니다.

Fs = QTData.SampleRate;
[~,dBP] = bandpass(signalsTrain{1},[0.5 40],Fs);

각 신호에 대역통과 필터를 적용하기 위해 익명 함수 BPfun을 지정합니다.

BPfun = @(X) filter(dBP,X);

signalsFilteredTrain = cellfun(BPfun,signalsTrain,'UniformOutput',false);
signalsFilteredTest  = cellfun(BPfun,signalsTest,'UniformOutput',false);

일반적인 경우의 원시 및 필터링된 신호를 플로팅합니다.

subplot(2,1,1)
plot(signalsTrain{210}(2001:3000))
title('Raw')
grid

subplot(2,1,2)
plot(signalsFilteredTrain{210}(2001:3000))
title('Filtered')
grid

필터링된 심전도 신호을 사용하여 신경망 훈련시키기

동일한 신경망 아키텍처를 사용하여 필터링된 심전도 신호에 대해 LSTM 신경망을 훈련시킵니다.

filteredNet = trainNetwork(signalsFilteredTrain,labelsTrain,layers,options);

신호를 전처리하면 훈련 정확도가 향상되어 80%를 넘어섭니다.

필터링된 심전도 신호 분류하기

업데이트된 LSTM 신경망을 사용하여 전처리된 테스트 데이터를 분류합니다.

predFilteredTest = classify(filteredNet,signalsFilteredTest,'MiniBatchSize',50);

분류 성능을 정오분류표로 시각화합니다.

figure
confusionchart([predFilteredTest{:}],[labelsTest{:}],'Normalization','column-normalized');

단순 전처리를 수행하면 P파 분류가 약 10%, QRS 복합파 분류가 10%, T파 분류가 20% 향상됩니다.

심전도 신호의 시간-주파수 표현

시계열 데이터를 성공적으로 분류하기 위해 흔히 사용하는 접근법 중 하나는 시간-주파수 특징을 추출하여 이를 원본 데이터 대신 신경망에 제공하는 것입니다. 그러면 신경망은 시간 및 주파수에 걸쳐 패턴을 동시에 학습합니다 [7].

푸리에 싱크로스퀴즈 변환(FSST)은 신호 샘플별로 주파수 스펙트럼을 계산합니다. fsst 함수를 사용하여 훈련 신호 중 하나에 대한 변환을 살펴봅니다. 적합한 주파수 분해능을 제공하기 위해 길이가 128인 카이저 윈도우를 지정합니다.

fsst(signalsTrain{1},Fs,kaiser(128),'yaxis')

훈련 데이터셋에 있는 각 신호의 FSST를 계산합니다. 변환 출력의 관련 성분을 인덱싱하여 관심 주파수 범위 [0.5, 40]Hz에 대한 데이터를 추출합니다. FSST의 실수부와 허수부를 서로 다른 특징으로 간주하고 두 성분을 모두 신경망에 입력합니다.

신경망 훈련에 앞서 평균을 빼고 표준편차로 나누어 훈련 특징을 표준화합니다.

signalsFsstTrain = cell(size(signalsTrain));
meanTrain = cell(1,length(signalsTrain));
stdTrain = cell(1,length(signalsTrain));
for idx = 1:length(signalsTrain)
   [s,f,t] = fsst(signalsTrain{idx},Fs,kaiser(128));
   
   f_indices = (f > 0.5) & (f < 40);
   signalsFsstTrain{idx}= [real(s(f_indices,:)); imag(s(f_indices,:))];
   
   meanTrain{idx} = mean(signalsFsstTrain{idx},2);
   stdTrain{idx} = std(signalsFsstTrain{idx},[],2);
end

standardizeFun = @(x) (x - mean(cell2mat(meanTrain),2))./mean(cell2mat(stdTrain),2);
signalsFsstTrain = cellfun(standardizeFun,signalsFsstTrain,'UniformOutput',false);

테스트 데이터에 대해 이 절차를 반복합니다. 훈련 데이터의 평균 및 표준편차를 사용하여 테스트 특징을 표준화합니다.

signalsFsstTest = cell(size(signalsTest));
for idx = 1:length(signalsTest)
   [s,f,t] = fsst(signalsTest{idx},Fs,kaiser(128));
   
   f_indices =  (f > 0.5) & (f < 40);
   signalsFsstTest{idx}= [real(s(f_indices,:)); imag(s(f_indices,:))]; 
end

signalsFsstTest = cellfun(standardizeFun,signalsFsstTest,'UniformOutput',false);

신경망 아키텍처 조정하기

신경망이 하나의 값이 아니라 샘플별 주파수 스펙트럼을 받도록 LSTM 아키텍처를 수정합니다. 주파수 개수를 보려면 FSST의 크기를 살펴봅니다.

size(signalsFsstTrain{1})
ans = 1×2

          40        5000

sequenceInputLayer에는 입력 특징을 40개로 지정합니다. 나머지 신경망 파라미터는 변경하지 않은 채로 둡니다.

layers = [ ...
    sequenceInputLayer(40)
    lstmLayer(200,'OutputMode','sequence')
    fullyConnectedLayer(4)
    softmaxLayer
    classificationLayer];

심전도 신호의 FSST를 사용하여 신경망 훈련시키기

변환된 데이터셋을 사용하여 업데이트된 LSTM 신경망을 훈련시킵니다.

fsstNet = trainNetwork(signalsFsstTrain,labelsTrain,layers,options);

시간-주파수 특징을 사용하면 훈련 정확도가 향상되어 이제는 90%가 넘습니다.

FSST를 사용하여 테스트 데이터 분류하기

업데이트된 LSTM 신경망 및 추출된 FSST 특징을 사용하여 테스트 데이터를 분류합니다.

predFsstTest = classify(fsstNet,signalsFsstTest,'MiniBatchSize',50);

분류 성능을 정오분류표로 시각화합니다.

confusionchart([predFsstTest{:}],[labelsTest{:}],'Normalization','column-normalized');

시간-주파수 표현을 사용하면 P파 분류가 50%(약 35%에서 85%로), QRS 복합파 분류가 35%, T파 분류가 25% 향상됩니다.

displayWaveformLabels를 사용하여 단일 심전도 신호에 대한 실측 레이블과 신경망 예측을 비교합니다.

subplot(2,1,1)
displayWaveformLabels(signalsTest{50}(1400:1900),labelsTest{50}(1400:1900))
title('Ground Truth')

subplot(2,1,2)
displayWaveformLabels(signalsTest{50}(1400:1900),predFsstTest{50}(1400:1900))
title('Predicted')

결론

이 예제에서는 신호 전처리 및 시간-주파수 분석으로 LSTM 파형 분할 성능을 높일 수 있는 방법을 다뤘습니다. 대역통과 필터 및 푸리에 기반 싱크로스퀴즈 때문에 모든 출력 클래스에 걸쳐 평균적으로 60%에서 85% 이상 성능이 향상되었습니다.

참고 문헌

[1] McSharry, Patrick E., et al. "A dynamical model for generating synthetic electrocardiogram signals." IEEE® Transactions on Biomedical Engineering. Vol. 50, No. 3, 2003, pp. 289–294.

[2] Laguna, Pablo, Raimon Jané, Pere Caminal. "Automatic detection of wave boundaries in multilead ECG signals: Validation with the CSE database." Computers and Biomedical Research. Vol. 27, No. 1, 1994, pp. 45–60.

[3] Goldberger, Ary L., Luis A. N. Amaral, Leon Glass, Jeffery M. Hausdorff, Plamen Ch. Ivanov, Roger G. Mark, Joseph E. Mietus, George B. Moody, Chung-Kang Peng, H. Eugene Stanley. "PhysioBank, PhysioToolkit, and PhysioNet: Components of a New Research Resource for Complex Physiologic Signals." Circulation. Vol. 101, No. 23, 2000, pp. e215–e220. [Circulation Electronic Pages, http://circ.ahajournals.org/content/101/23/e215.full].

[4] Laguna, Pablo, Roger G. Mark, Ary L. Goldberger, George B. Moody. "A Database for Evaluation of Algorithms for Measurement of QT and Other Waveform Intervals in the ECG." Computers in Cardiology. Vol.24, 1997, pp. 673–676.

[5] Sörnmo, Leif, Pablo Laguna. "Electrocardiogram (ECG) signal processing." Wiley Encyclopedia of Biomedical Engineering, 2006.

[6] Kohler, B-U., Carsten Hennig, Reinhold Orglmeister. "The principles of software QRS detection." IEEE Engineering in Medicine and Biology Magazine. Vol. 21, No. 1, 2002, pp. 42–57.

[7] Salamon, Justin, Juan Pablo Bello. "Deep convolutional neural networks and data augmentation for environmental sound classification." IEEE Signal Processing Letters. Vol. 24, No. 3, 2017, pp. 279–283.

참고 항목

함수

관련 항목