Генерация кода Для Обнаружения объектов Используя глубокое обучение YOLO v3

В этом примере показано, как сгенерировать CUDA® MEX для вы только смотрите однажды (YOLO) v3 детектор объектов с пользовательскими слоями. Пример использует обнаружение объектов YOLO v3, чтобы проиллюстрировать:

  • Генерация кода CUDA для нейронной сети для глубокого обучения с пользовательскими слоями.

  • Преобразуйте глубокое обучение dlnetwork объект в DAGNetwork объект для генерации кода.

YOLO v3 улучшает YOLO v2 путем добавления обнаружения в нескольких шкалах, чтобы помочь обнаружить меньшие объекты. Кроме того, функция потерь, используемая для обучения, разделена на среднеквадратическую ошибку для регрессии ограничительной рамки и бинарную перекрестную энтропию для предметной классификации, чтобы помочь улучшить точность обнаружения. Этот пример использует сеть YOLO v3, обученную в Обнаружении объектов Используя пример YOLO v3 Глубокого обучения от Computer Vision Toolbox(TM). Для получения дополнительной информации смотрите, что Обнаружение объектов Использует Глубокое обучение (Computer Vision Toolbox) YOLO v3.

Сторонние необходимые условия

Необходимый

  • CUDA включил NVIDIA® графический процессор и совместимый драйвер.

Дополнительный

Для сборок неMEX, таких как статические, динамические библиотеки или исполняемые файлы, этот пример имеет следующие дополнительные требования.

Проверьте среду графического процессора

Чтобы проверить, что компиляторы и библиотеки для выполнения этого примера настраиваются правильно, используйте coder.checkGpuInstall (GPU Coder) функция.

envCfg = coder.gpuEnvConfig('host');
envCfg.DeepLibTarget = 'cudnn';
envCfg.DeepCodegen = 1;
envCfg.Quiet = 1;
coder.checkGpuInstall(envCfg);

Сеть YOLO v3

Сеть YOLO v3 в этом примере основана на squeezenet, и использует сеть извлечения признаков в SqueezeNet со сложением двух голов обнаружения в конце. Вторая голова обнаружения является дважды размером первой головы обнаружения, таким образом, это лучше способно обнаружить маленькие объекты. Обратите внимание на то, что любое количество глав обнаружения различных размеров может быть задано на основе размера объектов, которые будут обнаружены. Использование сети YOLO v3 поля привязки, оцененные с помощью обучающих данных, чтобы иметь лучшее начальное уголовное прошлое, соответствующее типу набора данных и помочь сети учиться предсказывать поля точно. Для получения информации о полях привязки смотрите Поля Привязки для Обнаружения объектов (Computer Vision Toolbox).

Сеть YOLO v3 в этом примере проиллюстрирована в следующей схеме.

Каждый глава обнаружения предсказывает координаты ограничительной рамки (x, y, ширина, высота), объектное доверие и вероятности класса для соответствующих масок поля привязки. Поэтому для каждой головы обнаружения, количество выхода просачивается, последний слой свертки является номером времен маски поля привязки количество элементов предсказания на поле привязки. Головы обнаружения включают выходной слой сети.

Предварительно обученная Сеть YOLO v3

Загрузите сеть YOLO v3, обученную в Обнаружении объектов Используя пример YOLO v3 Глубокого обучения. Чтобы обучить сеть самостоятельно, смотрите, что Обнаружение объектов Использует Глубокое обучение (Computer Vision Toolbox) YOLO v3.

pretrained = load("yolov3SqueezeNetVehicleExample_20a.mat");
net = pretrained.net;

Подготовьте предварительно обученную модель к генерации кода

Предварительно обученной моделью YOLO v3 является dlnetwork объект, который не поддерживается для генерации кода. Извлеките график слоев dlnetwork объект при помощи layerGraph функция.

lGraph = layerGraph(net);

График слоев выводится layerGraph функция не включает выходные слои. Добавьте слой регрессии в график слоев для каждых из его выходных параметров при помощи addLayers и connectLayers функции.

outLayerIdx = 1:numel(lGraph.Layers);
isOutLayer = arrayfun(@(x) any(strcmp(x.Name, net.OutputNames)), lGraph.Layers);
outLayerIdx(~isOutLayer) = [];

for iOut = 1:numel(outLayerIdx)
    outLayer = lGraph.Layers(outLayerIdx(iOut));
    newLayer = regressionLayer('Name', [outLayer.Name '_output_' num2str(iOut)]);
    lGraph = addLayers(lGraph, newLayer);
    lGraph = connectLayers(lGraph, outLayer.Name, newLayer.Name);
end

Количество выходных слоев сети - то же самое как количество голов обнаружения в сети.

Пользовательский сверхдискретизировали слой

Использование сети YOLO v3 пользовательское сверхдискретизировало слой для повышающей дискретизации входных данных изображения. Здесь, мы используем совместимую версию генерации кода upsampleLayer упомянутый в Обнаружении объектов Используя Глубокое обучение (Computer Vision Toolbox) пример YOLO v3. upsampleBy2Layer сверхдискретизировал входное изображение путем тиражирования соседних пиксельных значений на коэффициент 2 использований repelem функция. Поскольку генерация кода для repelem функция поддерживается только для векторных или 2D матричных входных параметров, upsampleBy2Layer реализация должна быть параметрирована при помощи coder.target (MATLAB Coder) функция. При выполнении симуляции на MATLAB, одном вызове repelem достаточно, когда вход может быть матрицей N-D. Для генерации кода пример использует вложенные циклы for так, чтобы вход к repelem функция является 2D матрицей для каждого вызова repelem в сгенерированном коде. Кроме того, для выходного размера слоя генерации кода требуется, чтобы быть постоянным во время генерации кода. Следовательно, свойство UpsampleFactor слоя изменяется, чтобы быть Константом.

type('upsampleBy2Layer.m')
% Upsample by replicating neighbouring pixel values.

% Copyright 2020 The MathWorks, Inc.

classdef upsampleBy2Layer < nnet.layer.Layer
    properties (Constant)
        % factor to upsample the input.
        UpSampleFactor = 2
    end
    
    methods
        function layer = upsampleBy2Layer(name)
            
            % Set layer name.
            layer.Name = name;
            
            % Set layer description.
            layer.Description = "upSamplingLayer with factor " + layer.UpSampleFactor;
            
        end
        
        function Z = predict(layer, X)
            % Z = predict(layer, X) forwards the input data X through the
            % layer and outputs the result Z.
            if coder.target('MATLAB')
                Z = repelem(X,layer.UpSampleFactor,layer.UpSampleFactor);
            else
                numChannels = size(X, 3);
                numBatches = size(X, 4);
                Zsize = coder.const([size(X, 1) size(X, 2) numChannels numBatches] .* [layer.UpSampleFactor layer.UpSampleFactor 1 1]);
                Z = coder.nullcopy(zeros(Zsize, 'like', X));
                
                coder.gpu.kernel(-1, -1);
                for iBatch = 1:numBatches
                    for iChannel = 1:numChannels
                        Z(:, :, iChannel, iBatch) = repelem(X(:, :, iChannel, iBatch), layer.UpSampleFactor, layer.UpSampleFactor);
                    end
                end
            end
        end
    end
end

Замените upsampleLayer существующий в предварительно обученной сети с ее совместимой версией генерации кода upsampleBy2Layer при помощи replaceLayer функция.

isUpsampleLayer = arrayfun(@(x) isa(x, 'upsampleLayer'), lGraph.Layers);
layerNameToReplace = lGraph.Layers(isUpsampleLayer).Name;
lGraph = replaceLayer(lGraph, layerNameToReplace, upsampleBy2Layer(layerNameToReplace));

Соберите layerGraph в DAGNetwork

Соберите layerGraph в DAGNetwork возразите готовый использовать для генерации кода при помощи assembleNetwork функция.

dagNet = assembleNetwork(lGraph)
dagNet = 
  DAGNetwork with properties:

         Layers: [72×1 nnet.cnn.layer.Layer]
    Connections: [80×2 table]
     InputNames: {'data'}
    OutputNames: {'conv2Detection1_output_1'  'conv2Detection2_output_2'}

Сохраните сеть в MAT-файл.

matFile = 'yolov3DAGNetwork.mat';
save(matFile, 'dagNet');

yolov3Detect Функция точки входа

yolov3Detect функция точки входа берет входное изображение и передает его обучившему сеть для предсказания через yolov3Predict функция. yolov3Predict функционируйте загружает сетевой объект из MAT-файла в персистентную переменную и снова использует постоянный объект для последующих вызовов предсказания. А именно, функция использует представление DAGNetwork сети, обученной в Обнаружении объектов Используя Глубокое обучение (Computer Vision Toolbox) пример YOLO v3. Предсказания от координат ячейки сетки YOLO v3 получены из yolov3Predict вызовы затем преобразованы в координаты ограничительной рамки при помощи функций поддержки generateTiledAnchors и applyAnchorBoxOffsets.

type('yolov3Detect.m')
function [bboxes,scores,labels] = yolov3Detect(matFile, im, networkInputSize, networkOutputs, confidenceThreshold, overlapThreshold, classes)
% The yolov3Detect function detects the bounding boxes, scores, and labels in an image.
coder.extrinsic('generateYOLOv3Detections');

%% Preprcess Data
% This example applies all the preprocessing transforms to the data set
% applied during training, except data augmentation. Because the example
% uses a pretrained YOLO v3 network, the input data must be representative
% of the original data and left unmodified for unbiased evaluation.

% Specifically the following preprocessing operations are applied to the
% input data. 
%     1. Resize the images to the network input size, as the images are bigger than networkInputSize. 
%     2. Scale the image pixels in the range [0 1].

im = preprocessData(im, networkInputSize);
imageSize = size(im,[1,2]);

%% Define Anchor Boxes
% Specify the anchor boxes estimated on the basis of the preprocessed
% training data used when training the YOLO v3 network. These anchor box
% values are same as mentioned in
% <docid:vision_ug#mw_47d9a223-5ec7-4d36-a020-4f9d147ecdec Object Detection
% Using YOLO v3 Deep Learning> example. For details on estimating anchor
% boxes, see <docid:vision_ug#mw_f9f22f48-0ad0-4f37-8bc1-22a2046637f2
% Anchor Boxes for Object Detection>.

anchors = [150   127;
    97    90;
    68    67;
    38    42;
    41    29;
    31    23];

% Specify anchorBoxMasks to select anchor boxes to use in both the
% detection heads of the YOLO v3 network. anchorBoxMasks is a cell array of
% size M-by-1, where M denotes the number of detection heads. Each
% detection head consists of a 1-by-N array of row index of anchors in
% anchorBoxes, where N is the number of anchor boxes to use. Select anchor
% boxes for each detection head based on size-use larger anchor boxes at
% lower scale and smaller anchor boxes at higher scale. To do so, sort the
% anchor boxes with the larger anchor boxes first and assign the first
% three to the first detection head and the next three to the second
% detection head.

area = anchors(:, 1).*anchors(:, 2);
[~, idx] = sort(area, 'descend');
anchors = anchors(idx, :);
anchorBoxMasks = {[1,2,3]
    [4,5,6]
    };

%% Predict on Yolov3
% Predict and filter the detections based on confidence threshold.
predictions = yolov3Predict(matFile,im,networkOutputs,anchorBoxMasks);

%% Generate Detections
anchorIndex = 2:5; % indices corresponding to x,y,w,h predictions for bounding boxes
tiledAnchors = generateTiledAnchors(predictions,anchors,anchorBoxMasks,anchorIndex);
predictions = applyAnchorBoxOffsets(tiledAnchors, predictions, networkInputSize, anchorIndex);
[bboxes,scores,labels] = generateYOLOv3Detections(predictions, confidenceThreshold, overlapThreshold, imageSize, classes);

% Apply suppression to the detections to filter out multiple overlapping
% detections.
if ~isempty(scores)
    [bboxes, scores, labels] = selectStrongestBboxMulticlass(bboxes, scores, labels ,...
        'RatioType', 'Union', 'OverlapThreshold', overlapThreshold);
end
end

function YPredCell = yolov3Predict(matFile,im,networkOutputs,anchorBoxMask)
% Predict the output of network and extract the confidence, x, y,
% width, height, and class.

% load the deep learning network for prediction
persistent net;

if isempty(net)
    net = coder.loadDeepLearningNetwork(matFile);
end

YPredictions = cell(size(networkOutputs));
[YPredictions{:}] = predict(net, im);
YPredCell = extractPredictions(YPredictions, anchorBoxMask);

% Apply activation to the predicted cell array.
YPredCell = applyActivations(YPredCell);
end

Выполните функцию точки входа для обнаружения объектов

Выполните эти шаги, чтобы выполнить функцию точки входа на изображении от тестовых данных.

  • Задайте порог доверия как 0,5, чтобы сохранить только обнаружения с оценками достоверности выше этого значения.

  • Задайте порог перекрытия как 0,5, чтобы удалить перекрывающиеся обнаружения.

  • Считайте изображение из входных данных.

  • Используйте функцию точки входа yolov3Detect получить предсказанные ограничительные рамки, оценки достоверности и метки класса.

  • Отобразите изображение с ограничительными рамками и оценками достоверности.

Задайте желаемые пороги.

confidenceThreshold = 0.5;
overlapThreshold = 0.5;

Получите сетевой входной размер от входного слоя обучившего сеть и количества сетевых выходных параметров.

networkInputIdx = arrayfun(@(x)isa(x,'nnet.cnn.layer.ImageInputLayer'),net.Layers);
networkInputSize = net.Layers(networkInputIdx).InputSize;
networkOutputs = numel(dagNet.OutputNames);

Считайте данные изображения в качестве примера, полученные из набора маркированных данных от Обнаружения объектов Используя Глубокое обучение (Computer Vision Toolbox) пример YOLO v3. Это изображение содержит один экземпляр объекта транспортного средства типа.

I = imread('vehicleImage.jpg');

Задайте имена классов.

classNames = {'vehicle'};

Вызовите обнаружить метод на сеть YOLO v3 и отобразите результаты.

[bboxes,scores,~] = yolov3Detect(matFile, I, networkInputSize, networkOutputs, confidenceThreshold, overlapThreshold, classNames);

% Display the detections on the image
IAnnotated = insertObjectAnnotation(I, 'rectangle', bboxes, scores);
figure
imshow(IAnnotated)

Сгенерируйте MEX CUDA

Сгенерировать код CUDA® для yolov3Detect функция точки входа, создайте объект настройки графического процессора кода для цели MEX и установите выходной язык на C++. Используйте coder.DeepLearningConfig (GPU Coder) функция, чтобы создать настройку глубокого обучения CuDNN возражает и присвоить ее DeepLearningConfig свойство объекта настройки графического процессора кода.

cfg = coder.gpuConfig('mex');
cfg.TargetLang = 'C++';
cfg.DeepLearningConfig = coder.DeepLearningConfig('cudnn');

args = {coder.Constant(matFile), I, coder.Constant(networkInputSize), networkOutputs, ...
    confidenceThreshold, overlapThreshold, classNames};

codegen -config cfg yolov3Detect -args args
Code generation successful: View report

Чтобы сгенерировать код CUDA® для цели TensorRT создают и используют объект настройки глубокого обучения TensorRT вместо объекта настройки CuDNN. Точно так же, чтобы сгенерировать код для цели MKLDNN, создайте объект настройки центрального процессора кода и используйте объект настройки глубокого обучения MKLDNN в качестве его DeepLearningConfig свойство.

Запустите сгенерированный MEX

Вызовите сгенерированный MEX CUDA с тем же входом I изображений как прежде и отображают результаты.

[bboxes, scores, labels] = yolov3Detect_mex(matFile, I, networkInputSize, networkOutputs, ...
    confidenceThreshold, overlapThreshold, classNames);
figure;
IAnnotated = insertObjectAnnotation(I, 'rectangle', bboxes, scores);
imshow(IAnnotated);

Служебные функции

Служебные описанные ниже функции похожи на тех используемых в Обнаружении объектов Используя Глубокое обучение (Computer Vision Toolbox) пример YOLO v3, за исключением preProcessData функция. В этом примере мы только предварительно обрабатываем данные изображения, различающиеся в Обнаружении объектов Используя Глубокое обучение (Computer Vision Toolbox) пример YOLO v3, где ограничительные рамки также обрабатываются.

type('applyActivations.m')
function YPredCell = applyActivations(YPredCell)
for idx = 1:3
    YPredCell{:, idx} = sigmoidActivation(YPredCell{:,idx});
end
for idx = 4:5
    YPredCell{:, idx} = exp(YPredCell{:, idx});
end
YPredCell{:, 6} = sigmoidActivation(YPredCell{:, 6});
end

function out = sigmoidActivation(x)
out = 1./(1+exp(-x));
end
type('extractPredictions.m')
function predictions = extractPredictions(YPredictions, anchorBoxMask)
predictions = cell(size(YPredictions, 1),6);
for ii = 1:size(YPredictions, 1)
    numAnchors = size(anchorBoxMask{ii},2);
    % Confidence scores.
    startIdx = 1;
    endIdx = numAnchors;
    predictions{ii,1} = YPredictions{ii}(:,:,startIdx:endIdx,:);
    
    % X positions.
    startIdx = startIdx + numAnchors;
    endIdx = endIdx+numAnchors;
    predictions{ii,2} = YPredictions{ii}(:,:,startIdx:endIdx,:);
    
    % Y positions.
    startIdx = startIdx + numAnchors;
    endIdx = endIdx+numAnchors;
    predictions{ii,3} = YPredictions{ii}(:,:,startIdx:endIdx,:);
    
    % Width.
    startIdx = startIdx + numAnchors;
    endIdx = endIdx+numAnchors;
    predictions{ii,4} = YPredictions{ii}(:,:,startIdx:endIdx,:);
    
    % Height.
    startIdx = startIdx + numAnchors;
    endIdx = endIdx+numAnchors;
    predictions{ii,5} = YPredictions{ii}(:,:,startIdx:endIdx,:);
    
    % Class probabilities.
    startIdx = startIdx + numAnchors;
    predictions{ii,6} = YPredictions{ii}(:,:,startIdx:end,:);
end
end
type('generateTiledAnchors.m')
function tiledAnchors = generateTiledAnchors(YPredCell,anchorBoxes,anchorBoxMask,anchorIndex)
% Generate tiled anchor offset for converting the predictions from the YOLO 
% v3 grid cell coordinates to bounding box coordinates

tiledAnchors = cell(size(anchorIndex));
for i=1:size(YPredCell,1)
    anchors = anchorBoxes(anchorBoxMask{i}, :);
    [h,w,~,n] = size(YPredCell{i,1});
    [tiledAnchors{i,2}, tiledAnchors{i,1}] = ndgrid(0:h-1,0:w-1,1:size(anchors,1),1:n);
    [~,~,tiledAnchors{i,3}] = ndgrid(0:h-1,0:w-1,anchors(:,2),1:n);
    [~,~,tiledAnchors{i,4}] = ndgrid(0:h-1,0:w-1,anchors(:,1),1:n);
end
end
type('applyAnchorBoxOffsets.m')
function YPredCell = applyAnchorBoxOffsets(tiledAnchors,YPredCell,inputImageSize,anchorIndex)
% Convert the predictions from the YOLO v3 grid cell coordinates to bounding box coordinates
for i=1:size(YPredCell,1)
    [h,w,~,~] = size(YPredCell{i,1});  
    YPredCell{i,anchorIndex(1)} = (tiledAnchors{i,1}+YPredCell{i,anchorIndex(1)})./w;
    YPredCell{i,anchorIndex(2)} = (tiledAnchors{i,2}+YPredCell{i,anchorIndex(2)})./h;
    YPredCell{i,anchorIndex(3)} = (tiledAnchors{i,3}.*YPredCell{i,anchorIndex(3)})./inputImageSize(2);
    YPredCell{i,anchorIndex(4)} = (tiledAnchors{i,4}.*YPredCell{i,anchorIndex(4)})./inputImageSize(1);
end
end
type('preprocessData.m')
function image = preprocessData(image, targetSize)
% Resize the images and scale the pixels to between 0 and 1.

imgSize = size(image);

% Convert an input image with single channel to 3 channels.
if numel(imgSize) == 1
    image = repmat(image,1,1,3);
end
image = im2single(imresize(image, coder.const(targetSize(1:2))));

end

Ссылки

1. Redmon, Джозеф и Али Фархади. “YOLOv3: Инкрементное Улучшение”. Предварительно распечатайте, представленный 8 апреля 2018. https://arxiv.org/abs/1804.02767.