主要内容

训练和部署用于语义分割的全卷积网络

这个例子展示了如何使用GPU Coder™在NVIDIA®GPU上训练和部署一个完全卷积的语义分割网络。

语义分割网络对图像中的每个像素进行分类,从而得到按类分割的图像。语义分割的应用包括用于自动驾驶的道路分割和用于医疗诊断的癌细胞分割。要了解更多信息,请参见开始使用深度学习进行语义分割(计算机视觉工具箱)

为了说明训练过程,本例训练FCN-8s[1],一种用于语义图像分割的卷积神经网络(CNN)。用于语义分割的其他类型的网络包括完全卷积网络,如SegNet和U-Net。你也可以把这个训练过程应用到这些网络上。

本例使用CamVid数据集[2]来自剑桥大学的培训。该数据集是包含在驾驶时获得的街道视图的图像集合。该数据集提供了包括汽车、行人和道路在内的32个语义类的像素级标签。

第三方的先决条件

要求

  • CUDA®启用NVIDIA GPU和兼容驱动程序。

可选

检查GPU环境

使用coder.checkGpuInstall函数验证运行此示例所需的编译器和库是否正确设置。

envCfg = code . gpuenvconfig (“主机”);envCfg。DeepLibTarget =“cudnn”;envCfg。DeepCodegen = 1;envCfg。安静= 1;coder.checkGpuInstall (envCfg);

设置

本例创建了基于VGG-16网络初始化权重的全卷积语义分割网络。vgg16函数检查vgg16网络支持包的深度学习工具箱模型是否存在,并返回预先训练好的vgg16模型。金宝app

vgg16 ();

下载预训练版本的FCN。这个预训练的模型使您可以运行整个示例,而无需等待训练完成。doTraining标志控制该示例是否使用该示例的训练网络或预训练的FCN网络进行代码生成。

doTraining = false;如果~doTraining pretrainedURL =“//www.tatmou.com/金宝appsupportfiles/gpucoder/cnn_models/fcn/FCN8sCamVid.mat”;disp (“下载预训练的FCN (448 MB)…”);websave (“FCN8sCamVid.mat”, pretrainedURL);结束
下载预训练的FCN (448 MB)…

下载CamVid数据集

从这些url下载CamVid数据集。

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(pwd,“CamVid”);如果~存在(outputFolder“dir”mkdir(outputFolder) labelsZip = fullfile(outputFolder)“labels.zip”);imagesZip = fullfile(输出文件夹,“images.zip”);disp (“正在下载16mb CamVid数据集标签……”);websave (labelsZip labelURL);解压缩(labelsZip fullfile (outputFolder“标签”));disp (“正在下载557 MB CamVid数据集图像……”);websave (imagesZip imageURL);解压缩(imagesZip fullfile (outputFolder“图片”));结束

数据下载时间视乎你的互联网连接而定。直到下载操作完成,示例才继续执行。或者,使用web浏览器首先将数据集下载到本地磁盘。然后,使用outputFolder变量指向下载文件的位置。

加载CamVid图像

使用imageDatastore加载CamVid图像。imageDatastore使您能够有效地将大量图像集合加载到磁盘上。

imgDir = fullfile(输出文件夹,“图片”701 _stillsraw_full);imds = imageDatastore(imgDir);

显示其中一个图像。

I = readimage(imds,25);I = histeq(I);imshow(我)

加载CamVid像素标记图像

使用pixelLabelDatastore(计算机视觉工具箱)加载CamVid像素标签图像数据。pixelLabelDatastore将像素标签数据和标签ID封装到类名映射中。

按照SegNet论文[3]中描述的训练方法,将CamVid中原有的32个类分组为11个类。指定这些类。

类= [“天空”“建筑”“极”“路”“路面”“树”“SignSymbol”“篱笆”“汽车”“行人”“自行车”];

为了将32个类减少到11个类,将原始数据集中的多个类分组在一起。例如,“Car”是“Car”、“SUVPickupTruck”、“Truck_Bus”、“Train”和“OtherMoving”的组合。方法返回分组的标签idcamvidPixelLabelIDs金宝app支持功能。

labelIDs = camvidPixelLabelIDs();

使用类和标签id创建pixelLabelDatastore。

labelDir = fullfile(输出文件夹,“标签”);pxds = pixelLabelDatastore(labelDir,classes,labelIDs);

读取并显示一个像素标记的图像,方法是将其覆盖在图像之上。

C = readimage(pxds,25);cmap = camvidColorMap;B = labeloverlay(I,C,“ColorMap”,提出);imshow (B) pixelLabelColorbar(提出、类);

没有颜色覆盖的区域没有像素标签,在训练期间不会使用。

分析数据集统计信息

要查看CamVid数据集中类标签的分布,请使用countEachLabel(计算机视觉工具箱).这个函数根据类标签计算像素的数量。

tbl = countEachLabel(pxds)
台=11×3表名称PixelCount ImagePixelCount ______________ __________ _______________{'天空'}7.6801e+07 4.8315e+08{'建筑物'}1.1737e+08 4.8315e+08{'杆'}4.7987e+ 08 4.8453e+08{'道路'}3.4054e +08 4.8453e+08{'树'}5.4259e+07 4.4209e +08{'标志'}5.2242e+06 4.6863e+08{'围栏'}6.9211e+06 2.516e+08{'行人'}3.4029e+06 4.4444e+08{'骑车人'}2.5912e+06 2.6196e+08

按类可视化像素计数。

frequency = tbl.PixelCount/sum(tbl.PixelCount);bar(1:numel(classes),frequency) xticks(1:numel(classes)) xticklabels(tb . name) xtickangle(45) ylabel(“频率”

理想情况下,所有类都有相同数量的观测值。CamVid中的类是不平衡的,这是街景汽车数据集的一个常见问题。这样的场景比行人和骑自行车的人有更多的天空、建筑和道路像素,因为天空、建筑和道路在图像中覆盖了更多的区域。如果处理不当,这种不平衡可能不利于学习过程,因为学习倾向于优势阶级。在本例后面,您将使用类加权来处理这个问题。

调整CamVid数据大小

CamVid数据集中的图像是720 * 960。控件将图像和像素标签图像调整为360 * 480以减少训练时间和内存使用resizeCamVidImages而且resizeCamVidPixelLabels金宝app支持功能。

imageFolder = fullfile(输出文件夹,“imagesResized”, filesep);imds = resizeCamVidImages(imds,imageFolder);labelFolder = fullfile(输出文件夹,“labelsResized”, filesep);pxds = resizeCamVidPixelLabels(pxds,labelFolder);

准备训练和测试集

SegNet通过使用数据集中60%的图像进行训练。其余的图像用于测试。下面的代码将图像和像素标签数据随机分成一个训练集和一个测试集。

[imdsTrain,imdsTest,pxdsTrain,pxdsTest] = partitionCamVidData(imds,pxds);

60/40分割的结果是以下数量的训练和测试图像:

numTrainingImages = nummel (imdsTrain.Files)
numTrainingImages = 421
numTestingImages = nummel (imdste . files)
numTestingImages = 280

创建网络

使用fcnLayers(计算机视觉工具箱)创建使用VGG-16权重初始化的全卷积网络层。的fcnLayers函数执行网络转换来传输VGG-16的权重,并添加语义分割所需的额外层。的输出。fcnLayers函数是一个表示FCN的LayerGraph对象。LayerGraph对象封装了网络层和层之间的连接。

imageSize = [360 480];numClasses = nummel(类);lgraph = fcnLayers(imageSize,numClasses);

图像大小是根据数据集中图像的大小选择的。类的数量根据CamVid中的类选择。

使用类权重来平衡类

CamVid中的类不平衡。方法计算的像素标签计数可用于改进训练countEachLabel(计算机视觉工具箱)函数并计算中位数频率类权重[3]。

imageFreq = tbl。PixelCount ./ tb . imagepixelcount;classWeights = median(imageFreq) ./ imageFreq;

指定类的权重pixelClassificationLayer(计算机视觉工具箱)

pxLayer = pixelClassificationLayer(“名字”“标签”“类”资源描述。的名字,“ClassWeights”classWeights)
pxLayer = PixelClassificationLayer与属性:名称:'标签'类:[11×1 categorical] ClassWeights: [11×1 double] OutputSize: 'auto'超参数LossFunction: 'crossentropyex'

通过删除当前的pixelClassificationLayer并添加新层来更新具有新pixelClassificationLayer的SegNet网络。当前的pixelClassificationLayer命名为pixelLabels。删除它使用removeLayers(深度学习工具箱)函数,使用addLayers(深度学习工具箱)函数,并将新层连接到网络的其余部分connectLayers(深度学习工具箱)函数。

lgraph = removeLayers(“pixelLabels”);lgraph = addLayers(lgraph, pxLayer);lgraph = connectLayers(“softmax”“标签”);

选择培训选项

训练的优化算法是Adam,它是由自适应矩估计.使用trainingOptions(深度学习工具箱)函数指定用于Adam的超参数。

选项= trainingOptions(“亚当”...“InitialLearnRate”1 e - 3,...“MaxEpochs”, 100,...“MiniBatchSize”4...“洗牌”“every-epoch”...“CheckpointPath”tempdir,...“VerboseFrequency”2);

“MiniBatchSize”为4可以减少训练时的内存使用。您可以根据系统中的GPU内存数量增加或减少这个值。

'CheckpointPath'被设置为一个临时位置。这个名称-值对可以在每个训练阶段结束时保存网络检查点。如果由于系统故障或停电导致培训中断,您可以从保存的检查点恢复培训。确保'CheckpointPath'指定的位置有足够的空间存储网络检查点。

数据增加

数据增强是通过在训练过程中随机转换原始数据来提高网络的准确性。通过使用数据增强,您可以在不增加标记训练样本数量的情况下为训练数据添加更多的多样性。要对图像和像素标签数据应用相同的随机转换,请使用数据存储组合和转换。首先,结合imdsTrain而且pxdsTrain

dsTrain = combine(imdsTrain, pxdsTrain);

接下来,使用数据存储转换应用在支持函数中定义的所需数据增强金宝appaugmentImageAndLabel.这里使用随机左/右反射和+/- 10像素的随机X/Y平移进行数据增强。

xTrans = [-10 10];yTrans = [-10 10];dsTrain = transform(dsTrain, @(data)augmentImageAndLabel(data,xTrans,yTrans));

请注意,数据扩充并不应用于测试和验证数据。理想情况下,测试和验证数据应该代表原始数据,并且不进行修改,以便进行公正的评估。

开始训练

开始使用trainNetwork如果doTrainingFlag为true。否则,加载一个预先训练好的网络。

训练是在NVIDIA™Titan Xp上验证的,GPU内存为12 GB。如果你的GPU内存较少,你可能会耗尽内存。如果您的系统中没有足够的内存,请尝试降低MiniBatchSize财产trainingOptions为1。训练这个网络大约需要5个小时或更长时间,具体取决于您的GPU硬件。

doTraining = false;如果doTraining [net, info] = trainNetwork(dsTrain,lgraph,options);保存(“FCN8sCamVid.mat”“净”);结束

将DAG网络对象保存为一个名为FCN8sCamVid.mat.这个mat文件在代码生成期间使用。

执行MEX代码生成

fcn_predict.m函数接受图像输入,利用保存的深度学习网络对图像进行预测FCN8sCamVid.mat文件。该函数从加载网络对象FCN8sCamVid.mat变成一个持久变量mynet并在后续的预测调用中重用持久对象。

类型(“fcn_predict.m”
版权所有:The MathWorks, Inc. persistent mynet;if isempty(mynet) mynet = code . loaddeeplearningnetwork (' fcn8scamid .mat');End % pass in input out = predict(mynet,in);

为MEX目标设置生成GPU配置对象,目标语言为c++。使用编码器。DeepLearningConfig函数来创建一个cuDNN深度学习配置对象,并将其分配给DeepLearningConfigGPU代码配置对象的属性。运行codegen命令指定输入大小[360,480,3]。这个大小对应于FCN的输入层。

cfg = code . gpuconfig (墨西哥人的);cfg。TargetLang =“c++”;cfg。DeepLearningConfig =编码器。DeepLearningConfig (“cudnn”);codegen配置cfgfcn_predictarg游戏{(360480 3 uint8)}报告
代码生成成功:查看报告

运行生成的MEX

加载并显示输入图像。

Im = imread(“testImage.png”);imshow (im);

通过调用fcn_predict_mex在输入图像上。

Predict_scores = fcn_predict_mex(im);

predict_scoresVariable是一个三维矩阵,有11个通道,对应于每个类的像素级预测分数。通过使用最大预测分数来计算通道以获得像素级标签。

[~,argmax] = max(predict_scores,[],3);

在输入图像上叠加分段标签并显示分段区域。

类= [“天空”“建筑”“极”“路”“路面”“树”“SignSymbol”“篱笆”“汽车”“行人”“自行车”];cmap = camvidColorMap();SegmentedImage = labeloverlay(im,argmax,“ColorMap”,提出);图imshow (SegmentedImage);pixelLabelColorbar(提出、类);

清理

清除内存中加载的静态网络对象。

清晰的墨西哥人

金宝app支持功能

函数data = augmentImageAndLabel(data, xTrans, yTrans)使用随机反射和增加图像和像素标签图像%的翻译。i = 1:size(data,1) tform = randomAffine2d(...“XReflection”,真的,...“XTranslation”xTrans,...“YTranslation”, yTrans);将视图置于输出空间中图像的中心%允许翻译将输出图像移出视图。rout = affineOutputView(size(data{i,1}), tform,“BoundsStyle”“centerOutput”);使用相同的变换扭曲图像和像素标签。数据{i,1} = imwarp(数据{i,1}, tform,“OutputView”,溃败);数据{i,2} = imwarp(数据{i,2}, tform,“OutputView”,溃败);结束结束

参考文献

[1]朗,J., E.谢尔哈默,T.达雷尔。“用于语义分割的全卷积网络”IEEE计算机视觉与模式识别会议论文集,2015,pp. 3431-3440。

[2]布罗斯托,G. J.福克尔,R.西波拉。“视频中的语义对象类:高清地面真相数据库。”模式识别信.第30卷,第2期,2009年,pp 88-97。

[3]巴德里纳拉亚南,V.肯德尔,R.西波拉。“SegNet:一种用于图像分割的深度卷积编码器-解码器架构”arXiv预印本arXiv:1511.00561, 2015。

另请参阅

功能

对象

相关的例子

更多关于