Main Content

このページの内容は最新ではありません。最新版の英語を参照するには、ここをクリックします。

YOLO v4 深層学習を使用したオブジェクトの検出

この例では、You Only Look Once version 4 (YOLO v4) 深層学習ネットワークを使用して、イメージ内のオブジェクトを検出する方法を説明します。この例では、次の作業を行います。

  • YOLO v4 オブジェクト検出ネットワークの学習、検証、テスト用のデータセットを構成。また、ネットワーク効率を向上させるため、学習データセットのデータ拡張を実行。

  • YOLO v4 オブジェクト検出ネットワークの学習で使用するため、学習データからアンカー ボックスを計算。

  • 関数 yolov4ObjectDetector を使用して YOLO v4 オブジェクト検出器を作成し、関数 trainYOLOv4ObjectDetector を使用して検出器の学習を実行。

この例では、イメージに含まれる車両を検出するための事前学習済みの YOLO v4 オブジェクト検出器についても説明します。この事前学習済みのネットワークは、バックボーン ネットワークとして CSPDarkNet-53 を使用し、車両データセットで学習を行っています。YOLO v4 オブジェクト検出ネットワークの詳細については、YOLO v4 入門を参照してください。

データセットの読み込み

この例では、295 個のイメージを含んだ小さな車両データセットを使用します。これらのイメージの多くは、Caltech の Cars 1999 データセットおよび Cars 2001 データセットからのものです (Caltech Computational Vision の Web サイトで入手可能)。Pietro Perona 氏によって作成されたもので、許可を得て使用しています。各イメージには、1 個または 2 個のラベル付けされた車両インスタンスが含まれています。小さなデータセットは YOLO v4 の学習手順を調べるうえで役立ちますが、実際にロバストな検出器に学習させるにはラベル付けされたイメージがより多く必要になります。

車両のイメージを解凍し、車両のグラウンド トゥルース データを読み込みます。

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

車両データは 2 列の table に保存されています。1 列目にはイメージ ファイルのパスが含まれ、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 fullpath 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);

validateInputData を使用して、次のような無効なイメージ、境界ボックス、またはラベルを検出します。

  • 無効なイメージ形式であるか、NaN を含むサンプル

  • ゼロ/NaN/Inf を含むか、空である境界ボックス

  • 欠損ラベル/非カテゴリカル ラベル。

境界ボックスの値は、有限の正の整数でなければならず、かつ NaN であってはなりません。境界ボックスの高さと幅の値は、正でなければならず、イメージ境界の内側に収まっていなければなりません。

validateInputData(trainingData);
validateInputData(validationData);
validateInputData(testData);

学習イメージとボックス ラベルのうちの 1 つを表示します。

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

reset(trainingData);

YOLO v4 オブジェクト検出器ネットワークの作成

学習に使用するネットワーク入力サイズを指定します。

inputSize = [608 608 3];

検出するオブジェクト クラスの名前を指定します。

className = "vehicle";

関数estimateAnchorBoxesを使用して、学習データ内のオブジェクトのサイズに基づいてアンカー ボックスを推定します。学習前のイメージのサイズ変更を考慮するには、アンカー ボックスを推定する学習データのサイズを変更します。transform を使用して学習データの前処理を行い、アンカー ボックスの数を定義してアンカー ボックスを推定します。補助関数 preprocessData を使用して、ネットワークの入力サイズに合わせて学習データのサイズを変更します。

rng("default")
trainingDataForEstimation = transform(trainingData,@(data)preprocessData(data,inputSize));
numAnchors = 9;
[anchors,meanIoU] = estimateAnchorBoxes(trainingDataForEstimation,numAnchors);

すべての検出ヘッドで使用する anchorBoxes を指定します。anchorBoxes は、M 行 1 列の cell 配列です。ここで、M は検出ヘッドの数を表します。各検出ヘッドは、anchors の N 行 2 列の行列で構成されます。ここで、N は使用するアンカーの数です。特徴マップのサイズに基づいて、各検出ヘッドの anchorBoxes を選択します。スケールが小さい場合は大きい anchors を使用し、スケールが大きい場合は小さい anchors を使用します。これを行うには、より大きいアンカー ボックスが先頭に来るように anchors を並べ替えてから、最初の 3 つを最初の検出ヘッドに割り当て、次の 3 つを 2 番目の検出ヘッドに割り当て、最後の 3 つを 3 番目の検出ヘッドに割り当てます。

area = anchors(:, 1).*anchors(:,2);
[~,idx] = sort(area,"descend");

anchors = anchors(idx,:);
anchorBoxes = {anchors(1:3,:)
    anchors(4:6,:)
    anchors(7:9,:)
    };

アンカー ボックスの選択の詳細については、学習データからのアンカー ボックスの推定 (Computer Vision Toolbox™) およびアンカー ボックスによるオブジェクトの検出を参照してください。

関数 yolov4ObjectDetector を使用して、YOLO v4 オブジェクト検出器を作成します。COCO データセットで学習させた事前学習済みの YOLO v4 検出ネットワークの名前を指定します。クラス名および推定されるアンカー ボックスを指定します。

detector = yolov4ObjectDetector("csp-darknet53-coco",className,anchorBoxes,InputSize=inputSize);

データ拡張の実行

学習精度を上げるため、データ拡張を実行します。関数 transform を使用して、カスタムのデータ拡張を学習データに適用します。補助関数 augmentData によって、入力データに以下の拡張が適用されます。

  • HSV 空間でのカラー ジッターの付加

  • 水平方向のランダムな反転

  • 10% のランダムなスケーリング

データ拡張は、テスト データと検証データには適用されないことに注意してください。理想的には、テスト データと検証データは元のデータを代表するもので、バイアスのない評価を行うために変更なしで使用されなければなりません。

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)

学習オプションの指定

trainingOptions を使用してネットワーク学習オプションを指定します。Adam ソルバーを使用して、一定の学習率 0.001 でオブジェクト検出器を 70 エポック学習させます。"ResetInputNormalization" を false に設定し、"BatchNormalizationStatistics" を "moving" に設定する必要があります。"ValidationData" を検証データに設定し、"ValidationFrequency" を 1000 に設定します。データをより頻繁に検証するには、"ValidationFrequency" を減らして学習時間を増やします。"ExecutionEnvironment" を使用して、ネットワークの学習に使用するハードウェア リソースを決定します。既定値は "auto" です。この設定では、利用可能な場合は GPU が選択され、そうでない場合は CPU が選択されます。"CheckpointPath" を一時的な場所に設定します。これにより、学習プロセス中に部分的に学習させた検出器を保存できます。停電やシステム障害などで学習が中断された場合に、保存したチェックポイントから学習を再開できます。

options = trainingOptions("adam",...
    GradientDecayFactor=0.9,...
    SquaredGradientDecayFactor=0.999,...
    InitialLearnRate=0.001,...
    LearnRateSchedule="none",...
    MiniBatchSize=4,...
    L2Regularization=0.0005,...
    MaxEpochs=70,...
    BatchNormalizationStatistics="moving",...
    DispatchInBackground=true,...
    ResetInputNormalization=false,...
    Shuffle="every-epoch",...
    VerboseFrequency=20,...
    ValidationFrequency=1000,...
    CheckpointPath=tempdir,...
    ValidationData=validationData);

YOLO v4 オブジェクト検出器の学習

関数 trainYOLOv4ObjectDetector を使用して YOLO v4 オブジェクト検出器に学習させます。この例は、24 GB メモリ搭載の NVIDIA™ Titan RTX GPU で実行されます。この設定を使用してこのネットワークに学習させるのに約 6 時間かかりました。学習時間は使用するハードウェアによって異なります。ネットワークに学習させる代わりに、Computer Vision Toolbox ™ に用意されている事前学習済みの YOLO v4 オブジェクト検出器を使用することもできます。

補助関数 downloadPretrainedYOLOv4Detector を使用して、事前学習済みの検出器をダウンロードします。doTraining の値を false に設定します。拡張した学習データで検出器に学習させる場合は、doTraining の値を true に設定します。

doTraining = false;
if doTraining       
    % Train the YOLO v4 detector.
    [detector,info] = trainYOLOv4ObjectDetector(augmentedTrainingData,detector,options);
else
    % Load pretrained detector for the example.
    detector = downloadPretrainedYOLOv4Detector();
end
Downloading pretrained detector...

テスト イメージに対して検出器を実行します。

I = imread("highway.png");
[bboxes,scores,labels] = detect(detector,I);

結果を表示します。

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

テスト セットを使用した検出器の評価

大規模なイメージ セットで学習済みのオブジェクト検出器を評価し、パフォーマンスを測定します。Computer Vision Toolbox™ には、平均適合率や対数平均ミス率などの一般的なメトリクスを測定するためのオブジェクト検出器評価関数 (evaluateObjectDetection) が用意されています。この例では、平均適合率メトリクスを使用してパフォーマンスを評価します。平均適合率は、検出器が正しい分類を実行できること (適合率) と検出器がすべての関連オブジェクトを検出できること (再現率) を示す単一の数値です。

すべてのテスト イメージに対して検出器を実行します。

detectionResults = detect(detector,testData);

平均適合率メトリクスを使用してオブジェクト検出器を評価します。

metrics = evaluateObjectDetection(detectionResults,testData);
classID = 1;
precision = metrics.ClassMetrics.Precision{classID};
recall = metrics.ClassMetrics.Recall{classID};

適合率/再現率 (PR) の曲線は、さまざまなレベルの再現率における検出器の適合率を示しています。すべてのレベルの再現率で適合率が 1 になるのが理想的です。より多くのデータを使用すると平均適合率を向上できますが、学習に必要な時間が長くなる場合があります。PR 曲線をプロットします。

figure
plot(recall,precision)
xlabel("Recall")
ylabel("Precision")
grid on
title(sprintf("Average Precision = %.2f",metrics.ClassMetrics.mAP(classID)))

サポート関数

データ拡張を実行するための補助関数。

function data = 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.

data = cell(size(A));
for ii = 1:size(A,1)
    I = A{ii,1};
    bboxes = A{ii,2};
    labels = A{ii,3};
    sz = size(I);

    if numel(sz) == 3 && sz(3) == 3
        I = jitterColorHSV(I,...
            contrast=0.0,...
            Hue=0.1,...
            Saturation=0.2,...
            Brightness=0.2);
    end
    
    % Randomly flip image.
    tform = randomAffine2d(XReflection=true,Scale=[1 1.1]);
    rout = affineOutputView(sz,tform,BoundsStyle="centerOutput");
    I = imwarp(I,tform,OutputView=rout);
    
    % Apply same transform to boxes.
    [bboxes,indices] = bboxwarp(bboxes,tform,rout,OverlapThreshold=0.25);
    labels = labels(indices);
    
    % Return original data only when all boxes are removed by warping.
    if isempty(indices)
        data(ii,:) = A(ii,:);
    else
        data(ii,:) = {I,bboxes,labels};
    end
end
end

function data = preprocessData(data,targetSize)
% Resize the images and scale the pixels to between 0 and 1. Also scale the
% corresponding bounding boxes.

for ii = 1:size(data,1)
    I = data{ii,1};
    imgSize = size(I);
    
    bboxes = data{ii,2};

    I = im2single(imresize(I,targetSize(1:2)));
    scale = targetSize(1:2)./imgSize(1:2);
    bboxes = bboxresize(bboxes,scale);
    
    data(ii,1:2) = {I,bboxes};
end
end

事前学習済みの YOLO v4 オブジェクト検出器をダウンロードするための補助関数。

function detector = downloadPretrainedYOLOv4Detector()
% Download a pretrained yolov4 detector.
if ~exist("yolov4CSPDarknet53VehicleExample_22a.mat", "file")
    if ~exist("yolov4CSPDarknet53VehicleExample_22a.zip", "file")
        disp("Downloading pretrained detector...");
        pretrainedURL = "https://ssd.mathworks.com/supportfiles/vision/data/yolov4CSPDarknet53VehicleExample_22a.zip";
        websave("yolov4CSPDarknet53VehicleExample_22a.zip", pretrainedURL);
    end
    unzip("yolov4CSPDarknet53VehicleExample_22a.zip");
end
pretrained = load("yolov4CSPDarknet53VehicleExample_22a.mat");
detector = pretrained.detector;
end

参照

[1] Alexey Bochkovskiy, Chien-Yao Wang, and Hong-Yuan Mark Liao. “YOLOv4: Optimal Speed and Accuracy of Object Detection.” 2020, arXiv:2004.10934. https://arxiv.org/abs/2004.10934.

参考

| | | | (Deep Learning Toolbox) |

関連する例

詳細