parfeval을 사용하여 다중 딥러닝 신경망 훈련시키기

이 예제에서는 parfeval을 사용하여 딥러닝 신경망에 대한 신경망 아키텍처의 심도를 기반으로 파라미터 스윕을 수행하고 훈련 중에 데이터를 가져오는 방법을 보여줍니다.

딥러닝 훈련에는 종종 몇 시간 또는 며칠이 소요되며 양질의 아키텍처를 찾기가 어려울 수 있습니다. 양질의 모델을 찾을 때 병렬 연산을 사용하여 검색 속도를 높이고 검색을 자동화할 수 있습니다. 복수의 GPU(그래픽 처리 장치)가 장착된 컴퓨터를 사용하는 경우에는 로컬 병렬 풀을 사용하여 이 예제를 데이터 세트의 로컬 복사본에 대해 실행할 수 있습니다. 더 많은 리소스를 사용하기 위해 딥러닝 훈련을 클라우드로 확장할 수 있습니다. 이 예제에서는 클라우드에 있는 클러스터의 신경망 아키텍처의 심도를 기반으로 파라미터 스윕을 수행하기 위해 parfeval을 사용하는 방법을 보여줍니다. parfeval을 사용하면 MATLAB 사용을 차단하지 않으면서 백그라운드에서 훈련시킬 수 있고, 결과가 만족할 만한 수준이면 예정보다 이르게 중단하는 옵션이 제공됩니다. 다른 파라미터를 기반으로 파라미터 스윕을 수행하도록 이 스크립트를 수정할 수 있습니다. 이 예제에서는 DataQueue를 사용하여 연산 중에 워커로부터 피드백을 받는 방법도 보여줍니다.

요구 사항

이 예제를 실행하려면 먼저 클러스터를 구성하고 데이터를 클라우드로 업로드해야 합니다. MATLAB에서는 MATLAB 데스크탑에서 직접 클라우드에 클러스터를 만들 수 있습니다. 탭의 병렬 메뉴에서 클러스터 생성 및 관리를 선택합니다. 클러스터 프로파일 관리자에서 클라우드 클러스터 생성을 클릭합니다. 또는 MathWorks Cloud Center를 사용하여 계산 클러스터를 만들고 액세스할 수도 있습니다. 자세한 내용은 Getting Started with Cloud Center를 참조하십시오. 이 예제에서는 MATLAB 탭의 병렬 > 디폴트 클러스터 선택에서 방금 만든 클러스터가 디폴트 값으로 설정되어 있는지 확인하십시오. 그런 다음 Amazon S3 버킷으로 데이터를 업로드하면 MATLAB에서 직접 사용할 수 있습니다. 이 예제에서는 기존에 Amazon S3에 저장되어 있는 CIFAR-10 데이터 세트 복사본을 사용합니다. 자세한 지침은 딥러닝 데이터를 클라우드로 업로드하기 항목을 참조하십시오.

클라우드에서 데이터 세트 불러오기

imageDatastore를 사용하여 클라우드에서 훈련 데이터 세트와 테스트 데이터 세트를 불러옵니다. 훈련 데이터 세트를 훈련용 세트와 검증용 세트로 분할하고, 테스트 데이터 세트는 파라미터 스윕에서 얻게 될 가장 좋은 신경망을 테스트할 용도로 보관해 둡니다. 이 예제에서는 Amazon S3에 저장되어 있는 CIFAR-10 데이터 세트 복사본을 사용합니다. 워커가 클라우드의 데이터저장소에 액세스할 수 있도록 하려면 AWS 자격 증명의 환경 변수가 올바르게 설정되었는지 확인하십시오. 딥러닝 데이터를 클라우드로 업로드하기 항목을 참조하십시오.

imds = imageDatastore('s3://cifar10cloud/cifar10/train', ...
    'IncludeSubfolders',true, ...
    'LabelSource','foldernames');

imdsTest = imageDatastore('s3://cifar10cloud/cifar10/test', ...
    'IncludeSubfolders',true, ...
    'LabelSource','foldernames');

[imdsTrain,imdsValidation] = splitEachLabel(imds,0.9);

augmentedImageDatastore 객체를 만들어서 증대 영상 데이터로 신경망을 훈련시킵니다. 무작위 평행 이동 및 가로 반사를 사용합니다. 데이터 증대는 신경망이 과적합되는 것을 방지하고 훈련 영상의 정확한 세부 정보가 기억되지 않도록 하는 데 도움이 됩니다.

imageSize = [32 32 3];
pixelRange = [-4 4];
imageAugmenter = imageDataAugmenter( ...
    'RandXReflection',true, ...
    'RandXTranslation',pixelRange, ...
    'RandYTranslation',pixelRange);
augmentedImdsTrain = augmentedImageDatastore(imageSize,imdsTrain, ...
    'DataAugmentation',imageAugmenter, ...
    'OutputSizeMode','randcrop');

여러 신경망을 동시에 훈련시키기

훈련 옵션을 정의합니다. 미니 배치 크기를 설정하고, 미니 배치 크기에 비례하여 초기 학습률을 확장합니다. trainNetwork가 Epoch당 한 번씩 신경망을 검증하도록 검증 빈도를 설정합니다.

miniBatchSize = 128;
initialLearnRate = 1e-1 * miniBatchSize/256;
validationFrequency = floor(numel(imdsTrain.Labels)/miniBatchSize);
options = trainingOptions('sgdm', ...
    'MiniBatchSize',miniBatchSize, ... % Set the mini-batch size
    'Verbose',false, ... % Do not send command line output.
    'InitialLearnRate',initialLearnRate, ... % Set the scaled learning rate.
    'L2Regularization',1e-10, ...
    'MaxEpochs',30, ...
    'Shuffle','every-epoch', ...
    'ValidationData',imdsValidation, ...
    'ValidationFrequency', validationFrequency);

파라미터 스윕을 수행할 신경망 아키텍처의 심도를 지정합니다. parfeval을 사용하여 여러 신경망을 동시에 훈련시키는 병렬 파라미터 스윕을 수행합니다. 루프를 사용하여 스윕의 여러 신경망 아키텍처를 모두 반복합니다. 신경망 심도를 제어하는 입력 인수를 받아 CIFAR-10에 대한 아키텍처를 생성하는 헬퍼 함수 createNetworkArchitecture를 스크립트 끝에 만듭니다. parfeval을 사용하여 클러스터에 있는 워커로 trainNetwork가 수행하는 연산 작업을 넘겨줍니다. parfeval은 연산이 완료되면 훈련된 신경망과 훈련 정보를 저장할 future 변수를 반환합니다.

netDepths = 1:4;
for idx = 1:numel(netDepths)
    networksFuture(idx) = parfeval(@trainNetwork,2, ...
        augmentedImdsTrain,createNetworkArchitecture(netDepths(idx)),options);
end
Starting parallel pool (parpool) using the 'MyCluster' profile ...
Connected to the parallel pool (number of workers: 4).

parfeval은 MATLAB 사용을 차단하지 않으므로 명령을 계속해서 실행할 수 있습니다. 여기서는 networksFuturefetchOutputs를 사용하여 훈련된 신경망과 해당 훈련 정보를 가져옵니다. fetchOutputs 함수는 future 변수가 끝날 때까지 기다립니다.

[trainedNetworks,trainingInfo] = fetchOutputs(networksFuture);

trainingInfo 구조체에 액세스하여 신경망의 최종 검증 정확도를 가져옵니다.

accuracies = [trainingInfo.FinalValidationAccuracy]
accuracies = 1×4

   72.5600   77.2600   79.4000   78.6800

정확도 측면에서 가장 좋은 신경망을 선택합니다. 테스트 데이터 세트를 기준으로 신경망의 성능을 테스트합니다.

[~, I] = max(accuracies);
bestNetwork = trainedNetworks(I(1));
YPredicted = classify(bestNetwork,imdsTest);
accuracy = sum(YPredicted == imdsTest.Labels)/numel(imdsTest.Labels)
accuracy = 0.7840

테스트 데이터에 대해 정오분류표를 계산합니다.

figure('Units','normalized','Position',[0.2 0.2 0.4 0.4]);
confusionchart(imdsTest.Labels,YPredicted,'RowSummary','row-normalized','ColumnSummary','column-normalized');

훈련 중에 피드백 데이터 보내기

각 워커에서 훈련 진행 상황을 보여주는 플롯을 준비하고 초기화합니다. 변경되는 데이터를 간편하게 보기 위해 animatedLine을 사용합니다.

f = figure;
f.Visible = true;
for i=1:4
    subplot(2,2,i)
    xlabel('Iteration');
    ylabel('Training accuracy');
    lines(i) = animatedline;
end

DataQueue를 사용하여 워커에서 클라이언트로 훈련 진행 상황 데이터를 보내고 데이터를 플로팅합니다. afterEach를 사용하여 워커가 훈련 진행 상황 피드백을 보낼 때마다 플롯을 업데이트합니다. 파라미터 opts에는 워커, 훈련 반복 및 훈련 정확도에 대한 정보가 포함됩니다.

D = parallel.pool.DataQueue;
afterEach(D, @(opts) updatePlot(lines, opts{:}));

파라미터 스윕을 수행할 신경망 아키텍처의 심도를 지정하고 parfeval을 사용하여 병렬 파라미터 스윕을 수행합니다. 스크립트를 현재 풀에 첨부 파일로 추가하여 워커들이 이 스크립트의 헬퍼 함수에 액세스할 수 있도록 합니다. 워커에서 클라이언트로 훈련 진행 상황을 전송할 출력 함수를 훈련 옵션에 정의합니다. 훈련 옵션은 워커의 인덱스에 따라 달라지며, for 루프 안에 포함되어야 합니다.

netDepths = 1:4;
addAttachedFiles(gcp,mfilename);
for idx = 1:numel(netDepths)
    
    miniBatchSize = 128;
    initialLearnRate = 1e-1 * miniBatchSize/256; % Scale the learning rate according to the mini-batch size.
    validationFrequency = floor(numel(imdsTrain.Labels)/miniBatchSize);
    
    options = trainingOptions('sgdm', ...
        'OutputFcn',@(state) sendTrainingProgress(D,idx,state), ... % Set an output function to send intermediate results to the client.
        'MiniBatchSize',miniBatchSize, ... % Set the corresponding MiniBatchSize in the sweep.
        'Verbose',false, ... % Do not send command line output.
        'InitialLearnRate',initialLearnRate, ... % Set the scaled learning rate.
        'L2Regularization',1e-10, ...
        'MaxEpochs',30, ...
        'Shuffle','every-epoch', ...
        'ValidationData',imdsValidation, ...
        'ValidationFrequency', validationFrequency);
    
    networksFuture(idx) = parfeval(@trainNetwork,2, ...
        augmentedImdsTrain,createNetworkArchitecture(netDepths(idx)),options);
end

parfeval은 클러스터에 있는 워커에 대해 trainNetwork를 호출합니다. 연산이 백그라운드에서 진행되므로 계속해서 MATLAB을 사용할 수 있습니다. parfeval 연산을 중지하려면 연산에 대응되는 future 변수에 대해 cancel을 호출하면 됩니다. 예를 들어, 신경망의 성능이 부진한 것을 발견한 경우 해당 신경망의 future 변수를 취소하면 됩니다. 이렇게 하면 대기 중이던 다음 future 변수가 연산을 시작합니다.

여기서는 future 변수에 대해 fetchOutputs를 호출하여 훈련된 신경망과 해당 훈련 정보를 가져옵니다.

[trainedNetworks,trainingInfo] = fetchOutputs(networksFuture);

각 신경망의 최종 검증 정확도를 가져옵니다.

accuracies = [trainingInfo.FinalValidationAccuracy]
accuracies = 1×4

   72.9200   77.4800   76.9200   77.0400

헬퍼 함수

함수를 사용하여 CIFAR-10 데이터 세트에 대한 신경망 아키텍처를 정의하고, 입력 인수를 사용하여 신경망 심도를 조정합니다. 코드를 단순화하려면 입력값을 컨벌루션하는 컨벌루션 블록을 사용하십시오. 풀링 계층은 공간 차원을 다운샘플링합니다.

function layers = createNetworkArchitecture(netDepth)
imageSize = [32 32 3];
netWidth = round(16/sqrt(netDepth)); % netWidth controls the number of filters in a convolutional block

layers = [
    imageInputLayer(imageSize)
    
    convolutionalBlock(netWidth,netDepth)
    maxPooling2dLayer(2,'Stride',2)
    convolutionalBlock(2*netWidth,netDepth)
    maxPooling2dLayer(2,'Stride',2)
    convolutionalBlock(4*netWidth,netDepth)
    averagePooling2dLayer(8)
    
    fullyConnectedLayer(10)
    softmaxLayer
    classificationLayer
    ];
end

신경망 아키텍처에서 컨벌루션 블록을 만드는 함수를 정의합니다.

function layers = convolutionalBlock(numFilters,numConvLayers)
layers = [
    convolution2dLayer(3,numFilters,'Padding','same')
    batchNormalizationLayer
    reluLayer
    ];

layers = repmat(layers,numConvLayers,1);
end

DataQueue를 통해 훈련 진행 상황을 클라이언트로 보내는 함수를 정의합니다.

function sendTrainingProgress(D,idx,info)
if info.State == "iteration"
    send(D,{idx,info.Iteration,info.TrainingAccuracy});
end
end

워커가 중간 결과를 전송하면 이에 따라 플롯을 업데이트하는 업데이트 함수를 정의합니다.

function updatePlot(lines,idx,iter,acc)
addpoints(lines(idx),iter,acc);
drawnow limitrate nocallbacks
end

참고 항목

| | | (Parallel Computing Toolbox) | (Parallel Computing Toolbox)

관련 항목