主要内容

SSD深度学习对象检测

这个例子展示了如何训练一个单射检测器(SSD)。

概述

深度学习是一种强大的机器学习技术,可以自动学习检测任务所需的图像特征。有几种使用深度学习的目标检测技术,如Faster R-CNN、You Only Look Once (YOLO v2)和SSD。方法训练SSD车辆检测器trainSSDObjectDetector函数。有关更多信息,请参见对象检测(计算机视觉工具箱)

下载Pretrained探测器

下载一个预先训练的检测器,以避免必须等待训练完成。如果你想训练探测器,设置doTraining变量为true。

doTraining = false;如果~ doTraining & & ~存在(“ssdResNet50VehicleExample_22b.mat”“文件”) disp (“正在下载预先训练的检测器(44 MB)…”);pretrainedURL =“//www.ru-cchi.com/supportfiles/vision/data/ssdResNet50VehicleExample_22b.mat”;websave (“ssdResNet50VehicleExample_22b.mat”, pretrainedURL);结束

加载数据集

本例使用了一个包含295张图像的小型车辆数据集。这些图片大多来自加州理工学院赛车1999年和2001年的数据集,由Pietro Perona创建,并经许可使用。每张图片包含一个或两个标记的车辆实例。一个小的数据集对于探索SSD训练过程是有用的,但在实践中,需要更多的标记图像来训练一个健壮的检测器。

解压缩vehicleDatasetImages.zipdata =负载(“vehicleDatasetGroundTruth.mat”);vehicleDataset = data.vehicleDataset;

训练数据存储在一个表中。第一列包含图像文件的路径。其余列包含车辆的ROI标签。显示数据的前几行。

vehicleDataset (1:4,:)
ans =4×2表imageFilename车辆  _________________________________ _________________ {' vehicleImages / image_00001.jpg '} {[220 136 35 28]} {' vehicleImages / image_00002.jpg '} {[45 175 126 61]} {' vehicleImages / image_00003.jpg '} {[45 108 120 33]} {' vehicleImages / image_00004.jpg '} {[124 112 38 36]}

将数据集拆分为训练检测器的训练集和评估检测器的测试集。选择60%的数据进行训练。用剩下的时间做评估。

rng (0);shuffledIndices = randperm(高度(vehicleDataset));idx = floor(0.6 * length(shuffledIndices));trainingData = vehicleDataset (shuffledIndices (1: idx):);testData = vehicleDataset (shuffledIndices (idx + 1:结束):);

使用imageDatastore而且boxLabelDatastore在训练和评估过程中加载图像和标记数据。

imdsTrain = imageDatastore (trainingData {:,“imageFilename”});bldsTrain = boxLabelDatastore (trainingData (:,“汽车”));imdsTest = imageDatastore (testData {:,“imageFilename”});bldsTest = boxLabelDatastore (testData (:,“汽车”));

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

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

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

data =阅读(trainingData);我={1}数据;bbox ={2}数据;annotatedImage = insertShape(我“矩形”, bbox);annotatedImage = imresize (annotatedImage 2);图imshow (annotatedImage)

图中包含一个axes对象。坐标轴对象包含一个image类型的对象。

创建SSD对象检测网络

使用ssdObjectDetector功能用于自动创建SSD对象检测器。ssdObjectDetector需要指定几个参数化SSD对象检测器的输入,包括基本网络也称为特征提取网络,输入大小,类名,锚框和检测网络源。使用输入基网的特定层来指定检测网络源。检测网络将自动连接到输入基网络通过ssdObjectDetector函数。

特征提取网络通常是一个经过预先训练的CNN(参见预训练深度神经网络更多的细节)。本例使用ResNet-50进行特征提取。其他预先训练的网络,如MobileNet v2或ResNet-18,也可以根据应用程序需求使用。检测子网络相对于特征提取网络是一个小的CNN,由几个卷积层和特定于SSD的层组成。

网= resnet50 ();lgraph = layerGraph(净);

在选择网络输入大小时,要考虑训练图像的大小,以及以所选大小处理数据所产生的计算成本。在可行的情况下,选择与训练图像大小接近的网络输入大小。然而,为了减少运行这个例子的计算成本,网络输入大小被选择为[300 300 3]。在培训期间,trainSSDObjectDetector自动调整训练图像到网络输入大小。

inputSize = [300 300 3];

定义要检测的对象类。

一会= {“汽车”};

从ResNet-50网络中删除额外的层,并添加额外的层,使基础网络更健壮,以学习检测。

iRemoveLayers()助手函数将删除在中提到的层之后的层layerGraph对象。我们需要这个函数从预训练的网络(如ResNet-50)中去除分类和全连接层,使其准备用于骨干网络。为了使主干特征提取网络更健壮,我们将增加7个额外的卷积层。

ssdLayerGraph = iRemoveLayers (lgraph,“activation_40_relu”);weightsInitializerValue =“glorot”;biasInitializerValue =“零”在基本网络的顶部附加额外的层。。extraLayers = [];%添加conv6_1和对应的reLUfilterSize = 1;numFilters = 256;numChannels = 1024;conv6_1 = convolution2dLayer(filterSize, numFilters, NumChannels = NumChannels,...Name =“conv6_1”...WeightsInitializer = weightsInitializerValue,...BiasInitializer = biasInitializerValue);relu6_1 = reluLayer(名称=“relu6_1”);extraLayers = [extraLayers;conv6_1;relu6_1];%添加conv6_2和对应的reLUfilterSize = 3;numFilters = 512;numChannels = 256;conv6_2 = convolution2dLayer(filterSize, numFilters, NumChannels = NumChannels,...填充= iSamePadding (filterSize),...Stride = [2,2],...Name =“conv6_2”...WeightsInitializer = weightsInitializerValue,...BiasInitializer = biasInitializerValue);relu6_2 = reluLayer(名称=“relu6_2”);extraLayers = [extraLayers;conv6_2;relu6_2];%添加conv7_1和对应的reLUfilterSize = 1;numFilters = 128;numChannels = 512;conv7_1 = convolution2dLayer(filterSize, numFilters, NumChannels = NumChannels,...Name =“conv7_1”...WeightsInitializer = weightsInitializerValue,...BiasInitializer = biasInitializerValue);relu7_1 = reluLayer(名称=“relu7_1”);extraLayers = [extraLayers;conv7_1;relu7_1];%添加conv7_2和对应的reLUfilterSize = 3;numFilters = 256;numChannels = 128;conv7_2 = convolution2dLayer(filterSize, numFilters, NumChannels = NumChannels,...填充= iSamePadding (filterSize),...Stride = [2,2],...Name =“conv7_2”...WeightsInitializer = weightsInitializerValue,...BiasInitializer = biasInitializerValue);relu7_2 = reluLayer(名称=“relu7_2”);extraLayers = [extraLayers;conv7_2;relu7_2];%添加conv8_1和对应的reLUfilterSize = 1;numFilters = 128;numChannels = 256;conv8_1 = convolution2dLayer(filterSize, numFilters, NumChannels = NumChannels,...Name =“conv8_1”...WeightsInitializer = weightsInitializerValue,...BiasInitializer = biasInitializerValue);relu8_1 = reluLayer(名称=“relu8_1”);extraLayers = [extraLayers;conv8_1;relu8_1];%添加conv8_2和对应的reLUfilterSize = 3;numFilters = 256;numChannels = 128;conv8_2 = convolution2dLayer(filterSize, numFilters, NumChannels = NumChannels,...Name =“conv8_2”...WeightsInitializer = weightsInitializerValue,...BiasInitializer = biasInitializerValue);relu8_2 = reluLayer(名称=“relu8_2”);extraLayers = [extraLayers;conv8_2;relu8_2];%添加conv9_1和对应的reLUfilterSize = 1;numFilters = 128;numChannels = 256;conv9_1 = convolution2dLayer(filterSize, numFilters, NumChannels = NumChannels,...填充= iSamePadding (filterSize),...Name =“conv9_1”...WeightsInitializer = weightsInitializerValue,...BiasInitializer = biasInitializerValue);relu9_1 = reluLayer (“名字”“relu9_1”);extraLayers = [extraLayers;conv9_1;relu9_1];如果~isempty(extraLayers) lastLayerName = ssdLayerGraph.Layers(end).Name;ssdLayerGraph = addLayers(ssdLayerGraph, extraLayers);ssdLayerGraph = connectLayers(ssdLayerGraph, lastLayerName, extraLayers(1).Name);结束

指定将添加检测网络源的网络的层名称。

detNetworkSource = [“activation_22_relu”“activation_40_relu”“relu6_2”“relu7_2”“relu8_2”];

指定锚框。锚盒(M × 1单元阵列)计数(M)必须与检测网络源计数相同。

anchorBoxes ={(60岁30;30、60;60岁,21岁,42岁,30);...(60 111; 60111; 111年,35;64年,60岁,111年,42岁,78年,60岁);...[162111, 111162, 162, 64, 94111, 162, 78, 115111];...(213162; 162213; 213年,94;123162;213115;151162);...(264213; 213264; 264151; 187213)};

创建SSD对象检测器对象。

探测器= ssdObjectDetector (ssdLayerGraph,一会,anchorBoxes DetectionNetworkSource = detNetworkSource InputSize = InputSize ModelName =“ssdVehicle”);

数据增加

数据增强是通过训练过程中对原始数据的随机变换来提高网络精度的一种方法。通过使用数据增强,您可以为训练数据添加更多的多样性,而不必实际增加标记训练样本的数量。使用变换增加训练数据通过

  • 随机地水平翻转图像和相关框标签。

  • 随机缩放图像,关联框标签。

  • 抖动图像颜色。

注意,数据扩充没有应用到测试数据。理想情况下,测试数据应该代表原始数据,并且不进行修改,以便进行无偏性评估。

augmentedTrainingData =变换(trainingData @augmentData);

通过多次读取相同的图像来可视化增强的训练数据。

augmentedData =细胞(4,1);k = 1:4 data = read(augmentedTrainingData);augmentedData{k} = insertShape(data{1},rectangle = data{2});重置(augmentedTrainingData);结束figure montage(augmentedData,BorderSize = 10)

图中包含一个axes对象。坐标轴对象包含一个image类型的对象。

训练数据进行预处理

对增强后的训练数据进行预处理,为训练做准备。

preprocessedTrainingData =变换(augmentedTrainingData @(数据)preprocessData(数据、inputSize));

读取预处理后的训练数据。

data =阅读(preprocessedTrainingData);

显示图像和边框。

我={1}数据;bbox ={2}数据;annotatedImage = insertShape(我“矩形”, bbox);annotatedImage = imresize (annotatedImage 2);图imshow (annotatedImage)

图中包含一个axes对象。坐标轴对象包含一个image类型的对象。

训练SSD对象检测器

使用trainingOptions指定网络培训选项。集“CheckpointPath”到一个临时地点。这使得在训练过程中可以保存部分训练过的检测器。如果培训中断,例如由于停电或系统故障,您可以从保存的检查点恢复培训。

选择= trainingOptions (“个”...MiniBatchSize = 16,...InitialLearnRate = 1 e - 3,...LearnRateSchedule =“分段”...LearnRateDropPeriod = 30,...LearnRateDropFactor = 0.8,...MaxEpochs = 20,...VerboseFrequency = 50,...CheckpointPath = tempdir,...洗牌=“every-epoch”);

使用trainSSDObjectDetector(计算机视觉工具箱)时,对SSD对象检测器进行培训doTraining为true。否则,加载一个预先训练的网络。

如果doTraining培训SSD检测器。[detector, info] = trainSSDObjectDetector(预处理的trainingdata,检测器,选项);其他的为示例加载预先训练的检测器。pretrained =负载(“ssdResNet50VehicleExample_22b.mat”);探测器= pretrained.detector;结束

本例在NVIDIA™Titan X GPU上验证,内存为12 GB。如果你的GPU内存较少,你可能会耗尽内存。如果发生这种情况,降低'MiniBatchSize的使用trainingOptions函数。使用这种设置训练这个网络大约需要2个小时。根据使用的硬件不同,培训时间也不同。

作为一个快速测试,在一个测试图像上运行检测器。

data =阅读(testData);我={1 1}数据;我= imresize(我inputSize (1:2));[bboxes,分数]=检测(探测器,I);

显示结果。

我= insertObjectAnnotation (,“矩形”bboxes,分数);图imshow(我)

图中包含一个axes对象。坐标轴对象包含一个image类型的对象。

使用测试集评估检测器

在大量的图像上评估训练过的目标检测器的性能。计算机视觉工具箱™提供对象检测器评估功能,以测量常见指标,如平均精度(evaluateDetectionPrecision)和对数平均失踪率(evaluateDetectionMissRate).对于本例,使用平均精度度量来评估性能。平均精度提供了一个单一的数字,它包含了探测器做出正确分类的能力(精度)和探测器找到所有相关物体的能力(回忆).

对测试数据应用与训练数据相同的预处理变换。注意,数据扩充没有应用到测试数据。试验数据应代表原始数据,不作任何修改,以便进行无偏评价。

preprocessedTestData =变换(testData @(数据)preprocessData(数据、inputSize));

在所有的测试图像上运行检测器。

detectionResults = detect(检测器,preprocesedtestdata, MiniBatchSize = 32);

用平均精度度量对目标检测器进行评估。

[ap,recall,precision] = evaluateDetectionPrecision(detectionResults, preprocsedtestdata);

精度/召回(PR)曲线强调了检测器在不同召回级别下的精确度。理想情况下,所有召回级别的精确度都是1。使用更多的数据可以帮助提高平均精度,但可能需要更多的训练时间。

图绘制(召回、精密)包含(“回忆”) ylabel (“精度”网格)标题(sprintf ('平均精度= %.2f'据美联社)),

图中包含一个axes对象。标题为Average Precision = 0.88的axes对象包含一个类型为line的对象。

代码生成

训练和评估了检测器之后,就可以为ssdObjectDetector使用GPU编码器™。有关更多细节,请参见单次多盒检测器目标检测的代码生成(计算机视觉工具箱)的例子。

支持功能

函数B = augmentData (A)应用随机水平翻转和随机X/Y缩放。盒子,如果重叠超过0.25,则裁剪边界外缩放的%。同时,抖动图像颜色。B =细胞(大小(A));我= {1};深圳=大小(I);如果numel(sz)==3 && sz(3) ==3 I = jitterColorHSV(I,...对比= 0.2,...颜色= 0,...饱和= 0.1,...亮度= 0.2);结束%随机翻转和缩放图像。tform = randomAffine2d(XReflection = true, Scale = [1 1.1]);路由= affineOutputView(sz,tform, BoundsStyle =“CenterOutput”);B{1} = imwarp(I,tform,OutputView =路由);如果需要,消毒盒子。。A{2} = helperSanitizeBoxes(A{2}, sz);对盒子应用相同的变换。[B{2},indices] = bboxwarp(A{2},tform,路由,OverlapThreshold = 0.25);B{3} ={3}(指标);只有在通过翘曲删除所有框时才返回原始数据。如果isempty(indexes) B = A;结束结束函数targetSize data = preprocessData(数据)将图像和边框大小调整为targetSize。Sz = size(data{1},[1 2]);规模= targetSize(1:2)。/深圳;{1} = imresize数据(数据{1},targetSize (1:2));如果需要,消毒盒子。。data{2} = helperSanitizeBoxes(data{2}, sz);%调整盒子。{2} = bboxresize数据(数据{2},规模);结束函数lgraph = iRemoveLayers(lgraph, lastLayer)删除lastLayer之后的所有图层。dg = vision.internal.cnn.RCNNLayers.digraph (lgraph);找到最后一层。id = findnode (dg, char (lastLayer));搜索从特征提取开始的所有节点%层。如果~(sum(id)==0) id = dfsearch(dg,id);名称= dg.Nodes.Name (id:);lgraph = removeLayers(lgraph, names(2:end));结束结束函数p = iSamePadding(FilterSize) p = floor(FilterSize / 2);结束

参考文献

[1]刘,魏,德拉戈米尔·安格洛夫,杜米特鲁·埃尔汉,克里斯蒂安·舍格迪,斯科特·里德,程杨富,亚历山大·C.伯格。SSD:单发多盒探测器。第14届欧洲计算机视觉会议,ECCV 2016。施普林格1 - 2016。

另请参阅

应用程序

功能

对象

相关的话题

Baidu
map