主要内容

本页采用了机器翻译。点击此处可查看英文原文。

基于 YOLO v4 深度学习的目标检测

本示例演示了如何使用“只需看一次”第 4 版 (YOLO v4) 深度学习网络在图像中检测目标。例如,在此示例中,您将

  • 配置用于 YOLO v4 目标检测网络的训练、验证和测试的数据集。您还将对训练数据集进行数据增强,以提高网络的效率。

  • 根据训练数据计算锚框,用于训练 YOLO v4 目标检测网络。

  • 使用 yolov4ObjectDetector 函数创建一个 YOLO v4 目标检测器,并使用 trainYOLOv4ObjectDetector 函数对该检测器进行训练。

该示例还提供了一个预训练的 YOLO v4 目标检测器,用于检测图像中的车辆。该预训练网络以 tiny-yolov4-coco 作为骨干网络,并在车辆数据集上进行了训练。有关 YOLO v4 目标检测网络的信息,请参阅YOLO v4 入门指南

加载数据集

此示例使用包含 295 个图像的小型车辆数据集。这些图像中的许多来自加州理工学院 (Caltech) 1999 年和 2001 年的 Cars 数据集,该数据集可在皮耶特罗·佩罗纳 (Pietro Perona) 创建的加州理工学院计算机视觉网站上获取,并已获得授权使用。每个图像中包含一到两个标注为“车辆”的实例。虽然小型数据集有助于探索 YOLO v4 的训练流程,但在实际应用中,需要更多的标注图像来训练一个稳健的检测器。

解压缩车辆图像并加载车辆真实值数据。

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

车辆数据存储在一个包含两列的表中。第一列包含图像文件路径,第二列包含边界框。

显示数据集的前几行。

vehicleDataset(1:4,:)
ans=4×2 table
    '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]

添加本地车辆数据文件夹的完整路径。

vehicleDataset.imageFilename = fullfile(pwd,vehicleDataset.imageFilename);

将数据集分成训练集、验证集和测试集。选择 60% 的数据用于训练,10% 用于验证,其余用于测试经过训练的检测器。

rng("default");
shuffledIndices = randperm(height(vehicleDataset));
idx = floor(0.6 * length(shuffledIndices) );

trainingIdx = 1:idx;
trainingDataTbl = vehicleDataset(shuffledIndices(trainingIdx),:);

validationIdx = idx+1 : idx + 1 + floor(0.1 * length(shuffledIndices) );
validationDataTbl = vehicleDataset(shuffledIndices(validationIdx),:);

testIdx = validationIdx(end)+1 : length(shuffledIndices);
testDataTbl = vehicleDataset(shuffledIndices(testIdx),:);

使用 imageDatastore 和 boxLabelDatastore 创建数据存储,以便在训练和评估期间加载图像和标签数据。

imdsTrain = imageDatastore(trainingDataTbl{:,"imageFilename"});
bldsTrain = boxLabelDatastore(trainingDataTbl(:,"vehicle"));

imdsValidation = imageDatastore(validationDataTbl{:,"imageFilename"});
bldsValidation = boxLabelDatastore(validationDataTbl(:,"vehicle"));

imdsTest = imageDatastore(testDataTbl{:,"imageFilename"});
bldsTest = boxLabelDatastore(testDataTbl(:,"vehicle"));

组合图像和边界框标签数据存储。

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

当数据集包含以下一项或多项内容时,请使用 validateInputData 来检测无效的图像、边界框或标签:

  • 图像格式无效或包含 NaN 值的样本

  • 包含零值/NaN 值/Inf 值/空值的边界框

  • 缺失或非分类标签

边界框的值必须是有限的正整数,且不能为 NaN。边界框的高度和宽度值必须为正数,且必须位于图像边界内。

validateInputData(trainingData);
validateInputData(validationData);
validateInputData(testData);

显示其中一个训练图像和边界框标签。

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

reset(trainingData);

创建一个 YOLO v4 目标检测网络

指定用于训练的网络输入大小。

inputSize = [416 416 3];

指定要检测的目标类的名称。

className = "vehicle";

使用 estimateAnchorBoxes 函数,根据训练数据中目标的大小来估计锚框。考虑到训练前会对图像大小进行调整,用来估计锚框的训练数据的大小也要调整。使用 transform 函数对训练数据进行预处理,然后定义锚框的数量并估计锚框的位置。使用 preprocessData 辅助函数将训练数据调整为与网络输入尺寸一致。

rng("default")
trainingDataForEstimation = transform(trainingData,@(data)preprocessData(data,inputSize));
numAnchors = 6;
[anchors,meanIoU] = estimateAnchorBoxes(trainingDataForEstimation,numAnchors);

anchorBoxes 参量指定为所有检测头中要使用的锚框。锚框被指定为一个 [M x 1] 元胞数组,其中 M 表示检测头的数量。每个检测头由一个 [N x 2] 矩阵组成,该矩阵存储在 anchors 参量中,其中 N 是要使用的锚点数量。根据特征图的大小,为每个检测头指定相应的 anchorBoxes。在较小比例尺下使用较大的锚点,在较大比例尺下使用较小的锚点。为此,请按区域对锚点进行降序排序,并将前三个分配给第一个检测头,最后三个分配给第二个检测头。

area = anchors(:, 1).*anchors(:,2);
[~,idx] = sort(area,"descend");

anchors = anchors(idx,:);
anchorBoxes = {anchors(1:3,:)
    anchors(4:6,:)};

有关选择锚框的详细信息,请参阅Estimate Anchor Boxes from Training Data (Computer Vision Toolbox™) 和Anchor Boxes for Object Detection

使用 yolov4ObjectDetector 函数创建 YOLO v4 目标检测器。指定在 COCO 数据集上预训练的 YOLO v4 检测网络的名称。请指定类名和预估的锚框。

detector = yolov4ObjectDetector("tiny-yolov4-coco",className,anchorBoxes,InputSize=inputSize);

执行数据增强

进行数据增强以提高训练准确率。使用 transform 函数对训练数据应用自定义数据增强。augmentData 辅助函数对输入数据进行以下扩展:

  • HSV 空间中的色彩抖动增强

  • 随机水平翻转

  • 随机缩放 10%

请注意,数据增强不适用于测试数据和验证数据。理想情况下,测试数据和验证数据应代表原始数据并且保持不变,以便进行无偏置的评估。

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)

指定训练选项

使用 trainingOptions 指定网络训练选项。使用 Adam 优化器,以恒定学习率 0.001 对目标检测器进行 80 个 epoch 的训练。若要获得验证损失最低的训练済み检测器,请将 OutputNetwork 设置为 "best-validation-loss"。将 ValidationData 设置为验证数据,将 ValidationFrequency 设置为 1000。为了更频繁地验证数据,您可以减少 ValidationFrequency 的次数,但这也会增加训练时间。使用 ExecutionEnvironment 来确定将使用哪些硬件资源来训练该网络。ExecutionEnvironment 的默认值为 "auto",如果 GPU 可用,则选择 GPU;否则选择 CPU。将 CheckpointPath 设置为一个临时位置,以便在训练过程中保存部分训练完成的检测器。如果训练过程中出现中断(例如因停电或系统故障),您可以从已保存的检查点继续训练。

options = trainingOptions("adam", ...
    GradientDecayFactor=0.9, ...
    SquaredGradientDecayFactor=0.999, ...
    InitialLearnRate=0.001, ...
    LearnRateSchedule="none", ...
    MiniBatchSize=4, ...
    L2Regularization=0.0005, ...
    MaxEpochs=80, ...
    DispatchInBackground=true, ...
    ResetInputNormalization=true, ...
    Shuffle="every-epoch", ...
    VerboseFrequency=20, ...
    ValidationFrequency=1000, ...
    CheckpointPath=tempdir, ...
    ValidationData=validationData, ...
    OutputNetwork="best-validation-loss");

训练 YOLO v4 目标检测器

使用 trainYOLOv4ObjectDetector 函数训练 YOLO v4 目标检测器。此示例在配备 24 GB 内存的 NVIDIA™ RTX A5000 上运行。使用此设置训练此网络大约需要 33 分钟。训练时间会因您使用的硬件而异。除了训练网络之外,您还可以在 Computer Vision Toolbox™ 中使用预训练的 YOLO v4 目标检测器。

使用 downloadPretrainedYOLOv4Detector 辅助函数下载预训练的检测器。要在增强的训练数据上训练检测器,请将 doTraining 的值设置为 true

doTraining = false;
if doTraining       
    % Train the YOLO v4 detector.
    [detector,info] = trainYOLOv4ObjectDetector(augmentedTrainingData,detector,options);
else
    % Load pretrained detector for the example.
    detector = downloadPretrainedYOLOv4Detector();
end

在测试图像上运行检测器。

I = imread("highway.png");
[bboxes,scores,labels] = detect(detector,I);

显示结果。

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

使用测试集评估检测器

使用目标检测器分析器,将检测器的性能可视化,并根据真实值对其进行评估。该 App 将在测试集上运行检测器,计算平均精确率等度量,绘制精确率-召回率曲线,并在测试集中的每个图像上显示检测结果。您可以将真实值数据与检测器的正确和错误预测结果并排可视化显示,并快速跳转到检测器出错最多的图像,从而更好地了解其性能。例如,检测器在某些特定场景下可能会出现故障,这可能表明应使用包含这些特定场景的额外数据对检测器进行重新训练。

objectDetectorAnalyzer(detector,testData)

选择“精确率-召回率曲线”选项卡,即可查看各车辆类别的精确率-召回率曲线。曲线显示,当评估的重叠阈值为 0.5 时,该检测器在测试集上的表现良好,但随着重叠阈值增加到 0.7 和 0.8,其性能逐渐下降。曲线上的圆形标记指示了检测器的工作点。精确率-召回率曲线上的一个工作点,是指一个具体的检测分数阈值设置,该设置决定了检测器在精确率与召回率之间所达到的平衡。您可以调整分数阈值滑块,以确定适合您应用的最佳工作点。

浏览其他选项卡,查看更多检测度量:

  • 数据集和类别摘要:总结了该数据集在所有类别中的表现。

  • 混淆矩阵:显示每个类中已找到和未找到的对象数量。

  • 按区域大小的检测情况:根据目标大小可视化正确和错误的检测结果,以发现由目标大小引起的错误。

有关评估度量的自定义可视化,请参阅 evaluateObjectDetection。有关目标检测度量的更多信息,请参阅Evaluate Object Detector Performance

支持函数

用于执行数据增强的辅助函数。

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(ii,:) = 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);
    
    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

用于下载预训练的 YOLO v4 目标检测器的辅助函数。

function detector = downloadPretrainedYOLOv4Detector()
% Download a pretrained yolov4 detector.
if ~exist("yolov4TinyVehicleExample_24a.mat", "file")
    if ~exist("yolov4TinyVehicleExample_24a.zip", "file")
        disp("Downloading pretrained detector...");
        pretrainedURL = "https://ssd.mathworks.com/supportfiles/vision/data/yolov4TinyVehicleExample_24a.zip";
        websave("yolov4TinyVehicleExample_24a.zip", pretrainedURL);
    end
    unzip("yolov4TinyVehicleExample_24a.zip");
end
pretrained = load("yolov4TinyVehicleExample_24a.mat");
detector = pretrained.detector;
end

参考

[1] Alexey Bochkovskiy, Chien-Yao Wang, and Hong-Yuan Mark Liao. “YOLOv4: Optimal Speed and Accuracy of Object Detection.” 2020, arXiv:2004.10934. https://arxiv.org/abs/2004.10934.

另请参阅

App

函数

主题