利用NVIDIA-NGC中的MATLAB容器加速语义分割
利用NVIDIA-NGC中的MATLAB容器加速语义分割
Speeding Up Semantic Segmentation Using MATLAB Container from NVIDIA NGC
使用单一GPU训练深度学习模式的时代已经一去不复返了。对于计算密集型算法(如语义分割),单个GPU可能需要几天时间来优化模型。但多GPU硬件很贵。不会再有了;NVIDIA的云上多GPU硬件实例,比如AWS P3,只允许你支付你使用的东西。云实例允许您利用支持Tensor核心的最新一代硬件,以适度的投资实现显著的性能引导。您可能听说过设置云实例很困难,但NVIDIA NGC使生活变得更轻松。NGC是用于深度学习、机器学习和HPC的GPU优化软件的中心。NGC负责所有的管道,因此开发人员和数据科学家可以专注于生成可操作的见解。
这篇文章通过在云实例上使用NVIDIA GPUs和从NGC获得的用于深入学习的MATLAB容器,走过了加速语义分割的最简单路径。首先,我们将解释语义分割。接下来,我们将使用NGC提供的MATLAB R2018b容器在两个不同的P3实例上展示在MATLAB中训练的语义分割模型的性能结果。最后,我们将在MATLAB中介绍一些技巧,这些技巧使深入学习变得容易,并有助于管理内存使用。
What is Semantic Segmentation?
用于深度学习的语义分割算法为图像中的每个像素分配一个标签或类别。与传统的包围盒方法相比,这种密集的识别方法在某些应用中提供了关键的能力。在自动驾驶中,它是标记为“道路”的广义区域与精确的、像素级的确定道路可行驶表面之间的区别。在医学成像中,它意味着将矩形区域标记为“癌细胞”与知道细胞的确切形状和大小之间的区别。
Figure 1. Example of an image with semantic labels for every pixel
我们使用MATLAB对语义分割进行了测试,训练出一个SegNet模型,该模型具有四个编码层和四个解码层的编解码结构。与该模型相关的数据集是CamVid数据集,它是一个驱动数据集,每个像素都用一个语义类(例如天空、道路、车辆等)标记。与原研究不同的是,我们使用随机梯度下降法进行训练,并对预先训练的VGG-16模型的层和权重进行了预初始化。
Performance Testing Using MATLAB on P3 Instances with NVIDIA GPUs
虽然语义分割是有效的,但它需要付出巨大的计算和内存代价。我们使用AWS P3实例和NGC提供的MATLAB容器运行测试。使用容器需要一个AWS帐户和一个有效的MATLAB许可证。您可以获得免费的云使用的试用版MATLAB许可证。Mathworks提供了如何在AWS上设置MATLAB容器的可用说明。
最初的SegNet在2015年的实现花费了大约一周的时间运行在作者使用的Tesla K40上,正如在最初的论文中提到的。下面是在一个p3.2xlarge实例上使用一个V100 NVIDIA GPU在MATLAB中进行语义分割网络训练的过程图。图2显示了它花费了大约121分钟,这比原来的论文要快得多。
接下来,我们使用p3.16xlarge实例上可用的八个V100 NVIDIA gpu执行相同的测试。MATLAB代码中唯一需要更改的是:将训练选项参数ExecutionEnvironment设置为multi-gpu。图3展示了一个训练图,显示这个过程现在花费了37分钟,比使用p3.2xlarge实例快3.25倍。
Figure 3. Training Progress for SegNet in MATLAB on eight V100 NVIDIA GPUs
性能提升了3.25倍,显示了最新的NVIDIA多GPU硬件的强大功能,并带有Tensor内核,使原本“大约一周”的时间缩短至37分钟。我敢打赌,SegNet的作者们在开发算法的时候,一定希望有这样的硬件!
Making Deep Learning Easier with MATLAB
现在让我们深入探讨为什么应该使用MATLAB开发深层学习算法,如语义分割。MATLAB包含许多有用的工具和命令,以便于执行深入学习。最有用的MATLAB命令之一是imageDatastore,它允许您有效地管理大量图像。该命令创建一个数据库,允许将整个数据集作为单个对象处理。MiniBatchSize参数对于语义分割尤其重要,它决定了每次迭代使用多少图像。默认值256会占用太多内存进行语义分割,因此我们将该值设置为4。
如imageDataAugmenter函数所示,数据增强代表了MATLAB中另一种强大的深度学习能力。数据扩充通过使用翻译、旋转、反射、缩放、裁剪等向网络提供更多示例来扩展数据集。这有助于提高模型精度。这使用随机左/右反射的数据增强和正负10像素的X/Y转换。这种数据扩充被合并到一个pixelLabelDatastore中,这样每次迭代时操作都会发生,并且避免了不必要的数据集副本。
How We Performed Semantic Segmentation in MATLAB
本节介绍了我们用于上述测试的代码的关键部分。本测试中使用的完整的MATLAB代码在这里提供。最后一行是训练的地方。
以下代码下载数据集并在本地计算机上解压它。
imageURL = 'http://web4.cs.ucl.ac.uk/staff/g.brostow/MotionSegRecData/files/701_StillsRaw_full.zip';
labelURL = 'http://web4.cs.ucl.ac.uk/staff/g.brostow/MotionSegRecData/data/LabeledApproved_full.zip';
outputFolder = fullfile(tempdir,'CamVid');
if ~exist(outputFolder, 'dir')
mkdir(outputFolder)
labelsZip = fullfile(outputFolder,'labels.zip');
imagesZip = fullfile(outputFolder,'images.zip');
disp('Downloading 16 MB CamVid dataset labels...');
websave(labelsZip, labelURL);
unzip(labelsZip, fullfile(outputFolder,'labels'));
disp('Downloading 557 MB CamVid dataset images...');
websave(imagesZip, imageURL);
unzip(imagesZip, fullfile(outputFolder,'images'));
end
此代码生成一个临时文件夹,用于解压缩实例上的文件。使用容器时,一旦容器关闭,这些文件将丢失。如果您想为数据维护一个一致的位置,您应该将代码更改为使用S3存储桶或其他永久位置。
下面的代码显示了用于训练语义分割网络的来自CamVid数据集的11个类。在原始数据集中,有32个类。
imgDir = fullfile(outputFolder,'images','701_StillsRaw_full');
imds = imageDatastore(imgDir);
classes = [
"Sky"
"Building"
"Pole"
"Road"
"Pavement"
"Tree"
"SignSymbol"
"Fence"
"Car"
"Pedestrian"
"Bicyclist"
];
labelIDs = camvidPixelLabelIDs();
labelDir = fullfile(outputFolder,'labels');
pxds = pixelLabelDatastore(labelDir,classes,labelIDs);
在下一节中,我们将根据SegNet的分辨率调整CamVid数据的大小,并将数据集划分为训练集和测试集。
imageFolder = fullfile(outputFolder,'imagesResized',filesep);
imds = resizeCamVidImages(imds,imageFolder);
labelFolder = fullfile(outputFolder,'labelsResized',filesep);
pxds = resizeCamVidPixelLabels(pxds,labelFolder);
[imdsTrain,imdsTest,pxdsTrain,pxdsTest] = partitionCamVidData(imds,pxds);
numTrainingImages = numel(imdsTrain.Files)
numTestingImages = numel(imdsTest.Files)
现在让我们创建一个SegNet网络。从VGG-16权重开始,调整它们以平衡类权重。
imageSize = [360 480 3];
numClasses = numel(classes);
lgraph = segnetLayers(imageSize,numClasses,'vgg16');
imageFreq = tbl.PixelCount ./ tbl.ImagePixelCount;
classWeights = median(imageFreq) ./ imageFreq;
pxLayer = pixelClassificationLayer('Name','labels','ClassNames',tbl.Name,'ClassWeights',classWeights);
lgraph = removeLayers(lgraph,'pixelLabels');
lgraph = addLayers(lgraph, pxLayer);
lgraph = connectLayers(lgraph,'softmax','labels');
接下来,选择培训选项。MiniBatchSize参数对于语义分割尤其重要,它决定了每次迭代使用多少图像。默认值256需要太多内存用于语义分段,因此我们将该值设置为4。ExecutionEnvironment选项设置为multi-gpu以使用p3.16xlarge实例上的多个V100 NVIDIA gpu。有关培训选项的更多详细信息,请参阅文档。
options = trainingOptions('sgdm', ...
'Momentum',0.9, ...
'ExecutionEnvironment','multi-gpu',...
'InitialLearnRate',1e-3, ...
'L2Regularization',0.0005, ...
'MaxEpochs',100, ...
'MiniBatchSize',4 * gpuDeviceCount, ...
'Shuffle','every-epoch', ...
'Plots','training-progress',...
'VerboseFrequency',2);
MATLAB中用于深度学习的另一个强大功能是imageDataAugmenter,它为网络提供了更多示例,有助于提高精度。此示例使用随机左/右反射的数据增强和正负10像素的X/Y转换。这将合并到一个pixelLabelDatastore中,以便在每次迭代时执行操作,并避免不必要的数据集副本。
augmenter = imageDataAugmenter('RandXReflection',true,...
'RandXTranslation',[-10 10],'RandYTranslation',[-10 10]);
pximds = pixelLabelImageDatastore(imdsTrain,pxdsTrain,...
'DataAugmentation',augmenter);
现在我们可以开始训练了。下一行用于训练的代码在p3.16xlarge实例上运行大约需要37分钟。我们在绘图窗口中测量训练所用的时间,以跟踪训练进度。请参阅图2和图3,查看在p3.2xlarge和p3.16xlarge实例上运行此函数所需的测量时间。
[net, info] = trainNetwork(pximds,lgraph,options);
结论
MATLAB使工程师能够很容易地训练深度学习模型,利用NVIDIA GPU加速训练过程。使用MATLAB,从单一GPU机器上的训练切换到多GPU机器只需要一行代码,如上面最后的代码片段所示。我们展示了如何通过在NGC上的MATLAB深度学习容器中训练神经网络来加速深度学习应用程序,该容器旨在充分利用高性能NVIDIA®GPU。下一步,下载代码并在MATLAB中的awsp3实例上亲自尝试。