主要内容

训练分类网络对三维点云中的物体进行分类

此示例演示了[1],其中点云数据被预处理成体素化编码,然后直接与简单的三维卷积神经网络架构一起使用来执行对象分类。在最近的方法中,如[2],点云数据的编码可以更复杂,并且可以学习编码,与执行分类/对象检测/分割任务的网络一起进行端到端训练。然而,从不规则的无序点移动到网格结构的一般模式,可以馈送到convnets在所有这些方法中仍然相似。

导入和分析数据

在本例中,我们使用悉尼城市对象数据集。在这个例子中,我们使用数据中的1-3次折叠作为训练集,4次折叠作为验证集。

dataPath = downloadSydneyUrbanObjects(tempdir);dsTrain = loadSydneyUrbanObjectsData(dataPath,[1 2 3]);dsVal = loadSydneyUrbanObjectsData(dataPath,4);

分析训练集,以了解数据中存在的标签和标签的整体分布。

dsLabels = transform(dsTrain,@(data) data{2});标签= readall(dsLabels);图直方图(标签)

从直方图中可以明显看出,在训练数据中存在类不平衡的问题,其中某些对象类喜欢而且行人比不太常见的课程更常见吗乌特

数据增强管道

为了避免过拟合和增加分类器的鲁棒性,在训练网络时,一定数量的随机数据增强通常是一个好主意。的函数randomAffine2dpctransform使得在点云数据上定义随机仿射变换变得容易。另外,我们在每个点云的每个点上添加一些随机的逐点抖动。这个函数augmentPointCloudData包含在下面的支持功能部分中。金宝app

dsTrain = transform(dsTrain,@augmentPointCloudData);

验证点云数据的增加是否合理。

dataOut =预览(dsTrain);图pcshow (dataOut {1});标题(dataOut {2});

接下来,我们向每个输入点云添加一个简单的体素化转换,就像前面的例子中讨论的那样,将我们的输入点云转换为可以与卷积神经网络一起使用的伪图像。使用简单的占用网格。

dsTrain = transform(dsTrain,@formOccupancyGrid);dsVal = transform(dsVal,@formOccupancyGrid);

检查将输入网络的最终体素化体的样本,以验证体素化是否正常工作。

data = preview(dsTrain);图p = patch(isosurface(data{1},0.5));p.FaceColor =“红色”;p.EdgeColor =“没有”;Daspect([1 1 1])视图(45,45)camlight;照明冯氏标题(数据{2});

定义网络架构

在本例中,我们使用[1]中描述的简单3-D分类体系结构。

图层= [image3dInputLayer([32 32 32],“名字”“inputLayer”“归一化”“没有”),...32岁的convolution3dLayer (5“步”2,“名字”“Conv1”),...leakyReluLayer (0.1,“名字”“leakyRelu1”),...32岁的convolution3dLayer (3“步”, 1“名字”“Conv2”),...leakyReluLayer (0.1,“名字”“leakyRulu2”),...maxPooling3dLayer (2“步”2,“名字”“maxPool”),...fullyConnectedLayer (128“名字”“fc1”),...reluLayer (“名字”“relu”),...dropoutLayer (0.5,“名字”“dropout1”),...fullyConnectedLayer(14日“名字”“取得”),...softmaxLayer (“名字”“softmax”),...classificationLayer (“名字”“crossEntropyLoss”));voxnet = layerGraph(图层);图绘制(voxnet);

设置培训选项

使用带有动量的随机梯度下降,并分段调整学习速率计划。这个例子是在TitanX GPU上运行的,对于内存较少的GPU,可能需要减小批处理大小。虽然3D卷积网络具有概念简单的优势,但它们在训练时使用大量内存的缺点。

miniBatchSize = 32;dsLength = length(dsTrain.UnderlyingDatastore.Files);iterationsPerEpoch = floor(dlength /miniBatchSize);dropPeriod = floor(8000/iterationsPerEpoch);选项= trainingOptions(“个”“InitialLearnRate”, 0.01,“MiniBatchSize”miniBatchSize,...“LearnRateSchedule”“分段”...“LearnRateDropPeriod”dropPeriod,...“ValidationData”dsVal,“MaxEpochs”现年60岁的...“DispatchInBackground”假的,...“洗牌”“永远”);

列车网络的

voxnet = trainNetwork(dsTrain,voxnet,options);
单CPU训练。|======================================================================================================================| | 时代| |迭代时间| Mini-batch | |验证Mini-batch | |验证基地学习  | | | | ( hh: mm: ss) | | | | |损失损失精度精度  | |======================================================================================================================| | 1 | 1 | 00:00:12 | | 0.00% 3.23% | 2.6579 | 2.6466 | 0.0100 | | 4 | 50 | 00:01:53 |31.25% | 29.03% | 2.1520 | 2.3095 | 0.0100 | | 100 | | 00:03:33 | | 28.12% 36.77% | 2.2633 | 2.1510 | 0.0100 | | 150 | | 00:05:11 | | 43.75% 46.45% | 2.0506 | 1.9057 | 0.0100 | | 16 | 200 | 00:06:49 | | 37.50% 52.26% | 1.8627 | 1.6161 | 0.0100 | | 250 | | 00:08:35 | | 50.00% 59.35% | 1.8573 | 1.4587 | 0.0100 | | 24 | 300 | 00:10:14 | | 34.38% 58.06% | 1.8636 | 1.4360 | 0.0100 | | 350 | | 27 00:11:51 | | 62.50% 61.94% | 1.4174 | 1.3093 | 0.0100 | | 400 | | 31日00:13:31 | | | 65.62% 64.52%35 1.1966 | 1.2727 | 0.0100 | | 450 | | 00:15:09 | | 56.25% 61.94% | 1.3562 | 1.2473 | 0.0100 | | 39 | 500 | 00:16:49 | | 62.50% 66.45% | 1.2819 | 1.1354 | 0.0100 | | 43 | 550 | 00:18:27 | | 56.25% 65.16% | 1.4563 | 1.1351 | 0.0100 | | | 600 | 00:20:05 | | 56.25% 66.45% | 1.3096 | 1.1142 | 0.0100 | | 650 | | 00:21:40 | | 56.25% 65.16% | 1.0104 | 1.1023 | 0.0100 | | | 700 | 00:23:21 | | 75.00% 70.32% | 0.9403 | 1.0848 | 0.0100 | | 58 | 750 | 00:25:00 | | 65.62% 71.61% | 1.0909 | 1.1003 |0.0100 | | 60 | 780 | 00:25:59 | 65.62% | 72.26% | 0.9628 | 1.0406 | 0.0100 | |======================================================================================================================|

评估网络

遵循[1],本例只形成了来自Sydney Urban Objects的训练和验证集。使用验证评估训练网络的性能,因为它没有用于训练网络。

valLabelSet = transform(dsVal,@(data) data{2});valLabels = readall(valLabelSet);outputLabels = category (voxnet,dsVal);accuracy = nnz(outputLabels == valLabels) / numel(outputLabels);disp(精度)
0.7226

查看混淆矩阵以研究不同标签类别的准确性

confusionchart (valLabels outputLabels)

在训练集中注意到的标签不平衡是分类准确性的一个问题。混淆图显示了行人(最常见的类别)比货车(不太常见的类别)更高的精度和回忆率。由于本示例的目的是演示使用点云数据的基本分类网络训练方法,因此将不会探讨可能采取的下一步措施来提高分类性能,例如重新采样训练集或实现更好的标签平衡或使用更健壮的损失函数来标记不平衡(例如加权交叉熵)。

参考文献

1)Voxnet:用于实时物体识别的三维卷积神经网络, Daniel Maturana, Sebastian Scherer, 2015 IEEE/RSJ智能机器人与系统国际会议(IROS)

2)PointPillars:用于点云对象检测的快速编码器,Alex H. Lang, Sourabh Vora等,CVPR 2019

3)悉尼城市对象数据集,阿拉斯泰尔·夸德罗斯,詹姆斯·安德伍德,伯特兰·杜拉德,悉尼城市景观

金宝app支持功能

函数datasetPath = downloadSydneyUrbanObjects(dataLoc)如果nargin = 0 dataLoc = pwd();结束dataLoc = string(dataLoc);url =“http://www.acfr.usyd.edu.au/papers/data/”;name =“sydney-urban-objects-dataset.tar.gz”如果~ (fullfile (dataLoc,存在“sydney-urban-objects-dataset”),“dir”) disp (“正在下载悉尼城市对象数据集……”);解压(url +名字,dataLoc);结束datasetPath = dataLoc.append(“sydney-urban-objects-dataset”);结束函数ds = loadSydneyUrbanObjectsData(数据路径,折叠)loadSydneyUrbanObjectsData数据存储与点云和%悉尼城市对象数据集的相关分类标签。% ds = loadSydneyUrbanObjectsData(数据路径)构造一个数据存储表示悉尼市区的点云和相关类别对象数据集。输入数据路径是一个字符串或字符数组%表示悉尼城市对象根目录的路径%的数据集。% ds = loadSydneyUrbanObjectsData(___,折叠)可选允许控件中包含的所需折叠的%规格%输出ds。例如,[1 2 4]指定要第一个,%数据集的第二次和第四次折叠。默认值:[1 2 3 4]。如果Nargin < 2折= 1:4;结束Datapath = string(Datapath);路径= fullfile(数据路径,“对象”, filesep);现在,在数据存储中包含所有的折叠foldNames{1} = importdata(fullfile(数据路径,“折叠”“fold0.txt”));foldNames{2} = importdata(fullfile(数据路径,“折叠”“fold1.txt”));foldNames{3} = importdata(fullfile(数据路径,“折叠”“fold2.txt”));foldNames{4} = importdata(fullfile(数据路径,“折叠”“fold3.txt”));names = foldNames(折叠);Names = vertcat(Names {:});fullFilenames = append(路径,名称);ds = fileDatastore(fullfilename,“ReadFcn”@extractTrainingData,“FileExtensions”“。斌”);%洗牌ds。Files = ds.Files(randperm(length(ds.Files)));结束函数dataOut = extractTrainingData(fname) [pointData,intensity] = readbin(fname);[~,name] = fileparts(fname);Name = string(Name);name = extractBefore(name,“。”);Name = replace(Name,“_”' ');labelNames = [“四轮驱动”“建筑”“公共汽车”“汽车”“行人”“支柱”...“极”“红绿灯”“交通标志”“树”“卡车”“树干”“哑巴”“范”];label = categorical(name,labelNames);dataOut = {pointCloud(pointData,“强度”、强度),标签};结束函数[pointData,intensity] = readbin(fname)% readbin来自悉尼城市对象二进制的读点和强度数据%的文件。% names = ['t','intensity','id',…% ' x ', ' y ', ' z ',……%“方位”、“范围”、“pid”)% formats = ['int64', 'uint8', 'uint8',…% 'float32', 'float32', 'float32',…% 'float32', 'float32', 'int32']Fid = fopen(fname,“r”);c = onCleanup(@() fclose(fid));fseek(支撑材10 1);移动到距离开始10字节的第一个X点位置X = fread(fid,inf,“单一”, 30);fseek (fid, 14日1);Y = fread(fid,inf,“单一”, 30);fseek(支撑材,18岁,1);Z = fread(fid,inf,“单一”, 30);fseek(支撑材8 1);强度=读取(fid,inf,“uint8”33);pointData = [X,Y,Z];结束函数dataOut = formOccupancyGrid(data) grid = pcbin(data{1},[32 32 32]);occuancygrid =零(大小(网格),“单一”);ii = 1:numel(grid) occuancygrid (ii) = ~isempty(grid{ii});结束标签=数据{2};dataOut = {occuancygrid,label};结束函数dataOut = augmentPointCloudData(data) ptCloud =数据{1};标签=数据{2};%随机旋转Z轴。tform = randomAffine3d(“旋转”,@() deal([0 0 1],360*rand),“规模”[0.98, 1.02],“XReflection”,真的,“YReflection”,真正的);%绕z轴随机旋转ptCloud = pctransform(ptCloud,tform);%对点云中的每个点应用抖动amountOfJitter = 0.01;numPoints = size(ptCloud.Location,1);D = 0 (size(ptCloud.Location),“喜欢”, ptCloud.Location);D(:,1) = diff(ptCloud.XLimits)*rand(numPoints,1);D(:,2) = diff(ptCloud.YLimits)*rand(numPoints,1);D(:,3) = diff(ptCloud.ZLimits)*rand(numPoints,1);D = amountOfJitter.*D;ptCloud = pctransform(ptCloud,D);dataOut = {ptCloud,label};结束