IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 人工智能 -> PointNet论文的精简解读 -> 正文阅读

[人工智能]PointNet论文的精简解读

目录

一、论文动机

现有的问题:

作者的思路及面临的问题:

二、论文方法

如何解决点云无序性问题?作者提出了三种想法。

针对点云的刚体运动不变性

三、网络结构

四、代码阅读

五、Reference(两篇都是对原论文的翻译)


一、论文动机

现有的问题:

因为卷积神经网络一般都要求高度规则的输入数据格式,如图像和3D体素,所以现有的方法一般是把点云转换成体素格式或者投影到二维图中。但是由于算力的限制,体素的分辨率最多只能达到32*32*32,而且体素是立方体,对于点云表面的特征都没有表达出来。

作者的思路及面临的问题:

我们能否设计一个神经网络直接对点云的特征进行学习,而不进行转换?

面临两个问题:1.如何解决点云的无序性问题

? ? ? ? ? ? ? ? ? ? ? ? ? 2.如何保证点云的刚体运动不变性

二、论文方法

如何解决点云无序性问题?作者提出了三种想法。

1.对点云排序之后送入——高维空间不存在稳定的排序,因为有点的扰动。

2.将点云作为一个序列来用RNN处理——RNN对于长度较小的序列具有很好的鲁棒性,但点云序列太长了

3.使用对称函数来解决无序性问题,如均值池化,最大池化。实验证明最大池化效果最好。

针对点云的刚体运动不变性

引入两个对齐输入点和点特征的联合对齐网络,在点云进行特征提取之前将所有的输入对齐到规范空间。通过T-Net网络预测一个仿射变换矩阵,与点云进行相乘。

为什么联合对齐网络可以引入刚体运动不变性?

三、网络结构

?原理就是对输入的点云先进行对齐,引入刚体运动不变性,然后通过MLP对特征升维,再对特征进行对齐,然后再MLP升维特征,这时进行最大池化,解决无序性问题,得到1024维的全局特征,如果是分类,则直接对全局特征进行MLP操作,输出k维结果就是分类结果。如果是分割的话,则把全局特征与每个点的特征进行拼接,然后两次MLP得到每个点的分割结果。

四、代码阅读

import torch
import torch.nn as nn
import torch.nn.parallel
import torch.utils.data
from torch.autograd import Variable
import numpy as np
import torch.nn.functional as F

#nn.relu模块调用一般用在init里,而F.relu是函数调用一般用在forward里
#Conv1d和Linear的区别,当你必须保留语义分割中的空间信息时,使用卷积 Conv1d() 。当你不需要做任何与空间信息相关的事情时,比如在基本分类(mnist、猫狗分类器)中,使用线性层 Linear() 
class STN3d(nn.Module):  #第一个T-Net
    def __init__(self, channel):
        super(STN3d, self).__init__()
        self.conv1 = torch.nn.Conv1d(channel, 64, 1)
        self.conv2 = torch.nn.Conv1d(64, 128, 1)
        self.conv3 = torch.nn.Conv1d(128, 1024, 1)
        self.fc1 = nn.Linear(1024, 512)
        self.fc2 = nn.Linear(512, 256)
        self.fc3 = nn.Linear(256, 9)
        self.relu = nn.ReLU()

        self.bn1 = nn.BatchNorm1d(64)
        self.bn2 = nn.BatchNorm1d(128)
        self.bn3 = nn.BatchNorm1d(1024)
        self.bn4 = nn.BatchNorm1d(512)
        self.bn5 = nn.BatchNorm1d(256)

    def forward(self, x):
        batchsize = x.size()[0] #x-(B,C,N)N表示n个点,C表示特征数
        x = F.relu(self.bn1(self.conv1(x)))
        x = F.relu(self.bn2(self.conv2(x)))
        x = F.relu(self.bn3(self.conv3(x))) #(B,1024,N)
        x = torch.max(x, 2, keepdim=True)[0] #只返回最大值的每个数(B,1024,1)
        x = x.view(-1, 1024)

        x = F.relu(self.bn4(self.fc1(x)))
        x = F.relu(self.bn5(self.fc2(x)))
        x = self.fc3(x)  #(B,9)

        iden = Variable(torch.from_numpy(np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]).astype(np.float32))).view(1, 9).repeat(
            batchsize, 1)  #tensor不能反向传播,variable可以反向传播。同时在单位矩阵的基础上进行调整
        if x.is_cuda:
            iden = iden.cuda()
        x = x + iden
        x = x.view(-1, 3, 3)
        return x


class STNkd(nn.Module):   #特征T-Net
    def __init__(self, k=64):
        super(STNkd, self).__init__()
        self.conv1 = torch.nn.Conv1d(k, 64, 1)
        self.conv2 = torch.nn.Conv1d(64, 128, 1)
        self.conv3 = torch.nn.Conv1d(128, 1024, 1)
        self.fc1 = nn.Linear(1024, 512)
        self.fc2 = nn.Linear(512, 256)
        self.fc3 = nn.Linear(256, k * k)
        self.relu = nn.ReLU()

        self.bn1 = nn.BatchNorm1d(64)
        self.bn2 = nn.BatchNorm1d(128)
        self.bn3 = nn.BatchNorm1d(1024)
        self.bn4 = nn.BatchNorm1d(512)
        self.bn5 = nn.BatchNorm1d(256)

        self.k = k

    def forward(self, x):
        batchsize = x.size()[0]
        x = F.relu(self.bn1(self.conv1(x)))
        x = F.relu(self.bn2(self.conv2(x)))
        x = F.relu(self.bn3(self.conv3(x)))
        x = torch.max(x, 2, keepdim=True)[0]
        x = x.view(-1, 1024)

        x = F.relu(self.bn4(self.fc1(x)))
        x = F.relu(self.bn5(self.fc2(x)))
        x = self.fc3(x)

        iden = Variable(torch.from_numpy(np.eye(self.k).flatten().astype(np.float32))).view(1, self.k * self.k).repeat(
            batchsize, 1)
        if x.is_cuda:
            iden = iden.cuda()
        x = x + iden
        x = x.view(-1, self.k, self.k)
        return x


class PointNetEncoder(nn.Module):
    def __init__(self, global_feat=True, feature_transform=False, channel=3):
        super(PointNetEncoder, self).__init__()
        self.stn = STN3d(channel)
        self.conv1 = torch.nn.Conv1d(channel, 64, 1)
        self.conv2 = torch.nn.Conv1d(64, 128, 1)
        self.conv3 = torch.nn.Conv1d(128, 1024, 1)
        self.bn1 = nn.BatchNorm1d(64)
        self.bn2 = nn.BatchNorm1d(128)
        self.bn3 = nn.BatchNorm1d(1024)
        self.global_feat = global_feat
        self.feature_transform = feature_transform
        if self.feature_transform:
            self.fstn = STNkd(k=64)

    def forward(self, x):
        B, D, N = x.size()
        trans = self.stn(x)
        x = x.transpose(2, 1) #交换矩阵的两个维度,也可以torch.transpose
        if D > 3:
            feature = x[:, :, 3:]
            x = x[:, :, :3]
        x = torch.bmm(x, trans) #计算两个tensor的矩阵乘法,torch.bmm(a,b),tensor a 的size为(b,h,w),tensor b的size为(b,w,m) 也就是说两个tensor的第一维是相等的,然后第一个数组的第三维和第二个数组的第二维度要求一样,对于剩下的则不做要求,输出维度 (b,h,m)
        if D > 3:
            x = torch.cat([x, feature], dim=2)
        x = x.transpose(2, 1)
        x = F.relu(self.bn1(self.conv1(x)))  #(B,64,N)

        if self.feature_transform:
            trans_feat = self.fstn(x)
            x = x.transpose(2, 1)
            x = torch.bmm(x, trans_feat)
            x = x.transpose(2, 1)
        else:
            trans_feat = None

        pointfeat = x  #这里记录一下点的特征,后面分割用
        x = F.relu(self.bn2(self.conv2(x))) #(B,128,N)
        x = self.bn3(self.conv3(x))   #(B,1024,N)
        x = torch.max(x, 2, keepdim=True)[0]
        x = x.view(-1, 1024)   #(B,1024)
        if self.global_feat:
            return x, trans, trans_feat
        else:
            x = x.view(-1, 1024, 1).repeat(1, 1, N)   #(B,1024,N)
            return torch.cat([x, pointfeat], 1), trans, trans_feat  #如果有分割,则返回合并后的特征

##对齐特征的时候,由于特征空间维度更高,优化难度大,所以加了一项正则项,让求解出来的仿射变换矩阵接近于正交,这里返回的在后面损失函数会用到
#扩充维度可以先view扩充一维,再repeat
def feature_transform_reguliarzer(trans):
    d = trans.size()[1]
    I = torch.eye(d)[None, :, :]
    if trans.is_cuda:
        I = I.cuda()
    loss = torch.mean(torch.norm(torch.bmm(trans, trans.transpose(2, 1)) - I, dim=(1, 2))) #torch.norm默认求所有元素的平方和,dim指定维度,是在一二维求,得到(B,loss)再求平均 
    return loss

#损失函数nn.CrossEntropyLoss()=F.log_softmax() + F.nll_loss()

五、Reference(两篇都是对原论文的翻译)

(3条消息) 【论文翻译】从零开始PointNet论文分析与代码复现_花花大魔王的博客-CSDN博客

PointNet 文献阅读及拓展阅读_万俟淋曦的博客-CSDN博客

  人工智能 最新文章
2022吴恩达机器学习课程——第二课(神经网
第十五章 规则学习
FixMatch: Simplifying Semi-Supervised Le
数据挖掘Java——Kmeans算法的实现
大脑皮层的分割方法
【翻译】GPT-3是如何工作的
论文笔记:TEACHTEXT: CrossModal Generaliz
python从零学(六)
详解Python 3.x 导入(import)
【答读者问27】backtrader不支持最新版本的
上一篇文章      下一篇文章      查看所有文章
加:2022-10-17 12:33:42  更:2022-10-17 12:37:56 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 20:15:11-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码