主要内容

基于深度学习的语义分割

这个例子展示了如何使用深度学习训练一个语义分割网络。

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

为了说明训练过程,本示例训练Deeplab v3+[1],这是一种用于语义图像分割的卷积神经网络(CNN)。用于语义分割的其他网络类型包括全卷积网络(FCN)、SegNet和U-Net。这里展示的训练过程也可以应用于这些网络。

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

设置

这个示例创建了Deeplab v3+网络,其权重由预训练的Resnet-18网络初始化。ResNet-18是一种高效的网络,非常适合处理资源有限的应用程序。其他预先训练的网络,如MobileNet v2或ResNet-50,也可以根据应用程序需求使用。有关更多细节,请参见预训练深度神经网络(深度学习工具箱)

要获得预先训练的Resnet-18,安装resnet18(深度学习工具箱).安装完成后,运行以下代码以验证安装是否正确。

resnet18 ();

另外,请下载DeepLab v3+的预训练版本。预训练模型允许您运行整个示例,而不必等待训练完成。

pretrainedURL =“https://ssd.mathworks.com/万博1manbetxsupportfiles/vision/data/deeplabv3plusResnet18CamVid.zip”;pretrainedFolder = fullfile(tempdir,“pretrainedNetwork”);pretrainedNetworkZip = fullfile(pretrainedFolder,“deeplabv3plusResnet18CamVid.zip”);如果~存在(pretrainedNetworkZip“文件”mkdir (pretrainedFolder);disp (下载预训练网络(58 MB)…);websave (pretrainedNetworkZip pretrainedURL);结束解压缩(pretrainedNetworkZip pretrainedFolder)

强烈建议使用支持cuda的NVIDIA™GPU来运行这个示例。使用GPU需要并行计算工具箱™。有关支持的计算功能的信息,请参见万博1manbetxGPU支万博1manbetx持按版本划分(并行计算工具箱)

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

注:资料下载时间视乎阁下的互联网连接情况而定。上面使用的命令会阻塞MATLAB直到下载完成。或者,您可以使用web浏览器先将数据集下载到本地磁盘。若要使用从网上下载的文件,请更改outputFolder变量设置为下载文件的位置。

加载CamVid图像

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

imgDir = fullfile(outputFolder,“图片”701 _stillsraw_full);imds = imageDatastore(imgDir);

展示其中一张图片。

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

加载CamVid像素标签图像

使用pixelLabelDatastore加载CamVid像素标签图像数据。一个pixelLabelDatastore将像素标签数据和标签ID封装到类名映射中。

我们使训练更容易,我们将CamVid中的32个原始类分组为11个类。指定这些类。

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

为了将32个类减少到11个,将原始数据集中的多个类分组在一起。例如,“Car”是“Car”、“SUVPickupTruck”、“Truck_Bus”、“Train”和“OtherMoving”的组合。使用辅助函数返回分组的标签id万博1manbetxcamvidPixelLabelIDs,它列在本例的末尾。

labelIDs = camvidPixelLabelIDs();

使用类和标签id创建pixelLabelDatastore。

labelDir = fullfile(outputFolder,“标签”);pxds = pixelLabelDatastore(labelDir,classes,labelIDs);

通过将像素标记的图像叠加在图像之上,读取并显示其中一个图像。

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

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

分析数据集统计信息

要查看CamVid数据集中类标签的分布,请使用countEachLabel.这个函数根据类标签计算像素的数量。

tbl = countEachLabel(pxds)
台=11×3表名称PixelCount ImagePixelCount ______________ __________ _______________ {'Sky'} 7.6801e+07 4.8315e+08 {'Building'} 1.1737e+08 4.8315e+08 {'Pole'} 4.7987e+ 08 4.8315e+08 {'Road'} 1.4054e+08 4.8459e +08 {'Pavement'} 3.3614e+ 08 4.8459e +08 {'Pavement'} 5.4259e+07 4.479e+08 {'SignSymbol'} 5.2242e+06 4.6863e+08 {'Fence'} 6.9211e+06 4.516e +08 {'Car'} 2.4437e+ 06 4.4444e+08 {'Pedestrian'} 3.4029e+06 4.4444e+08 {'Bicyclist'} 2.5912e+06 2.6196e+08

按类可视化像素计数。

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

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

CamVid数据集中的图像大小为720 * 960。在NVIDIA™Titan X (12 GB内存)上进行训练时,图像大小的选择使足够大的批图像能够装入内存中。如果您的GPU没有足够的内存或减少训练批处理大小,您可能需要将图像的大小调整到更小的尺寸。

准备培训、验证和测试集

Deeplab v3+使用数据集中60%的图像进行训练。其余的图像被平均分成20%和20%分别用于验证和测试。下面的代码将图像和像素标签数据随机分为训练集、验证集和测试集。

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

60/20/20分割会产生以下数量的训练、验证和测试图像:

numTrainingImages = numel(imdsTrain.Files)
numTrainingImages = 421
numValImages = numel(imdsVal.Files)
numValImages = 140
numTestingImages = numel(imdste . files)
numTestingImages = 140

创建网络

使用deeplabv3plusLayers功能,创建基于ResNet-18的DeepLab v3+网络。为您的应用程序选择最佳的网络需要经验分析,这是另一个级别的超参数调优。例如,您可以尝试不同的基础网络,如ResNet-50或MobileNet v2,或者您可以尝试其他语义分割网络体系结构,如SegNet、全卷积网络(FCN)或U-Net。

%指定网络映像大小。这通常与训练图像的大小相同。imageSize = [720 960 3];指定类的数量。numClasses = numel(classes);%创建DeepLab v3+。lgraph = deeplabv3plusLayers(imageSize, numClasses,“resnet18”);

使用类加权平衡类

如前所述,CamVid中的类是不平衡的。为了改进培训,您可以使用类权重来平衡类。使用前面计算的像素标签计数countEachLabel并计算中值频率类权重。

imageFreq = tbl。像素计数。/ tbl.ImagePixelCount;classWeights = median(imageFreq) ./ imageFreq ./
classWeights =11×10.3182 0.2082 5.0924 0.1744 0.7103 0.4175 4.5371 1.8386 1.0000 6.6059

参数指定类的权重pixelClassificationLayer

pxLayer = pixelClassificationLayer(“名字”“标签”“类”资源描述。的名字,“ClassWeights”, classWeights);lgraph = replaceLayer(lgraph,“分类”, pxLayer);

选择培训项目

用于训练的优化算法是带有动量的随机梯度下降(SGDM)。使用trainingOptions(深度学习工具箱)指定用于SGDM的超参数。

定义验证数据。dsVal = combine(imdsVal,pxdsVal);确定培训选项。options = trainingOptions(“个”...“LearnRateSchedule”“分段”...“LearnRateDropPeriod”10...“LearnRateDropFactor”, 0.3,...“动量”, 0.9,...“InitialLearnRate”1 e - 3,...“L2Regularization”, 0.005,...“ValidationData”dsVal,...“MaxEpochs”30岁的...“MiniBatchSize”8...“洗牌”“every-epoch”...“CheckpointPath”tempdir,...“VerboseFrequency”2,...“阴谋”“训练进步”...“ValidationPatience”4);

学习率使用分段时间表。学习率每10个周期降低0.3倍。这允许网络以较高的初始学习率快速学习,同时能够在学习率下降时找到接近局部最优的解。

通过设置验证数据对网络进行测试“ValidationData”参数。的“ValidationPatience”设置为4,以便在验证精度收敛时尽早停止训练。这可以防止网络在训练数据集上过拟合。

使用8的小批处理大小来减少训练时的内存使用量。您可以根据系统上的GPU内存数量增加或减少这个值。

此外,“CheckpointPath”设置为临时位置。这个名称-值对支持在每个训练阶段结束时保存网络检查点。如果由于系统故障或断电导致培训中断,您可以从保存的检查点恢复培训。确保指定的位置“CheckpointPath”有足够的空间来存储网络检查点。例如,节省100个Deeplab v3+检查点需要大约6 GB的磁盘空间,因为每个检查点是61 MB。

数据增加

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

dsTrain = combine(imdsTrain, pxdsTrain);

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

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

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

开始训练

开始使用trainNetwork(深度学习工具箱)如果doTrainingFlag是真的。否则,加载一个预先训练的网络。

注意:训练是在NVIDIA™Titan X上验证的,其GPU内存为12 GB。如果您的GPU内存较少,您可能会在训练过程中耗尽内存。如果发生这种情况,请尝试设置“MiniBatchSize”到1英寸trainingOptions,或者减少网络输入,调整训练数据的大小。训练这个网络大约需要70分钟。这取决于你的GPU硬件,可能需要更长的时间。

doTraining = false;如果doTraining [net, info] = trainNetwork(dsTrain,lgraph,options);其他的pretrainedNetwork = fullfile(pretrainedFolder,“deeplabv3plusResnet18CamVid.mat”);data = load(pretrainedNetwork);Net = data.net;结束

在一个映像上测试网络

作为一个快速的完整性检查,在一个测试图像上运行训练过的网络。

I = readimage(imdsTest,35);C = semanticseg(I, net);

显示结果。

B = labeloverlay(I,C,“Colormap”提出,“透明”, 0.4);imshow(B) pixelLabelColorbar(cmap, classes);

比较下列结果C储存着预期的基本事实pxdsTest.绿色和洋红色区域强调了分割结果与预期的地面真相不同的区域。

expectedResult = readimage(pxdsTest,35);实际= uint8(C);expectedResult = uint8;预计imshowpair(实际)

从视觉上看,语义分割结果对道路、天空和建筑等类重叠良好。然而,行人和汽车等较小的物体就不那么准确了。每个类的重叠量可以使用交叉过并(IoU)度量来度量,也称为Jaccard指数。使用jaccard函数来测量欠条。

iou = jaccard(C,expectedResult);表(类、借据)
ans =11×2表类iou ____________ _______“天空”0.91837“建筑”0.84479“柱子”0.31203“道路”0.93698“路面”0.82838“树”0.89636“标志”0.57644“围栏”0.71046“汽车”0.66688“行人”0.48417“自行车”0.68431

IoU指标证实了可视化结果。道路、天空、建筑物等类别的欠条得分较高,而行人、汽车等类别的欠条得分较低。其他常见的细分指标包括骰子bfscore轮廓匹配得分。

评估培训网络

若要测量多个测试图像的准确性,请运行semanticseg在整个测试集中。在分割图像时,使用4的小批处理大小来减少内存使用量。您可以根据系统上的GPU内存数量增加或减少这个值。

pxdsResults = semantiseg (imdsTest,net,...“MiniBatchSize”4...“WriteLocation”tempdir,...“详细”、假);

semanticseg返回测试集的结果作为pixelLabelDatastore对象。中的每个测试图像的实际像素标签数据imdsTest写入磁盘中“WriteLocation”参数。使用evaluateSemanticSegmentation对测试集结果进行语义分割度量。

metrics = evaluateSemanticSegmentation(pxdsResults,pxdsTest,“详细”、假);

evaluateSemanticSegmentation返回整个数据集、单个类和每个测试图像的各种指标。要查看数据集级别的度量,请检查指标。DataSetMetrics

指标。DataSetMetrics
ans =表1×5GlobalAccuracy MeanAccuracy MeanIoU WeightedIoU MeanBFScore  ______________ ____________ _______ ___________ ___________ 0.87695 0.85392 0.6302 0.80851 0.65051

数据集指标提供了网络性能的高级概述。要查看每个类对总体性能的影响,请检查使用的每个类的度量指标。ClassMetrics

指标。ClassMetrics
ans =11×3表精度IoU平均bfscore ________ _______ ___________天空0.93112 0.90209 0.8952建筑物0.78453 0.76098 0.58511杆0.71586 0.21477 0.5144路0.93024 0.91465 0.76696路面0.88466 0.70571 0.70919树0.87377 0.76323 0.70875标志0.79377 0.76323 0.70875围栏0.81506 0.46484 0.48565汽车0.90956 0.76799 0.69233行人0.87629 0.4366 0.60792自行车0.87844 0.60829 0.55089

尽管数据集的总体性能相当高,但类指标显示,诸如行人骑自行车,不像类那样被分割,比如天空,建筑.包含更多代表性不足的类的样本的附加数据可能有助于改进结果。

万博1manbetx支持功能

函数labelIDs = camvidPixelLabelIDs()返回每个类对应的标签id。CamVid数据集有32个类。把它们分成下面的11个类原始的SegNet训练方法[1]。这11个类是:%的“天空”“建筑”,“极”,“路”,“路面”、“树”、“SignSymbol”,“篱笆”,“汽车”,“行人”和“骑自行车的人”。CamVid像素标签id作为RGB颜色值提供。将它们分组% 11类,并将它们作为m × 3矩阵的单元格数组返回。的原始CamVid类名列在每个RGB值旁边。请注意%表示Other/Void类被排除在下面。labelIDs = {...%的“天空”[128 128 128;...%的“天空”%“建设”[000 128 064;...%“桥”1.28亿;...%“建设”064 192000;...%的“墙”064 000 064;...%的“隧道”192000 128;...%的“拱门”%“极”[192 192 128;...%”Column_Pole”000 000 064;...%”TrafficCone”%的道路[128 064 128;...%的“路”128000 192;...%”LaneMkgsDriv”192000 064;...%”LaneMkgsNonDriv”%“路面”[000 000 192;...%“人行道”064 192 128;...%”ParkingBlock”128 128 192;...%”RoadShoulder”%的“树”[128 128 000;...%的“树”192 192 000;...%”VegetationMisc”%”SignSymbol”[192 128 128;...%”SignSymbol”128 128 064;...%”Misc_Text”000 064 064;...%”TrafficLight”%“栅栏”[064 064 128;...%“栅栏”%的“汽车”[064 000 128;...%的“汽车”064 128 192;...%”SUVPickupTruck”192 128 192;...%”Truck_Bus”192 064 128;...%“训练”128 064 064;...%”OtherMoving”%“行人”[064 064 000;...%“行人”192 128 064;...%的“孩子”064 000 192;...%”CartLuggagePram”064 128 064;...%的“动物”%“自行车”[000 128 192;...%“自行车”192 000 192;...%”MotorcycleScooter”]};结束
函数一会pixelLabelColorbar(提出)在当前轴上添加一个颜色条。颜色条被格式化%以显示带有颜色的类名。甘氨胆酸colormap(提出)为当前图形添加颜色条。C = colorbar(“对等”甘氨胆酸,);使用类名作为标记。c.TickLabels = classNames;numClasses = size(cmap,1);%中心勾号标签。c.Ticks = 1/(numClasses*2):1/numClasses:1;%移除勾标记。c.TickLength = 0;结束
函数cmap = camvidColorMap()定义CamVid数据集使用的色彩图。Cmap = [128 128 128 128 .%的天空128 0 0%的建筑192 192 192%极128 64 128%的道路60 40 222%的人行道上128 128 0%的树192 128 128% SignSymbol64 64 128%的栅栏64 0 128%的车64 64 0%行人0 128 192%骑自行车];%在[0 1]之间规范化。Cmap = Cmap ./ 255;结束
函数[imdsTrain, imdsVal, imdsTest, pxdsTrain, pxdsVal, pxdsTest] = partitionCamVidData(imds,pxds)对CamVid数据进行分区,随机选取60%的数据进行训练。的% rest用于测试。设置初始随机状态,例如再现性。rng (0);numFiles = numel(imds.Files);shuffledIndices = randperm(numFiles);使用60%的图像进行训练。numTrain = round(0.60 * numFiles);trainingIdx = shuffledIndices(1:numTrain);使用20%的图像进行验证numVal = round(0.20 * numFiles);valIdx = shuffledIndices(numTrain+1:numTrain+numVal);剩下的部分用于测试。testdx = shuffledIndices(numTrain+numVal+1:end);为培训和测试创建映像数据存储。trainingImages = imds.Files(trainingIdx);valImages = imds.Files(valIdx);testImages = imds.Files(testdx);imdsTrain = imageDatastore(trainingImages);imdsVal = imageDatastore(valImages);imdsTest = imageDatastore(testImages);提取类和标签id信息。classes = pxds.ClassNames;labelIDs = camvidPixelLabelIDs();为培训和测试创建像素标签数据存储。trainingLabels = pxds.Files(trainingIdx);valLabels = pxds.Files(valIdx);testLabels = pxds.Files(testdx);pxdsTrain = pixelLabelDatastore(trainingLabels, classes, labelIDs);pxdsVal = pixelLabelDatastore(valLabels, classes, labelIDs);pxdsTest = pixelLabelDatastore(testLabels, classes, labelIDs);结束
函数data = augmentImageAndLabel(data, xTrans, yTrans)使用随机反射和增强图像和像素标签图像%的翻译。i = 1:size(data,1)...“XReflection”,真的,...“XTranslation”xTrans,...“YTranslation”, yTrans);%将输出空间中图像中心的视图居中%,允许翻译将输出图像移出视图。路由= affineOutputView(size(data{i,1}), tform,“BoundsStyle”“centerOutput”);使用相同的变换扭曲图像和像素标签。。Data {i,1} = imwarp(Data {i,1}, tform,“OutputView”,溃败);Data {i,2} = imwarp(Data {i,2}, tform,“OutputView”,溃败);结束结束

参考文献

[1]陈良杰等,“基于Atrous可分离卷积的语义图像分割编码器-解码器”。大会(2018)。

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

另请参阅

|||||||(深度学习工具箱)|(深度学习工具箱)|(深度学习工具箱)|

相关的话题