Main Content

웨이블릿 분석 및 딥러닝을 사용하여 시계열 분류하기

이 예제에서는 CWT(연속 웨이블릿 변환) 및 심층 CNN(컨벌루션 신경망)을 사용하여 사람의 ECG(심전도) 신호를 분류하는 방법을 보여줍니다.

처음부터 심층 CNN을 훈련하려면 계산량이 많이 필요하고 다량의 훈련 데이터가 필요합니다. 충분한 양의 훈련 데이터를 사용할 수 없고 실제에 가까운 새로운 훈련 표본을 합성하기 어려운 다양한 응용 사례가 존재합니다. 그러한 경우 개념적으로 유사한 작업을 위해 대규모 데이터 세트에 대해 훈련된 기존 신경망을 활용하는 것이 바람직합니다. 이같이 기존 신경망을 활용하는 것을 전이 학습이라고 합니다. 이 예제에서는 영상 인식을 위해 사전 훈련된 두 개의 심층 CNN인 GoogLeNet과 SqueezeNet을 조정하여 시간-주파수 표현을 기반으로 ECG 파형을 분류합니다.

원래 GoogLeNet 및 SqueezeNet은 영상을 1000개의 범주로 분류하도록 설계된 심층 CNN입니다. 여기서는 CNN의 신경망 아키텍처를 재사용하여 시계열 데이터의 CWT에서 얻은 영상을 기반으로 ECG 신호를 분류합니다. 이 예제에 사용된 데이터는 PhysioNet에서 공개적으로 사용 가능합니다.

데이터 설명

이 예제에서는 심장 부정맥(ARR: arrhythmia)이 있는 사람, 울혈성 심부전(CHF: congestive heart failure)이 있는 사람, 정상 동율동(NSR: normal sinus rhythms)을 가진 사람의 세 그룹에서 얻은 ECG 데이터를 사용합니다. 그리고 3개의 PhysioNet 데이터베이스에서 총 162개의 ECG 기록을 사용합니다. 그 데이터베이스는 MIT-BIH Arrhythmia Database [3][7],MIT-BIH Normal Sinus Rhythm Database [3], The BIDMC Congestive Heart Failure Database [1][3]입니다. 더 구체적으로는 부정맥이 있는 사람의 기록 96개, 울혈성 심부전이 있는 사람의 기록 30개, 정상 동율동을 가진 사람의 기록 36개를 사용합니다. 목표는 ARR, CHF, NSR을 구분하도록 분류기를 훈련시키는 것입니다.

데이터 다운로드하기

첫 번째 단계는 GitHub 리포지토리에서 데이터를 다운로드하는 것입니다. 웹 사이트에서 데이터를 다운로드하려면 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는 두 개의 필드, 즉 DataLabels를 갖는 구조체형 배열입니다. Data 필드는 각 행이 128Hz로 샘플링된 ECG 기록인 162×65536 행렬입니다. Labels는 진단 레이블로 구성된 162×1 셀형 배열로, 레이블은 Data의 각 행에 대응합니다. 세 진단 범주는 'ARR', 'CHF', 'NSR'입니다.

각 범주의 전처리된 데이터를 저장하기 위해 먼저 tempdir 내부에 ECG 데이터 디렉터리 dataDir을 생성합니다. 그런 다음 'data'에 각 ECG 범주의 이름을 딴 3개의 서브디렉터리를 만듭니다. 헬퍼 함수 helperCreateECGDirectories가 이 작업을 수행합니다. helperCreateECGDirectoriesECGData, ECG 데이터 디렉터리의 이름, 부모 디렉터리의 이름을 입력 인수로 받습니다. tempdir을 쓰기 권한이 있는 다른 디렉터리로 바꿀 수 있습니다. 이 예제의 마지막 부분에 있는 지원 함수 섹션에서 이 헬퍼 함수의 소스 코드를 확인할 수 있습니다.

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

각 ECG 범주의 대표를 플로팅합니다. 헬퍼 함수 helperPlotReps가 이 작업을 수행합니다. helperPlotRepsECGData를 입력값으로 받습니다. 이 예제의 마지막 부분에 있는 지원 함수 섹션에서 이 헬퍼 함수의 소스 코드를 확인할 수 있습니다.

helperPlotReps(ECGData)

시간-주파수 표현 만들기

폴더를 만든 후 ECG 신호의 시간-주파수 표현을 만듭니다. 이러한 표현을 스케일로그램이라고 합니다. 스케일로그램은 신호의 CWT 계수의 절댓값입니다.

스케일로그램을 만들려면 CWT 필터 뱅크를 미리 계산합니다. CWT 필터 뱅크를 미리 계산하는 것은 동일한 파라미터를 사용하여 많은 신호의 CWT를 구할 때 권장되는 방법입니다.

스케일로그램을 만들기 전에 그중 하나를 검토합니다. 1000개 샘플을 갖는 신호에 대해 cwtfilterbank (Wavelet Toolbox)를 사용하여 CWT 필터 뱅크를 만듭니다. 그 필터 뱅크를 사용하여 신호의 처음 1000개 샘플의 CWT를 얻고 계수에서 스케일로그램을 구합니다.

Fs = 128;
fb = cwtfilterbank('SignalLength',1000,...
    'SamplingFrequency',Fs,...
    'VoicesPerOctave',12);
sig = ECGData.Data(1,1:1000);
[cfs,frq] = wt(fb,sig);
t = (0:999)/Fs;figure;pcolor(t,frq,abs(cfs))
set(gca,'yscale','log');shading interp;axis tight;
title('Scalogram');xlabel('Time (s)');ylabel('Frequency (Hz)')

헬퍼 함수 helperCreateRGBfromTF를 사용하여 스케일로그램을 RGB 영상으로 만들고 dataDir의 적절한 서브디렉터리에 씁니다. 이 헬퍼 함수의 소스 코드는 이 예제의 마지막 부분에 있는 지원 함수 섹션에 있습니다. GoogLeNet 아키텍처와 호환되도록 각 RGB 영상은 224×224×3 크기의 배열입니다.

helperCreateRGBfromTF(ECGData,parentDir,dataDir)

훈련 데이터와 검증 데이터로 나누기

스케일로그램 영상을 영상 데이터저장소로 불러옵니다. imageDatastore 함수는 폴더 이름을 기준으로 영상에 자동으로 레이블을 지정하고 데이터를 ImageDatastore 객체로 저장합니다. 영상 데이터저장소를 사용하면 메모리에 담을 수 없는 데이터를 포함하여 다량의 영상 데이터를 저장할 수 있고 CNN 훈련 중에 영상 배치를 효율적으로 읽어 들일 수 있습니다.

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

무작위로 영상을 두 그룹으로 나눕니다. 하나는 훈련용이고 다른 하나는 검증용입니다. 영상의 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

GoogLeNet

불러오기

사전 훈련된 GoogLeNet 신경망을 불러옵니다. Deep Learning Toolbox™ Model for GoogLeNet Network 지원 패키지가 설치되어 있지 않은 경우, 필요한 지원 패키지로 연결되는 애드온 탐색기 링크가 제공됩니다. 지원 패키지를 설치하려면 링크를 클릭한 다음 설치를 클릭하십시오.

net = googlenet;

신경망에서 계층 그래프를 추출하고 표시합니다.

lgraph = layerGraph(net);
numberOfLayers = numel(lgraph.Layers);
figure('Units','normalized','Position',[0.1 0.1 0.8 0.8]);
plot(lgraph)
title(['GoogLeNet Layer Graph: ',num2str(numberOfLayers),' Layers']);

신경망의 Layers 속성의 첫 번째 요소를 검사합니다. GoogLeNet에 224×224×3 크기의 RGB 영상이 필요함을 확인합니다.

net.Layers(1)
ans = 
  ImageInputLayer with properties:

                Name: 'data'
           InputSize: [224 224 3]

   Hyperparameters
    DataAugmentation: 'none'
       Normalization: 'zerocenter'
                Mean: [224×224×3 single]

GoogLeNet 신경망 파라미터 수정하기

신경망 아키텍처의 각 계층은 필터로 간주될 수 있습니다. 앞쪽 계층은 블롭, 가장자리, 색 등 영상의 보다 일반적인 특징을 식별합니다. 뒤에 오는 계층은 범주를 구별하기 위해 보다 구체적인 특징에 중점을 둡니다. GoogLeNet은 영상을 1,000가지 사물 범주로 분류하도록 사전 훈련되었습니다. ECG 분류 문제에 맞게 GoogLeNet을 다시 훈련시켜야 합니다.

과적합을 방지하기 위해 드롭아웃 계층이 사용됩니다. 드롭아웃 계층은 주어진 확률에 따라 입력 요소를 무작위로 0으로 설정합니다. 자세한 내용은 dropoutLayer 항목을 참조하십시오. 디폴트 확률은 0.5입니다. 신경망의 마지막 드롭아웃 계층 'pool5-drop_7x7_s1'을 확률 0.6의 드롭아웃 계층으로 바꿉니다.

newDropoutLayer = dropoutLayer(0.6,'Name','new_Dropout');
lgraph = replaceLayer(lgraph,'pool5-drop_7x7_s1',newDropoutLayer);

신경망의 컨벌루션 계층은 마지막 학습 가능한 계층과 마지막 분류 계층이 입력 영상을 분류하는 데 사용하는 영상 특징을 추출합니다. GoogLeNet의 두 계층 'loss3-classifier''output'은 신경망이 추출하는 특징을 클래스 확률, 손실 값 및 예측된 레이블로 조합하는 방법에 대한 정보를 포함합니다. GoogLeNet이 RGB 영상을 분류하도록 다시 훈련시키려면 이 두 계층을 데이터에 맞게 조정된 새로운 계층으로 바꾸십시오.

완전 연결 계층 'loss3-classifier'를 필터의 개수가 클래스 개수와 같은 새로운 완전 연결 계층으로 바꿉니다. 전이된 계층보다 새로운 계층에서 더 빠르게 학습할 수 있도록 하려면 완전 연결 계층의 학습률 인자 값을 높이십시오.

numClasses = numel(categories(imgsTrain.Labels));
newConnectedLayer = fullyConnectedLayer(numClasses,'Name','new_fc',...
    'WeightLearnRateFactor',5,'BiasLearnRateFactor',5);
lgraph = replaceLayer(lgraph,'loss3-classifier',newConnectedLayer);

분류 계층은 신경망의 출력 클래스를 지정합니다. 분류 계층을 클래스 레이블이 없는 새로운 계층으로 바꿉니다. trainNetwork는 훈련을 진행할 때 계층의 출력 클래스를 자동으로 설정합니다.

newClassLayer = classificationLayer('Name','new_classoutput');
lgraph = replaceLayer(lgraph,'output',newClassLayer);

훈련 옵션을 설정하고 GoogLeNet 훈련시키기

신경망 훈련에는 손실 함수를 최소화하는 반복 수행이 필요합니다. 손실 함수를 최소화하기 위해 경사하강법 알고리즘이 사용됩니다. 각 반복에서 손실 함수의 기울기가 계산되고 경사하강법 알고리즘의 가중치가 업데이트됩니다.

다양한 옵션을 설정하여 훈련을 조정할 수 있습니다. InitialLearnRate는 손실 함수에 대한 음의 기울기 방향으로 초기 스텝 크기를 지정합니다. MiniBatchSize는 각 반복에 사용할 훈련 세트의 서브셋 크기를 지정합니다. Epoch 1회는 훈련 알고리즘이 전체 훈련 세트를 완전히 한 번 통과하는 것을 의미합니다. MaxEpochs는 훈련에 사용할 최대 Epoch 횟수를 지정합니다. 올바른 Epoch 횟수를 선택하는 일은 간단하지 않습니다. Epoch의 횟수를 줄이면 모델을 과소적합하는 효과가 있고 Epoch의 횟수를 늘리면 과적합이 발생합니다.

trainingOptions 함수를 사용하여 훈련 옵션을 지정할 수 있습니다. MiniBatchSize를 10으로, MaxEpochs를 10으로, InitialLearnRate를 0.0001로 설정합니다. Plotstraining-progress로 설정하여 훈련 진행 상황을 시각화합니다. 모멘텀을 사용한 확률적 경사하강법 최적화 함수를 사용합니다. 기본적으로, 사용 가능한 GPU가 있으면 GPU에서 훈련시킵니다. GPU를 사용하려면 Parallel Computing Toolbox™가 필요합니다. 지원되는 GPU를 보려면 릴리스별 GPU 지원 (Parallel Computing Toolbox) 항목을 참조하십시오. 재현이 가능하도록 ExecutionEnvironmentcpu로 설정합니다. 그러면 trainNetwork가 CPU를 사용합니다. 난수 시드값을 디폴트 값으로 설정합니다. GPU를 사용할 수 있으면 실행 시간이 더 빨라집니다.

options = trainingOptions('sgdm',...
    'MiniBatchSize',15,...
    'MaxEpochs',20,...
    'InitialLearnRate',1e-4,...
    'ValidationData',imgsValidation,...
    'ValidationFrequency',10,...
    'Verbose',1,...
    'ExecutionEnvironment','cpu',...
    'Plots','training-progress');
rng default

신경망을 훈련시킵니다. 훈련 과정은 일반적으로 데스크탑 CPU에서 1~5분 정도 걸립니다. 실행 중에 훈련 정보가 명령 창에 표시됩니다. 결과에는 검증 데이터에 대한 Epoch 횟수, 반복 횟수, 경과 시간, 미니 배치 정확도, 검증 정확도, 손실 함수 값이 포함됩니다.

trainedGN = trainNetwork(imgsTrain,lgraph,options);

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:03 |        6.67% |       18.75% |       4.9207 |       2.4141 |      1.0000e-04 |
|       2 |          10 |       00:00:23 |       66.67% |       62.50% |       0.9589 |       1.3191 |      1.0000e-04 |
|       3 |          20 |       00:00:43 |       46.67% |       75.00% |       1.2973 |       0.5928 |      1.0000e-04 |
|       4 |          30 |       00:01:04 |       60.00% |       78.13% |       0.7219 |       0.4576 |      1.0000e-04 |
|       5 |          40 |       00:01:25 |       73.33% |       84.38% |       0.4750 |       0.3367 |      1.0000e-04 |
|       7 |          50 |       00:01:46 |       93.33% |       84.38% |       0.2714 |       0.2892 |      1.0000e-04 |
|       8 |          60 |       00:02:07 |       80.00% |       87.50% |       0.3617 |       0.2433 |      1.0000e-04 |
|       9 |          70 |       00:02:29 |       86.67% |       87.50% |       0.3246 |       0.2526 |      1.0000e-04 |
|      10 |          80 |       00:02:50 |      100.00% |       96.88% |       0.0701 |       0.1876 |      1.0000e-04 |
|      12 |          90 |       00:03:11 |       86.67% |      100.00% |       0.2836 |       0.1681 |      1.0000e-04 |
|      13 |         100 |       00:03:32 |       86.67% |       96.88% |       0.4160 |       0.1607 |      1.0000e-04 |
|      14 |         110 |       00:03:53 |       86.67% |       96.88% |       0.3237 |       0.1565 |      1.0000e-04 |
|      15 |         120 |       00:04:14 |       93.33% |       96.88% |       0.1646 |       0.1476 |      1.0000e-04 |
|      17 |         130 |       00:04:35 |      100.00% |       96.88% |       0.0551 |       0.1330 |      1.0000e-04 |
|      18 |         140 |       00:04:57 |       93.33% |       96.88% |       0.0927 |       0.1347 |      1.0000e-04 |
|      19 |         150 |       00:05:18 |       93.33% |       93.75% |       0.1666 |       0.1325 |      1.0000e-04 |
|      20 |         160 |       00:05:39 |       93.33% |       96.88% |       0.0873 |       0.1164 |      1.0000e-04 |
|======================================================================================================================|

훈련된 신경망의 마지막 계층을 검사합니다. 분류 출력 계층에 3개의 클래스가 포함되어 있는지 확인합니다.

trainedGN.Layers(end)
ans = 
  ClassificationOutputLayer with properties:

            Name: 'new_classoutput'
         Classes: [ARR    CHF    NSR]
      OutputSize: 3

   Hyperparameters
    LossFunction: 'crossentropyex'

GoogLeNet 정확도 평가하기

검증 데이터를 사용하여 신경망을 평가합니다.

[YPred,probs] = classify(trainedGN,imgsValidation);
accuracy = mean(YPred==imgsValidation.Labels);
disp(['GoogLeNet Accuracy: ',num2str(100*accuracy),'%'])
GoogLeNet Accuracy: 96.875%

정확도는 훈련 시각화 Figure에 보고된 검증 정확도와 동일합니다. 스케일로그램은 훈련 모음과 검증 모음으로 분할되었습니다. 이 두 모음은 GoogLeNet 훈련에 모두 사용되었습니다. 훈련 결과를 평가하는 이상적인 방법은 신경망이 인식한 적이 없는 데이터를 분류하도록 하는 것입니다. 훈련 데이터, 검증 데이터, 테스트 데이터로 나눌 만큼 데이터 양이 충분하지 않으므로 여기서는 계산된 검증 정확도를 신경망 정확도로 간주합니다.

GoogLeNet 활성화 결과 탐색하기

CNN의 각 계층은 입력 영상에 대한 응답, 즉 활성화 결과를 생성합니다. 그러나 CNN 내에서 영상 특징 추출에 적합한 계층은 몇 개 되지 않습니다. 신경망 시작 부분에 있는 계층은 가장자리 및 블롭과 같은 기본 영상 특징을 캡처합니다. 이를 확인하기 위해 첫 번째 컨벌루션 계층의 신경망 필터 가중치를 시각화합니다. 첫 번째 계층에는 64개의 개별 가중치 세트가 있습니다.

wghts = trainedGN.Layers(2).Weights;
wghts = rescale(wghts);
wghts = imresize(wghts,5);
figure
montage(wghts)
title('First Convolutional Layer Weights')

활성화 결과를 살펴보고 원본 영상과 활성화 영역을 비교해 보면 GoogLeNet이 어떤 특징을 학습했는지 알아낼 수 있습니다. 자세한 내용은 컨벌루션 신경망의 활성화 시각화하기컨벌루션 신경망의 특징 시각화하기 항목을 참조하십시오.

ARR 클래스의 영상에서 컨벌루션 계층의 어느 영역이 활성화되었는지 확인합니다. 원본 영상의 대응하는 영역과 비교합니다. 컨벌루션 신경망의 각 계층은 여러 개의 2차원 배열로 이루어져 있습니다. 이러한 2차원 배열을 채널이라고 합니다. 영상을 신경망에 통과시킨 후 첫 번째 컨벌루션 계층 'conv1-7x7_s2'의 출력 활성화 결과를 살펴봅니다.

convLayer = 'conv1-7x7_s2';

imgClass = 'ARR';
imgName = 'ARR_10.jpg';
imarr = imread(fullfile(parentDir,dataDir,imgClass,imgName));

trainingFeaturesARR = activations(trainedGN,imarr,convLayer);
sz = size(trainingFeaturesARR);
trainingFeaturesARR = reshape(trainingFeaturesARR,[sz(1) sz(2) 1 sz(3)]);
figure
montage(rescale(trainingFeaturesARR),'Size',[8 8])
title([imgClass,' Activations'])

이 영상에 대한 가장 강한 채널을 찾습니다. 가장 강한 채널을 원본 영상과 비교합니다.

imgSize = size(imarr);
imgSize = imgSize(1:2);
[~,maxValueIndex] = max(max(max(trainingFeaturesARR)));
arrMax = trainingFeaturesARR(:,:,:,maxValueIndex);
arrMax = rescale(arrMax);
arrMax = imresize(arrMax,imgSize);
figure;
imshowpair(imarr,arrMax,'montage')
title(['Strongest ',imgClass,' Channel: ',num2str(maxValueIndex)])

SqueezeNet

SqueezeNet은 아키텍처가 227×227×3 크기의 영상을 지원하는 심층 CNN입니다. GoogLeNet의 영상 차원과 다르더라도 SqueezeNet 차원에서 새 RGB 영상을 생성할 필요는 없습니다. 원본 RGB 영상을 사용할 수 있습니다.

불러오기

사전 훈련된 SqueezeNet 신경망을 불러옵니다. Deep Learning Toolbox™ Model for SqueezeNet Network 지원 패키지가 설치되어 있지 않은 경우, 필요한 지원 패키지로 연결되는 애드온 탐색기 링크가 제공됩니다. 지원 패키지를 설치하려면 링크를 클릭한 다음 설치를 클릭하십시오.

sqz = squeezenet;

신경망에서 계층 그래프를 추출합니다. SqueezeNet의 계층 수가 GoogLeNet보다 적은지 확인합니다. 또한 SqueezeNet이 227×227×3 크기의 영상에 맞게 구성되었는지 확인합니다.

lgraphSqz = layerGraph(sqz);
disp(['Number of Layers: ',num2str(numel(lgraphSqz.Layers))])
Number of Layers: 68
disp(lgraphSqz.Layers(1).InputSize)
   227   227     3

SqueezeNet 신경망 파라미터 수정하기

새 영상을 분류하도록 SqueezeNet을 다시 훈련시키려면 GoogLeNet에 대해 수행한 것과 유사하게 변경합니다.

마지막 6개의 신경망 계층을 검사합니다.

lgraphSqz.Layers(end-5:end)
ans = 
  6x1 Layer array with layers:

     1   'drop9'                             Dropout                 50% dropout
     2   'conv10'                            Convolution             1000 1x1x512 convolutions with stride [1  1] and padding [0  0  0  0]
     3   'relu_conv10'                       ReLU                    ReLU
     4   'pool10'                            Average Pooling         14x14 average pooling with stride [1  1] and padding [0  0  0  0]
     5   'prob'                              Softmax                 softmax
     6   'ClassificationLayer_predictions'   Classification Output   crossentropyex with 'tench' and 999 other classes

신경망의 마지막 드롭아웃 계층인 'drop9' 계층을 확률 0.6의 드롭아웃 계층으로 바꿉니다.

tmpLayer = lgraphSqz.Layers(end-5);
newDropoutLayer = dropoutLayer(0.6,'Name','new_dropout');
lgraphSqz = replaceLayer(lgraphSqz,tmpLayer.Name,newDropoutLayer);

GoogLeNet과 달리 SqueezeNet의 마지막 학습 가능한 계층은 1×1 컨벌루션 계층('conv10')이며 완전 연결 계층이 아닙니다. 'conv10' 계층을 필터의 개수가 클래스 개수와 같은 새로운 컨벌루션 계층으로 바꿉니다. GoogLeNet에서 한 것처럼, 새로운 계층의 학습률 인자를 높입니다.

numClasses = numel(categories(imgsTrain.Labels));
tmpLayer = lgraphSqz.Layers(end-4);
newLearnableLayer = convolution2dLayer(1,numClasses, ...
        'Name','new_conv', ...
        'WeightLearnRateFactor',10, ...
        'BiasLearnRateFactor',10);
lgraphSqz = replaceLayer(lgraphSqz,tmpLayer.Name,newLearnableLayer);

분류 계층을 클래스 레이블이 없는 새로운 계층으로 바꿉니다.

tmpLayer = lgraphSqz.Layers(end);
newClassLayer = classificationLayer('Name','new_classoutput');
lgraphSqz = replaceLayer(lgraphSqz,tmpLayer.Name,newClassLayer);

신경망의 마지막 6개 계층을 검사합니다. 드롭아웃 계층, 컨벌루션 계층, 출력 계층이 변경되었는지 확인합니다.

lgraphSqz.Layers(63:68)
ans = 
  6x1 Layer array with layers:

     1   'new_dropout'       Dropout                 60% dropout
     2   'new_conv'          Convolution             3 1x1 convolutions with stride [1  1] and padding [0  0  0  0]
     3   'relu_conv10'       ReLU                    ReLU
     4   'pool10'            Average Pooling         14x14 average pooling with stride [1  1] and padding [0  0  0  0]
     5   'prob'              Softmax                 softmax
     6   'new_classoutput'   Classification Output   crossentropyex

SqueezeNet을 위한 RGB 데이터 준비하기

RGB 영상의 차원은 GoogLeNet 아키텍처에 적합합니다. SqueezeNet 아키텍처에 맞도록 기존 RGB 영상의 크기를 자동으로 조정하는 증대 영상 데이터저장소를 생성합니다. 자세한 내용은 augmentedImageDatastore 항목을 참조하십시오.

augimgsTrain = augmentedImageDatastore([227 227],imgsTrain);
augimgsValidation = augmentedImageDatastore([227 227],imgsValidation);

훈련 옵션을 설정하고 SqueezeNet 훈련시키기

SqueezeNet과 함께 사용할 새 훈련 옵션 세트를 만듭니다. 난수 시드값을 디폴트 값으로 설정하고 신경망을 훈련시킵니다. 훈련 과정은 일반적으로 데스크탑 CPU에서 1~5분 정도 걸립니다.

ilr = 3e-4;
miniBatchSize = 10;
maxEpochs = 15;
valFreq = floor(numel(augimgsTrain.Files)/miniBatchSize);
opts = trainingOptions('sgdm',...
    'MiniBatchSize',miniBatchSize,...
    'MaxEpochs',maxEpochs,...
    'InitialLearnRate',ilr,...
    'ValidationData',augimgsValidation,...
    'ValidationFrequency',valFreq,...
    'Verbose',1,...
    'ExecutionEnvironment','cpu',...
    'Plots','training-progress');

rng default
trainedSN = trainNetwork(augimgsTrain,lgraphSqz,opts);

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:01 |       20.00% |       43.75% |       5.2508 |       1.2540 |          0.0003 |
|       1 |          13 |       00:00:11 |       60.00% |       50.00% |       0.9912 |       1.0519 |          0.0003 |
|       2 |          26 |       00:00:20 |       60.00% |       59.38% |       0.8554 |       0.8497 |          0.0003 |
|       3 |          39 |       00:00:30 |       60.00% |       59.38% |       0.8120 |       0.8328 |          0.0003 |
|       4 |          50 |       00:00:38 |       50.00% |              |       0.7885 |              |          0.0003 |
|       4 |          52 |       00:00:40 |       60.00% |       65.63% |       0.7091 |       0.7314 |          0.0003 |
|       5 |          65 |       00:00:49 |       90.00% |       87.50% |       0.4639 |       0.5893 |          0.0003 |
|       6 |          78 |       00:00:59 |       70.00% |       87.50% |       0.6021 |       0.4355 |          0.0003 |
|       7 |          91 |       00:01:08 |       90.00% |       90.63% |       0.2307 |       0.2945 |          0.0003 |
|       8 |         100 |       00:01:15 |       90.00% |              |       0.1827 |              |          0.0003 |
|       8 |         104 |       00:01:18 |       90.00% |       93.75% |       0.2139 |       0.2153 |          0.0003 |
|       9 |         117 |       00:01:28 |      100.00% |       90.63% |       0.0521 |       0.1964 |          0.0003 |
|      10 |         130 |       00:01:38 |       90.00% |       90.63% |       0.1134 |       0.2214 |          0.0003 |
|      11 |         143 |       00:01:47 |      100.00% |       90.63% |       0.0855 |       0.2095 |          0.0003 |
|      12 |         150 |       00:01:52 |       90.00% |              |       0.2394 |              |          0.0003 |
|      12 |         156 |       00:01:57 |      100.00% |       90.63% |       0.0606 |       0.1849 |          0.0003 |
|      13 |         169 |       00:02:06 |      100.00% |       90.63% |       0.0090 |       0.2071 |          0.0003 |
|      14 |         182 |       00:02:16 |      100.00% |       93.75% |       0.0127 |       0.3597 |          0.0003 |
|      15 |         195 |       00:02:25 |      100.00% |       93.75% |       0.0016 |       0.3414 |          0.0003 |
|======================================================================================================================|

신경망의 마지막 계층을 검사합니다. 분류 출력 계층에 3개의 클래스가 포함되어 있는지 확인합니다.

trainedSN.Layers(end)
ans = 
  ClassificationOutputLayer with properties:

            Name: 'new_classoutput'
         Classes: [ARR    CHF    NSR]
      OutputSize: 3

   Hyperparameters
    LossFunction: 'crossentropyex'

SqueezeNet 정확도 평가하기

검증 데이터를 사용하여 신경망을 평가합니다.

[YPred,probs] = classify(trainedSN,augimgsValidation);
accuracy = mean(YPred==imgsValidation.Labels);
disp(['SqueezeNet Accuracy: ',num2str(100*accuracy),'%'])
SqueezeNet Accuracy: 93.75%

결론

이 예제에서는 사전 훈련된 CNN GoogLeNet 및 SqueezeNet을 활용하여 ECG 신호의 세 가지 클래스를 분류하기 위해 전이 학습 및 연속 웨이블릿 분석을 사용하는 방법을 보여줍니다. ECG 신호의 웨이블릿 기반 시간-주파수 표현은 스케일로그램을 생성하는 데 사용됩니다. 스케일로그램의 RGB 영상이 생성됩니다. 영상은 두 심층 CNN을 미세 조정하는 데 사용됩니다. 서로 다른 신경망 계층의 활성화 결과도 살펴보았습니다.

이 예제에서는 사전 훈련된 CNN 모델을 사용하여, 신호를 분류하는 데 사용 가능한 하나의 워크플로를 보여줍니다. 다른 워크플로도 가능합니다. Deploy Signal Classifier on NVIDIA Jetson Using Wavelet Analysis and Deep Learning (Wavelet Toolbox)Deploy Signal Classifier Using Wavelets and Deep Learning on Raspberry Pi (Wavelet Toolbox)에서는 신호 분류를 위해 하드웨어에 코드를 배포하는 방법을 보여줍니다. GoogLeNet과 SqueezeNet은 ILSVRC(ImageNet Large-Scale Visual Recognition Challenge)[8]에서 사용되는 ImageNet 데이터베이스[10]의 서브셋에서 사전 훈련된 모델입니다. ImageNet 모음에는 물고기, 새, 가전 제품, 진균류와 같은 실제 사물의 영상이 포함되어 있습니다. 스케일로그램은 이러한 실제 사물들의 클래스에 속하지 않습니다. GoogLeNet 아키텍처와 SqueezeNet 아키텍처에 맞추기 위해 스케일로그램은 데이터 축소 과정도 거쳤습니다. 서로 다른 클래스의 스케일로그램을 구분하기 위해 사전 훈련된 CNN을 미세 조정하는 대신 원래의 스케일로그램 차원에서 CNN을 처음부터 훈련시킬 수도 있습니다.

참고 문헌

  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. Engin, M. "ECG beat classification using neuro-fuzzy network." Pattern Recognition Letters. Vol. 25, Number 15, 2004, pp.1715–1722.

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

  4. Leonarduzzi, R. F., G. Schlotthauer, and M. E. Torres. "Wavelet leader based multifractal analysis of heart rate variability during myocardial ischaemia." In Engineering in Medicine and Biology Society (EMBC), Annual International Conference of the IEEE, 110–113. Buenos Aires, Argentina: IEEE, 2010.

  5. Li, T., and M. Zhou. "ECG classification using wavelet packet entropy and random forests." Entropy. Vol. 18, Number 8, 2016, p.285.

  6. Maharaj, E. A., and A. M. Alonso. "Discriminant analysis of multivariate time series: Application to diagnosis based on ECG signals." Computational Statistics and Data Analysis. Vol. 70, 2014, pp. 67–87.

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

  8. Russakovsky, O., J. Deng, and H. Su et al. "ImageNet Large Scale Visual Recognition Challenge." International Journal of Computer Vision. Vol. 115, Number 3, 2015, pp. 211–252.

  9. Zhao, Q., and L. Zhang. "ECG feature extraction and classification using wavelet transform and support vector machines." In IEEE International Conference on Neural Networks and Brain, 1089–1092. Beijing, China: IEEE, 2005.

  10. ImageNet. http://www.image-net.org

지원 함수

helperCreateECGDataDirectories는 부모 디렉터리 내부에 데이터 디렉터리를 생성한 후 그 데이터 디렉터리 내부에 3개의 서브디렉터리를 생성합니다. 서브디렉터리는 ECGData에 있는 ECG 신호의 각 클래스의 이름을 따서 명명됩니다.

function helperCreateECGDirectories(ECGData,parentFolder,dataFolder)
% This function is only intended to support the ECGAndDeepLearningExample.
% 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

helperPlotRepsECGData에 있는 ECG 신호에 대한 각 클래스를 대표하는 처음 1000개 샘플을 플로팅합니다.

function helperPlotReps(ECGData)
% This function is only intended to support the ECGAndDeepLearningExample.
% 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

helperCreateRGBfromTFcwtfilterbank (Wavelet Toolbox)를 사용하여 ECG 신호의 연속 웨이블릿 변환을 구하고 웨이블릿 계수에서 스케일로그램을 생성합니다. 이 헬퍼 함수는 스케일로그램의 크기를 조정하고 이를 디스크에 jpeg 영상으로 씁니다.

function helperCreateRGBfromTF(ECGData,parentFolder,childFolder)
% This function is only intended to support the ECGAndDeepLearningExample.
% 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,[224 224]),fullfile(imgLoc,imFileName));
end
end

참고 항목

(Wavelet Toolbox) | | | | | |

관련 항목