YOLO v2 딥러닝을 사용한 객체 검출
이 예제에서는 YOLO(You Only Look Once) v2 객체 검출기를 훈련시키는 방법을 다룹니다.
딥러닝은 강건한 객체 검출기를 훈련시키는 데 사용할 수 있는 강력한 머신러닝 기법입니다. Faster R-CNN과 YOLO(You Only Look Once) v2를 비롯한 다양한 객체 검출 기법이 있습니다. 이 예제에서는 trainYOLOv2ObjectDetector
함수를 사용하여 YOLO v2 차량 검출기를 훈련시킵니다. 자세한 내용은 Getting Started with YOLO v2 (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
Downloading pretrained detector (98 MB)...
데이터셋 불러오기
이 예제에서는 295개의 영상을 포함하는 소규모의 차량 데이터셋을 사용합니다. 이러한 영상의 많은 부분은 Caltech Cars 1999 및 2001 데이터 세트에서 가져온 것으로, Pietro Perona가 만들었으며 허락 하에 사용되었습니다. 각 영상에는 차량에 대해 레이블 지정된 건수가 한 건 또는 두 건 있습니다. 작은 데이터셋은 YOLO v2 훈련 절차를 살펴보기에 유용하지만, 실전에서 강건한 검출기를 훈련시키려면 레이블이 지정된 영상이 더 많이 필요합니다. 차량 영상의 압축을 풀고 차량 ground truth 데이터를 불러옵니다.
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'} {[220 136 35 28]}
{'vehicleImages/image_00002.jpg'} {[175 126 61 45]}
{'vehicleImages/image_00003.jpg'} {[108 120 45 33]}
{'vehicleImages/image_00004.jpg'} {[124 112 38 36]}
% Add the full path to the local vehicle data folder.
vehicleDataset.imageFilename = fullfile(pwd,vehicleDataset.imageFilename);
데이터셋을 훈련 세트, 검증 세트, 테스트 세트로 분할합니다. 데이터의 60%를 훈련용으로, 데이터의 10%를 검증용으로, 나머지를 훈련된 검출기의 테스트용으로 선택합니다.
rng("default");
shuffledIndices = randperm(height(vehicleDataset));
idx = floor(0.6 * length(shuffledIndices) );
trainingIdx = 1:idx;
trainingDataTbl = vehicleDataset(shuffledIndices(trainingIdx),:);
validationIdx = idx+1 : idx + 1 + floor(0.1 * length(shuffledIndices) );
validationDataTbl = vehicleDataset(shuffledIndices(validationIdx),:);
testIdx = validationIdx(end)+1 : length(shuffledIndices);
testDataTbl = vehicleDataset(shuffledIndices(testIdx),:);
imageDatastore와 boxLabelDatastore를 사용하여 훈련과 평가 과정에서 영상 및 레이블 데이터를 불러오기 위한 데이터저장소를 만듭니다.
imdsTrain = imageDatastore(trainingDataTbl{:,"imageFilename"}); bldsTrain = boxLabelDatastore(trainingDataTbl(:,"vehicle")); imdsValidation = imageDatastore(validationDataTbl{:,"imageFilename"}); bldsValidation = boxLabelDatastore(validationDataTbl(:,"vehicle")); imdsTest = imageDatastore(testDataTbl{:,"imageFilename"}); bldsTest = boxLabelDatastore(testDataTbl(:,"vehicle"));
영상 데이터저장소와 상자 레이블 데이터저장소를 결합합니다.
trainingData = combine(imdsTrain,bldsTrain); validationData = combine(imdsValidation,bldsValidation); 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 전용 계층으로 구성됩니다.
먼저 신경망 입력 크기와 클래스 개수를 지정합니다. 신경망 입력 크기를 선택할 때는 신경망 자체에서 요구되는 최소 크기, 훈련 영상의 크기, 그리고 선택한 크기에서 데이터를 처리할 때 발생하는 계산 비용을 고려해야 합니다. 가능하다면, 훈련 영상의 크기와 가깝고 신경망에서 요구되는 입력 크기보다 큰 신경망 입력 크기를 선택하십시오. 예제를 실행하는 데 소요되는 계산 비용을 줄이려면 신경망을 실행하는 데 필요한 최소 크기인 [224 224 3]으로 신경망 입력 크기를 지정하십시오.
inputSize = [224 224 3];
검출할 객체 클래스의 이름을 지정합니다.
classes = "vehicle";
이 예제에서 사용되는 훈련 영상은 크기가 다양하고 신경망의 입력 크기인 224×224보다 큽니다. 이 문제를 해결하려면 훈련하기 전에 전처리 단계에서 영상의 크기를 조정해야 합니다.
다음으로, estimateAnchorBoxes
(Computer Vision Toolbox)를 사용하여 훈련 데이터의 사물 크기를 기반으로 앵커 상자를 추정합니다. 훈련 전 이루어지는 영상 크기 조정을 고려하기 위해 앵커 상자 추정에 사용하는 훈련 데이터를 크기 조정하십시오. transform
을 사용하여 훈련 데이터를 전처리한 후에 앵커 상자의 개수를 정의하고 앵커 상자를 추정합니다. 지원 함수 preprocessData
를 사용하여 훈련 데이터를 신경망의 입력 영상 크기로 크기 조정합니다.
trainingDataForEstimation = transform(trainingData,@(data)preprocessData(data,inputSize)); numAnchors = 7; [anchorBoxes,meanIoU] = estimateAnchorBoxes(trainingDataForEstimation,numAnchors)
anchorBoxes = 7×2
40 38
156 127
74 71
135 121
36 25
56 52
98 89
meanIoU = 0.8383
앵커 상자 선택에 관한 자세한 내용은 Estimate Anchor Boxes From Training Data (Computer Vision Toolbox)(Computer Vision Toolbox™) 및 객체 검출용 앵커 상자 (Computer Vision Toolbox) 항목을 참조하십시오.
사전 훈련된 ResNet-50 모델을 불러옵니다.
baseNet = imagePretrainedNetwork("resnet50");
"activation_40_relu"
뒤에 오는 계층들을 검출 하위 신경망으로 교체할 검출 신경망 소스로 "activation_40_relu"
를 선택합니다. 이 검출 신경망 소스 계층은 16배만큼 다운샘플링된 특징 맵을 출력합니다. 이 정도의 다운샘플링은 공간 분해능과 추출된 특징의 강도 사이를 적절히 절충한 값입니다. 신경망의 더 아래쪽에서 추출된 특징은 더 강한 영상 특징을 인코딩하나 공간 분해능이 줄어들기 때문입니다. 최적의 특징 추출 계층을 선택하려면 경험적 분석이 필요합니다.
detectionSource = "activation_40_relu";
YOLO v2 객체 검출 신경망을 만듭니다.
detector = yolov2ObjectDetector(baseNet,classes,anchorBoxes,DetectionNetworkSource=detectionSource);
analyzeNetwork
또는 Deep Learning Toolbox™의 심층 신경망 디자이너를 사용하여 신경망을 시각화할 수 있습니다.
데이터 증강
데이터 증강은 훈련 중에 원본 데이터를 무작위로 변환함으로써 신경망 정확도를 개선하는 데 사용됩니다. 데이터 증강을 사용하면 레이블이 지정된 훈련 샘플의 개수를 늘리지 않고도 훈련 데이터에 다양성을 더할 수 있습니다.
transform
을 사용하여 영상과 영상에 해당하는 상자 레이블을 가로 방향으로 무작위로 뒤집어서 훈련 데이터를 증강합니다. 테스트 데이터와 검증 데이터에는 데이터 증강이 적용되지 않는다는 것에 유의하십시오. 이상적인 경우라면 테스트 데이터와 검증 데이터가 원본 데이터를 대표해야 하므로 편향되지 않은 평가를 위해 수정되지 않은 상태로 남겨 두는 것이 좋습니다.
augmentedTrainingData = transform(trainingData,@augmentData);
동일한 영상을 여러 차례 읽어 들이고 증강된 훈련 데이터를 표시합니다.
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)); preprocessedValidationData = transform(validationData,@(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
를 사용하여 신경망 훈련 옵션을 지정합니다. ValidationData
를 전처리된 검증 데이터로 설정합니다. CheckpointPath
를 임시 위치로 설정합니다. 이렇게 하면 훈련 과정 도중에 부분적으로 훈련된 검출기를 저장할 수 있습니다. 정전이나 시스템 장애 등으로 인해 훈련이 중단될 경우, 저장된 검사 지점에서 훈련을 재개할 수 있습니다.
options = trainingOptions("adam", ... MiniBatchSize=16, .... InitialLearnRate=1e-3, ... MaxEpochs=10, ... CheckpointPath=tempdir, ... ValidationData=preprocessedValidationData);
doTraining
이 true인 경우, trainYOLOv2ObjectDetector
(Computer Vision Toolbox) 함수를 사용하여 YOLO v2 객체 검출기를 훈련시킵니다. 그렇지 않은 경우에는 사전 훈련된 신경망을 불러옵니다.
if doTraining % Train the YOLO v2 detector. [detector,info] = trainYOLOv2ObjectDetector(preprocessedTrainingData,detector,options); else % Load pretrained detector for the example. pretrained = load("yolov2ResNet50VehicleExample_19b.mat"); detector = pretrained.detector; end
12GB의 메모리가 탑재된 NVIDIA™ Titan X GPU를 사용하여 이 신경망을 훈련시키는 데 약 7분이 걸렸습니다. 훈련 시간은 사용하는 하드웨어에 따라 달라집니다. GPU 메모리가 이보다 적으면 메모리 부족이 발생할 수 있습니다. 메모리 부족이 발생할 경우 trainingOptions
함수를 사용하여 MiniBatchSize
를 줄이십시오.
짧게 테스트해 보려면 하나의 테스트 영상에 대해 검출기를 실행하십시오. 훈련 영상의 크기와 같게 영상을 크기 조정하는 것을 잊지 마십시오.
I = imread("highway.png");
I = imresize(I,inputSize(1:2));
[bboxes,scores] = detect(detector,I);
결과를 표시합니다.
I = insertObjectAnnotation(I,"rectangle",bboxes,scores);
figure
imshow(I)
테스트 세트를 사용하여 검출기 평가하기
훈련된 객체 검출기를 대규모 영상 세트를 대상으로 평가하여 성능을 측정합니다. Computer Vision Toolbox™에는 평균 정밀도, 로그-평균 미검출율과 같은 통상적인 메트릭을 측정해 주는 객체 검출기 평가 함수(evaluateObjectDetection
(Computer Vision Toolbox))가 있습니다. 이 예제에서는 평균 정밀도를 메트릭으로 사용하여 성능을 평가합니다. 평균 정밀도는 검출기가 올바른 분류를 수행하는 능력(정밀도)과 모든 관련 사물을 찾는 능력(재현율)을 통합하여 하나의 수치로 나타냅니다.
훈련 데이터와 동일한 전처리 변환을 테스트 데이터에 적용합니다. 테스트 데이터에는 데이터 증강이 적용되지 않는다는 것에 유의하십시오. 테스트 데이터는 원본 데이터를 대표해야 하므로 편향되지 않은 평가를 위해 수정되지 않은 상태로 남겨 둡니다.
preprocessedTestData = transform(testData,@(data)preprocessData(data,inputSize));
모든 테스트 영상에 대해 검출기를 실행합니다. 가능한 한 많은 객체를 검출하려면 검출 임계값을 낮은 값으로 설정하십시오. 이렇게 하면 전체 재현율 값 범위에서 검출기 정밀도를 평가하는 데 도움이 됩니다.
detectionThreshold = 0.01; detectionResults = detect(detector,preprocessedTestData,Threshold=detectionThreshold);
테스트 데이터 세트에 대해 객체 검출기를 평가합니다.
metrics = evaluateObjectDetection(detectionResults,preprocessedTestData);
평균 정밀도(AP) 메트릭과 정밀도/재현율(PR) 곡선을 계산합니다. 정밀도/재현율 곡선은 각기 다른 재현율 수준에서의 검출기의 정밀도를 나타냅니다. 이상적인 정밀도는 모든 재현율 수준에서 1입니다. 더 많은 데이터를 사용하면 평균 정밀도를 개선하는 데 도움이 될 수 있으나 더 많은 훈련 시간이 필요할 수 있습니다.
AP = averagePrecision(metrics,ClassName="vehicle"); [precision, recall] = precisionRecall(metrics,ClassName="vehicle");
PR 곡선을 플로팅하고 AP를 표시합니다.
figure plot(recall{:},precision{:}) xlabel("Recall") ylabel("Precision") grid on title("Average Precision = "+AP)
코드 생성
검출기의 훈련과 평가를 마친 후에는 GPU Coder™를 사용하여 yolov2ObjectDetector
에 대한 코드를 생성할 수 있습니다. 자세한 내용은 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); % Sanitize boxes, if needed. This helper function is attached as a % supporting file. Open the example in MATLAB to access this function. A{2} = helperSanitizeBoxes(A{2}); % 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. sz = size(data{1},[1 2]); scale = targetSize(1:2)./sz; data{1} = imresize(data{1},targetSize(1:2)); % Sanitize boxes, if needed. This helper function is attached as a % supporting file. Open the example in MATLAB to access this function. data{2} = helperSanitizeBoxes(data{2}); % Resize boxes to new image size. data{2} = bboxresize(data{2},scale); end
참고 문헌
[1] Redmon, Joseph, and Ali Farhadi. "YOLO9000: Better, Faster, Stronger." In 2017 IEEE Conference on Computer Vision and Pattern Recognition (CVPR), 6517–25. Honolulu, HI: IEEE, 2017. https://doi.org/10.1109/CVPR.2017.690.