Main Content

このページの翻訳は最新ではありません。ここをクリックして、英語の最新版を参照してください。

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

この例では、シングル ショット検出器 (SSD) に学習させる方法を説明します。

概要

深層学習は、検出タスクに必要なイメージの特徴を自動的に学習する強力な機械学習手法です。深層学習を使用したオブジェクト検出には、Faster R-CNN、You Only Look Once (YOLO v2)、SSD など、いくつかの方法があります。この例では、関数 trainSSDObjectDetector を使用して SSD 車両検出器に学習させます。詳細については、オブジェクトの検出 (Computer Vision Toolbox)を参照してください。

事前学習済みの検出器のダウンロード

学習の完了を待たなくて済むように、事前学習済みの検出器をダウンロードします。検出器に学習させる場合は、変数 doTraining を true に設定します。

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

データセットの読み込み

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

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

学習データはテーブルに保存されています。最初の列には、イメージ ファイルへのパスが含まれています。残りの列には、車両の ROI ラベルが含まれています。データの最初の数行を表示します。

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]}

データセットを、検出器を学習させるための学習セットと検出器を評価するためのテスト セットに分割します。データの 60% を学習用に選択します。残りを評価用に使用します。

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

imageDatastore および boxLabelDatastore を使用して、学習および評価中にイメージとラベル データを読み込みます。

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

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

イメージ データストアとボックス ラベル データストアを組み合わせます。

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

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

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

SSD オブジェクト検出ネットワークの作成

SSD オブジェクト検出ネットワークは、2 つのサブネットワークをもつと考えることができます。特徴抽出ネットワークに検出ネットワークが続きます。

通常、特徴抽出ネットワークは事前学習済みの CNN です (詳細については事前学習済みの深層ニューラル ネットワークを参照)。この例では特徴抽出に ResNet-50 を使用します。用途の要件によって、MobileNet v2 や ResNet-18 など、その他の事前学習済みのネットワークも使用できます。検出サブネットワークは特徴抽出ネットワークと比べて小さい CNN であり、少数の畳み込み層と SSD に固有の層で構成されます。

関数 ssdLayers を使用して、事前学習済みの ResNet-50 ネットワークを SSD オブジェクト検出ネットワークに自動的に変更します。ssdLayers では、SSD ネットワークをパラメーター化するためのいくつかの入力 (ネットワーク入力サイズやクラス数など) を指定する必要があります。ネットワーク入力サイズを選択する際には、学習イメージのサイズ、および選択したサイズでのデータの処理によって発生する計算コストを考慮します。可能な場合、学習イメージのサイズに近いネットワーク入力サイズを選択します。ただし、この例を実行する計算コストを削減するため、ネットワーク入力サイズを [300 300 3] に指定します。学習中、trainSSDObjectDetector によって、ネットワーク入力サイズに合わせて学習イメージのサイズが自動的に変更されます。

inputSize = [300 300 3];

検出するオブジェクト クラスの数を定義します。

numClasses = width(vehicleDataset)-1;

SSD オブジェクト検出ネットワークを作成します。

lgraph = ssdLayers(inputSize, numClasses, 'resnet50');

Deep Learning Toolbox™ から analyzeNetwork または DeepNetworkDesigner を使用してネットワークを可視化できます。カスタム SSD ネットワークを層単位で作成することもできます。詳細については、SSD オブジェクト検出ネットワークの作成 (Computer Vision 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));

前処理された学習データを読み取ります。

data = read(preprocessedTrainingData);

イメージと境界ボックスを表示します。

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

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

trainingOptions を使用してネットワーク学習オプションを指定します。'CheckpointPath' を一時的な場所に設定します。これにより、学習プロセス中に部分的に学習させた検出器を保存できます。停電やシステム障害などで学習が中断された場合に、保存したチェックポイントから学習を再開できます。

options = trainingOptions('sgdm', ...
        'MiniBatchSize', 16, ....
        'InitialLearnRate',1e-1, ...
        'LearnRateSchedule', 'piecewise', ...
        'LearnRateDropPeriod', 30, ...
        'LearnRateDropFactor', 0.8, ...
        'MaxEpochs', 300, ...
        'VerboseFrequency', 50, ...        
        'CheckpointPath', tempdir, ...
        'Shuffle','every-epoch');

doTraining が true の場合、関数 trainSSDObjectDetector (Computer Vision Toolbox) を使用して、SSD オブジェクト検出器に学習させます。そうでない場合は、事前学習済みのネットワークを読み込みます。

if doTraining
    % Train the SSD detector.
    [detector, info] = trainSSDObjectDetector(preprocessedTrainingData,lgraph,options);
else
    % Load pretrained detector for the example.
    pretrained = load('ssdResNet50VehicleExample_20a.mat');
    detector = pretrained.detector;
end

この例は、12 GB メモリ搭載の NVIDIA™ Titan X GPU で検証済みです。GPU のメモリがこれより少ない場合、メモリ不足が発生する可能性があります。これが発生した場合は、関数 trainingOptions を使用して 'MiniBatchSize' を減らします。この設定を使用してこのネットワークに学習させるのに約 2 時間かかりました。学習所要時間は使用するハードウェアによって異なります。

迅速なテストとして、1 つのテスト イメージ上で検出器を実行します。

data = read(testData);
I = data{1,1};
I = imresize(I,inputSize(1:2));
[bboxes,scores] = detect(detector,I, 'Threshold', 0.4);

結果を表示します。

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

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

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

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

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

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

detectionResults = detect(detector, preprocessedTestData, 'Threshold', 0.4);

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

[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™ を使用して ssdObjectDetector のコードを生成できます。詳細については、シングル ショット マルチボックス検出器を使用したオブジェクト検出のコードの生成 (Computer Vision Toolbox)の例を参照してください。

サポート関数

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.
A{2} = helperSanitizeBoxes(A{2}, sz);
    
% 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.
data{2} = helperSanitizeBoxes(data{2}, sz);

% Resize boxes.
data{2} = bboxresize(data{2},scale);
end

参考文献

[1] Liu, Wei, Dragomir Anguelov, Dumitru Erhan, Christian Szegedy, Scott Reed, Cheng Yang Fu, and Alexander C. Berg. "SSD: Single shot multibox detector." In 14th European Conference on Computer Vision, ECCV 2016. Springer Verlag, 2016.

参考

アプリ

関数

オブジェクト

関連するトピック