用PointNet分类3D点云

在本教程中,我们将学习如何训练PointNet进行分类。 我们将主要关注数据和训练过程; 展示如何从头开始编码 Point Net 的教程位于此处。 本教程的代码位于这个Github库中,我们将使用的笔记本位于这个Github库中。 一些代码的灵感来自于这个Github库。

在这里插入图片描述

推荐:用 NSDT设计器 快速搭建可编程3D场景。

1、获取数据

我们将使用只有 16 个类的较小版本的 shapenet 数据集。 如果你使用的是Colab,可以运行以下代码来获取数据。 警告,这将需要很长时间。

!wget -nv https://shapenet.cs.stanford.edu/ericyi/shapenetcore_partanno_segmentation_benchmark_v0.zip --no-check-certificate
!unzip shapenetcore_partanno_segmentation_benchmark_v0.zip
!rm shapenetcore_partanno_segmentation_benchmark_v0.zip

如果你想在本地运行,请访问上面第一行的链接,数据将自动下载为 zip 文件。

该数据集包含 16 个带有类标识符的文件夹(自述文件中称为“synsetoffset”)。 文件夹结构为:

synsetoffset|- points                  # 来自 ShapeNetCore 模型的均匀采样点|- point_labels            # 每点分割标签|- seg_img                 #标签的可视化
train_test_split:           #带有训练/验证/测试拆分的 JSON 文件

自定义 PyTorch 数据集位于此处,解释代码超出了本教程的范围。 需要了解的重要一点是,数据集可以获取 (point_cloud, class) 或 (point_cloud, seg_labels)。 在训练和验证期间,我们向点云添加高斯噪声,并围绕垂直轴(本例中为 y 轴)随机旋转它们。 我们还对点云进行最小-最大归一化,以便它们的范围为 0-1。 我们可以像这样创建 shapenet 数据集的实例:

from shapenet_dataset import ShapenetDataset# __getitem__ returns (point_cloud, class)
train_dataset = ShapenetDataset(ROOT, npoints=2500, split='train', classification=True)

2、探索数据

在开始任何训练之前,让我们先探讨一些训练数据。 为此,我们将使用 Open3d 版本 0.16.0(必须为 0.16.0 或更高版本)。

!pip install open3d==0.16.0

我们现在可以使用以下代码查看示例点云。 你应该注意到,每次运行代码时点云都会以不同的方向显示。

import open3d as o3
from shapenet_dataset import ShapenetDatasetsample_dataset = train_dataset = ShapenetDataset(ROOT, npoints=20000, split='train', classification=False, normalize=False)points, seg = sample_dataset[4000]pcd = o3.geometry.PointCloud()
pcd.points = o3.utility.Vector3dVector(points)
pcd.colors = o3.utility.Vector3dVector(read_pointnet_colors(seg.numpy()))o3.visualization.draw_plotly([pcd])

在这里插入图片描述

图 1.随机旋转的噪声点云。 Y 轴是纵轴

你可能不会注意到噪音有太大差异,因为我们添加的量很小; 我们添加了少量,因为不想极大地破坏结构,但这一小量足以对模型产生影响。 现在我们就来看看训练分类的数据频率。
在这里插入图片描述

图 2. 训练分类数据点直方图

从图2中我们可以看出,这绝对不是一个平衡的训练集。 因此,我们可能想要应用类别权重,甚至使用焦点损失来帮助我们的模型学习。

3、PointNet损失函数

当训练PointNet进行分类时,我们可以使用 PyTorch 中的标准交叉熵损失,但我们还想添加包括论文中提到的正则化项。

正则化项强制特征变换矩阵正交,但为什么呢? 特征变换矩阵旨在旋转(变换)点云的高维表示。 我们如何确定这种学习的高维旋转实际上是在旋转点云? 为了回答这个问题,让我们考虑一些所需的旋转属性。

我们希望学习到的旋转是仿射的,这意味着它保留结构。 我们希望确保它不会做一些奇怪的事情,例如将其映射回较低维度的空间或弄乱结构。 我们不能只绘制 nx64 点云来检查这一点,但我们可以通过鼓励旋转正交来让模型学习有效的旋转。 这是因为正交矩阵同时保留长度和角度,而旋转矩阵是一种特殊类型的正交矩阵 。 我们可以“鼓励”模型通过使用以下项进行正则化来学习正交旋转矩阵:

在这里插入图片描述

图 3.PointNet正则化项

我们利用正交矩阵的一个基本属性,即它们的列和行是正交向量。 对于完全正交的矩阵,图 3 中的正则化项将等于 0。

在训练期间,我们只需将此项添加到我们的损失中。 如果你已经完成了之前关于如何编码PointNet的教程,可能还记得特征转换矩阵 A 由分类头返回。

现在让我们编写PointNet损失函数的代码。 我们已经添加了加权(平衡)交叉熵损失和焦点损失的术语,但解释它们超出了本教程的范围。 其代码位于此处。 该代码改编自这个Github库。

import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as Fclass PointNetLoss(nn.Module):def __init__(self, alpha=None, gamma=0, reg_weight=0, size_average=True):super(PointNetLoss, self).__init__()self.alpha = alphaself.gamma = gammaself.reg_weight = reg_weightself.size_average = size_average# sanitize inputsif isinstance(alpha,(float, int)): self.alpha = torch.Tensor([alpha,1-alpha])if isinstance(alpha,(list, np.ndarray)): self.alpha = torch.Tensor(alpha)# get Balanced Cross Entropy Lossself.cross_entropy_loss = nn.CrossEntropyLoss(weight=self.alpha)def forward(self, predictions, targets, A):# get batch sizebs = predictions.size(0)# get Balanced Cross Entropy Lossce_loss = self.cross_entropy_loss(predictions, targets)# reformat predictions and targets (segmentation only)if len(predictions.shape) > 2:predictions = predictions.transpose(1, 2) # (b, c, n) -> (b, n, c)predictions = predictions.contiguous() \.view(-1, predictions.size(2)) # (b, n, c) -> (b*n, c)# get predicted class probabilities for the true classpn = F.softmax(predictions)pn = pn.gather(1, targets.view(-1, 1)).view(-1)# get regularization termif self.reg_weight > 0:I = torch.eye(64).unsqueeze(0).repeat(A.shape[0], 1, 1) # .to(device)if A.is_cuda: I = I.cuda()reg = torch.linalg.norm(I - torch.bmm(A, A.transpose(2, 1)))reg = self.reg_weight*reg/bselse:reg = 0# compute loss (negative sign is included in ce_loss)loss = ((1 - pn)**self.gamma * ce_loss)if self.size_average: return loss.mean() + regelse: return loss.sum() + reg

4、训练PointNet用于分类

现在我们已经了解了数据和损失函数,我们可以继续进行训练。

对于我们的训练需要量化模型的表现。 通常我们会考虑损失和准确性,但对于这个分类问题,我们需要一个衡量错误分类和正确分类的指标。 想想典型的混淆矩阵:真阳性、假阴性、真阴性和假阳性; 我们想要一个在所有这些方面都表现良好的分类器。

马修斯相关系数 (MCC) 量化了我们的模型在所有这些指标上的表现,并且被认为是比准确性或 F1 分数更可靠的单一性能指标。 MCC 的范围从 -1 到 1,其中 -1 是最差的性能,1 是最好的性能,0 是随机猜测。 我们可以通过 torchmetrics 将 MCC 与 PyTorch 结合使用。

from torchmetrics.classification import MulticlassMatthewsCorrCoefmcc_metric = MulticlassMatthewsCorrCoef(num_classes=NUM_CLASSES).to(DEVICE)

训练过程是一个基本的 PyTorch 训练循环,在训练和验证之间交替。

我们使用 Adam 优化器和我们的点净损失函数以及上面图 3 中描述的正则化项。对于点净损失函数,我们选择设置 alpha,它对每个样本的重要性进行加权。

我们还设置了 gamma 来调节损失函数并迫使其专注于困难示例,其中困难示例是那些以较低概率分类的示例。 有关更多详细信息,请参阅笔记本中的注释。 人们注意到,使用循环学习率时模型训练得更好,因此我们在这里实现了它。

import torch.optim as optim
from point_net_loss import PointNetLossEPOCHS = 50
LR = 0.0001
REG_WEIGHT = 0.001 # manually downweight the high frequency classes
alpha = np.ones(NUM_CLASSES)
alpha[0] = 0.5  # airplane
alpha[4] = 0.5  # chair
alpha[-1] = 0.5 # tablegamma = 1optimizer = optim.Adam(classifier.parameters(), lr=LR)
scheduler = torch.optim.lr_scheduler.CyclicLR(optimizer, base_lr=0.0001, max_lr=0.01, step_size_up=2000, cycle_momentum=False)
criterion = PointNetLoss(alpha=alpha, gamma=gamma, reg_weight=REG_WEIGHT).to(DEVICE)classifier = classifier.to(DEVICE)

请按照笔记本进行训练循环,并确保你有 GPU。 如果没有,请删除调度程序并将学习率设置为 0.01,几个 epoch 后你应该会得到足够好的结果。 如果遇到任何 PyTorch 用户警告(由于 nn.MaxPool1D 的未来更新),可以通过以下方式抑制它们:

import warnings
warnings.filterwarnings("ignore")

5、训练结果

在这里插入图片描述

我们可以看到,训练和验证的准确率都上升了,但 MCC 仅在训练时上升,而在验证时却没有上升。 这可能是由于验证和测试分组中某些类的样本量非常小造成的; 因此在这种情况下,MCC 可能不是用于验证和测试的最佳单一指标。 这需要更多的调查来确定 MCC 何时是一个好的指标; 即多少不平衡对于 MCC 来说是过多? 每个类别需要多少样本才能使 MCC 有效?

我们来看看测试结果:

在这里插入图片描述

我们看到测试准确度约为 85%,但 MCC 略高于 0。由于我们只有 16 个类,让我们查看笔记本中的混淆矩阵,以更深入地了解测试结果。

在这里插入图片描述

图 6. 测试数据混淆矩阵。 资料来源:作者。

大多数情况下,分类是可以的,但也有一些不太常见的类别,例如“火箭”或“滑板”。 该模型在这些类别上的预测性能往往较差,而在这些不太常见的类别上的性能是导致 MCC 下降的原因。

另一件需要注意的事情是,当你检查结果(如笔记本中所示)时,将在更频繁的分类中获得良好的准确性和自信的表现。 然而,在频率较低的课程中,你会发现置信度较低且准确性较差。

6、检查关键集

现在我们将研究本教程中最有趣的部分,即关键集。 关键集是点云集的基本基础点。 这些点定义了它的基本结构。 这里有一些代码展示了如何可视化它们。

from open3d.web_visualizer import draw critical_points = points[crit_idxs.squeeze(), :]
critical_point_colors = read_pointnet_colors(seg.numpy())[crit_idxs.cpu().squeeze(), :]pcd = o3.geometry.PointCloud()
pcd.points = o3.utility.Vector3dVector(critical_points)
pcd.colors = o3.utility.Vector3dVector(critical_point_colors)# o3.visualization.draw_plotly([pcd])
draw(pcd, point_size=5) # does not work in Colab

这里有一些可视化,请注意,我使用“draw()”来获得更大的点大小,但它在 Colab 中不起作用。

在这里插入图片描述

图 7.点云集及其由PointNet学习的相应关键集

我们可以看到,关键集展示了其对应点云的整体结构,它们本质上是稀疏采样的点云。 这表明训练后的模型实际上已经学会了区分差异结构,并表明它实际上能够根据每个点云类别的区别结构对其进行分类。

7、结束语

我们学习了如何从头开始训练PointNet以及如何可视化点集。 如果你真的感兴趣,请尝试提高整体分类性能。 以下是一些帮助你入门的建议:

  • 使用不同的损失函数
  • 在循环学习率调度程序中尝试不同的设置
  • 尝试对PointNet架构进行修改
  • 尝试不同的数据增强
  • 使用更多数据 → 尝试完整的 shapenet 数据集

原文链接:PointNet分类3D点云 — BimAnt

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/81018.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

wordpress 打开缓慢处理

gravatar.com 头像网站被墙 追踪发现请求头像时长为21秒 解决方案一 不推荐,容易失效,网址要是要稳定为主,宁愿头像显示异常,也不能网址打不开 网上大部分搜索到的替换的CDN网址都过期了,例如:gravatar.du…

知识付费系统开发:构建高效智能的付费内容平台

随着数字化时代的来临,知识付费正迅速崭露头角,为知识创作者和求知者带来了全新的商机。在这个背景下,开发一款高效智能的知识付费系统成为了一项重要的任务。本文将深入探讨如何基于Python编程语言和相关技术构建一个智能的知识付费内容平台…

备份容灾哪家好怎么样

数字化时代,数据安全是我们不容忽视的问题。云呐容灾备份系统不仅提供了强大的数据保护功能,而且操作简单,使用方便。无论你是企业管理员,还是个人用户,都可以轻松上手。它还提供了丰富的报告和监控功能,让…

Unity 实现字幕打字效果

Text文本打字效果,TextMeshPro可以对应参考,差距不大,改改参数名就能用。改脚本原本被我集成到其他的程序集中,现在已经分离。 效果 实现功能 1.能够设置每行能够容纳的字数和允许的冗余 2.打字效果 3.每行打完上移 4.开头进入&…

springboot(1)

精要: 自动配置:针对很多Spring应用程序常见的应用功能,Spring Boot能自动提供相关配置。 起步依赖:告诉Spring Boot需要什么功能,它就能引入需要的库。 命令行界面:这是Spring Boot的可选特性&#xff0…

嵌入式开发学习(STC51-18-LCD液晶显示)

内容 在LCD1602液晶上显示字符信息; LCD1602介绍 简介 1602液晶也叫1602字符型液晶,它能显示2行字符信息,每行又能显示16个字符; 它是一种专门用来显示字母、数字、符号的点阵型液晶模块; 它是由若干个5x7或者5x…

座舱开发的“道”与“术”

前言: 近年来,随着汽车“新四化”浪潮的兴起,软件定义已成为产业共识,将深度参与到整个汽车的定义、开发验证销售以及服务全过程。一方面确保软件可升级,跨车型、软件甚至跨车企软件重用。另一方面对于硬来讲&#xf…

任务 13、MidJourney种子激发极致创作,绘制震撼连贯画作

13.1 任务概述 通过本次实验任务,学员将深入了解Midjourney种子的概念和重要性,以及种子对生成图像的影响。他们将学会在Midjourney平台中设置种子值并调整其参数,以达到所需的效果。此外,任务还详细介绍了Midjourney V4.0版本中…

UNIX网络编程——UDP协议,CS架构

目录 一.socket创建通信的套接字 二.IPv4地址结构 三.通用地址结构 四. 两种地址结构的使用场合 五.sendto发送数据 六.bind固定地址信息​编辑 七.recvfrom接受UDP的消息​编辑 一.socket创建通信的套接字 二.IPv4地址结构 三.通用地址结构 四. 两种地址结构的使用场合…

【Linux】结合Python 简易实现监控公司网站,邮件发送异常

目录 背景 实现思路 邮件4小时内只会发送一次,如果执行了发送邮件的脚本,就使用sed命令将对应的调用代码置为无效 请求脚本 Python邮件发送脚本 定时任务设置 恢复邮件发送能力脚本 资料获取方法 背景 由于一些原因,博主负责测试的网…

入门Echarts数据可视化:从基础到实践

目录 引言数据可视化的重要性Echarts资源与拓展 Echarts简介及开发准备什么是EchartsEcharts的特点与优势安装Echarts引入Echarts库 第一个图表使用Echarts绘制一个简单的柱状图数据准备与图表配置数据格式要求图表标题与标签设置 实践与性能优化提升图表渲染性能的技巧响应式设…

多态总结

什么是多态? 所谓多态,就是同一个操作,作用在了不同的对象上,就会有不同的解释,进而产生不同的执行结果。使用时,是采用父类指针指向子类对象的方法。其中,重载和重写是常见的实现多态的手段。…

深入理解机器学习与极大似然之间的联系

似然函数:事件A的发生含着有许多其它事件的发生。所以我就把这些其它事件发生的联合概率来作为事件A的概率,也就是似然函数。数据类型的不同(离散型和连续性)就有不同的似然函数 极大似然极大似然估计方法(Maximum Li…

sql server 删除指定字符串

replace方法 update #test set FIVCODEreplace(FIVCODE,440,) WHERE SOURCEFENTRYID140728

无数资深果粉称之为 Mac 装机必备软件的 ——CleanMyMac X

它就是被无数资深果粉称之为 Mac 装机必备软件的 ——CleanMyMac X。或许你没用过它,但是大概率你身边一定有它的资深用户,作为 MacPaw 旗下的老牌清理软件,在全球已经拥有超过 2500 万次的下载量。 它有着五大强悍的功能,可以帮…

浅谈智慧消防助力现代社会火灾防控

安科瑞 华楠 摘 要:随着我国经济水平的不断提高,科学技术取得了长足进步。科学技术的进步推动着社会不断前进,改变了各行各业的人们的生活。随着各种新型的技术尤其是人工智能技术的出现,社会进入了智能化时代。消防作为维护我们…

淘宝整店商品如何批量获取?获取淘宝店铺所有商品接口item_search_shop

在竞争日益激烈的电商行业,不少商家出于以下的考虑,想要实现一键批量获取淘宝店铺的所有商品。 竞争分析:通过获取某个店铺内的所有商品信息,可以对竞争对手的产品进行全面的了解和分析。可以了解到对手的产品种类、价格、销量等情…

【福建事业单位-资料分析】01 速算技巧-基期与现期

【福建事业单位-资料分析】01 速算技巧-基期与现期 一、速算技巧(基于选项的速算)1.1 计算类别——截位直除练习总结 二、速算技巧-比较类-分数比较2.1 一大一小(一大/一小)2.2 同大同小①分子分母都变大,保留两位直接…

LVS工作环境配置

一、LVS-DR工作模式配置 模拟环境如下: 1台客户机 1台LVS负载调度器 2台web服务器 1、环境部署 (1)LVS负载调度器 yum install -y ipvsadm # 在LVS负载调度器上进行环境安装 ifconfig ens33:200 192.168.134.200/24 # 配置LVS的VIP…

Idea配置Scala开发环境

1.首先安装scala插件: File--->Setting---->plugins,在输入框中输入scala,然后点击“Install”即可安装scala,需要稍微等待几分钟。 2 创建项目: File ---->new---->project-----Maven--->Next----输入名称(test…