基于 Faster R-CNN深度学习的物体检测
这个例子展示如何使用一个称为 Faster R-CNN (区域卷积神经网络) 的深度学习的技巧训练物体检测器。
综述
这个例子展示如何训练一个 Faster R-CNN 物体检测器,用于检测车辆。Faster R-CNN1 是 R-CNN2 和 Fast R-CNN3 物体检测技巧的推广。所有这三个技巧都使用卷积神经网络 (CNN)。区别在于他们如何选择需要处理的区域,以及他们如何将这些区域进行分类。R-CNN 和 Fast R-CNN 使用区域建议算法作为运行 CNN 的预处理步骤。建议算法典型的例子有 EdgeBoxes4 或者选择性搜索5,独立于 CNN。在 Fast R-CNN 中,这种技巧变成了运行 CNN 的计算瓶颈。 Faster R-CNN 通过把区域建议机制用 CNN 实现,从而解决了这个问题,使得区域建议变成了 CNN 训练和预测的一部分。
在本例中,汽车探测器用来自 计算机视觉系统工具箱TM trainFasterRCNNObjectDetector
函数来训练。这个例子有如下几个部分:
- 载入数据集。
- 设计卷积神经网络 (CNN)。
- 配置训练选项。
- 训练 Faster R-CNN 物体检测器。
- 评价训练过的检测器。
注意:这个例子需要计算机视觉系统工具箱TM,图像处理工具箱TM,和深度学习工具箱TM。
强烈推荐使用支持 CUDA 的 NVIDIATM GPU,计算匹配版本在 3.0 或更高。使用 GPU 需要并行计算工具箱TM。
载入数据集
这个例子使用小车辆数据集,由 295 张图片组成。每一张图片有一到两个做过标记车辆的标签。小数据集有利于探索 Faster R-CNN 训练过程,但实践中,需要更多的图片,用以训练更强大的探测器。
% 载入汽车数据集
data = load('fasterRCNNVehicleTrainingData.mat')
vehicleDatsaset = data.vehicleTrainingData;
训练数据集存于一张表中。第一列含有图像文件的路径。余下的列含有车辆的 ROI 标签。
% 显示数据集的前几列
vehicleDataset(1:4, :)
ans=4×2 table
imageFilename vehicle
__________________________ ____________
'vehicles/image_00001.jpg' [1x4 double]
'vehicles/image_00002.jpg' [1x4 double]
'vehicles/image_00003.jpg' [1x4 double]
'vehicles/image_00004.jpg' [1x4 double]
从数据集中显示一张图片,以理解其中一张图片的类型:
% 补全汽车数据集文件夹的完整路径
dataDir = fullfile(toolboxdir('vision'),'visiondata');
vehicleDataset.imageFilename = fullfile(dataDir, vehicleDataset.imageFilename);
% 读取其中一张图片
I = imread(vehicleDataset.imageFilename{10});
% 插入 ROI 标签
I = insertShape(I, 'Rectangle', vehicleDataset.vehicle{10});
% 修改图片大小并显示
I = imresize(I,3);
figure
imshow(I)
把数据集分割为训练集,用于训练检测器,以及用于评价检测器的测试集。选择 60% 的数据用于训练。余下的用于评价。
% 把数据分为训练和测试集
idx = floor(0.6 * height(vehicleDataset));
trainingData = vehicleDataset(1:idx,:);
testData = vehicleDataset(idx:end,:);
训练卷积神经网络 (CNN)
CNN 是 Faster R-CNN 物体检测器的根基。通过深度学习工具箱TM的函数一层一层建立 CNN。
从 imageInputLayer
开始,它定义了输入的类型和大小。对分类任务,输入通常是训练图片的大小。对检测任务,CNN 需要分析更小的图片,所以,输入大小必须与数据集中最小的物体大小相似。在这个数据集中,所有物体都大于 [16 16]
,所以,选择输入尺寸为 [32 32]
。这个输入尺寸是处理时间和空间细节之间的权衡后得出的结果。
% 建立图像输入层
inputLayer = imageInputLayer([32 32 3]);
接下来,定义网络的中间层。中间层以 convolution2dLayer
, reluLayer
(整流线性单元),以及 maxPooling2dLayer
的重复构成。这些层是卷积神经网络的核心积木。
% 定义卷积层参数
filterSize = [3 3];
numFilters = 32;
% 建立中间层
middleLayers = [
convolution2dLayer(filterSize, numFilters, 'Padding', 1)
reluLayer()
convolution2dLayer(filterSize, numFilters, 'Padding', 1)
reluLayer()
maxPooling2dLayer(3, 'Stride',2)
];
你可以建立更深的神经网络,通过重复这些基本层。然而,为了避免数据过早降采样,不要用太多的池化层。在网络中过早降采样会造成有用信息的损失。
CNN 的结束层通常是 fullyConnectedLayer
和 softmaxLayer
的结合。
finalLayers = [
% 增加一个全连接层,有 64 个神经元的输出。这层的输出大小是一个长度为 64 的数组。
fullyConnectedLayer(64)
% 增加一个 ReLU 非线性
reluLayer()
% 增加最后一个完全连接层。这时,网络必须产生输出,用以度量图像属于哪一个物体类别或者是背景。
% 这种度量用后面我们会看到的损失层实现。
fullyConnectedLayer(width(vehicleDataset))
% 增加 softmax 损失层和分类层。
softmaxLayer()
classificationLayer()
];
结合输入、中间、结束层。
layers = [
inputLayer
middleLayers
finalLayers
]
layers =
11x1 Layer array with layers:
1 '' Image Input 32x32x3 images with 'zerocenter' normalization
2 '' Convolution 32 3x3 convolutions with stride [1 1] and padding [1 1 1 1]
3 '' ReLU ReLU
4 '' Convolution 32 3x3 convolutions with stride [1 1] and padding [1 1 1 1]
5 '' ReLU ReLU
6 '' Max Pooling 3x3 max pooling with stride [2 2] and padding [0 0 0 0]
7 '' Fully Connected 64 fully connected layer
8 '' ReLU ReLU
9 '' Fully Connected 2 fully connected layer
10 '' Softmax softmax
11 '' Classification Output crossentropyex
配置训练选项
trainFasterRCNNObjectDetector
以四步训练检测器。头两步训练 Faster R-CNN 中需要的区域建议和检测网络。最后两步结合前面给出的两个网络,产生单个用于检测的网络1。训练每一步都可能会有不同的收敛率,所以,每一步设置不同的参数是有好处的。为了明确网络训练的选项,使用 trainingOptions
:
% 第 1 步的选项
optionsStage1 = trainingOptions('sgdm', ...
'MaxEpochs', 10, ...
'MiniBatchSize', 1, ...
'InitialLearnRate', 1e-3, ...
'CheckpointPath', tempdir);
% 第 2 步的选项
optionsStage2 = trainingOptions('sgdm', ...
'MaxEpochs', 10, ...
'MiniBatchSize', 1, ...
'InitialLearnRate', 1e-3, ...
'CheckpointPath', tempdir);
% 第 3 步的选项
optionsStage3 = trainingOptions('sgdm', ...
'MaxEpochs', 10, ...
'MiniBatchSize', 1, ...
'InitialLearnRate', 1e-3, ...
'CheckpointPath', tempdir);
% 第 4 步的选项
optionsStage4 = trainingOptions('sgdm', ...
'MaxEpochs', 10, ...
'MiniBatchSize', 1, ...
'InitialLearnRate', 1e-3, ...
'CheckpointPath', tempdir);
options = [
optionsStage1
optionsStage2
optionsStage3
optionsStage4
];
这里,头两步的学习率比后两步的学习率高。因为后两步是精调步,网络权重可以比前两步调节得更慢。Faster R-CNN 中的小批次尺寸必须是 1,它在每一个迭代中对每一张图片都会处理多个图像区域。
此外,'CheckpointPath'
是所有训练选项的暂存路径。这个名称-值对让我们保存训练到一半的检测器。如果训练被中断,比如突然断电或者系统崩溃,你仍然可以通过存档点继续训练。
训练 Faster R-CNN
现在 CNN 和训练选项都已经定义完毕,你可以用 trainFasterRCNNObjectDetector
训练检测器。
训练过程中,训练图像中的多个图像区域被处理。采样图像中的区域数被 'NumRegionsToSample'
。'PositiveOverlapRange'
和 'NegativeOverlapRange'
名称-值对控制了被用于训练的图像区域。正训练样本是与真相边界框重叠在
0.6
0.6
0.6 到
1.0
1.0
1.0 的范围,这通过边界框 IoU 度量求出。负样本是范围在
0
0
0 到
0.3
0.3
0.3 的样本。这些参数的最佳值应该通过验证集确定。
对 Faster R-CNN 训练,使用 MATLAB 并行池以减少训练时间是强烈推荐的。trainFasterRCNNObjectDetector
根据你的并行设置,自动建立并使用并行池。确保并行池在训练前已经启用。
强烈推荐使用支持 CUDA 的 NVIDIATM GPU 进行训练,计算匹配版本在 3.0 或更高。
为了节约运行这个例子的时间,我们从磁盘上读出一个预先训练好的网络。要训练自己的网络,可以配置 doTrainingAndEval
变量为真。
% 为了节约本例的运算时间,我们从磁盘上读出预先训练好的网络。
% 将此标记设为 true,以训练网络
doTrainingAndEval = false;
if doTrainingAndEval
% 设置随机种子,以使得训练可以重复.
rng(0);
% 训练 Faster R-CNN 检测器. 选择 BoxPyramidScale 为 1.2 使得我们可以识别多尺度的物体。
detector = trainFasterRCNNObjectDetector(trainingData, layers, options, ...
'NegativeOverlapRange', [0 0.3], ...
'PositiveOverlapRange', [0.6 1], ...
'NumRegionsToSample', [256 128 256 128], ...
'BoxPyramidScale', 1.2);
else
% 载入预先训练好的检测器
detector = data.detector;
end
快速验证训练效果,可以在测试图像上运行检测器。
% 读取一副测试图像
I = imread(testData.imageFilename{1});
% 运行检测器
[bboxes,scores] = detect(detector,I);
% 标记检测结果
I = insertObjectAnnotation(I,'rectangle',bboxes,scores);
figure
imshow(I)
在测试集上评价检测器
计算机视觉系统工具箱TM 提供物体检测器的常用的评价函数,如平均精度 (evaluateDetectionPrecision
) 和对数平均错失率 (evaluateDetectionMissRate
)。这里使用平均精度度量。平均精度给出了一个数值,结合了检测器进行正确分类的能力(精度)以及检测器找出所有相关物体的能力(召回)。
检测器评价的第一步是收集检测结果,通过在测试集上运行检测器实现。为了避免长时间的等待。我们把评价结果直接从磁盘上读出。配置前一节中的 doTrainingAndEval
会执行评价的过程。
if doTrainingAndEval
% 在测试集上对每一张图片运行检测器,并且收集结果
resultsStruct = struct([]);
for i = 1:height(testData)
% 读取图片
I = imread(testData.imageFilename{i});
% 运行检测器
[bboxes, scores, labels] = detect(detector, I);
% 收集结果
resultsStruct(i).Boxes = bboxes;
resultsStruct(i).Scores = scores;
resultsStruct(i).Labels = labels;
end
% 转化为表格
results = struct2table(resultsStruct);
else
% 从磁盘上直接读取
results = data.results;
end
% 从测试集数据中读取期望的边界框
expectedResults = testData(:, 2:end);
% 用平均精度度量评价物体检测器
[ap, recall, precision] = evaluateDetectionPrecision(results, expectedResults);
精度/召回 (PR) 曲线突出显示了检测器在不同的召回率下的表现。理想状态下,在每一个召回水平上,精度都是 1。网络中额外的层会增加平均精度,但可能会需要更多的训练数据和训练时间。
% 精度/召回曲线作图
figure
plot(recall,precision)
xlabel('Recall')
ylabel('Precision')
grid on
title(sprintf('Average Precision = %.2f', ap))
小结
本例中给出了如何用深度学习训练一个汽车检测器。你可以用相似的过程训练交通信号灯、行人,或者其他物体。
参考文献
Ren, S., K. He, R. Gershick, and J. Sun. “Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks.” IEEE Transactions of Pattern Analysis and Machine Intelligence. Vol. 39, Issue 6, June 2017, pp. 1137-1149. ↩︎ ↩︎
Girshick, R., J. Donahue, T. Darrell, and J. Malik. “Rich Feature Hierarchies for Accurate Object Detection and Semantic Segmentation.” Proceedings of the 2014 IEEE Conference on Computer Vision and Pattern Recognition. Columbus, OH, June 2014, pp. 580-587. ↩︎
Girshick, R. “Fast R-CNN.” Proceedings of the 2015 IEEE International Conference on Computer Vision. Santiago, Chile, Dec. 2015, pp. 1440-1448. ↩︎
Zitnick, C. L., and P. Dollar. “Edge Boxes: Locating Object Proposals from Edges.” European Conference on Computer Vision. Zurich, Switzerland, Sept. 2014, pp. 391-405. ↩︎
Uijlings, J. R. R., K. E. A. van de Sande, T. Gevers, and A. W. M. Smeulders. “Selective Search for Object Recognition.” International Journal of Computer Vision. Vol. 104, Number 2, Sept. 2013, pp. 154-171. ↩︎