Main Content

image-to-image 회귀를 위해 데이터저장소 준비하기

이 예제에서는 image-to-image 회귀 신경망을 훈련시키기 위해 ImageDatastoretransform 함수와 combine 함수를 사용하여 데이터저장소를 준비하는 방법을 보여줍니다.

이 예제에서는 잡음 제거 신경망 훈련에 적합한 파이프라인을 사용하여 데이터를 전처리하는 방법을 보여줍니다. 그런 다음 전처리된 잡음 데이터를 사용하여 영상 잡음을 제거하도록 간단한 컨벌루션 오토인코더 신경망을 훈련시킵니다.

전처리 파이프라인을 사용하여 데이터 준비하기

이 예제에서는 입력 영상 픽셀 중 일부가 0 또는 1(각각 검은색과 흰색)로 설정된 점잡음 모델을 사용합니다. 잡음이 있는 영상은 신경망 입력값으로 기능합니다. 잡음이 빠진 원본 영상은 예상 신경망 응답 변수로 기능합니다. 신경망은 점잡음을 검출하고 제거하는 것을 학습합니다.

데이터 불러오기

숫자 데이터 세트에 있는 원본 영상을 imageDatastore로서 불러옵니다. 이 데이터저장소에는 0부터 9까지 숫자를 나타내는 합성 영상 10,000개가 있습니다. 이 영상은 서로 다른 글꼴로 만들어진 숫자 영상에 무작위 변환을 적용하여 생성됩니다. 각 숫자 영상은 28×28픽셀입니다. 이 데이터저장소에는 범주당 동일한 개수의 영상이 포함되어 있습니다.

digitDatasetPath = fullfile(matlabroot,"toolbox","nnet", ...
    "nndemos","nndatasets","DigitDataset");
imds = imageDatastore(digitDatasetPath, ...
    IncludeSubfolders=true,LabelSource="foldernames");

파일 I/O 비용을 최소화할 수 있도록 읽기 크기를 큰 값으로 지정하십시오.

imds.ReadSize = 500;

훈련, 검증, 테스트를 위해 데이터 분할하기

훈련에 앞서 shuffle 함수를 사용하여 숫자 데이터를 섞습니다.

rng("default")
imds = shuffle(imds);

splitEachLabel 함수를 사용하여 imds를 훈련, 검증 및 테스트를 위한 원래 그대로의 영상을 포함하는 세 개의 영상 데이터저장소로 나눕니다.

[imdsTrain,imdsVal,imdsTest] = splitEachLabel(imds,0.95,0.025);

입력 영상에 합성 잡음 추가하기

이 예제에서는 신경망 입력값으로 사용할 각 입력 영상에 합성 잡음을 추가합니다. imnoise (Image Processing Toolbox) 함수를 사용하여 영상에 점잡음을 추가하는 addNoise라는 헬퍼 함수를 정의합니다. addNoise 함수를 사용하려면 입력 데이터의 형식이 영상 데이터로 구성된 셀형 배열이어야 합니다. 이는 ImageDatastoreread 함수가 반환하는 데이터의 형식과 일치합니다.

function dataOut = addNoise(data)
    dataOut = data;
    for idx = 1:size(data,1)
       dataOut{idx} = imnoise(data{idx},"salt & pepper");
    end
end

transform 함수를 사용하여 입력 영상에 잡음을 적용합니다. transform 함수는 기본 데이터저장소에서 데이터를 읽어 들인 후 addNoise 함수에 정의된 연산을 사용하여 데이터를 처리합니다. transform 함수의 출력값은 TransformedDatastore입니다.

dsTrainNoisy = transform(imdsTrain,@addNoise);
dsValNoisy = transform(imdsVal,@addNoise);
dsTestNoisy = transform(imdsTest,@addNoise);

데이터 전처리하기

combine 함수를 사용하여 잡음이 있는 영상과 원래 그대로의 영상을 단일 데이터저장소로 결합하는데, 이 데이터저장소가 trainnet에 데이터를 입력하게 됩니다. 이 결합된 데이터저장소는 데이터 배치를 trainnet에 필요한 것과 같이 2열 셀형 배열로 읽어 들입니다. combine 함수의 출력값은 CombinedDatastore입니다.

dsTrain = combine(dsTrainNoisy,imdsTrain);
dsVal = combine(dsValNoisy,imdsVal);
dsTest = combine(dsTestNoisy,imdsTest);

입력 영상과 응답 영상의 쌍을 전처리하는 commonPreprocessing이라는 헬퍼 함수를 정의합니다. 헬퍼 함수는 다음의 전처리 단계를 수행합니다.

  1. 영상 데이터를 single 데이터형으로 변환합니다.

  2. imresize 함수를 사용하여 영상 데이터의 크기가 입력 계층의 크기(32×32 픽셀)와 일치하도록 조정합니다.

  3. rescale 함수를 사용하여 데이터를 [0, 1] 범위로 정규화합니다.

헬퍼 함수를 사용하려면 입력 데이터의 형식이 영상 데이터로 구성된 2열 셀형 배열이어야 합니다. 이는 CombinedDatastoreread 함수가 반환하는 데이터의 형식과 일치합니다.

function dataOut = commonPreprocessing(data)
    dataOut = cell(size(data));
    for col = 1:size(data,2)
        for idx = 1:size(data,1)
            temp = single(data{idx,col});
            temp = imresize(temp,[32,32]);
            temp = rescale(temp);
            dataOut{idx,col} = temp;
        end
    end
end

transform 함수를 사용하여 입력 데이터와 응답 데이터에 전처리를 적용합니다. 훈련 데이터 세트, 검증 데이터 세트, 테스트 데이터 세트를 전처리합니다.

dsTrain = transform(dsTrain,@commonPreprocessing);
dsVal = transform(dsVal,@commonPreprocessing);
dsTest = transform(dsTest,@commonPreprocessing);

훈련 데이터 증강하기

증강은 과적합을 줄이고 훈련된 신경망에 존재하는 회전에 강인성을 더해 줍니다. 검증 데이터 세트나 테스트 데이터 세트에는 무작위 증강이 필요하지 않습니다.

rot90 함수를 사용하여 데이터에 무작위 90도 회전을 추가하는 augmentImages라는 헬퍼 함수를 정의합니다. 신경망 입력값과 이에 대응하는 예상 응답 변수에 동일한 회전이 적용됩니다. 이 함수를 사용하려면 입력 데이터의 형식이 영상 데이터로 구성된 2열 셀형 배열이어야 합니다. 이는 CombinedDatastoreread 함수가 반환하는 데이터의 형식과 일치합니다.

function dataOut = augmentImages(data)
    dataOut = cell(size(data));
    for idx = 1:size(data,1)
        rot90Val = randi(4,1,1)-1;
        dataOut(idx,:) = {rot90(data{idx,1},rot90Val), ...
            rot90(data{idx,2},rot90Val)};
    end
end

transform 함수를 사용하여 훈련 데이터에 무작위 증강을 적용합니다.

dsTrain = transform(dsTrain,@augmentImages);

전처리된 데이터 미리 보기

훈련 데이터를 준비하려면 몇 가지 전처리 연산이 필요하므로 전처리된 데이터를 미리 봄으로써 훈련에 앞서 데이터가 올바른지 확인합니다. preview 함수를 사용하여 데이터를 미리 봅니다.

잡음이 있는 영상과 원래 그대로의 영상으로 이루어진 쌍의 예를 montage (Image Processing Toolbox) 함수를 사용하여 시각화합니다. 훈련 데이터가 올바르게 보입니다. 왼쪽 열에 있는 입력 영상에서 점잡음이 보입니다. 잡음이 더해진 것 외에는 입력 영상과 응답 영상이 동일합니다. 입력 영상과 응답 영상 양쪽에 모두 무작위 90도 회전이 동일하게 적용되었습니다.

exampleData = preview(dsTrain);
inputs = exampleData(:,1);
responses = exampleData(:,2);
minibatch = cat(2,inputs,responses);
montage(minibatch',Size=[8 2])
title("Inputs (Left) and Responses (Right)")

컨벌루션 오토인코더 신경망 정의하기

컨벌루션 오토인코더는 영상 잡음 제거를 위해 자주 사용되는 아키텍처입니다. 컨벌루션 오토인코더는 인코더와 디코더라는 두 단계로 구성됩니다. 인코더는 원본 입력 영상을 너비와 높이는 줄어들었으나 원본 입력 영상에 비해 공간 위치당 특징 맵이 많다는 점에서 보다 심층적인 기저의 표현으로 압축합니다. 압축된 기저의 표현은 원본 영상에 있는 고주파 특징을 복원하는 능력에서 어느 정도의 공간 분해능을 잃게 되지만, 한편으로는 원본 영상을 인코딩할 때 잡음이 있는 아티팩트를 포함시키지 않아야 한다는 사실을 학습합니다. 디코더는 인코딩된 신호를 반복적으로 업샘플링하여 원래의 너비, 높이, 채널 개수로 되돌려 놓습니다. 인코더가 잡음을 제거하기 때문에 디코딩된 최종 영상에는 잡음 아티팩트가 줄어들어 있습니다.

이 예제에서는 다음과 같은 Deep Learning Toolbox™의 계층을 사용하여 컨벌루션 오토인코더 신경망을 정의합니다.

영상 입력 계층을 만듭니다. 2배로 다운샘플링 및 업샘플링을 하면서 생기는 채우기 문제를 단순화하기 위해 입력 크기를 32×32로 선택합니다. 32는 2, 4, 8로 나누어 떨어지기 때문입니다.

imageLayer = imageInputLayer([32,32,1]);

인코딩 계층을 만듭니다. 인코더에서의 다운샘플링은 풀 크기를 2로 하고 스트라이드를 2로 하는 최댓값 풀링을 통해 이루어집니다.

encodingLayers = [ ...
    convolution2dLayer(3,8,Padding="same"), ...
    reluLayer, ...
    maxPooling2dLayer(2,Padding="same",Stride=2), ...
    convolution2dLayer(3,16,Padding="same"), ...
    reluLayer, ...
    maxPooling2dLayer(2,Padding="same",Stride=2), ...
    convolution2dLayer(3,32,Padding="same"), ...
    reluLayer, ...
    maxPooling2dLayer(2,Padding="same",Stride=2)];

디코딩 계층을 만듭니다. 디코더는 전치된 인코딩된 신호를 스트라이드가 2인 컨벌루션 계층을 사용하여 업샘플링하며, 2배 업샘플링합니다. 신경망은 clippedReluLayer를 마지막 활성화 계층으로 사용하여 출력값이 [0, 1] 범위 내에 오도록 강제합니다.

decodingLayers = [ ...
    transposedConv2dLayer(2,32,Stride=2), ...
    reluLayer, ...
    transposedConv2dLayer(2,16,Stride=2), ...
    reluLayer, ...
    transposedConv2dLayer(2,8,Stride=2), ...
    reluLayer, ...
    convolution2dLayer(1,1,Padding="same"), ...
    clippedReluLayer(1.0)];    

영상 입력 계층, 인코딩 계층 및 디코딩 계층을 결합하여 컨벌루션 오토인코더 신경망 아키텍처를 형성합니다.

layers = [imageLayer,encodingLayers,decodingLayers];

훈련 옵션 정의하기

Adam 최적화를 사용하여 신경망을 훈련시킵니다. trainingOptions 함수를 사용하여 하이퍼파라미터 설정을 지정합니다. 훈련을 Epoch 50회 수행합니다.

options = trainingOptions("adam", ...
    MaxEpochs=50, ...
    MiniBatchSize=imds.ReadSize, ...
    ValidationData=dsVal, ...
    ValidationPatience=5, ...
    Plots="training-progress", ...
    OutputNetwork="best-validation", ...
    Verbose=false);

신경망 훈련시키기

trainnet 함수를 사용하여 신경망을 훈련시킵니다. 회귀의 경우 평균제곱오차 손실을 사용합니다. 기본적으로 trainnet 함수는 GPU를 사용할 수 있으면 GPU를 사용합니다. GPU에서 훈련시키려면 Parallel Computing Toolbox™ 라이선스와 지원되는 GPU 장치가 필요합니다. 지원되는 장치에 대한 자세한 내용은 GPU 연산 요구 사항 (Parallel Computing Toolbox) 항목을 참조하십시오. GPU를 사용할 수 없는 경우, trainnet 함수는 CPU를 사용합니다. 실행 환경을 지정하려면 ExecutionEnvironment 훈련 옵션을 사용하십시오. 훈련은 NVIDIA Titan XP에서 15분 정도 걸립니다.

net = trainnet(dsTrain,layers,"mse",options);

modelDateTime = string(datetime("now",Format="yyyy-MM-dd-HH-mm-ss"));
save("trainedImageToImageRegressionNet-"+modelDateTime+".mat","net");    

잡음 제거 신경망의 성능 평가하기

minibatchpredict 함수를 사용하여 테스트 세트에서 출력 영상을 얻습니다.

ypred = minibatchpredict(net,dsTest);

preview 함수를 사용하여 테스트 세트에서 잡음이 있는 영상과 원래 그대로의 영상으로 이루어진 쌍을 얻습니다.

testBatch = preview(dsTest);

잡음 제거가 얼마나 잘 작동하는지 가늠해 볼 수 있도록 샘플 입력 영상과 이에 연관되어 예측된 신경망의 출력값을 시각화합니다. 예상과 같이 신경망의 출력 영상에서는 입력 영상의 잡음 아티팩트가 대부분 제거되었습니다. 잡음 제거된 영상은 인코딩 및 디코딩 과정의 결과로 약간 흐릿합니다.

idx = 1;
y = ypred(:,:,:,idx);
x = testBatch{idx,1};
ref = testBatch{idx,2};
montage({x,y})

피크 신호 대 잡음비(PSNR)를 분석하여 신경망의 성능을 평가합니다. 예상과 같이 출력 영상의 PSNR이 잡음이 있는 입력 영상보다 높습니다.

psnrNoisy = psnr(x,ref)
psnrNoisy = single
    20.8214
psnrDenoised = psnr(y,ref)
psnrDenoised = single
    21.5986

참고 항목

| | | | |

관련 예제

세부 정보