基于深度学习的语义分割
这个例子展示了如何使用深度学习训练一个语义分割网络。
语义分割网络对图像中的每个像素进行分类,从而得到按类分割的图像。语义分割的应用包括用于自动驾驶的道路分割和用于医疗诊断的癌细胞分割。要了解更多信息,请参见语义分割基础.
为了说明训练过程,本例训练SegNet[1],一种用于语义图像分割的卷积神经网络(CNN)。用于语义分割的其他类型网络包括全卷积网络(FCN)和U-Net。这里展示的训练过程也可以应用于这些网络。
本例使用CamVid数据集[2]来自剑桥大学的培训。此数据集是包含在驾驶时获得的街道级别视图的图像集合。该数据集为包括汽车、行人和道路在内的32个语义类提供了像素级标签。
设置
本例创建了SegNet网络,权重初始化自vg -16网络。要获得VGG-16,请安装VGG-16网络的神经网络工具箱™模型.安装完成后,运行以下代码验证安装是否正确。
vgg16 ();
此外,下载预训练版本的SegNet。预训练模型允许您运行整个示例,而不必等待训练完成。
pretrainedURL =“//www.tianjin-qmedu.com/万博1manbetxsupportfiles/vision/data/segnetVGG16CamVid.mat”;pretrainedFolder = fullfile(tempdir,“pretrainedSegNet”);pretrainedSegNet = fullfile(pretrainedFolder,“segnetVGG16CamVid.mat”);如果~存在(pretrainedFolder“dir”mkdir (pretrainedFolder);disp (“下载预训练的SegNet (107 MB)…”);websave (pretrainedSegNet pretrainedURL);结束
强烈建议使用具有cuda功能、计算能力为3.0或更高版本的NVIDIA™GPU来运行此示例。GPU的使用需要并行计算工具箱™。
下载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”);如果~存在(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“图片”));结束
注:资料下载时间视乎阁下的互联网连接而定。上面使用的命令阻塞MATLAB,直到下载完成。或者,您可以使用web浏览器先将数据集下载到本地磁盘。要使用从web下载的文件,请更改outputFolder
变量设置为下载文件的位置。
加载CamVid图像
使用imageDatastore
加载CamVid图像。的imageDatastore
使您能够有效地在磁盘上加载大量映像。
imgDir = fullfile(输出文件夹,“图片”,701 _stillsraw_full);imds = imageDatastore(imgDir);
显示其中一个图像。
I = readimage(imds,1);I = histeq(I);imshow(我)
加载CamVid像素标记图像
使用pixelLabelDatastore
加载CamVid像素标签图像数据。一个pixelLabelDatastore
将像素标签数据和标签ID封装为类名映射。
遵循原始SegNet论文中使用的程序[1],将CamVid中原有的32个类分组为11个类。指定这些类。
类= [“天空”“建筑”“极”“路”“路面”“树”“SignSymbol”“篱笆”“汽车”“行人”“自行车”];
为了将32个类减少到11个,将原始数据集中的多个类分组在一起。例如,“Car”是“Car”、“SUVPickupTruck”、“Truck_Bus”、“Train”和“OtherMoving”的组合。使用支持函数返回分组的标签id万博1manbetxcamvidPixelLabelIDs
,在本例的末尾列出。
labelIDs = camvidPixelLabelIDs();
使用类和标签id创建pixelLabelDatastore。
labelDir = fullfile(输出文件夹,“标签”);pxds = pixelLabelDatastore(labelDir,classes,labelIDs);
读取并显示一个像素标记的图像,方法是将其覆盖在图像之上。
C = readimage(pxds,1);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+06 4.8315e+08 'Road' 1.4054e+08 4.8453e+08 'Pavement' 3.4054e +08 4.8453e+08 'Pavement' 3.3614e+ 08 4.7209e+08 'Tree' 5.4259e+07 4.4209e +08 'SignSymbol' 5.2242e+06 4.6863e+08 'Fence' 6.9211e+06 2.516e+08 'Car' 2.4437e+07 4.4444e+08 'Pedestrian' 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
是本例末万博1manbetx尾列出的支持函数。
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
创建网络
使用segnetLayers
创建一个使用VGG-16权重初始化的SegNet网络。segnetLayers
自动执行从vg -16传输权重所需的网络转换,并添加语义分割所需的额外层。的输出segnetLayers
是一个LayerGraph
表示SegNet的对象。一个LayerGraph
对象封装了网络层和层之间的连接。
imageSize = [360 480 3];numClasses = nummel(类);lgraph = segnetLayers(imageSize,numClasses,“vgg16”);
图像大小是根据数据集中图像的大小选择的。类的数量根据CamVid中的类选择。
使用类加权来平衡类
如前所述,CamVid中的类是不平衡的。为了改进训练,您可以使用类权重来平衡类。使用前面计算的像素标签计数countEachLabel
并计算中值频率类权重[1].
imageFreq = tbl。PixelCount ./ tb . 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)
pxLayer = PixelClassificationLayer与属性:名称:'labels' ClassNames: {11×1 cell} ClassWeights: [11×1 double] OutputSize: 'auto'超参数LossFunction: 'crossentropyex'
更新SegNet网络pixelClassificationLayer
通过移除电流pixelClassificationLayer
然后添加新图层。当前的pixelClassificationLayer
命名为pixelLabels。使用removeLayers
,使用addLayers
,并将新层连接到网络的其余部分connectLayers
.
lgraph = removeLayers(“pixelLabels”);lgraph = addLayers(lgraph, pxLayer);lgraph = connectLayers(“softmax”,“标签”);
选择培训选项
用于训练的优化算法是随机动量梯度下降(SGDM)。使用trainingOptions
指定用于SGDM的超参数。
选项= trainingOptions(“个”,...“动量”, 0.9,...“InitialLearnRate”1 e - 3,...“L2Regularization”, 0.0005,...“MaxEpochs”, 100,...“MiniBatchSize”4...“CheckpointPath”tempdir,...“洗牌”,“every-epoch”,...“VerboseFrequency”2);
4的小批大小用于减少训练时的内存使用。您可以根据系统上的GPU内存数量增加或减少这个值。
此外,“CheckpointPath”
设置为临时位置。这个名称-值对可以在每个训练阶段结束时保存网络检查点。如果培训中断,例如由于停电或系统故障,您可以从保存的网络检查点恢复培训。确保'CheckpointPath'指定的位置有足够的空间存储网络检查点。例如,保存100个SegNet检查点需要大约11 GB的磁盘空间,因为1个SegNet网络保存到磁盘时是107 MB。
数据增加
在训练过程中使用数据增强为网络提供更多的例子,因为它有助于提高网络的准确性。这里使用随机左/右反射和+/- 10像素的随机X/Y平移进行数据增强。使用imageDataAugmenter
指定这些数据增强参数。
augmenter = imageDataAugmenter(“RandXReflection”,真的,...“RandXTranslation”-10年[10],“RandYTranslation”, -10年[10]);
imageDataAugmenter
万博1manbetx支持其他几种类型的数据增强。其中的选择需要实证分析,这是另一个层次的超参数调整。
开始训练
结合训练数据和数据增强选择使用pixelLabelImageDatastore
.的pixelLabelImageDatastore
读取批量训练数据,应用数据增强,并将增强后的数据发送给训练算法。
pximds = pixelLabelImageDatastore(imdsTrain,pxdsTrain,...“DataAugmentation”、增压器);
开始使用trainNetwork
如果doTraining
Flag为true。否则,加载一个预先训练好的网络。
注意:这个网络是在NVIDIA™Titan X上训练的,它有12 GB的GPU内存。如果你的GPU内存较少,你很可能会在训练过程中耗尽内存。如果发生这种情况,试着降低MiniBatchSize
财产trainingOptions
为1。训练这个网络大约花了5个小时。根据GPU硬件的不同,可能需要更长的时间。
doTraining = false;如果doTraining [net, info] = trainNetwork(pximds,lgraph,options);其他的data = load(pretrainedSegNet);Net = data.net;结束
在一个映像上测试网络
作为一个快速的完整性检查,在一个测试映像上运行训练好的网络。
I = read(imdsTest);C = semanticseg(I, net);
显示结果。
B = labeloverlay(I,C,“Colormap”提出,“透明”, 0.4);imshow(B) pixelLabelColorbar(cmap, classes);
比较C
与存储在pxdsTest
.绿色和洋红色区域突出了分割结果与预期地面真相不同的区域。
expectedResult = read(pxdsTest);实际= uint8(C);expect = uint8(expectedResult);预计imshowpair(实际)
从视觉上看,道路、天空和建筑物等类的语义分割结果重叠良好。然而,像行人和汽车这样的小物体就没有那么准确了。每个类的重叠量可以使用交集-联合(IoU)度量,也称为Jaccard指数。使用jaccard
函数来测量借据。
iou = jaccard(C, expectedResult);表(类、借据)
ans =11×2表类iou ____________ ________“天空”0.92659“建筑”0.7987“极点”0.16978“道路”0.95177“路面”0.41877“树”0.43401“标志符号”0.32509“栅栏”0.492“汽车”0.068756“行人”0“骑自行车的人”0
借条指标确认了视觉结果。道路类、天空类、建筑物类的借条得分较高,而行人类、汽车类的借条得分较低。其他常见的细分指标包括骰子指数和Boundary-F1轮廓匹配分数。
评估训练网络
若要测量多个测试图像的准确性,请运行semanticseg
在整个测试集中。mini-batch的大小为4,可以减少GPU内存的使用。您可以根据系统上的GPU内存数量增加或减少这个值。
pxdsResults = semanticseg(imdsTest,net,...“MiniBatchSize”4...“WriteLocation”tempdir,...“详细”、假);
semanticseg
返回测试集的结果pixelLabelDatastore
对象。中每个测试图像的实际像素标签数据imdsTest
写入磁盘中“WriteLocation”
参数。使用evaluateSemanticSegmentation
对测试集结果进行语义分割度量。
metrics = evaluateSemanticSegmentation(pxdsResults,pxdsTest, pxdsTest)“详细”、假);
evaluateSemanticSegmentation
返回整个数据集、单个类和每个测试图像的各种指标。要查看数据集级别的度量,请检查指标。DataSetMetrics
.
指标。DataSetMetrics
ans =表1×5GlobalAccuracy MeanAccuracy MeanIoU WeightedIoU MeanBFScore ______________ ____________ _______ ___________ ___________ 0.88204 0.85097 0.60893 0.79795 0.60981
数据集指标提供了网络性能的高级概述。要查看每个类对整体性能的影响,请检查使用的每个类度量指标。ClassMetrics
.
指标。ClassMetrics
ans =11×3表精度IoU平均bfscore ________ _______ ___________天空0.93493 0.89244 0.88152建筑物0.79776 0.75263 0.59707杆0.72635 0.18662 0.52252路0.93676 0.18672 0.71043路面0.90674 0.72865 0.70362树0.86657 0.73747 0.66421标志符号0.7559 0.34519 0.43401栅栏0.82807 0.50592 0.5083汽车0.91187 0.75001 0.64352行人0.84866 0.35046 0.45551自行车0.84705 0.54208 0.46818
尽管数据集的整体性能相当高,但类指标显示了代表性不足的类,例如行人
,骑自行车
,车
不分段以及类,如路
,天空
,建筑
.包含更多代表性不足的类别样本的附加数据可能有助于改善结果。
参考文献
Badrinarayanan, Vijay, Alex Kendall和Roberto Cipolla。“SegNet:一种用于图像分割的深度卷积编码器-解码器架构”arXiv预印本arXiv:1511.00561, 2015。
[2] Brostow, Gabriel J., Julien Fauqueur和Roberto Cipolla。“视频中的语义对象类:高清地面真相数据库。”模式识别信件第30卷,第2期,2009,pp 88-97。
万博1manbetx支持功能
函数labelIDs = camvidPixelLabelIDs()返回每个类对应的标签id。%CamVid数据集有32个类。把他们分成11个类%原始SegNet训练方法[1]。%这11个类是:%的“天空”“建筑”,“极”,“路”,“路面”、“树”、“SignSymbol”,“栅栏”、“汽车”、“行人”和“骑自行车的人”。%CamVid像素标签id作为RGB颜色值提供。把它们分成% 11类,并将它们作为m × 3矩阵的单元格数组返回。的%原始CamVid类名与每个RGB值一起列出。请注意%,其他/Void类被排除在下面。labelIDs = {...%的“天空”[128 128 128;...%的“天空”]%“建设”[000 128 064;...%“桥”12.80万;...%“建设”064 192 000;...%的“墙”064 000 064;...%的“隧道”192 000 128;...%的“拱门”]%“极”[192 192 128;...%”Column_Pole”064;...%”TrafficCone”]%的道路[128 064 128;...%的“路”128 000 192;...%”LaneMkgsDriv”192 000 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;结束函数imds = resizeCamVidImages(imds, imageFolder)将图像大小调整为[360 - 480]。如果~存在(imageFolder“dir”mkdir (imageFolder)其他的imds = imageDatastore(imageFolder);返回;如果图像已经调整大小,则跳过结束重置(imd)而hasdata (imd)读取图像。[I,info] = read(imds);%调整图像大小。I = imresize(I,[360 480]);写入磁盘。[~, filename, ext] = fileparts(info.Filename);(I,[imageFolder filename ext])结束imds = imageDatastore(imageFolder);结束函数pxds = resizeCamVidPixelLabels(pxds, labelFolder)将像素标签数据调整为[360 480]。classes = pxds.ClassNames;labelIDs = 1:数字(类);如果~存在(labelFolder“dir”mkdir (labelFolder)其他的pxds = pixelLabelDatastore(labelFolder,classes,labelIDs);返回;如果图像已经调整大小,则跳过结束重置(pxds)而hasdata (pxds)读取像素数据。[C,info] = read(pxds);从分类转换为uint8。L = uint8(C);调整数据大小。使用“最近的”插值%保留标签id。L = imresize(L,[360 480],“最近的”);%将数据写入磁盘。[~, filename, ext] = fileparts(info.Filename);写入(L,[labelFolder filename ext])结束labelIDs = 1:数字(类);pxds = pixelLabelDatastore(labelFolder,classes,labelIDs);结束函数[imdsTrain, imdsTest, pxdsTrain, pxdsTest] = partitionCamVidData(imds,pxds)对CamVid数据进行分区,随机选取60%的数据进行训练。的% rest用于测试。设置初始随机状态,例如再现性。rng (0);numFiles = nummel (imds.Files);shuffledIndices = randperm(numFiles);使用60%的图像进行训练。N = round(0.60 * numFiles);trainingIdx = shuffledIndices(1:N);使用其余的测试。testdx = shuffledIndices(N+1:结束);为训练和测试创建图像数据存储。trainingImages = imds.Files(trainingIdx);testImages = imds.Files(testdx);imdsTrain = imageDatastore(trainingImages);imdsTest = imageDatastore(testImages);提取类和标签id信息。classes = pxds.ClassNames;labelIDs = 1: number (pxds.ClassNames);为训练和测试创建像素标签数据存储。trainingLabels = pxds.Files(trainingIdx);testLabels = pxds.Files(testdx);pxdsTrain = pixelLabelDatastore(trainingLabels, classes, labelIDs);pxdsTest = pixelLabelDatastore(testLabels, classes, labelIDs);结束
参考文献
Badrinarayanan, Vijay, Alex Kendall和Roberto Cipolla。“SegNet:一种用于图像分割的深度卷积编码器-解码器架构”arXiv预印本arXiv:1511.00561, 2015。
[2] Brostow, Gabriel J., Julien Fauqueur和Roberto Cipolla。“视频中的语义对象类:高清地面真相数据库。”模式识别信件第30卷,第2期,2009,pp 88-97。
另请参阅
countEachLabel
|evaluateSemanticSegmentation
|imageDataAugmenter
|labeloverlay
|pixelClassificationLayer
|pixelLabelDatastore
|pixelLabelImageDatastore
|segnetLayers
|semanticseg
|trainNetwork
|trainingOptions
相关的话题
- 语义分割基础
- 标记像素进行语义分割
- 深度学习基础(神经网络工具箱)
- 预训练卷积神经网络(神经网络工具箱)