Main Content

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

YOLO v2 深層学習を使用したマルチクラス オブジェクト検出

この例では、カスタム データ セットに対してマルチクラス オブジェクト検出を実行する方法を示します。

概要

深層学習は、YOLO v2、YOLO v4、YOLOX、SSD、Faster R-CNN などのロバストなマルチクラス オブジェクト検出器の学習に使用できる強力な機械学習手法です。この例では、関数trainYOLOv2ObjectDetectorを使用して、YOLO v2 マルチクラス オブジェクト検出器に学習させます。学習済みのオブジェクト検出器は、複数の屋内オブジェクトの検出や識別を行うことができます。YOLOX、YOLO v4、SSD、および Faster R-CNN などの他のマルチクラス オブジェクト検出器の学習の詳細については、深層学習を使用したオブジェクト検出入門およびオブジェクト検出器の選択を参照してください。

この例では、最初に、事前学習済みの YOLO v2 オブジェクト検出器を使用してイメージ内の複数のオブジェクトを検出する方法を示します。次に、オプションでデータ セットをダウンロードし、転移学習を使用してカスタム データ セットで YOLO v2 に学習させることができます。

事前学習済みのオブジェクト検出器の読み込み

事前学習済みの YOLO v2 オブジェクト検出器をダウンロードし、ワークスペースに読み込みます。

pretrainedURL = "https://www.mathworks.com/supportfiles/vision/data/yolov2IndoorObjectDetector23b.zip";
pretrainedFolder = fullfile(tempdir,"pretrainedNetwork");
pretrainedNetworkZip = fullfile(pretrainedFolder,"yolov2IndoorObjectDetector23b.zip"); 

if ~exist(pretrainedNetworkZip,"file")
    mkdir(pretrainedFolder)
    disp("Downloading pretrained network (6 MB)...")
    websave(pretrainedNetworkZip,pretrainedURL)
end

unzip(pretrainedNetworkZip,pretrainedFolder)

pretrainedNetwork = fullfile(pretrainedFolder,"yolov2IndoorObjectDetector.mat");
pretrained = load(pretrainedNetwork);
detector = pretrained.detector;

複数の屋内オブジェクトの検出

ターゲット クラスのオブジェクトを含むテスト イメージを読み取り、そのイメージに対してオブジェクト検出器を実行し、検出結果の注釈が付けられたイメージを表示します。

I = imread("indoorTest.jpg");
[bbox,score,label] = detect(detector,I);

annotatedImage = insertObjectAnnotation(I,"rectangle",bbox,label,LineWidth=4,FontSize=24);
figure
imshow(annotatedImage)

学習用データの読み込み

この例では、Bishwo Adhikari [1] によって作成された屋内オブジェクト検出データセットを使用します。データ セットは、fire extinguisher (消火器)、chair (椅子)、clock (時計)、trash bin (ゴミ箱)、screen (画面)、および printer (プリンター) の 7 つのクラスを含む屋内シーンから収集された 2213 個のラベル付きイメージで構成されています。各イメージには、これらのクラスのラベル付きインスタンスが 1 つ以上含まれています。データ セットが既にダウンロードされているかどうかを確認し、ダウンロードされていない場合は、websave を使用してダウンロードします。

dsURL = "https://zenodo.org/record/2654485/files/Indoor%20Object%20Detection%20Dataset.zip?download=1"; 
outputFolder = fullfile(tempdir,"indoorObjectDetection"); 
imagesZip = fullfile(outputFolder,"indoor.zip");

if ~exist(imagesZip,"file")   
    mkdir(outputFolder)       
    disp("Downloading 401 MB Indoor Objects Dataset images...") 
    websave(imagesZip,dsURL)
    unzip(imagesZip,fullfile(outputFolder))  
end

データ セットのイメージを保存するための imageDatastore オブジェクトを作成します。

datapath = fullfile(outputFolder,"Indoor Object Detection Dataset");
imds = imageDatastore(datapath,IncludeSubfolders=true, FileExtensions=".jpg");

annotationsIndoor.mat ファイルには、データ内の各イメージの注釈と、学習セット、検証セット、テスト セットに使用するデータ セット イメージのインデックスを指定するベクトルが含まれています。ファイルをワークスペースに読み込み、変数 data から学習セット、検証セット、テスト セットに対応する注釈とインデックスを抽出します。6 個のイメージにはラベルが関連付けられていないため、インデックスは 2213 個のイメージではなく合計 2207 個のイメージを指定します。ラベルを含むイメージのインデックスを使用して、これらの 6 個のイメージをイメージ データストアと注釈データストアから削除します。

data = load("annotationsIndoor.mat");
blds = data.BBstore;
trainingIdx = data.trainingIdx;
validationIdx = data.validationIdx;
testIdx = data.testIdx;
cleanIdx = data.idxs;

% Remove the 6 images with no labels.
imds = subset(imds,cleanIdx);
blds = subset(blds,cleanIdx);

学習データの解析

データをより深く理解するために、オブジェクト クラスのラベルとサイズの分布を解析します。この解析は、学習データを準備する方法や、この特定のデータ セット用にオブジェクト検出器を構成する方法を決定するのに役立つため、非常に重要です。

クラス分布の解析

関数 countEachLabel を使用して、データ セット内の境界ボックス クラス ラベルの分布を測定します。

tbl = countEachLabel(blds)
tbl=7×3 table
         Label          Count    ImageCount
    ________________    _____    __________

    exit                 545        504    
    fireextinguisher    1684        818    
    chair               1662        850    
    clock                280        277    
    trashbin             228        170    
    screen               115         94    
    printer               81         81    

カウントをクラスごとに可視化します。

bar(tbl.Label,tbl.Count)
ylabel("Frequency")

このデータ セット内のクラスは不均衡です。学習プロセスでは上位クラスに有利なバイアスがかかるため、こうした不均衡が学習プロセスに悪影響を及ぼす可能性があります。不均衡に対処するには、データを追加する、過小評価されているクラスをオーバーサンプリングする、損失関数を変更する、データ拡張を適用するなどの補完的な手法を 1 つ以上使用します。どのアプローチを使用する場合でも、データ セットに最適な解決策を決定するには経験的解析を実行しなければなりません。この例では、データ拡張を使用して学習プロセスにおけるバイアスを減らします。

オブジェクト サイズの解析およびオブジェクト検出器の選択

データ セット内のすべての境界ボックスとラベルを読み取り、境界ボックスの対角線の長さを計算します。

data = readall(blds);
bboxes = vertcat(data{:,1});
labels = vertcat(data{:,2});
diagonalLength = hypot(bboxes(:,3),bboxes(:,4));

オブジェクトの長さをクラス別にグループ化します。

G = findgroups(labels);
groupedDiagonalLength = splitapply(@(x){x},diagonalLength,G);

各クラスのオブジェクトの長さの分布を可視化します。

figure
classes = tbl.Label;
numClasses = numel(classes);
for i = 1:numClasses
    len = groupedDiagonalLength{i};
    x = repelem(i,numel(len),1);
    plot(x,len,"o")
    hold on
end
hold off
ylabel("Object extent (pixels)")

xticks(1:numClasses)
xticklabels(classes)

この可視化により、どのタイプのオブジェクト検出器を構成するかを決定するのに役立つ重要なデータ セット属性が示されます。

  1. 各クラス内のオブジェクト サイズのばらつき

  2. クラス間のオブジェクト サイズのばらつき

このデータ セットでは、クラス間のサイズの範囲にかなりのオーバーラップがあります。さらに、各クラス内でのサイズのばらつきはそれほど大きくありません。これは、単一のマルチクラス検出器に学習させて、さまざまなサイズのオブジェクトを処理できることを意味します。サイズ範囲がオーバーラップしていない場合、または各オブジェクト サイズの範囲が桁違いである場合は、異なるサイズ範囲に対して複数の検出器に学習させる方が実用的です。

サイズのばらつきに基づいて、どのオブジェクト検出器に学習させるかを決定できます。各クラス内のサイズのばらつきが小さい場合は、YOLO v2 などの単一スケールのオブジェクト検出器を使用します。各クラスに大きなばらつきがある場合は、YOLO v4 や SSD などのマルチスケールのオブジェクト検出器を選択します。このデータ セットのオブジェクト サイズは同じ桁にとどまっているため、まずは YOLO v2 を使用します。高度なマルチスケール検出器はより高いパフォーマンスが得られる可能性がありますが、学習には YOLO v2 よりも多くの時間とリソースが必要になる場合があります。単純なソリューションでパフォーマンス要件を満たせない場合、より高度な検出器を使用します。

サイズ分布情報を使用して、学習イメージのサイズを選択します。固定サイズにすることで、学習中にバッチ処理ができるようになります。GPU メモリなどの学習環境のリソース制約に基づき、学習イメージのサイズによって、バッチ サイズをどの程度の大きさにするかが決まります。特に GPU を使用する場合は、より大きなデータ バッチを処理することでスループットが向上し、学習時間が短縮されます。ただし、元のデータのサイズを非常に小さいサイズに変更した場合、学習イメージのサイズがオブジェクトの解像度に影響を与える可能性があります。

次のセクションでは、このデータ セットのサイズ解析情報を使用して YOLO v2 オブジェクト検出器を構成します。

YOLO v2 オブジェクト検出器アーキテクチャの定義

次の手順を使用して、YOLO v2 オブジェクト検出器を構成します。

  1. 転移学習用に事前学習済みの検出器を選択します。

  2. 学習イメージのサイズを選択します。

  3. オブジェクトの位置とクラスの予測に使用するネットワークの特徴を選択します。

  4. オブジェクト検出器の学習に使用する前処理済みデータからアンカー ボックスを推定します。

転移学習用に事前学習済みの Tiny YOLO v2 検出器を選択します。Tiny YOLO v2 は、大規模オブジェクト検出データ セットである COCO [2] で学習済みの軽量ネットワークです。事前学習済みのオブジェクト検出器を使用した転移学習では、ネットワークに最初から学習させる場合と比べて、学習時間が短縮されます。あるいは、より大規模な Darknet-19 YOLO v2 事前学習済み検出器を使用することもできますが、大規模なネットワークで実験する前に、より単純なネットワークから始めてパフォーマンスのベースラインを確立することを検討してください。Tiny または Darknet-19 YOLO v2 の事前学習済み検出器を使用するには、Computer Vision Toolbox™ Model for YOLO v2 Object Detection が必要です。

pretrainedDetector = yolov2ObjectDetector("tiny-yolov2-coco");

次に、YOLO v2 の学習イメージのサイズを選択します。学習イメージのサイズを選択するときは、次のサイズ パラメーターを考慮します。

  1. イメージ内のオブジェクト サイズの分布と、イメージのサイズ変更がオブジェクト サイズに与える影響。

  2. 選択したサイズでデータをバッチ処理するために必要な計算リソース。

  3. ネットワークに必要な最小入力サイズ。

事前学習済みの Tiny YOLO v2 ネットワークの入力サイズを決定します。

pretrainedDetector.Network.Layers(1).InputSize
ans = 1×3

   416   416     3

屋内オブジェクト検出データセット内のイメージのサイズは [720 1024 3] です。オブジェクト解析によると、最小のオブジェクトは約 20×20 ピクセルです。

この例を実行する際の精度と計算コストのバランスを維持するため、サイズを [720 720 3] に指定します。このサイズにすることで、各イメージのサイズを変更しても、このデータ セット内のオブジェクトの空間解像度に大きな影響が及ばなくなります。この例を独自のデータ セットに適用する場合は、データに基づいて学習イメージのサイズを変更しなければなりません。最適な入力サイズを決定するには、経験的な解析が必要です。

inputSize = [720 720 3];

イメージと境界ボックスのデータストアを統合します。

ds = combine(imds,blds);

transform を使用して、イメージとそれに対応する境界ボックスのサイズを変更する前処理関数を適用します。この関数は境界ボックスをサニタイズして有効な形状に変換します。

preprocessedData = transform(ds,@(data)resizeImageAndLabel(data,inputSize));

前処理したイメージとその境界ボックス ラベルの 1 つを表示して、サイズ変更したイメージ内のオブジェクトの特徴がまだ視認できることを確認します。

data = preview(preprocessedData);
I = data{1};
bbox = data{2};
label = data{3};
imshow(I)
showShape("rectangle",bbox,Label=label)

YOLO v2 は、1 つのネットワーク層から抽出された特徴を使用してイメージ内のオブジェクトの位置とクラスを予測する、単一スケールの検出器です。特徴抽出層は、深層学習ベースのオブジェクト検出器の重要なハイパーパラメーターです。特徴抽出層を選択するときは、データ セット内のオブジェクト サイズの範囲に適した空間解像度で特徴を出力する層を選択します。

オブジェクト検出に使用されるほとんどのネットワークでは、データがネットワークを流れる際、特徴量が 2 のべき乗で空間的にダウンサンプリングされます。たとえば、ネットワークは、特定の入力サイズから開始して空間的に 4 分の 1、8 分の 1、16 分の 1、32 分の 1 にダウンサンプリングされた特徴マップを生成する層をもつことができます。データ セット内のオブジェクトのサイズが小さい場合 (たとえば、10×10 ピクセル未満)、16 分の 1 および 32 分の 1 にダウンサンプリングされた特徴マップの空間解像度は、オブジェクト位置を正確に推定するのに十分でない可能性があります。逆に、オブジェクトが大きい場合、4 分の 1 または 8 分の 1 にダウンサンプリングされた特徴マップは、それらのオブジェクトのグローバル コンテキストを十分に符号化できない可能性があります。

このデータ セットでは、16 分の 1 にダウンサンプリングされた特徴マップを出力する Tiny YOLO v2 ネットワークの "leaky_relu_5" 層を指定します。このダウンサンプリングの量は、空間分解能と抽出される特徴の強度との適切なトレードオフです (ネットワークでさらに抽出された特徴により、より強力なイメージの特徴が符号化されますが、空間分解能は低下します)。

featureLayer = "leaky_relu_5";

analyzeNetwork を使用して、Tiny YOLO v2 ネットワークを可視化し、16 分の 1 にダウンサンプリングされた特徴を出力する層の名前を決定できます。

次に、estimateAnchorBoxes を使用して、学習データからアンカー ボックスを推定します。前処理済みデータからアンカー ボックスを推定することで、選択した学習イメージのサイズに基づいて推定値を取得できます。学習データからのアンカー ボックスの推定の例に記載されている手順を使用して、データ セットに適したアンカー ボックスの数を決定できます。この手順に基づくと、5 つのアンカー ボックスが、計算コストと精度の間で適切なトレードオフになることがわかります。他のハイパーパラメーターと同様に、データのアンカー ボックスの数は、経験的な解析を使用して最適化しなければなりません。

numAnchors = 5;
aboxes = estimateAnchorBoxes(preprocessedData,numAnchors);

最後に、選択した学習イメージ サイズと推定されたアンカー ボックスを使用して、7 つのクラスで転移学習を行うように YOLO v2 ネットワークを構成します。

numClasses = 7;
pretrainedNet = pretrainedDetector.Network;
lgraph = yolov2Layers(inputSize, numClasses, aboxes, pretrainedNet, featureLayer);

関数 analyzeNetwork (Deep Learning Toolbox) または Deep Learning Toolbox™ の深層学習ネットワーク デザイナー アプリを使用して、ネットワークを可視化できます。

学習データの準備

再現性を得るために、rng を使用して乱数発生器をシード 0 で初期化し、関数 shuffle を使用してデータ セットをシャッフルします。

rng(0);
preprocessedData = shuffle(preprocessedData);

関数 subset を使用して、データ セットを学習、テスト、検証の各サブセットに分割します。

dsTrain = subset(preprocessedData,trainingIdx);
dsVal = subset(preprocessedData,validationIdx);
dsTest = subset(preprocessedData,testIdx);

データ拡張

データ拡張を使用して、学習の際に元のデータをランダムに変換することでネットワークの精度を向上させます。データ拡張では、ラベル付き学習サンプルの数を増やさずに、学習データをさらに多様化できます。transform を使用し、以下の手順で学習データを拡張します。

  • イメージおよび関連する境界ボックス ラベルを水平方向にランダムに反転。

  • イメージおよび関連する境界ボックス ラベルをランダムにスケーリング。

  • イメージの色にジッターを付加。

augmentedTrainingData = transform(dsTrain,@augmentData);

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

data = read(augmentedTrainingData);
I = data{1};
bbox = data{2};
label = data{3};
imshow(I)
showShape("rectangle",bbox,Label=label)

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

trainingOptions を使用してネットワーク学習オプションを指定します。

opts = trainingOptions("rmsprop", ...
        InitialLearnRate=0.001, ...
        MiniBatchSize=8, ...
        MaxEpochs=10, ...
        LearnRateSchedule="piecewise", ...
        LearnRateDropPeriod=5, ...
        VerboseFrequency=30, ...
        L2Regularization=0.001, ...
        ValidationData=dsVal, ...
        ValidationFrequency=50, ...
        OutputNetwork="best-validation-loss");

これらの学習オプションは、実験マネージャーを使用して選択されました。実験マネージャーを使用したハイパーパラメーター調整の詳細については、Train Object Detectors in Experiment Managerを参照してください。

関数trainYOLOv2ObjectDetectorを使用して YOLO v2 オブジェクト検出器に学習させるには、doTrainingtrue に設定します。

doTraining = false;
if doTraining
    [detector,info] = trainYOLOv2ObjectDetector(augmentedTrainingData,lgraph,opts);
end

この例は、24 GB のメモリを搭載した NVIDIA™ GeForce RTX 3090 Ti GPU で検証され、学習を完了するのに約 45 分かかりました。学習所要時間は使用するハードウェアによって異なります。GPU のメモリがこれより少ない場合、メモリ不足が発生する可能性があります。このような場合は、関数 trainingOptions (Deep Learning Toolbox) を使用するときに、MiniBatchSize に小さい値を指定します。

オブジェクト検出器の評価

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

テスト データ セットに対して検出器を実行します。できるだけ多くのオブジェクトを検出するには、検出しきい値を低い値に設定します。これは、検出器の適合率を、再現率の値の全範囲にわたって評価するのに役立ちます。

detectionThreshold = 0.01;
results = detect(detector,dsTest,MiniBatchSize=8,Threshold=detectionThreshold);

evaluateObjectDetection を使用して、テスト セットの結果に関するオブジェクト検出メトリクスを計算します。この関数は、1 つ以上の Intersection Over Union (IoU) しきい値で検出器を評価します。IoU しきい値は、予測境界ボックスが真陽性としてカウントされるために必要な、予測境界ボックスとグラウンド トゥルース境界ボックスの間のオーバーラップ量を定義します。

iouThresholds = [0.5 0.75 0.9];
metrics = evaluateObjectDetection(results,dsTest,iouThresholds);

クラス メトリクス全体をリストし、平均適合率の平均 (mAP) を検査して、検出器のパフォーマンスがどの程度優れているかを確認します。

metrics.ClassMetrics 
ans=7×5 table
                        NumObjects      mAP           AP            Precision             Recall     
                        __________    _______    ____________    ________________    ________________

    chair                  167        0.58139    {3×1 double}    {3×14872 double}    {3×14872 double}
    clock                   26        0.54996    {3×1 double}    {3×3116  double}    {3×3116  double}
    exit                    42        0.54586    {3×1 double}    {3×3280  double}    {3×3280  double}
    fireextinguisher       123        0.62289    {3×1 double}    {3×4579  double}    {3×4579  double}
    printer                  7        0.26068    {3×1 double}    {3×4436  double}    {3×4436  double}
    screen                  12        0.14779    {3×1 double}    {3×10530 double}    {3×10530 double}
    trashbin                20         0.3004    {3×1 double}    {3×7556  double}    {3×7556  double}

すべての IoU しきい値の平均適合率の値を棒グラフで可視化します。

figure
classAP = metrics.ClassMetrics{:,"AP"}';
classAP = classAP{:};
bar(classAP')
xticklabels(metrics.ClassNames)
ylabel("AP")

プロットは、他のクラスに比べてサンプル数が少ない 3 つのクラス (printer、screen、および trash bin) では検出器のパフォーマンスが低かったことを示しています。高い IoU しきい値では、検出器のパフォーマンスも低下しています。これらの結果に基づいて、パフォーマンスを向上させる次のステップは、クラス分布の解析セクションで特定されたクラスの不均衡の問題に対処することです。クラスの不均衡に対処するには、その少数のクラスが含まれるイメージを追加するか、そのようなクラスが含まれるイメージを複製してデータ拡張を使用します。そのような拡張には追加の実験が必要であり、この例の範囲を超えています。

オブジェクトのサイズが検出器のパフォーマンスに与える影響

関数metricsByAreaを使用して、オブジェクト サイズが検出器のパフォーマンスに及ぼす影響を調査します。この関数は、指定した範囲のオブジェクト サイズに対して検出器メトリクスを計算します。用途に応じて、サイズ範囲の事前定義セットに基づいてオブジェクト サイズの範囲を定義することも、この例のように推定されたアンカー ボックスを使用することもできます。このアンカー ボックス推定手法では、オブジェクトのサイズを自動的にクラスタリングし、入力データに基づいてサイズ範囲のセットを提供します。

検出器からアンカー ボックスを抽出し、それぞれの面積を計算し、面積を並べ替えます。

areas = prod(detector.AnchorBoxes,2);
areas = sort(areas);

計算した面積を使用して、面積の範囲の限界値を定義します。最後の範囲の上限は最大面積の 3 倍のサイズに設定しており、このデータ セット内のオブジェクトには十分です。

lowerLimit = [0; areas];
upperLimit = [areas; 3*areas(end)];
areaRanges = [lowerLimit upperLimit]
areaRanges = 6×2

           0        2774
        2774        9177
        9177       15916
       15916       47799
       47799      124716
      124716      374148

関数 metricsByArea を使用して、chair クラス用に定義したサイズ範囲全体にわたってオブジェクト検出メトリクスを評価します。他のクラス名を指定すると、それらのクラスのオブジェクト検出メトリクスを対話的に評価できます。

classes = string(detector.ClassNames);
areaMetrics = metricsByArea(metrics,areaRanges,ClassName=classes(3))
areaMetrics=6×6 table
           AreaRange            NumObjects      mAP           AP            Precision           Recall     
    ________________________    __________    _______    ____________    _______________    _______________

             0          2774         0              0    {3×1 double}    {3×168  double}    {3×168  double}
          2774          9177         3         0.2963    {3×1 double}    {3×569  double}    {3×569  double}
          9177         15916         6        0.72222    {3×1 double}    {3×2444 double}    {3×2444 double}
         15916         47799        45        0.62727    {3×1 double}    {3×6753 double}    {3×6753 double}
         47799    1.2472e+05        84        0.56828    {3×1 double}    {3×4492 double}    {3×4492 double}
    1.2472e+05    3.7415e+05        29        0.59673    {3×1 double}    {3×451  double}    {3×451  double}

NumObjects 列に、テスト データ セット内の面積範囲内にあるオブジェクトの数が表示されます。検出器は chair クラス全体で良好なパフォーマンスを示しているものの、他のサイズ範囲と比較して検出器の平均適合率が低いサイズ範囲があります。検出器がうまく機能していない範囲には、サンプルが 11 個しかありません。このサイズ範囲でのパフォーマンスを向上させるには、このサイズのサンプルをさらに追加するか、データ拡張を使用して一連のサイズ範囲全体にわたるさらに多くのサンプルを作成します。

検出器のパフォーマンスをさらに向上させる方法についてより深い洞察を得るために、他のクラスを調べることができます。

適合率メトリクスと再現率メトリクスの計算

適合率/再現率 (PR) 曲線と検出信頼度スコアを並べてプロットします。適合率/再現率曲線は、各クラスのさまざまな再現率レベルにおける検出器の適合率を示します。PR 曲線の隣に検出器スコアをプロットすることで、用途に適した適合率と再現率を達成する検出しきい値を選択できます。

クラスを選択し、そのクラスの適合率と再現率のメトリクスを抽出して、適合率/再現率曲線をプロットします。

class = classes(3);

% Extract precision and recall values.
precision = metrics.ClassMetrics{class,"Precision"};
recall = metrics.ClassMetrics{class,"Recall"};

% Plot precision/recall curves.
figure
tiledlayout(1,2)
nexttile
plot(recall{:}',precision{:}')
ylim([0 1])
xlim([0 1])
grid on
xlabel("Recall")
ylabel("Precision")
title(class + " Precision/Recall")
legend(string(iouThresholds) + " IoU",Location="south")

テスト セットの検出結果からすべてのラベルとスコアを抽出し、選択したクラスに対応するスコアを並べ替えます。この方法では、適合率/再現率の値を計算する際に使用される順序と一致するようにスコアを並べ替え、適合率/再現率を可視化し、結果を並べてスコア表示できるようにします。

allLabels = vertcat(results{:,3}{:});
allScores = vertcat(results{:,2}{:});

classScores = allScores(allLabels == class);
classScores = [1; sort(classScores,"descend")];

chair クラスの適合率/再現率曲線の隣にスコアを可視化します。

nexttile
plot(recall{1,:}',classScores)
ylim([0 1])
xlim([0 1])
ylabel("Score")
xlabel("Recall")
grid on
title(class + " Detection Scores")

この Figure が示すように、検出しきい値によって適合率と再現率をトレードオフできます。用途に最適な適合率/再現率特性が得られるしきい値を選択します。たとえば、IoU しきい値が 0.5 の場合、検出しきい値を 0.4 に設定すると、chair クラスでは再現率レベル 0.9 で適合率 0.9 を達成できます。適合率/再現率の特性はクラスごとに異なる可能性があるため、最終的な検出しきい値を選択する前に、すべてのクラスの適合率/再現率曲線を解析します。

展開

検出器の学習と評価が完了したら、オプションで、GPU Coder™ を使用してコードを生成し、yolov2ObjectDetector を展開できます。詳細については、YOLO v2 を使用したオブジェクト検出のコードの生成 (GPU Coder)の例を参照してください。

まとめ

この例では、マルチクラス オブジェクト検出器の学習および評価を行う方法を示します。この例を独自のデータに適用する場合は、データ セット内のオブジェクト クラスとサイズ分布を注意深く評価してください。データによっては、最適な結果を得るために、異なるハイパーパラメーターを使用したり、YOLO v4 や YOLOX などの別のオブジェクト検出器を使用したりする必要があります。

サポート関数

function B = augmentData(A)
% Apply random horizontal flipping, and random X/Y scaling, and jitter image color.
% The function clips boxes scaled outside the bounds if the overlap is above 0.25.
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 to the example as a
% supporting file. Open the example in MATLAB to use 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 have been removed by warping.
if isempty(indices)
    B = A;
end
end
function data = resizeImageAndLabel(data,targetSize)
% Resize the images, and scale the corresponding bounding boxes.

    scale = (targetSize(1:2))./size(data{1},[1 2]);
    data{1} = imresize(data{1},targetSize(1:2));
    data{2} = bboxresize(data{2},scale);

    data{2} = floor(data{2});
    imageSize = targetSize(1:2);
    boxes = data{2};
    % Set boxes with negative values to have value 1.
    boxes(boxes <= 0) = 1;
    
    % Validate if bounding box in within image boundary.
    boxes(:,3) = min(boxes(:,3),imageSize(2) - boxes(:,1) - 1);
    boxes(:,4) = min(boxes(:,4),imageSize(1) - boxes(:,2) - 1);
    
    data{2} = boxes; 

end

参考文献

[1] Adhikari, Bishwo; Peltomaki, Jukka; Huttunen, Heikki. (2019).Indoor Object Detection Dataset [Data set]. 7th European Workshop on Visual Information Processing 2018 (EUVIP), Tampere, Finland.

[2] Lin, Tsung-Yi, Michael Maire, Serge Belongie, Lubomir Bourdev, Ross Girshick, James Hays, Pietro Perona, Deva Ramanan, C. Lawrence Zitnick, and Piotr Dollár. “Microsoft COCO: Common Objects in Context,” May 1, 2014. https://arxiv.org/abs/1405.0312v3.