Main Content

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

事前学習済みの ONNX YOLO v2 オブジェクト検出器のインポート

この例では、事前学習済みの ONNX™(Open Neural Network Exchange) You Only Look Once (YOLO) v2 [1] オブジェクト検出ネットワークをインポートし、このネットワークを使用してオブジェクトを検出する方法を説明します。ネットワークをインポートした後、GPU Coder™ を使用してこのネットワークを組み込みプラットフォームに展開したり、trainYOLOv2ObjectDetector による転移学習を使用してカスタム データでこのネットワークに再学習させたりすることができます。

ONNX YOLO v2 ネットワークのダウンロード

事前学習済みの Tiny YOLO v2 ネットワークに関連するファイルをダウンロードします [2][3]

pretrainedURL = 'https://onnxzoo.blob.core.windows.net/models/opset_8/tiny_yolov2/tiny_yolov2.tar.gz';
pretrainedNetZip = 'yolov2Tmp.tar.gz';
if ~exist(pretrainedNetZip,'file')
    disp('Downloading pretrained network (58 MB)...');
    websave(pretrainedNetZip,pretrainedURL);
end
Downloading pretrained network (58 MB)...

YOLO v2 ネットワークの解凍

ダウンロードしたファイルを解凍し、Tiny YOLO v2 ネットワークを展開します。'model.onnx' モデルを読み込みます。これは、PASCAL VOC データセットで事前学習させた ONNX YOLO v2 ネットワークです。このネットワークは、20 種類のクラスに属するオブジェクトを検出できます [4]

pretrainedNetTar = gunzip(pretrainedNetZip);
onnxfiles = untar(pretrainedNetTar{1});
pretrainedNet = onnxfiles{1,2};

ONNX YOLO v2 層のインポート

関数 importONNXLayers を使用して、ダウンロードしたネットワークをインポートします。

lgraph = importONNXLayers(pretrainedNet,'ImportWeights',true);
Warning: Imported layers have no output layer because ONNX files do not specify the network's output layer type. The layers will not be trainable until an output layer is added. Either add an output layer to the imported layers, or specify the output layer type using 'OutputLayerType' in the call to importONNXLayers.

この例では、インポートした層に出力層を追加するため、この警告は無視して構いません。YOLO v2 の変換層と出力層の追加の節で、インポートした層に YOLO v2 出力層と YOLO v2 変換層を追加する方法が説明されています。

この例のネットワークには、サポートされていない層は含まれていません。インポートしたいネットワークにサポートされていない層が含まれている場合、関数はそれらをプレースホルダー層としてインポートすることに注意してください。インポートしたネットワークを使用する前に、これらの層を置き換えなければなりません。プレースホルダー層の置き換えの詳細については、findPlaceholderLayers (Deep Learning Toolbox) を参照してください。

YOLO v2 アンカー ボックスの定義

YOLO v2 は、事前定義されたアンカー ボックスを使用してオブジェクトの位置を予測します。インポートしたネットワークで使用されているアンカー ボックスは、Tiny YOLO v2 ネットワーク コンフィギュレーション ファイルで定義されています [5]。ONNX のアンカーは、最終畳み込み層の出力サイズ (13 x 13) に基づき定義されています。yolov2ObjectDetector でアンカーを使用するには、ネットワークの入力サイズ (416 x 416) に合わせてアンカー ボックスのサイズを変更します。yolov2ObjectDetector のアンカー ボックスは、[高さ, 幅] の形式で指定しなければなりません。

onnxAnchors = [1.08,1.19; 3.42,4.41; 6.63,11.38; 9.42,5.11; 16.62,10.52];

inputSize = lgraph.Layers(1,1).InputSize(1:2);
lastActivationSize = [13,13];
upScaleFactor = inputSize./lastActivationSize;
anchorBoxesTmp = round(upScaleFactor.* onnxAnchors);
anchorBoxes = [anchorBoxesTmp(:,2),anchorBoxesTmp(:,1)];

検出層の重みの並べ替え

効率よく処理を行うには、インポートしたネットワークに含まれる最終畳み込み層の重みとバイアスを並べ替えて、yolov2ObjectDetector が必要とする配置の活性化を取得しなければなりません。yolov2ObjectDetector には、最終畳み込み層の特徴マップに含まれる 125 個のチャネルを次の配置で入力する必要があります。

  • チャネル 1 から 5 - アンカー 5 個の IoU の値

  • チャネル 6 から 10 - アンカー 5 個の X の値

  • チャネル 11 から 15 - アンカー 5 個の Y の値

  • チャネル 16 から 20 - アンカー 5 個の幅の値

  • チャネル 21 から 25 - アンカー 5 個の高さの値

  • チャネル 26 から 30 - アンカー 5 個のクラス 1 の確率値

  • チャネル 31 から 35 - アンカー 5 個のクラス 2 の確率値

  • チャネル 121 から 125 - アンカー 5 個のクラス 20 の確率値

ただし、サイズが 13 x 13 の最終畳み込み層では、活性化の配置が異なります。特徴マップ内の 25 個のチャネルは、それぞれ次のように対応しています。

  • チャネル 1 - X の値

  • チャネル 2 - Y の値

  • チャネル 3 - 幅の値

  • チャネル 4 - 高さの値

  • チャネル 5 - IoU の値

  • チャネル 6 - クラス 1 の確率値

  • チャネル 7 - クラス 2 の確率値

  • チャネル 25 - クラス 20 の確率値

この例の最後にリストされている補助関数 rearrangeONNXWeights を使用して、インポートしたネットワークに含まれる最終畳み込み層の重みとバイアスを並べ替え、yolov2ObjectDetector が必要とする形式の活性化を取得します。

weights = lgraph.Layers(end,1).Weights;
bias = lgraph.Layers(end,1).Bias;
layerName = lgraph.Layers(end,1).Name;

numAnchorBoxes = size(onnxAnchors,1);
[modWeights,modBias] = rearrangeONNXWeights(weights,bias,numAnchorBoxes);

並べ替えた重みとバイアスを使用して、インポートしたネットワークに含まれる最終畳み込み層の重みとバイアスを新しい畳み込み層の重みとバイアスに置き換えます。

filterSize = size(modWeights,[1 2]);
numFilters = size(modWeights,4);
modConvolution8 = convolution2dLayer(filterSize,numFilters,...
    'Name',layerName,'Bias',modBias,'Weights',modWeights);
lgraph = replaceLayer(lgraph,'convolution8',modConvolution8);

YOLO v2 の変換層と出力層の追加

YOLO v2 検出ネットワークは、YOLO v2 の変換層と出力層を必要とします。これら 2 つの層を作成し、それらを直列に積み重ね、最終畳み込み層に YOLO v2 変換層を接続します。

classNames = tinyYOLOv2Classes;

layersToAdd = [
    yolov2TransformLayer(numAnchorBoxes,'Name','yolov2Transform');
    yolov2OutputLayer(anchorBoxes,'Classes',classNames,'Name','yolov2Output');
    ];

lgraph = addLayers(lgraph, layersToAdd);
lgraph = connectLayers(lgraph,layerName,'yolov2Transform');

インポートしたネットワークに含まれる ElementwiseAffineLayer は、yolov2ObjectDetector によって実行される前処理手順を複製します。そのため、インポートされたネットワークから ElementwiseAffineLayer を削除します。

yoloScaleLayerIdx = find(...
    arrayfun( @(x)isa(x,'nnet.onnx.layer.ElementwiseAffineLayer'), ...
    lgraph.Layers));

if ~isempty(yoloScaleLayerIdx)
    for i = 1:size(yoloScaleLayerIdx,1)
        layerNames {i} = lgraph.Layers(yoloScaleLayerIdx(i,1),1).Name;
    end
    lgraph = removeLayers(lgraph,layerNames);
    lgraph = connectLayers(lgraph,'Input_image','convolution');
end

YOLO v2 オブジェクト検出器の作成

関数 assembleNetwork を使用して層グラフを組み立て、関数 yolov2ObjectDetector を使用して YOLO v2 オブジェクト検出器を作成します。

net = assembleNetwork(lgraph)
net = 
  DAGNetwork with properties:

         Layers: [34×1 nnet.cnn.layer.Layer]
    Connections: [33×2 table]
     InputNames: {'Input_image'}
    OutputNames: {'yolov2Output'}

yolov2Detector = yolov2ObjectDetector(net)
yolov2Detector = 
  yolov2ObjectDetector with properties:

            ModelName: 'importedNetwork'
              Network: [1×1 DAGNetwork]
    TrainingImageSize: [416 416]
          AnchorBoxes: [5×2 double]
           ClassNames: [aeroplane    bicycle    bird    boat    bottle    bus    car    cat    chair    cow    diningtable    dog    horse    motorbike    person    pottedplant    sheep    sofa    train    tvmonitor]

インポートした YOLO v2 検出器を使用したオブジェクトの検出

インポートした検出器を使用して、テスト イメージ内のオブジェクトを検出します。結果を表示します。

I = imread('car1.jpg');
% Convert image to BGR format.
I = cat(3,I(:,:,3),I(:,:,2),I(:,:,1));
[bboxes, scores, labels] = detect(yolov2Detector, I);
detectedImg = insertObjectAnnotation(I, 'rectangle', bboxes, scores);
figure
imshow(detectedImg);

サポート関数

function [modWeights,modBias] = rearrangeONNXWeights(weights,bias,numAnchorBoxes)
%rearrangeONNXWeights rearranges the weights and biases of an imported YOLO
%v2 network as required by yolov2ObjectDetector. numAnchorBoxes is a scalar
%value containing the number of anchors that are used to reorder the weights and
%biases. This function performs the following operations:
%   * Extract the weights and biases related to IoU, boxes, and classes.
%   * Reorder the extracted weights and biases as expected by yolov2ObjectDetector.
%   * Combine and reshape them back to the original dimensions.

weightsSize = size(weights);
biasSize = size(bias);
sizeOfPredictions = biasSize(3)/numAnchorBoxes;

% Reshape the weights with regard to the size of the predictions and anchors.
reshapedWeights = reshape(weights,prod(weightsSize(1:3)),sizeOfPredictions,numAnchorBoxes);

% Extract the weights related to IoU, boxes, and classes.
weightsIou = reshapedWeights(:,5,:);
weightsBoxes = reshapedWeights(:,1:4,:);
weightsClasses = reshapedWeights(:,6:end,:);

% Combine the weights of the extracted parameters as required by
% yolov2ObjectDetector.
reorderedWeights = cat(2,weightsIou,weightsBoxes,weightsClasses);
permutedWeights = permute(reorderedWeights,[1 3 2]);

% Reshape the new weights to the original size.
modWeights = reshape(permutedWeights,weightsSize);

% Reshape the biases with regared to the size of the predictions and anchors.
reshapedBias = reshape(bias,sizeOfPredictions,numAnchorBoxes);

% Extract the biases related to IoU, boxes, and classes.
biasIou = reshapedBias(5,:);
biasBoxes = reshapedBias(1:4,:);
biasClasses = reshapedBias(6:end,:);

% Combine the biases of the extracted parameters as required by yolov2ObjectDetector.
reorderedBias = cat(1,biasIou,biasBoxes,biasClasses);
permutedBias = permute(reorderedBias,[2 1]);

% Reshape the new biases to the original size.
modBias = reshape(permutedBias,biasSize);
end


function classes = tinyYOLOv2Classes()
% Return the class names corresponding to the pretrained ONNX tiny YOLO v2
% network.
%
% The tiny YOLO v2 network is pretrained on the Pascal VOC data set,
% which contains images from 20 different classes [4].

classes = [ ...
    " aeroplane", "bicycle", "bird", "boat", "bottle", "bus", "car",...
    "cat", "chair", "cow", "diningtable", "dog", "horse", "motorbike",...
    "person", "pottedplant", "sheep", "sofa", "train", "tvmonitor"];
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.

[2] "Tiny YOLO v2 Model." https://github.com/onnx/models/tree/master/vision/object_detection_segmentation/tiny-yolov2

[3] "Tiny YOLO v2 Model License." https://github.com/onnx/onnx/blob/master/LICENSE.

[4] Everingham, Mark, Luc Van Gool, Christopher K. I. Williams, John Winn, and Andrew Zisserman. "The Pascal Visual Object Classes (VOC) Challenge." International Journal of Computer Vision 88, no. 2 (June 2010): 303–38. https://doi.org/10.1007/s11263-009-0275-4.

[5] "yolov2-tiny-voc.cfg" https://github.com/pjreddie/darknet/blob/master/cfg/yolov2-tiny-voc.cfg.