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

В этом примере показано, как обучить детектор объектов YOLO v3.

Глубокое обучение является мощным методом машинного обучения, который можно использовать, чтобы обучить устойчивые детекторы объектов. Несколько методов для обнаружения объектов существуют, включая Faster R-CNN, вы только смотрите однажды (YOLO) v2, и один детектор выстрела (SSD). В этом примере показано, как обучить детектор объектов YOLO v3. YOLO v3 улучшает YOLO v2 путем добавления обнаружения в нескольких шкалах, чтобы помочь обнаружить меньшие объекты. Кроме того, функция потерь, используемая в обучении, разделена на среднеквадратическую ошибку для регрессии ограничительной рамки и бинарную перекрестную энтропию для предметной классификации, чтобы помочь улучшить точность обнаружения.

Загрузите предварительно обученную сеть

Загрузите предварительно обученную сеть, чтобы избежать необходимости ожидать обучения завершиться. Если вы хотите обучить сеть, установите doTraining переменная к true.

doTraining = false;
if ~doTraining
    if ~exist('yolov3SqueezeNetVehicleExample_20a.mat','file')
        disp('Downloading pretrained detector (8.9 MB)...');
        pretrainedURL = 'https://www.mathworks.com/supportfiles/vision/data/yolov3SqueezeNetVehicleExample_20a.mat';
        websave('yolov3SqueezeNetVehicleExample_20a.mat', pretrainedURL);
    end
    pretrained = load("yolov3SqueezeNetVehicleExample_20a.mat");
    net = pretrained.net;
end
Downloading pretrained detector (8.9 MB)...

Загрузка данных

Этот пример использует маленький набор маркированных данных, который содержит 295 изображений. Каждое изображение содержит один или два помеченных экземпляра транспортного средства. Небольшой набор данных полезен для исследования метода обучения YOLO v3, но на практике, более помеченные изображения необходимы, чтобы обучить устойчивую сеть.

Разархивируйте изображения транспортного средства и загрузите достоверные данные транспортного средства.

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

% Add the full path to the local vehicle data folder.
vehicleDataset.imageFilename = fullfile(pwd,vehicleDataset.imageFilename);

Разделите набор данных в набор обучающих данных для того, чтобы обучить сеть и набор тестов для оценки сети. Используйте 60% данных для набора обучающих данных и остальных для набора тестов.

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

Создайте datastore изображений для загрузки изображений.

imdsTrain = imageDatastore(trainingDataTbl.imageFilename);
imdsTest = imageDatastore(testDataTbl.imageFilename);

Создайте datastore для ограничительных рамок основной истины.

bldsTrain = boxLabelDatastore(trainingDataTbl(:, 2:end));
bldsTest = boxLabelDatastore(testDataTbl(:, 2:end));

Задайте мини-пакетный размер. Установите ReadSize из учебного datastore изображений и поля помечают datastore равным мини-пакетному размеру.

miniBatchSize = 8;
imdsTrain.ReadSize = miniBatchSize;
bldsTrain.ReadSize = miniBatchSize;

Объедините изображение и хранилища данных метки поля.

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

Увеличение данных

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

Используйте transform функция, чтобы применить пользовательские увеличения данных к обучающим данным. augmentData функция помощника, перечисленная в конце примера, применяет следующие увеличения к входным данным.

  • Увеличение колебания цвета на HSV-пробеле

  • Случайный горизонтальный щелчок

  • Случайное масштабирование на 10 процентов

augmentedTrainingData = transform(trainingData,@augmentData);

Считайте то же изображение четыре раза и отобразите увеличенные обучающие данные.

% Visualize the augmented images.
augmentedData = cell(4,1);
for k = 1:4
    data = read(augmentedTrainingData);
    augmentedData{k} = insertShape(data{1,1},'Rectangle',data{1,2});
    reset(augmentedTrainingData);
end
figure
montage(augmentedData,'BorderSize',10)

Предварительно обработайте обучающие данные

Задайте сетевой входной размер. При выборе сетевого входного размера считайте минимальный размер требуемым запустить саму сеть, размер учебных изображений и вычислительную стоимость, понесенную путем обработки данных в выбранном размере. Когда это возможно, выберите размер входного сигнала сети, который близок к размеру обучающего изображения и больше, чем размер входного сигнала, необходимый для сети. Чтобы уменьшать вычислительную стоимость выполнения примера, задайте сетевой входной размер [227 227 3].

networkInputSize = [227 227 3];

Предварительно обработайте увеличенные обучающие данные, чтобы подготовиться к обучению. preprocessData функция помощника, перечисленная в конце примера, применяет следующие операции предварительной обработки к входным данным.

  • Измените размер изображений к сетевому входному размеру, когда изображения больше, чем 227 227.

  • Масштабируйте пиксели изображения в области значений [0 1].

preprocessedTrainingData = transform(augmentedTrainingData, @(data)preprocessData(data, networkInputSize));

Считайте предварительно обработанные обучающие данные.

data = read(preprocessedTrainingData);

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

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

Задайте Сеть YOLO v3

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

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

Можно использовать Deep Network Designer, чтобы создать сеть, показанную в схеме.

Во-первых, используйте transform предварительно обрабатывать обучающие данные для вычисления полей привязки, когда учебные изображения, используемые в этом примере, больше, чем 227 227 и отличаются по размеру. Задайте количество привязок как 6, чтобы достигнуть хорошего компромисса между количеством привязок и означать IoU. Используйте estimateAnchorBoxes функционируйте, чтобы оценить поля привязки. Для получения дополнительной информации при оценке полей привязки, смотрите Оценочные Поля Привязки От Обучающих данных.

trainingDataForEstimation = transform(trainingData,@(data)preprocessData(data,networkInputSize));
numAnchors = 6;
[anchorBoxes, meanIoU] = estimateAnchorBoxes(trainingDataForEstimation, numAnchors)
anchorBoxes = 6×2

    35    25
   165   138
    73    70
   151   125
   113   103
    42    38

meanIoU = 0.8369

Задайте anchorBoxMasks выбрать поля привязки, чтобы использовать в обоих головы обнаружения. anchorBoxMasks массив ячеек [Mx1], где M обозначает количество голов обнаружения. Каждая голова обнаружения состоит из [1xN] массив индекса строки привязок в anchorBoxes, где N является количеством полей привязки, чтобы использовать. Выберите поля привязки для каждой головы обнаружения на основе размера — используют большие поля привязки в более низкой шкале и меньшие поля привязки в более высокой шкале. Для этого отсортируйте поля привязки с большими полями привязки сначала и присвойте первые три первой голове обнаружения и следующие три второй голове обнаружения.

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

Загрузите сеть SqueezeNet, предварительно обученную на наборе данных Imagenet. Можно также принять решение загрузить различную предварительно обученную сеть, такую как MobileNet-v2 или ResNet-18. YOLO v3 выполняет лучше и обучается быстрее, когда вы используете предварительно обученную сеть.

Затем создайте сеть извлечения признаков. Выбор оптимального слоя извлечения признаков требует метода проб и ошибок, и можно использовать analyzeNetwork найти имена потенциальных слоев извлечения признаков в сети. В данном примере используйте squeezenetFeatureExtractor функция помощника, перечисленная в конце этого примера, чтобы удалить слои после слоя 'fire9-concat' извлечения признаков. Слои после этого слоя характерны для задач классификации и не помогают с обнаружением объектов.

baseNetwork = squeezenet;
lgraph = squeezenetFeatureExtractor(baseNetwork, networkInputSize);

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

classNames = trainingDataTbl.Properties.VariableNames(2:end);
numClasses = size(classNames, 2);
numPredictorsPerAnchor = 5 + numClasses;
lgraph = addFirstDetectionHead(lgraph, anchorBoxMasks{1}, numPredictorsPerAnchor);
lgraph = addSecondDetectionHead(lgraph, anchorBoxMasks{2}, numPredictorsPerAnchor);

Наконец, соедините головы обнаружения путем подключения первой головы обнаружения со слоем извлечения признаков и второй головы обнаружения к выходу первой головы обнаружения. Кроме того, объедините сверхдискретизированные функции во второй голове обнаружения с функциями от 'fire5-concat' слой, чтобы получить больше значимой семантической информации во второй голове обнаружения.

lgraph = connectLayers(lgraph, 'fire9-concat', 'conv1Detection1');
lgraph = connectLayers(lgraph,'relu1Detection1','upsample1Detection2');
lgraph = connectLayers(lgraph,'fire5-concat','depthConcat1Detection2/in2');

Головы обнаружения включают выходной слой сети. Чтобы извлечь выходные функции, задайте имена голов обнаружения, использующих массив формы [Mx1]. M является количеством голов обнаружения. Задайте имена голов обнаружения в порядке, в котором это происходит в сети.

networkOutputs = ["conv2Detection1"
    "conv2Detection2"
    ];

Задайте опции обучения

Задайте эти опции обучения.

  • Определите номер итераций к 2 000.

  • Установите скорость обучения на 0.001.

  • Установите период прогрева на 1 000. Этот параметр обозначает количество итераций, чтобы увеличить скорость обучения экспоненциально на основе формулы learningRate×(iterationwarmupPeriod). Это помогает в стабилизации градиентов на уровнях высшего образования.

  • Установитесь коэффициент регуляризации L2 на 0,0005.

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

  • Инициализируйте скорость градиента как []. Это используется SGDM, чтобы сохранить скорость градиентов.

numIterations = 2000;
learningRate = 0.001;
warmupPeriod = 1000;
l2Regularization = 0.0005;
penaltyThreshold = 0.5;
velocity = [];

Обучите модель

Обучайтесь на графическом процессоре, если вы доступны. Используя графический процессор требует Parallel Computing Toolbox™, и CUDA® включил NVIDIA®, графический процессор с вычисляет возможность 3.0 или выше. Чтобы автоматически обнаружить, если вы имеете графический процессор в наличии, установите executionEnvironment к "auto". Если вы не имеете графического процессора или не хотите использовать один в обучении, устанавливать executionEnvironment к "cpu". Чтобы гарантировать использование графического процессора для обучения, установите executionEnvironment к "gpu".

executionEnvironment = "auto";

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

Наконец, задайте пользовательский учебный цикл. Для каждой итерации:

  • Читайте из preprocessedTrainingData и создайте пакет изображений и основных блоков истинности с помощью createBatchData поддерживание функции.

  • Преобразуйте пакет изображений к dlarray объекты с базовым одним типом и указывают, что размерность маркирует 'SSCB' (пространственный, пространственный, канал, пакет).

  • Для обучения графического процессора преобразуйте данные в gpuArray объекты.

  • Оцените градиенты модели с помощью dlfeval и modelGradients функция. Функциональный modelGradients, перечисленный как функция поддержки, возвращает градиенты потери относительно настраиваемых параметров в net, соответствующая мини-пакетная потеря и состояние текущего пакета.

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

  • Определите скорость обучения на основе количества итераций с помощью piecewiseLearningRateWithWarmup поддерживание функции.

  • Обновите сетевые параметры с помощью sgdmupdate функция.

  • Обновите state параметры net со скользящим средним значением.

  • Обновите график процесса обучения.

if doTraining
    % Convert layer graph to dlnetwork.
    net = dlnetwork(lgraph);
    
    % Create subplots for the learning rate and mini-batch loss.
    fig = figure;
    [lossPlotter, learningRatePlotter] = configureTrainingProgressPlotter(fig);
    
    % Custom training loop.
    for iteration = 1:numIterations
        
        % Reset datastore.
        if ~hasdata(preprocessedTrainingData)
            reset(preprocessedTrainingData);
        end
        
        % Read batch of data and create batch of images and
        % ground truths.
        data = read(preprocessedTrainingData);
        [XTrain,YTrain] = createBatchData(data, classNames);
        
        % Convert mini-batch of data to dlarray.
        XTrain = dlarray(single(XTrain),'SSCB');
        
        % If training on a GPU, then convert data to gpuArray.
        if (executionEnvironment == "auto" && canUseGPU) || executionEnvironment == "gpu"
            XTrain = gpuArray(XTrain);
        end
        
        % Evaluate the model gradients and loss using dlfeval and the
        % modelGradients function.
        [gradients,loss,state] = dlfeval(@modelGradients, net, XTrain, YTrain, anchorBoxes, anchorBoxMasks, penaltyThreshold, networkOutputs);
        
        % Apply L2 regularization.
        gradients = dlupdate(@(g,w) g + l2Regularization*w, gradients, net.Learnables);
        
        % Determine the current learning rate value.
        currentLR = piecewiseLearningRateWithWarmup(iteration, learningRate, warmupPeriod, numIterations);
        
        % Update the network learnable parameters using the SGDM optimizer.
        [net, velocity] = sgdmupdate(net, gradients, velocity, currentLR);
        
        % Update the state parameters of dlnetwork.
        net.State = state;
        
        % Update training plot with new points.
        addpoints(lossPlotter, iteration, double(gather(extractdata(loss))));
        addpoints(learningRatePlotter, iteration, currentLR);
        drawnow  
    end
end

Оцените модель

Система Компьютерного зрения Toolbox™ обеспечивает функции оценки детектора объектов, чтобы измерить общие метрики, такие как средняя точность (evaluateDetectionPrecision) и средние журналом коэффициенты непопаданий (evaluateDetectionMissRate). В этом примере используется средняя метрика точности. Средняя точность обеспечивает один номер, который включает способность детектора сделать правильные классификации (точность) и способность детектора найти все соответствующие объекты (отзыв).

Выполнение этих шагов, чтобы оценить обученный dlnetwork объект net на тестовых данных.

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

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

  • Примените к тестовым данным то же преобразование предварительной обработки, что и к обучающим данным. Обратите внимание на то, что увеличение данных не применяется к тестовым данным. Тестовые данные должны быть представительными для исходных данных и быть оставлены немодифицированные для несмещенной оценки.

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

  • Вызовите evaluateDetectionPrecision с предсказанным results и preprocessedTestData в качестве аргументов.

confidenceThreshold = 0.5;
overlapThreshold = 0.5;

% Create the test datastore.
preprocessedTestData = transform(testData,@(data)preprocessData(data,networkInputSize));

% Create a table to hold the bounding boxes, scores, and labels returned by
% the detector. 
numImages = size(testDataTbl,1);
results = table('Size',[numImages 3],...
    'VariableTypes',{'cell','cell','cell'},...
    'VariableNames',{'Boxes','Scores','Labels'});

% Run detector on each image in the test set and collect results.
for i = 1:numImages
    
    % Read the datastore and get the image.
    data = read(preprocessedTestData);
    I = data{1};
    
    % Convert to dlarray. If GPU is available, then convert data to gpuArray.
    XTest = dlarray(I,'SSCB');
    if (executionEnvironment == "auto" && canUseGPU) || executionEnvironment == "gpu"
        XTest = gpuArray(XTest);
    end
    
    % Run the detector.
    [bboxes, scores, labels] = yolov3Detect(net, XTest, networkOutputs, anchorBoxes, anchorBoxMasks, confidenceThreshold, overlapThreshold, classNames);
    
    % Collect the results.
    results.Boxes{i} = bboxes;
    results.Scores{i} = scores;
    results.Labels{i} = labels;
end

% Evaluate the object detector using Average Precision metric.
[ap, recall, precision] = evaluateDetectionPrecision(results, preprocessedTestData);

Кривая отзыва точности (PR) показывает, насколько точный детектор на различных уровнях отзыва. Идеально, точность 1 на всех уровнях отзыва.

% Plot precision-recall curve.
figure
plot(recall, precision)
xlabel('Recall')
ylabel('Precision')
grid on
title(sprintf('Average Precision = %.2f', ap))

Обнаружьте Объекты Используя YOLO v3

Используйте сеть в обнаружении объектов.

  • Считайте изображение.

  • Преобразуйте изображение в dlarray и используйте графический процессор, если вы доступны..

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

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

% Read the datastore.
reset(preprocessedTestData)
data = read(preprocessedTestData);

% Get the image.
I = data{1};

% Convert to dlarray.
XTest = dlarray(I,'SSCB');

% If GPU is available, then convert data to gpuArray.
if (executionEnvironment == "auto" && canUseGPU) || executionEnvironment == "gpu"
    XTest = gpuArray(XTest);
end

[bboxes, scores, labels] = yolov3Detect(net, XTest, networkOutputs, anchorBoxes, anchorBoxMasks, confidenceThreshold, overlapThreshold, classNames);

% Display the detections on image.
if ~isempty(scores)
    I = insertObjectAnnotation(I, 'rectangle', bboxes, scores);
end
figure
imshow(I)

Вспомогательные Функции

Функция градиентов модели

Функциональный modelGradients берет в качестве входа dlnetwork объект net, мини-пакет входных данных XTrain с соответствующими основными блоками истинности YTrain, поля привязки, маска поля привязки, заданный порог штрафа и сетевой выход называют как входные параметры и возвращают градиенты потери относительно настраиваемых параметров в net, соответствующая мини-пакетная потеря и состояние текущего пакета.

Функция градиентов модели вычисляет общую сумму убытков и градиенты путем выполнения этих операций.

  • Сгенерируйте предсказания от входного пакета изображений с помощью функции поддержки yolov3Forward.

  • Соберите предсказания на центральном процессоре для постобработки.

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

  • Сгенерируйте цели для расчета потерь при помощи конвертированных предсказаний и достоверных данных. Эти цели сгенерированы для положений ограничительной рамки (x, y, ширина, высота), объектное доверие и вероятности класса. Смотрите функцию поддержки generateTargets.

  • Вычисляет среднеквадратическую ошибку предсказанных координат ограничительной рамки с целевыми полями. Смотрите функцию поддержки bboxOffsetLoss.

  • Определяет бинарную перекрестную энтропию предсказанной объектной оценки достоверности с оценкой достоверности целевого объекта. Смотрите функцию поддержки objectnessLoss.

  • Определяет бинарную перекрестную энтропию предсказанного класса объекта с целью. Смотрите функцию поддержки classConfidenceLoss.

  • Вычисляет общую сумму убытков как сумму всех потерь.

  • Вычисляет градиенты learnables относительно общей суммы убытков.

function [gradients, totalLoss, state] = modelGradients(net, XTrain, YTrain, anchors, mask, penaltyThreshold, networkOutputs)
inputImageSize = size(XTrain,1:2);

% Extract the predictions from the network.
[YPredCell, state] = yolov3Forward(net,XTrain,networkOutputs,mask);

% Gather the activations in the CPU for post processing and extract dlarray data. 
gatheredPredictions = cellfun(@ gather, YPredCell(:,1:6),'UniformOutput',false); 
gatheredPredictions = cellfun(@ extractdata, gatheredPredictions, 'UniformOutput', false);

% Convert predictions from grid cell coordinates to box coordinates.
tiledAnchors = generateTiledAnchors(gatheredPredictions(:,2:5),anchors,mask);
gatheredPredictions(:,2:5) = applyAnchorBoxOffsets(tiledAnchors, gatheredPredictions(:,2:5), inputImageSize);

% Generate target for predictions from the ground truth data.
[boxTarget, objectnessTarget, classTarget, objectMaskTarget, boxErrorScale] = generateTargets(gatheredPredictions, YTrain, inputImageSize, anchors, mask, penaltyThreshold);

% Compute the loss.
boxLoss = bboxOffsetLoss(YPredCell(:,[2 3 7 8]),boxTarget,objectMaskTarget,boxErrorScale);
objLoss = objectnessLoss(YPredCell(:,1),objectnessTarget,objectMaskTarget);
clsLoss = classConfidenceLoss(YPredCell(:,6),classTarget,objectMaskTarget);
totalLoss = boxLoss + objLoss + clsLoss;

% Compute gradients of learnables with regard to loss.
gradients = dlgradient(totalLoss, net.Learnables);
end

function [YPredCell, state] = yolov3Forward(net, XTrain, networkOutputs, anchorBoxMask)
% Predict the output of network and extract the confidence score, x, y,
% width, height, and class.
YPredictions = cell(size(networkOutputs));
[YPredictions{:}, state] = forward(net, XTrain, 'Outputs', networkOutputs);
YPredCell = extractPredictions(YPredictions, anchorBoxMask);

% Append predicted width and height to the end as they are required
% for computing the loss.
YPredCell(:,7:8) = YPredCell(:,4:5);

% Apply sigmoid and exponential activation.
YPredCell(:,1:6) = applyActivations(YPredCell(:,1:6));
end

function boxLoss = bboxOffsetLoss(boxPredCell, boxDeltaTarget, boxMaskTarget, boxErrorScaleTarget)
% Mean squared error for bounding box position.
lossX = sum(cellfun(@(a,b,c,d) mse(a.*c.*d,b.*c.*d),boxPredCell(:,1),boxDeltaTarget(:,1),boxMaskTarget(:,1),boxErrorScaleTarget));
lossY = sum(cellfun(@(a,b,c,d) mse(a.*c.*d,b.*c.*d),boxPredCell(:,2),boxDeltaTarget(:,2),boxMaskTarget(:,1),boxErrorScaleTarget));
lossW = sum(cellfun(@(a,b,c,d) mse(a.*c.*d,b.*c.*d),boxPredCell(:,3),boxDeltaTarget(:,3),boxMaskTarget(:,1),boxErrorScaleTarget));
lossH = sum(cellfun(@(a,b,c,d) mse(a.*c.*d,b.*c.*d),boxPredCell(:,4),boxDeltaTarget(:,4),boxMaskTarget(:,1),boxErrorScaleTarget));
boxLoss = lossX+lossY+lossW+lossH;
end

function objLoss = objectnessLoss(objectnessPredCell, objectnessDeltaTarget, boxMaskTarget)
% Binary cross-entropy loss for objectness score.
objLoss = sum(cellfun(@(a,b,c) crossentropy(a.*c,b.*c,'TargetCategories','independent'),objectnessPredCell,objectnessDeltaTarget,boxMaskTarget(:,2)));
end

function clsLoss = classConfidenceLoss(classPredCell, classTarget, boxMaskTarget)
% Binary cross-entropy loss for class confidence score.
clsLoss = sum(cellfun(@(a,b,c) crossentropy(a.*c,b.*c,'TargetCategories','independent'),classPredCell,classTarget,boxMaskTarget(:,3)));
end

Увеличение и функции обработки данных

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 = 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);
    
    % Convert an input image with single channel to 3 channels.
    if numel(imgSize) == 1 
        I = repmat(I,1,1,3);
    end
    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

function [x,y] = createBatchData(data, classNames)
% The createBatchData function creates a batch of images and ground truths
% from input data, which is a [Nx3] cell array returned by the transformed
% datastore for YOLO v3. It creates two 4-D arrays by concatenating all the
% images and ground truth boxes along the batch dimension. The function
% performs these operations on the bounding boxes before concatenating
% along the fourth dimension:
% * Convert the class names to numeric class IDs based on their position in
%   the class names.
% * Combine the ground truth boxes, class IDs and network input size into
%   single cell array.
% * Pad with zeros to make the number of ground truths consistent across
%   a mini-batch.

% Concatenate images along the batch dimension.
x = cat(4,data{:,1});

% Get class IDs from the class names.
groundTruthClasses = data(:,3);
classNames = repmat({categorical(classNames')},size(groundTruthClasses));
[~,classIndices] = cellfun(@(a,b)ismember(a,b),groundTruthClasses,classNames,'UniformOutput',false);

% Append the label indexes and training image size to scaled bounding boxes
% and create a single cell array of responses.
groundTruthBoxes = data(:,2);
combinedResponses = cellfun(@(bbox,classid)[bbox,classid],groundTruthBoxes,classIndices,'UniformOutput',false);
len = max( cellfun(@(x)size(x,1), combinedResponses ) );
paddedBBoxes = cellfun( @(v) padarray(v,[len-size(v,1),0],0,'post'), combinedResponses, 'UniformOutput',false);
y = cat(4,paddedBBoxes{:,1});
end

Сетевые функции создания

function lgraph = squeezenetFeatureExtractor(net, imageInputSize)
% The squeezenetFeatureExtractor function removes the layers after 'fire9-concat'
% in SqueezeNet and also removes any data normalization used by the image input layer.

% Convert to layerGraph.
lgraph = layerGraph(net);

lgraph = removeLayers(lgraph, {'drop9' 'conv10' 'relu_conv10' 'pool10' 'prob' 'ClassificationLayer_predictions'});
inputLayer = imageInputLayer(imageInputSize,'Normalization','none','Name','data');
lgraph = replaceLayer(lgraph,'data',inputLayer);
end

function lgraph = addFirstDetectionHead(lgraph,anchorBoxMasks,numPredictorsPerAnchor)
% The addFirstDetectionHead function adds the first detection head.

numAnchorsScale1 = size(anchorBoxMasks, 2);
% Compute the number of filters for last convolution layer.
numFilters = numAnchorsScale1*numPredictorsPerAnchor;
firstDetectionSubNetwork = [
    convolution2dLayer(3,256,'Padding','same','Name','conv1Detection1','WeightsInitializer','he')
    reluLayer('Name','relu1Detection1')
    convolution2dLayer(1,numFilters,'Padding','same','Name','conv2Detection1','WeightsInitializer','he')
    ];
lgraph = addLayers(lgraph,firstDetectionSubNetwork);
end

function lgraph = addSecondDetectionHead(lgraph,anchorBoxMasks,numPredictorsPerAnchor)
% The addSecondDetectionHead function adds the second detection head.

numAnchorsScale2 = size(anchorBoxMasks, 2);
% Compute the number of filters for the last convolution layer.
numFilters = numAnchorsScale2*numPredictorsPerAnchor;
secondDetectionSubNetwork = [
    upsampleLayer(2,'upsample1Detection2')
    depthConcatenationLayer(2,'Name','depthConcat1Detection2');
    convolution2dLayer(3,128,'Padding','same','Name','conv1Detection2','WeightsInitializer','he')
    reluLayer('Name','relu1Detection2')
    convolution2dLayer(1,numFilters,'Padding','same','Name','conv2Detection2','WeightsInitializer','he')
    ];
lgraph = addLayers(lgraph,secondDetectionSubNetwork);
end

Функция расписания скорости обучения

function currentLR = piecewiseLearningRateWithWarmup(iteration, learningRate, warmupPeriod, numIterations)
% The piecewiseLearningRateWithWarmup function computes the current
% learning rate based on the iteration number.

if iteration <= warmupPeriod
    % Increase the learning rate for number of iterations in warmup period.
    currentLR = learningRate * ((iteration/warmupPeriod)^4);
    
elseif iteration >= warmupPeriod && iteration < warmupPeriod+floor(0.6*(numIterations-warmupPeriod))
    % After warm up period, keep the learning rate constant if the remaining number of iteration is less than 60 percent. 
    currentLR = learningRate;
    
elseif iteration >= warmupPeriod+floor(0.6*(numIterations-warmupPeriod)) && iteration < warmupPeriod+floor(0.9*(numIterations-warmupPeriod))
    % If the remaining number of iteration is more than 60 percent but less
    % than 90 percent multiply the learning rate by 0.1.
    currentLR = learningRate*0.1;
    
else
    % If remaining iteration is more than 90 percent multiply the learning
    % rate by 0.01.
    currentLR = learningRate*0.01;
end
end

Предскажите функции

function [bboxes,scores,labels] = yolov3Detect(net, XTest, networkOutputs, anchors, anchorBoxMask, confidenceThreshold, overlapThreshold, classes)
% The yolov3Detect function detects the bounding boxes, scores, and labels in an image.

imageSize = size(XTest,[1,2]);

% Find the input image layer and get the network input size.
networkInputIdx = arrayfun( @(x)isa(x,'nnet.cnn.layer.ImageInputLayer'), net.Layers);
networkInputSize = net.Layers(networkInputIdx).InputSize;

% Predict and filter the detections based on confidence threshold.
predictions = yolov3Predict(net,XTest,networkOutputs,anchorBoxMask);
predictions = cellfun(@ gather, predictions,'UniformOutput',false);
predictions = cellfun(@ extractdata, predictions, 'UniformOutput', false);
tiledAnchors = generateTiledAnchors(predictions(:,2:5),anchors,anchorBoxMask);
predictions(:,2:5) = applyAnchorBoxOffsets(tiledAnchors, predictions(:,2:5), networkInputSize);
[bboxes,scores,labels] = generateYOLOv3Detections(predictions, confidenceThreshold, 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(net,XTrain,networkOutputs,anchorBoxMask)
% Predict the output of network and extract the confidence, x, y,
% width, height, and class.
YPredictions = cell(size(networkOutputs));
[YPredictions{:}] = predict(net, XTrain);
YPredCell = extractPredictions(YPredictions, anchorBoxMask);

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

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

function YPredCell = applyActivations(YPredCell)
YPredCell(:,1:3) = cellfun(@ sigmoid ,YPredCell(:,1:3),'UniformOutput',false);
YPredCell(:,4:5) = cellfun(@ exp,YPredCell(:,4:5),'UniformOutput',false);
YPredCell(:,6) = cellfun(@ sigmoid ,YPredCell(:,6),'UniformOutput',false);
end

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

function tiledAnchors = generateTiledAnchors(YPredCell,anchorBoxes,anchorBoxMask)
% Generate tiled anchor offset.
tiledAnchors = cell(size(YPredCell));
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

function tiledAnchors = applyAnchorBoxOffsets(tiledAnchors,YPredCell,inputImageSize)
% Convert grid cell coordinates to box coordinates.
for i=1:size(YPredCell,1)
    [h,w,~,~] = size(YPredCell{i,1});  
    tiledAnchors{i,1} = (tiledAnchors{i,1}+YPredCell{i,1})./w;
    tiledAnchors{i,2} = (tiledAnchors{i,2}+YPredCell{i,2})./h;
    tiledAnchors{i,3} = (tiledAnchors{i,3}.*YPredCell{i,3})./inputImageSize(2);
    tiledAnchors{i,4} = (tiledAnchors{i,4}.*YPredCell{i,4})./inputImageSize(1);
end
end

function [lossPlotter, learningRatePlotter] = configureTrainingProgressPlotter(f)
% Create the subplots to display the loss and learning rate.
figure(f);
clf
subplot(2,1,1);
ylabel('Learning Rate');
xlabel('Iteration');
learningRatePlotter = animatedline;
subplot(2,1,2);
ylabel('Total Loss');
xlabel('Iteration');
lossPlotter = animatedline;
end

Ссылки

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

Для просмотра документации необходимо авторизоваться на сайте