三亚西岛,陕西seo经理,便宜的云服务器租用,软文营销文章500字论文标题#xff1a;PointNet: Deep Learning on Point Sets for 3D Classification and Segmentation标签#xff1a;有监督 | 特征学习、点云分类、语义分割首先回答3个问题作为引子#xff1a;Q1#xff1a;什么是点云#xff1f;简单来说就是一堆三维点的集合#xf…论文标题PointNet: Deep Learning on Point Sets for 3D Classification and Segmentation标签有监督 | 特征学习、点云分类、语义分割首先回答3个问题作为引子Q1什么是点云简单来说就是一堆三维点的集合必须包括各个点的三维坐标信息其他信息比如各个点的法向量、颜色等均是可选。点云的文件格式可以有很多种包括xyznpyplyobjoff等有些是mesh不过问题不大因为mesh可以通过泊松采样等方式转化成点云。对于单个点云如果你使用np.loadtxt得到的实际上就是一个维度为 的张量num_channels一般为3表示点云的三维坐标。这里以horse.xyz文件为例实际就是文本文件打开后数据长这样局部总共有2048个点实际就是一堆点的信息这里只有三维坐标将其可视化出来长这样Q2为什么点云处理任务是重要的三维图形具有多种表现形式包括了mesh、体素、点云等甚至还有些方法使用多视图来对三维图形表征。而点云在以上各种形式的数据中算是日常生活中最能够大规模获取和使用的数据结构了包括自动驾驶、增强现实等在内的应用需要直接或间接从点云中提取信息点云处理也逐渐成为计算机视觉非常重要的一部分。Q3为什么PointNet是重要的这个后面会说直接对点云使用深度学习、解决了点云带来的一系列挑战PointNet应该是开创性的。但我觉得真正让PointNet具备很大影响力的还是它的简洁、高效和强大。首先要说清楚PointNet所作的事情就是对点云做特征学习并将学习到的特征去做不同的应用分类shape-wise feature、分割point-wise feature等。PointNet之所以影响力巨大就是因为它为点云处理提供了一个简单、高效、强大的特征提取器encoder几乎可以应用到点云处理的各个应用中其地位类似于图像领域的AlexNet。1 motivationrelated workVolumetric CNNs对体素应用3DCNN。缺点是点云的坐标空间的稀疏性导致转成体素后的分辨率问题以及3D卷积带来的开销Multiview CNNs将点云或者shape渲染成视图使用传统的图像卷积来做特征学习。这种方法确实取得了不错的效果但是缺点是应用非常局限像分割、补全等任务就不太好做Spectral CNNsfeature-based DNNwhy we want to do this?点云或者mesh大多数研究人员都是将其转化成3D体素或者多视图来做特征学习的这其中的工作包括了VoxelNet, MVCNN等。这些工作都或多或少存在了一些问题上面提到了。直接对点云做特征学习也不是不可以但有几个问题需要考虑特征学习需要对点云中各个点的排列保持不变性、特征学习需要对rigid transformation保持不变性等。虽然有挑战但是深度学习强大的表征能力以及其在图像领域取得的巨大成功因此是很有必要直接在点云上进行尝试的。 2 contribution我们设计了一个新颖的深层网络架构来处理三维中的无序点集我们设计的网络表征可以做三维图形分类、图形的局部分割以及场景的语义分割等任务我们提供了完备的经验和理论分析来证明PointNet的稳定和高效。充分的消融实验证明网络各个部分对于表征的有效性。3 solutionchallenges点云的几个特点无序性 -- 对称函数设计用于表征点不是孤立的需要考虑局部结构 -- 局部全局特征结合仿射变换无关性 -- alignment networkforward propagationPointNet网络结构图网络分成了分类网络和分割网络2个部分大体思路类似都是设计表征的过程分类网络设计global feature分割网络设计point-wise feature两者都是为了让表征尽可能discriminative也就是同类的能分到一类不同类的距离能拉开PointNet设计思路主要有以下3点1 Symmetry Function for Unordered Input要做到对点云点排列不变性有几种思路直接将点云中的点以某种顺序输入比如按照坐标轴从小到大这样为什么不这样做摘自原文in high dimensional space there in fact does not exist an ordering that is stable w.r.t. point perturbations in the general sense.简单来说就是很难找到一种稳定的排序方法2. 作为序列去训练一个RNN即使这个序列是随机排布的RNN也有能力学习到排布不变性。为什么不这样做摘自原文While RNN has relatively good robustness to input ordering for sequences with small length (dozens), it’s hard to scale to thousands of input elements, which is the common size for point sets. RNN很难处理好成千上万长度的这种输入元素比如点云。3. 使用一个简单的对称函数去聚集每个点的信息我们的目标左边 是我们的目标右边 是我们期望设计的对称函数。由上公式可以看出基本思路就是对各个元素即点云中的各个点使用 分别处理在送入对称函数 中处理以实现排列不变性。在实现中 就是MLP 就是max pooling对于以上三种不同的做法作者均作了实验来验证得出第三种方法效果最好2 Local and Global Information Aggregation对于分割任务我们需要point-wise feature因此分割网络和分类网络设计局部略有不同分割网络添加了每个点的local和global特征的拼接过程以此得到同时对局部信息和全局信息感知的point-wise特征提升表征效果。3 alignment network用于实现网络对于仿射变换、刚体变换等变换的无关性直接的思路将所有的输入点集对齐到一个统一的点集空间pn的做法直接预测一个变换矩阵3*3来处理输入点的坐标。因为会有数据增强的操作存在这样做可以在一定程度上保证网络可以学习到变换无关性。特征空间的对齐也可以这么做但是需要注意transformation matrix in the feature space has much higher dimension than the spatial transform matrix, which greatly increases the difficulty of optimization. 对于特征空间的alignment network由于特征空间维度比较高因此直接生成的alignment matrix会维度特别大不好优化因此这里需要加个loss约束一下。add a regularization term to our softmax training loss使得特征空间的变换矩阵A尽可能接近正交矩阵消融实验验证alignment network的效果loss看代码分类中常用的交叉熵alignment network中用于约束生成的alignment matrix的loss4 dataset and experimentsevaluate metric分类分类准确率acc分割mIoUdataset分类ModelNet40分割ShapeNet Part dataset和Stanford 3D semantic parsing datasetexperiments分类局部分割5 code看代码分析PointNet结构观察上图有4个值得关注的点1. 如何对点云使用MLP2. alignment network怎么做的3. 对称函数如何实现来提取global feature的4. loss?针对问题1以分类网络为例整体代码def get_model(point_cloud, is_training, bn_decayNone): Classification PointNet, input is BxNx3, output Bx40 batch_size point_cloud.get_shape()[0].valuenum_point point_cloud.get_shape()[1].valueend_points {}with tf.variable_scope(transform_net1) as sc:transform input_transform_net(point_cloud, is_training, bn_decay, K3)point_cloud_transformed tf.matmul(point_cloud, transform)input_image tf.expand_dims(point_cloud_transformed, -1)net tf_util.conv2d(input_image, 64, [1,3],paddingVALID, stride[1,1],bnTrue, is_trainingis_training,scopeconv1, bn_decaybn_decay)net tf_util.conv2d(net, 64, [1,1],paddingVALID, stride[1,1],bnTrue, is_trainingis_training,scopeconv2, bn_decaybn_decay)with tf.variable_scope(transform_net2) as sc:transform feature_transform_net(net, is_training, bn_decay, K64)end_points[transform] transformnet_transformed tf.matmul(tf.squeeze(net, axis[2]), transform)net_transformed tf.expand_dims(net_transformed, [2])net tf_util.conv2d(net_transformed, 64, [1,1],paddingVALID, stride[1,1],bnTrue, is_trainingis_training,scopeconv3, bn_decaybn_decay)net tf_util.conv2d(net, 128, [1,1],paddingVALID, stride[1,1],bnTrue, is_trainingis_training,scopeconv4, bn_decaybn_decay)net tf_util.conv2d(net, 1024, [1,1],paddingVALID, stride[1,1],bnTrue, is_trainingis_training,scopeconv5, bn_decaybn_decay)# Symmetric function: max poolingnet tf_util.max_pool2d(net, [num_point,1],paddingVALID, scopemaxpool)net tf.reshape(net, [batch_size, -1])net tf_util.fully_connected(net, 512, bnTrue, is_trainingis_training,scopefc1, bn_decaybn_decay)net tf_util.dropout(net, keep_prob0.7, is_trainingis_training,scopedp1)net tf_util.fully_connected(net, 256, bnTrue, is_trainingis_training,scopefc2, bn_decaybn_decay)net tf_util.dropout(net, keep_prob0.7, is_trainingis_training,scopedp2)net tf_util.fully_connected(net, 40, activation_fnNone, scopefc3)return net, end_pointsMLP的核心做法input_image tf.expand_dims(point_cloud_transformed, -1)
net tf_util.conv2d(input_image, 64, [1,3],paddingVALID, stride[1,1],bnTrue, is_trainingis_training,scopeconv1, bn_decaybn_decay)
net tf_util.conv2d(net, 64, [1,1],paddingVALID, stride[1,1],bnTrue, is_trainingis_training,scopeconv2, bn_decaybn_decay)这里input_image维度是 因此将点云看成是W和H分为N和3的2D图像维度是 然后直接基于这个“2D图像”做卷积第一个卷积核size是 正好对应的就是“2D图像”的一行也就是一个点三维坐标输出通道数是64因此输出张量维度应该是 第二个卷积核size是 卷积只改变通道数输出张量维度是 conv2d就是将卷积封装了一下核心部分也就是调用tf.nn.conv2d实现如下def conv2d(inputs,num_output_channels,kernel_size,scope,stride[1, 1],paddingSAME,use_xavierTrue,stddev1e-3,weight_decay0.0,activation_fntf.nn.relu,bnFalse,bn_decayNone,is_trainingNone): 2D convolution with non-linear operation.Args:inputs: 4-D tensor variable BxHxWxCnum_output_channels: intkernel_size: a list of 2 intsscope: stringstride: a list of 2 intspadding: SAME or VALIDuse_xavier: bool, use xavier_initializer if truestddev: float, stddev for truncated_normal initweight_decay: floatactivation_fn: functionbn: bool, whether to use batch normbn_decay: float or float tensor variable in [0,1]is_training: bool Tensor variableReturns:Variable tensorwith tf.variable_scope(scope) as sc:kernel_h, kernel_w kernel_sizenum_in_channels inputs.get_shape()[-1].valuekernel_shape [kernel_h, kernel_w,num_in_channels, num_output_channels]kernel _variable_with_weight_decay(weights,shapekernel_shape,use_xavieruse_xavier,stddevstddev,wdweight_decay)stride_h, stride_w strideoutputs tf.nn.conv2d(inputs, kernel,[1, stride_h, stride_w, 1],paddingpadding)biases _variable_on_cpu(biases, [num_output_channels],tf.constant_initializer(0.0))outputs tf.nn.bias_add(outputs, biases)if bn:outputs batch_norm_for_conv2d(outputs, is_training,bn_decaybn_decay, scopebn)if activation_fn is not None:outputs activation_fn(outputs)return outputs针对问题2这里以input_transform_net为例def input_transform_net(point_cloud, is_training, bn_decayNone, K3): Input (XYZ) Transform Net, input is BxNx3 gray imageReturn:Transformation matrix of size 3xK batch_size point_cloud.get_shape()[0].valuenum_point point_cloud.get_shape()[1].valueinput_image tf.expand_dims(point_cloud, -1)net tf_util.conv2d(input_image, 64, [1,3],paddingVALID, stride[1,1],bnTrue, is_trainingis_training,scopetconv1, bn_decaybn_decay)net tf_util.conv2d(net, 128, [1,1],paddingVALID, stride[1,1],bnTrue, is_trainingis_training,scopetconv2, bn_decaybn_decay)net tf_util.conv2d(net, 1024, [1,1],paddingVALID, stride[1,1],bnTrue, is_trainingis_training,scopetconv3, bn_decaybn_decay)net tf_util.max_pool2d(net, [num_point,1],paddingVALID, scopetmaxpool)net tf.reshape(net, [batch_size, -1])net tf_util.fully_connected(net, 512, bnTrue, is_trainingis_training,scopetfc1, bn_decaybn_decay)net tf_util.fully_connected(net, 256, bnTrue, is_trainingis_training,scopetfc2, bn_decaybn_decay)with tf.variable_scope(transform_XYZ) as sc:assert(K3)weights tf.get_variable(weights, [256, 3*K],initializertf.constant_initializer(0.0),dtypetf.float32)biases tf.get_variable(biases, [3*K],initializertf.constant_initializer(0.0),dtypetf.float32)biases tf.constant([1,0,0,0,1,0,0,0,1], dtypetf.float32)transform tf.matmul(net, weights)transform tf.nn.bias_add(transform, biases)transform tf.reshape(transform, [batch_size, 3, K])return transform实际上前半部分就是通过卷积和max_pooling对batch内各个点云提取global feature再将global feature降到 维度并reshape成 得到transform matrix通过数据增强丰富训练数据集网络确实应该学习到有效的transform matrix用来实现transformation invariance针对问题3max_pooling这个在论文的图中还是代码中都有体现代码甚至直接用注释注明了针对问题4def get_loss(pred, label, end_points, reg_weight0.001): pred: B*NUM_CLASSES,label: B, loss tf.nn.sparse_softmax_cross_entropy_with_logits(logitspred, labelslabel)classify_loss tf.reduce_mean(loss)tf.summary.scalar(classify loss, classify_loss)# Enforce the transformation as orthogonal matrixtransform end_points[transform] # BxKxKK transform.get_shape()[1].valuemat_diff tf.matmul(transform, tf.transpose(transform, perm[0,2,1]))mat_diff - tf.constant(np.eye(K), dtypetf.float32)mat_diff_loss tf.nn.l2_loss(mat_diff) tf.summary.scalar(mat loss, mat_diff_loss)return classify_loss mat_diff_loss * reg_weight无论是分类还是分割本质上都还是分类任务只是粒度不同罢了。因此loss一定有有监督分类任务中常用的交叉熵loss另外loss还有之前alignment network中提到的约束loss也就是上面的mat_diff_loss5 conclusionPointNet之所以影响力巨大并不仅仅是因为它是第一篇更重要的是它的网络很简洁简洁中蕴含了大量的工作来探寻出简洁这条路却非常的work这也就使得它能够成为一个工具一个为点云表征的encoder工具应用到更广阔的点云处理任务中。MLPmax pooling竟然就击败了众多SOTA令人惊讶。另外PointNet在众多细节设计也都进行了理论分析和消融实验验证保证了严谨性这也为PointNet后面能够大规模被应用提供了支持。