이 페이지의 내용은 이전 릴리스에 관한 것입니다. 해당 영문 페이지는 최신 릴리스에서 제거되었습니다.

YOLO v2 딥러닝을 사용한 객체 검출

이 예제에서는 YOLO(You Only Look Once) v2 사물 검출기를 훈련시키는 방법을 다룹니다.

딥러닝은 강건한 사물 검출기를 훈련시키는 데 사용할 수 있는 강력한 머신러닝 기법입니다. Faster R-CNN과 YOLO(You Only Look Once) v2를 비롯한 다양한 객체 검출 기법이 있습니다. 이 예제에서는 trainYOLOv2ObjectDetector 함수를 사용하여 YOLO v2 차량 검출기를 훈련시킵니다. 자세한 내용은 Object Detection using Deep Learning (Computer Vision Toolbox) 항목을 참조하십시오.

사전 훈련된 검출기 다운로드하기

훈련이 완료될 때까지 기다릴 필요가 없도록 사전 훈련된 검출기를 다운로드합니다. 검출기를 직접 훈련시키려면 doTraining 변수를 true로 설정하십시오.

doTraining = false;
if ~doTraining && ~exist('yolov2ResNet50VehicleExample_19b.mat','file')    
    disp('Downloading pretrained detector (98 MB)...');
    pretrainedURL = 'https://www.mathworks.com/supportfiles/vision/data/yolov2ResNet50VehicleExample_19b.mat';
    websave('yolov2ResNet50VehicleExample_19b.mat',pretrainedURL);
end

데이터셋 불러오기

이 예제에서는 295개의 영상을 포함하는 소규모의 차량 데이터셋을 사용합니다. 각 영상에는 차량에 대해 레이블 지정된 건수가 한 건 또는 두 건 있습니다. 작은 데이터셋은 YOLO v2 훈련 절차를 살펴보기에 유용하지만, 실전에서 강건한 검출기를 훈련시키려면 레이블이 지정된 영상이 더 많이 필요합니다. 차량 영상의 압축을 풀고 차량 실측 데이터를 불러옵니다.

unzip vehicleDatasetImages.zip
data = load('vehicleDatasetGroundTruth.mat');
vehicleDataset = data.vehicleDataset;

차량 데이터는 2열 테이블에 저장되어 있습니다. 첫 번째 열은 영상 파일 경로를 포함하고, 두 번째 열은 차량 경계 상자를 포함합니다.

% Display first few rows of the data set.
vehicleDataset(1:4,:)
ans=4×2 table
              imageFilename                vehicle   
    _________________________________    ____________

    {'vehicleImages/image_00001.jpg'}    {1×4 double}
    {'vehicleImages/image_00002.jpg'}    {1×4 double}
    {'vehicleImages/image_00003.jpg'}    {1×4 double}
    {'vehicleImages/image_00004.jpg'}    {1×4 double}

% Add the fullpath to the local vehicle data folder.
vehicleDataset.imageFilename = fullfile(pwd,vehicleDataset.imageFilename);

데이터 세트를 검출기 훈련에 사용할 훈련 세트와 검출기 평가에 사용할 테스트 세트로 분할합니다. 데이터의 60%를 훈련용으로 선택합니다. 나머지를 평가용으로 사용합니다.

rng(0);
shuffledIndices = randperm(height(vehicleDataset));
idx = floor(0.6 * length(shuffledIndices) );
trainingDataTbl = vehicleDataset(shuffledIndices(1:idx),:);
testDataTbl = vehicleDataset(shuffledIndices(idx+1:end),:);

imageDatastoreboxLabelDatastore를 사용하여 훈련과 평가 과정에서 영상 및 레이블 데이터를 불러오기 위한 데이터저장소를 만듭니다.

imdsTrain = imageDatastore(trainingDataTbl{:,'imageFilename'});
bldsTrain = boxLabelDatastore(trainingDataTbl(:,'vehicle'));

imdsTest = imageDatastore(testDataTbl{:,'imageFilename'});
bldsTest = boxLabelDatastore(testDataTbl(:,'vehicle'));

영상 데이터저장소와 상자 레이블 데이터저장소를 결합합니다.

trainingData = combine(imdsTrain,bldsTrain);
testData = combine(imdsTest,bldsTest);

상자 레이블과 함께 훈련 영상 중 하나를 표시합니다.

data = read(trainingData);
I = data{1};
bbox = data{2};
annotatedImage = insertShape(I,'Rectangle',bbox);
annotatedImage = imresize(annotatedImage,2);
figure
imshow(annotatedImage)

YOLO v2 객체 검출 네트워크 만들기

YOLO v2 객체 검출 네트워크는 2개의 하위 네트워크로 구성됩니다. 하나의 특징 추출 네트워크가 있고, 그 뒤에 검출 네트워크가 옵니다. 특징 추출 네트워크는 일반적으로 사전 훈련된 CNN입니다(자세한 내용은 사전 훈련된 심층 신경망 참조). 이 예제에서는 특징 추출에 ResNet-50을 사용합니다. 응용 사례의 요구 사항에 따라 MobileNet v2나 ResNet-18과 같은 여타 사전 훈련된 네트워크도 사용할 수 있습니다. 검출 하위 네트워크는 특징 추출 네트워크에 비해 규모가 작은 CNN으로, 몇 개의 컨벌루션 계층과 YOLO v2 전용 계층으로 구성됩니다.

yolov2Layers 함수를 사용하여, 사전 훈련된 ResNet-50 특징 추출 네트워크가 주어졌을 때 자동으로 YOLO v2 객체 검출 네트워크를 만듭니다. yolov2Layers를 사용할 때는 YOLO v2 네트워크를 파라미터화하는 다음과 같은 몇 개의 입력값을 지정해야 합니다.

  • 네트워크 입력 크기

  • 앵커 상자

  • 특징 추출 네트워크

먼저 네트워크 입력 크기와 클래스 개수를 지정합니다. 네트워크 입력 크기를 선택할 때는 네트워크 자체에서 요구되는 최소 크기, 훈련 영상의 크기, 그리고 선택한 크기에서 데이터를 처리할 때 발생하는 계산 비용을 고려해야 합니다. 가능하다면, 훈련 영상의 크기와 가깝고 네트워크에서 요구되는 입력 크기보다 큰 네트워크 입력 크기를 선택하십시오. 예제를 실행하는 데 소요되는 계산 비용을 줄이려면 네트워크를 실행하는 데 필요한 최소 크기인 [224 224 3]으로 네트워크 입력 크기를 지정하십시오.

inputSize = [224 224 3];

검출할 사물 클래스의 개수를 정의합니다.

numClasses = width(vehicleDataset)-1;

이 예제에서 사용되는 훈련 영상은 224x224보다 크고 크기가 다양하므로 훈련 전 전처리 단계에서 영상을 크기 조정해야 합니다.

다음으로, estimateAnchorBoxes를 사용하여 훈련 데이터의 사물 크기를 기반으로 앵커 상자를 추정합니다. 훈련 전 이루어지는 영상 크기 조정을 고려하기 위해 앵커 상자 추정에 사용하는 훈련 데이터를 크기 조정하십시오. transform을 사용하여 훈련 데이터를 전처리한 후에 앵커 상자의 개수를 정의하고 앵커 상자를 추정합니다. 지원 함수 preprocessData를 사용하여 훈련 데이터를 네트워크의 입력 영상 크기로 크기 조정합니다.

trainingDataForEstimation = transform(trainingData,@(data)preprocessData(data,inputSize));
numAnchors = 7;
[anchorBoxes, meanIoU] = estimateAnchorBoxes(trainingDataForEstimation, numAnchors)
anchorBoxes = 7×2

   145   122
    81    76
   160   132
    41    34
    63    62
   103    97
    33    23

meanIoU = 0.8630

앵커 상자 선택에 관한 자세한 내용은 (Computer Vision Toolbox™) 및 Anchor Boxes for Object Detection (Computer Vision Toolbox) 항목을 참조하십시오.

이제 resnet50을 사용하여 사전 훈련된 ResNet-50 모델을 불러옵니다.

featureExtractionNetwork = resnet50;

'activation_40_relu' 뒤에 오는 계층들을 검출 하위 네트워크로 교체할 특징 추출 계층으로 'activation_40_relu'를 선택합니다. 이 특징 추출 계층은 16배만큼 다운샘플링된 특징 맵을 출력합니다. 이 정도의 다운샘플링은 공간 분해능과 추출된 특징의 강도 사이를 적절히 절충한 값입니다. 네트워크의 더 아래쪽에서 추출된 특징은 더 강한 영상 특징을 인코딩하나 공간 분해능이 줄어들기 때문입니다. 최적의 특징 추출 계층을 선택하려면 경험적 분석이 필요합니다.

featureLayer = 'activation_40_relu';

YOLO v2 객체 검출 네트워크를 만듭니다.

lgraph = yolov2Layers(inputSize,numClasses,anchorBoxes,featureExtractionNetwork,featureLayer);

analyzeNetwork 또는 Deep Learning Toolbox™의 심층 네트워크 디자이너를 사용하여 네트워크를 시각화할 수 있습니다.

YOLO v2 네트워크 아키텍처를 더욱 세부적으로 제어해야 하는 경우에는 심층 네트워크 디자이너를 사용하여 YOLO v2 검출 네트워크를 직접 설계하십시오. 자세한 내용은 Design a YOLO v2 Detection Network (Computer Vision Toolbox) 항목을 참조하십시오.

데이터 증대

데이터 증대는 훈련 중에 원본 데이터를 무작위로 변환함으로써 네트워크 정확도를 개선하는 데 사용됩니다. 데이터 증대를 사용하면 레이블이 지정된 훈련 샘플의 개수를 늘리지 않고도 훈련 데이터에 다양성을 더할 수 있습니다.

transform을 사용하여 영상과 영상에 해당하는 상자 레이블을 가로 방향으로 무작위로 뒤집어서 훈련 데이터를 증대합니다. 테스트 데이터에는 데이터 증대가 적용되지 않는다는 것에 유의하십시오. 이상적인 경우라면 테스트 데이터가 원본 데이터를 대표해야 하므로 편향되지 않은 평가를 위해 수정되지 않은 상태로 남겨 두는 것이 좋습니다.

augmentedTrainingData = transform(trainingData,@augmentData);

동일한 영상을 여러 차례 읽어 들이고 증대된 훈련 데이터를 표시합니다.

% Visualize the augmented images.
augmentedData = cell(4,1);
for k = 1:4
    data = read(augmentedTrainingData);
    augmentedData{k} = insertShape(data{1},'Rectangle',data{2});
    reset(augmentedTrainingData);
end
figure
montage(augmentedData,'BorderSize',10)

훈련 데이터 전처리하기

증대된 훈련 데이터를 전처리하여 훈련에 사용할 수 있도록 준비합니다.

preprocessedTrainingData = transform(augmentedTrainingData,@(data)preprocessData(data,inputSize));

전처리된 훈련 데이터를 읽어 들입니다.

data = read(preprocessedTrainingData);

영상과 경계 상자를 표시합니다.

I = data{1};
bbox = data{2};
annotatedImage = insertShape(I,'Rectangle',bbox);
annotatedImage = imresize(annotatedImage,2);
figure
imshow(annotatedImage)

YOLO v2 사물 검출기 훈련시키기

trainingOptions를 사용하여 네트워크 훈련 옵션을 지정합니다. 'CheckpointPath'를 임시 위치로 설정합니다. 이렇게 하면 훈련 과정 도중에 부분적으로 훈련된 검출기를 저장할 수 있습니다. 정전이나 시스템 장애 등으로 인해 훈련이 중단될 경우, 저장된 검사 지점에서 훈련을 재개할 수 있습니다.

options = trainingOptions('sgdm', ...
        'MiniBatchSize', 16, ....
        'InitialLearnRate',1e-3, ...
        'MaxEpochs',20,...
        'CheckpointPath', tempdir, ...
        'Shuffle','never');

doTraining이 true인 경우, trainYOLOv2ObjectDetector 함수를 사용하여 YOLO v2 사물 검출기를 훈련시키십시오. 그렇지 않은 경우는 사전 훈련된 네트워크를 불러오십시오.

if doTraining       
    % Train the YOLO v2 detector.
    [detector,info] = trainYOLOv2ObjectDetector(preprocessedTrainingData,lgraph,options);
else
    % Load pretrained detector for the example.
    pretrained = load('yolov2ResNet50VehicleExample_19b.mat');
    detector = pretrained.detector;
end

이 예제는 12GB의 메모리가 탑재된 NVIDA™ Titan X GPU에서 검증되었습니다. GPU 메모리가 이보다 적으면 메모리 부족이 발생할 수 있습니다. 메모리 부족이 발생할 경우 trainingOptions 함수를 사용하여 'MiniBatchSize'를 줄이십시오. 이 설정을 사용하여 이 네트워크를 훈련시키는 데 약 7분이 걸렸습니다. 훈련 시간은 사용하는 하드웨어에 따라 달라집니다.

짧게 테스트해 보려면 하나의 테스트 영상에 대해 검출기를 실행하십시오. 훈련 영상의 크기와 같게 영상을 크기 조정하는 것을 잊지 마십시오.

I = imread(testDataTbl.imageFilename{1});
I = imresize(I,inputSize(1:2));
[bboxes,scores] = detect(detector,I);

결과를 표시합니다.

I = insertObjectAnnotation(I,'rectangle',bboxes,scores);
figure
imshow(I)

테스트 세트를 사용하여 검출기 평가하기

훈련된 사물 검출기를 대규모 영상 세트를 대상으로 평가하여 성능을 측정합니다. Computer Vision Toolbox™에는 평균 정밀도(evaluateDetectionPrecision), 로그-평균 미검출율(evaluateDetectionMissRate)과 같은 통상적인 측정 기준을 측정해주는 사물 검출기 평가 함수가 있습니다. 이 예제에서는 평균 정밀도를 측정 기준으로 사용하여 성능을 평가합니다. 평균 정밀도는 검출기가 올바른 분류를 수행하는 능력(정밀도)과 모든 관련 사물을 찾는 능력(재현율)을 통합하여 하나의 수치로 나타냅니다.

훈련 데이터와 동일한 전처리 변환을 테스트 데이터에 적용합니다. 테스트 데이터에는 데이터 증대가 적용되지 않는다는 것에 유의하십시오. 테스트 데이터는 원본 데이터를 대표해야 하므로 편향되지 않은 평가를 위해 수정되지 않은 상태로 남겨 둡니다.

preprocessedTestData = transform(testData,@(data)preprocessData(data,inputSize));

모든 테스트 영상에 대해 검출기를 실행합니다.

detectionResults = detect(detector, preprocessedTestData);

평균 정밀도를 측정 기준으로 사용하여 사물 검출기를 평가합니다.

[ap,recall,precision] = evaluateDetectionPrecision(detectionResults, preprocessedTestData);

정밀도/재현율(PR) 곡선은 각기 다른 재현율 수준에서의 검출기의 정밀도를 나타냅니다. 이상적인 정밀도는 모든 재현율 수준에서 1입니다. 더 많은 데이터를 사용하면 평균 정밀도를 개선하는 데 도움이 될 수 있으나 더 많은 훈련 시간이 필요할 수 있습니다. PR 곡선을 플로팅합니다.

figure
plot(recall,precision)
xlabel('Recall')
ylabel('Precision')
grid on
title(sprintf('Average Precision = %.2f',ap))

코드 생성

검출기의 훈련과 평가를 마친 후에는 GPU Coder™를 사용하여 yolov2ObjectDetector에 대한 코드를 생성할 수 있습니다. 자세한 내용은 Code Generation for Object Detection by Using YOLO v2 (GPU Coder) 예제를 참조하십시오.

지원 함수

function B = augmentData(A)
% Apply random horizontal flipping, and random X/Y scaling. Boxes that get
% scaled outside the bounds are clipped if the overlap is above 0.25. Also,
% jitter image color.
B = cell(size(A));

I = A{1};
sz = size(I);
if numel(sz)==3 && sz(3) == 3
    I = jitterColorHSV(I,...
        'Contrast',0.2,...
        'Hue',0,...
        'Saturation',0.1,...
        'Brightness',0.2);
end

% Randomly flip and scale image.
tform = randomAffine2d('XReflection',true,'Scale',[1 1.1]);
rout = affineOutputView(sz,tform,'BoundsStyle','CenterOutput');
B{1} = imwarp(I,tform,'OutputView',rout);

% Apply same transform to boxes.
[B{2},indices] = bboxwarp(A{2},tform,rout,'OverlapThreshold',0.25);
B{3} = A{3}(indices);

% Return original data only when all boxes are removed by warping.
if isempty(indices)
    B = A;
end
end

function data = preprocessData(data,targetSize)
% Resize image and bounding boxes to the targetSize.
scale = targetSize(1:2)./size(data{1},[1 2]);
data{1} = imresize(data{1},targetSize(1:2));
data{2} = bboxresize(data{2},scale);
end

참고 문헌

[1] Redmon, Joseph, and Ali Farhadi. "YOLO9000: Better, Faster, Stronger." 2017 IEEE Conference on Computer Vision and Pattern Recognition (CVPR). IEEE, 2017.