深度学习

理解和使用深度学习网络

基于深度学习的语义分割

今天我想给大家展示一个文档的例子展示了如何使用深度学习和计算机视觉系统工具箱来训练语义分割网络。

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

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

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

设置

本例创建了SegNet网络,权重初始化自vg -16网络。要获得VGG-16,请安装VGG-16网络的神经网络工具箱™模型.安装完成后,运行以下代码验证安装是否正确。

vgg16 ();

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

pretrainedURL =“//www.tatmou.com/金宝appsupportfiles/vision/data/segnetVGG16CamVid.mat”;pretrainedFolder = fullfile(tempdir,“pretrainedSegNet”);pretrainedSegNet = fullfile(pretrainedFolder,“segnetVGG16CamVid.mat”);如果~存在(pretrainedFolder“dir”mkdir (pretrainedFolder);disp (“下载预训练的SegNet (107 MB)…”);websave (pretrainedSegNet pretrainedURL);结束
下载预训练的SegNet (107 MB)…

强烈建议使用具有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像素标记图像

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

遵循原始SegNet论文中使用的程序(Badrinarayanan, Vijay, Alex Kendall和Roberto Cipolla)。“SegNet:一种用于图像分割的深度卷积编码器-解码器架构。”arXiv预印本arXiv:1511.00561, 201),将CamVid中的32个原始类分组为11个类。指定这些类。

classes =["天空" "建筑物" "杆子" "道路" "人行道" "树木" "标志符号" "栅栏" "汽车" "行人" "骑自行车的人"];

为了将32个类减少到11个,将原始数据集中的多个类分组在一起。例如,“Car”是“Car”、“SUVPickupTruck”、“Truck_Bus”、“Train”和“OtherMoving”的组合。使用支持函数返回分组的标签id金宝app camvidPixelLabelIDs,在本例的末尾列出。

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 ____________ __________ _______________“天空”76801167 483148800“建筑”117373718 483148800“杆”4798742 483148800“道路”140535728 484531200“人行道”33614414 472089600“树”54258673 447897600“标志符号”5224247 468633600“篱笆”6921061 251596800“汽车”3402909 444441600“自行车手”2591222 261964800

按类可视化像素计数。

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

创建网络

使用segnetLayers创建一个使用VGG-16权重初始化的SegNet网络。 segnetLayers自动执行从vg -16传输权重所需的网络手术,并添加语义分割所需的额外层。

imageSize = [360 480 3];numClasses = nummel(类);lgraph = segnetLayers(imageSize,numClasses,“vgg16”);

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

使用类加权来平衡类

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

imageFreq = tbl。PixelCount ./ tb . imagepixelcount;classWeights = median(imageFreq) ./ imageFreq ./
classWeights =11×10.318184709354742 0.092367332938507 0.174381825257403 0.710338097812948 0.41751856068787874 4.537074815482926 1.838648261914560 1.000000000000000 6.605878573155874

指定类的权重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...“洗牌”“every-epoch”...“VerboseFrequency”2);

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

数据增加

在训练过程中使用数据增强为网络提供更多的例子,因为它有助于提高网络的准确性。这里使用随机左/右反射和+/- 10像素的随机X/Y平移进行数据增强。

augmenter = imageDataAugmenter(“RandXReflection”,真的,...“RandXTranslation”-10年[10],“RandYTranslation”, -10年[10]);

imageDataAugmenter金宝app支持其他几种类型的数据增强。其中的选择需要实证分析,这是另一个层次的超参数调整。

开始训练

结合训练数据和数据增强选择使用pixelLabelImageDatastore.的 pixelLabelImageDatastore读取批量训练数据,应用数据增强,并将增强后的数据发送给训练算法。

pximds = pixelLabelImageDatastore(imdsTrain,pxdsTrain,...“DataAugmentation”、增压器);

开始训练,如果 doTrainingFlag为true。否则,加载一个预先训练好的网络。注意:在NVIDIA™Titan X上进行培训大约需要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.926585343977038“建筑”0.798698991022729“杆”0.169776501947919“路”0.951766120547122“路面”0.418766821629557“树”0.434014251781473“标志符号”0.325092056812204“栅栏”0.49200469780468“汽车”0.0687557042896258“行人”0“骑自行车的人”0

借条指标确认了视觉结果。道路类、天空类、建筑物类的借条得分较高,而行人类、汽车类的借条得分较低。其他常见的细分指标包括骰子指数Boundary-F1轮廓匹配分数。

评估训练网络

若要测量多个测试图像的准确性,请运行semanticseg在整个测试集中。

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.882035049405331 0.850970241394654 0.608927281006314 0.797947090677593 0.60980715338674

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

指标。ClassMetrics
ans =11×3表精度IoU MeanBFScore _________________ _________________ _________________天空0.934932109589398 0.8924352120430993建筑0.797763575866624 0.752633046400693 0.59707070666636343620018996 0.556622256135469 0.522519568793497路0.936763259117679 0.906720411900943 0.710433513101952路面0.906740772559168 0.728650096831083 0.7036199686386树0.866574402823008 0.737468334515386 0.664211092196979符号0.755895966085333 0.3451907986070.434011059025598围栏0.828068989656379 0.505920925889568 0.50829520978596汽车0.911873566421394 0.750012303035288 0.643524410331899行人0.84866313766479 0.350461157529184 0.45550879471499自行车0.847049655538425 0.542083155989493 0.468181589716695

尽管数据集的整体性能相当高,但类指标显示了代表性不足的类,例如 行人 骑自行车,不分段以及类,如 天空, 建筑.包含更多代表性不足的类别样本的附加数据可能有助于改善结果。

金宝app支持功能

函数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);结束
|
  • 打印
  • 发送电子邮件

评论

如欲留言,请点击在这里登录您的MathWorks帐户或创建一个新帐户。