2017年,Charles Qi等人在CVPR上发表了PointNet,开创了深度学习直接处理三维点云的先河。本文将从第一性原理出发,深入剖析PointNet的数学基础,包括置换不变性、对称函数、T-Net空间变换,以及完整的架构设计与损失函数推导。
目录
- 三维点云与深度学习的挑战
- PointNet的核心洞察
- 数学基础:置换不变性与对称函数
- T-Net:三维空间变换网络
- PointNet架构详解
- 损失函数与数学推导
- PointNet++:层次化点云学习
- 代码实现与实践
- 应用场景与前沿发展
三维点云与深度学习的挑战
三维数据的本质特征
与二维图像不同,三维点云具有独特的数学特性:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌─────────────────────────────────────────────────────────────┐
│ 三维点云的本质特征 │
├─────────────────────────────────────────────────────────────┤
│ 1. 置换不变性(Permutation Invariance) │
│ 点云的输入顺序不影响输出含义 │
│ {p₁, p₂, p₃} ≡ {p₃, p₁, p₂} ≡ {p₂, p₃, p₁} │
├─────────────────────────────────────────────────────────────┤
│ 2. 不规则性(Irregularity) │
│ 点没有规则网格结构,密度不均匀 │
├─────────────────────────────────────────────────────────────┤
│ 3. 置换不变性(Poincaré Invariance) │
│ 刚体变换下保持几何特性 │
├─────────────────────────────────────────────────────────────┤
│ 4. 连续性(Continuity) │
│ 相邻点通常属于同一表面或物体 │
└─────────────────────────────────────────────────────────────┘
传统方法的局限性
在PointNet之前,处理三维数据的主流方法:
| 方法 | 输入格式 | 核心操作 | 局限性 |
|---|---|---|---|
| 多视图CNN | 多个2D渲染图 | 2D卷积 | 信息丢失、视图选择问题 |
| 体素化 | 3D占用网格 | 3D卷积 | 内存爆炸、稀疏性浪费 |
| 深度图 | 2.5D深度图 | 2D卷积 | 仅前表面、遮挡问题 |
| 网格 | 多边形网格 | 图卷积 | 拓扑依赖、拓扑变化敏感 |
核心问题:这些方法都涉及某种”规则化”过程,将不规则的点云强制转换为规则格式,导致信息丢失或计算效率低下。
PointNet的核心洞察
关键突破
PointNet的核心思想简洁而深刻:直接处理原始点云,无需任何预处理。
1
2
3
4
5
6
7
8
9
传统方法流程:
点云 → 体素化/多视图投影 → 规则化表示 → CNN → 分类/分割
↓
信息损失×计算冗余
PointNet流程:
点云 → 共享MLP → 对称函数聚合 → 特征提取 → 分类/分割
↓
保持置换不变性
三个里程碑式贡献
- 统一框架:同时支持分类、语义分割、目标检测
- 理论保证:证明了对连续集合函数的逼近能力
- 效率革命:$O(n)$ 复杂度 vs 体素化的 $O(n^3)$
数学基础:置换不变性与对称函数
置换群的数学表述
点云可以表示为 $N$ 个点的集合 ${\mathbf{p}_1, \mathbf{p}_2, \ldots, \mathbf{p}_N}$,其中每个点 $\mathbf{p}_i \in \mathbb{R}^D$(通常 $D=3$ 或 $D=6$ 包含法向量)。
对称函数满足:
\[f(\{\mathbf{p}_1, \ldots, \mathbf{p}_N\}) = f(\{\mathbf{p}_{\sigma(1)}, \ldots, \mathbf{p}_{\sigma(N)}\})\]对所有排列 $\sigma \in S_N$ 成立,其中 $S_N$ 是 $N$ 个元素的对称群。
Stone-Weierstrass定理与对称函数逼近
Stone-Weierstrass定理(推广到点集):
任何定义在紧致集合上的连续函数,都可以被多项式函数一致逼近。
PointNet的核心定理:
设 $f: \mathcal{X} \to \mathbb{R}$ 是定义在点云空间上的连续函数,则对于任意 $\epsilon > 0$,存在一个PointNet型函数 $f_N$ 使得:
\[|f(\{\mathbf{p}_1, \ldots, \mathbf{p}_N\}) - f_N(\{\mathbf{p}_1, \ldots, \mathbf{p}_N\})| < \epsilon\]对所有满足某些正则性条件的点云成立。
对称函数的构建:Max Pooling
PointNet使用对称函数聚合来保证置换不变性:
\[\mathbf{h}^{(l)}_i = \text{MLP}^{(l)}(\mathbf{p}_i)\] \[\mathbf{g}(\{\mathbf{h}_1, \ldots, \mathbf{h}_N\}) = \max_{i=1,\ldots,N} \mathbf{h}_i\] \[\mathbf{f} = \gamma(\mathbf{g}(\{\mathbf{h}_1, \ldots, \mathbf{h}_N\}))\]其中:
- $\text{MLP}$:多层感知机(共享权重)
- $\max$:逐元素取最大值
- $\gamma$:另一个MLP
Max Pooling的数学性质
性质1:置换不变性
\[\max(\{x_1, \ldots, x_N\}) = \max(\{x_{\sigma(1)}, \ldots, x_{\sigma(N)}\})\]性质2:信息保持
\[\max_i x_i \geq x_j, \quad \forall j\]最大值”记住”了所有输入中的最大值信息。
性质3:计算效率
\[\text{计算复杂度:} \quad O(N \cdot d)\]其中 $d$ 是特征维度。这与点数量线性相关!
对称函数的其他选择
| 对称函数 | 表达式 | 优点 | 缺点 |
|---|---|---|---|
| Max Pooling | $\max_i \mathbf{h}_i$ | 高效、信息聚焦 | 丢失其他点信息 |
| Mean Pooling | $\frac{1}{N}\sum_i \mathbf{h}_i$ | 平滑、信息完整 | 对极端值不敏感 |
| Sum Pooling | $\sum_i \mathbf{h}_i$ | 可导、梯度丰富 | 可能溢出 |
| Attention | $\sum_i \alpha_i \mathbf{h}_i$ | 自适应权重 | 计算复杂 |
实验结论:Max Pooling在实际任务中表现最好,因为它能够”选择”最有判别力的特征。
T-Net:三维空间变换网络
空间变换的数学框架
T-Net(Transform Net)的灵感来自Jaderberg等人的空间变换网络(STN),但应用于三维空间。
刚体变换的数学表示:
\[\mathbf{p}' = \mathbf{R}\mathbf{p} + \mathbf{t}\]其中:
- $\mathbf{R} \in \mathbb{R}^{D \times D}$:旋转矩阵(正交,$\mathbf{R}^T\mathbf{R} = \mathbf{I}$)
- $\mathbf{t} \in \mathbb{R}^D$:平移向量
- 行列式 $\det(\mathbf{R}) = 1$(保向变换)
T-Net的架构设计
1
2
3
4
5
6
7
8
9
输入点云特征 (N × 64)
↓
MLP(64, 128, 1024) ← 共享权重的多层感知机
↓
Max Pooling ← 获取全局特征
↓
MLP(512, 256, D×D) ← 输出变换矩阵
↓
矩阵重塑
关键约束:
- 正交约束:$\mathbf{R}^T\mathbf{R} = \mathbf{I}$
直接施加这个约束很困难。PointNet使用奇异值分解(SVD):
\[\mathbf{U}, \mathbf{S}, \mathbf{V} = \text{SVD}(\mathbf{M})\] \[\mathbf{R} = \mathbf{U}\mathbf{V}^T\]这保证了 $\mathbf{R}$ 是正交矩阵。
- 行列式约束:$\det(\mathbf{R}) = 1$
如果 $\det(\mathbf{U}\mathbf{V}^T) < 0$,则取 $\mathbf{V}^T$ 最后一行的符号。
T-Net的数学推导
输入特征:
\[\mathbf{F} = \{\mathbf{f}_1, \mathbf{f}_2, \ldots, \mathbf{f}_N\} \in \mathbb{R}^{N \times K}\]其中 $K$ 是输入特征维度。
全局特征:
\[\mathbf{g} = \max_{i=1,\ldots,N} \mathbf{f}_i \in \mathbb{R}^K\]变换矩阵回归:
\[\mathbf{M} = \text{MLP}(\mathbf{g}) \in \mathbb{R}^{K \times K}\]正交化:
\(\mathbf{M} = \mathbf{U}\mathbf{S}\mathbf{V}^T\) \(\mathbf{R} = \mathbf{U}\mathbf{V}^T\)
变换应用:
\[\mathbf{F}' = \mathbf{F}\mathbf{R}\]T-Net的物理意义
1
2
3
4
5
6
7
原始点云 对齐后点云
○ ○ ○ ○
○ ○ ○ ○ ○ ○ ○ ○
○ ○ ○ ○
方向随机 规范化方向
(变换前) (T-Net变换后)
T-Net学习将输入点云”规范化”到标准姿态,消除旋转和平移的影响,使网络专注于学习几何特征。
PointNet架构详解
分类网络
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
┌────────────────────────────────────────────────────────────────┐
│ PointNet 分类网络 │
├────────────────────────────────────────────────────────────────┤
│ │
│ 输入点云 (N × 3) │
│ ↓ │
│ T-Net (3×3) ──→ 刚体变换 │
│ ↓ │
│ MLP(64, 64, 64) ──→ 逐点特征 (N × 64) │
│ ↓ │
│ T-Net (64×64) ──→ 特征空间变换 │
│ ↓ │
│ MLP(64, 128, 1024) ──→ 逐点高维特征 (N × 1024) │
│ ↓ │
│ Max Pooling ──→ 全局特征 (1024) │
│ ↓ │
│ MLP(512, 256, K) ──→ K类分类得分 │
│ │
└────────────────────────────────────────────────────────────────┘
各层详细计算:
| 层 | 输入形状 | 输出形状 | 操作 |
|---|---|---|---|
| Input | (N, 3) | (N, 3) | 原始坐标 |
| T-Net1 | (N, 3) | (N, 3) | $\mathbf{p}’ = \mathbf{R}_1\mathbf{p} + \mathbf{t}_1$ |
| MLP1 | (N, 3) | (N, 64) | Conv1d(3→64) + ReLU |
| MLP2 | (N, 64) | (N, 64) | Conv1d(64→64) + ReLU |
| MLP3 | (N, 64) | (N, 64) | Conv1d(64→64) + ReLU |
| MLP4 | (N, 64) | (N, 128) | Conv1d(64→128) + ReLU |
| MLP5 | (N, 128) | (N, 1024) | Conv1d(128→1024) |
| MaxPool | (N, 1024) | (1, 1024) | 逐元素最大值 |
| FC | (1024) | (512) | 全连接 + ReLU |
| FC | (512) | (256) | 全连接 + ReLU |
| FC | (256) | K | 全连接(K类) |
语义分割网络
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
┌────────────────────────────────────────────────────────────────┐
│ PointNet 分割网络 │
├────────────────────────────────────────────────────────────────┤
│ │
│ 输入点云 (N × 3) │
│ ↓ │
│ (分类网络的编码器部分) │
│ ↓ │
│ 逐点特征 (N × 1024) │
│ ↓ │
│ [全局特征] ──→ 拼接 ──→ MLP(1088, 512, 256, 128) │
│ ↓ │
│ 输出分数 (N × M) ← M类语义分割 │
│ │
└────────────────────────────────────────────────────────────────┘
关键创新:全局与局部特征融合
\[\mathbf{f}_{\text{fused}} = [\mathbf{f}_{\text{local}}; \mathbf{g}]\]其中:
- $\mathbf{f}_{\text{local}} \in \mathbb{R}^{1024}$:点的局部特征
- $\mathbf{g} \in \mathbb{R}^{1024}$:全局特征(来自Max Pooling)
拼接后:$\mathbf{f}_{\text{fused}} \in \mathbb{R}^{2048}$
理论分析:Critical Points与Upper Hull
PointNet论文的一个重要理论贡献是识别对分类贡献最大的点。
关键点集(Critical Points):
\[\mathcal{S} = \{\mathbf{p} \in \mathcal{S}_{\text{input}} : f(\mathbf{p}) \neq f(\mathcal{S}_{\text{input}} \setminus \{\mathbf{p}\})\}\]这些点的移除会改变网络的输出。
上确界壳(Upper Hull):
\[\text{UpperHull}(\mathcal{S}) = \{\mathbf{p} : \mathbf{p} \text{ 是 } \mathcal{S} \text{ 的凸包顶点}\}\]定理:对于连续函数 $f$,存在一个关键点集 $\mathcal{S}$,使得:
-
$ \mathcal{S} $ 的上界与函数的复杂度相关 - $f$ 可以由 $\mathcal{S}$ 唯一确定
这从数学上解释了为什么PointNet只需要少量关键点就能表示复杂的几何形状。
损失函数与数学推导
分类损失:交叉熵
对于 $K$ 分类问题,输出是概率分布 $\hat{\mathbf{y}} = [\hat{y}_1, \ldots, \hat{y}_K]$,真实标签是独热编码 $\mathbf{y} = [y_1, \ldots, y_K]$。
交叉熵损失:
\[L_{\text{cls}} = -\sum_{c=1}^{K} y_c \log \hat{y}_c\]其中 $\hat{y}c = \frac{e^{z_c}}{\sum{j=1}^{K} e^{z_j}}$(Softmax输出)。
梯度计算:
\[\frac{\partial L_{\text{cls}}}{\partial z_c} = \hat{y}_c - y_c\]这个简洁的结果使得反向传播非常高效。
分割损失:逐点交叉熵
对于语义分割,每个点都需要分类:
\[L_{\text{seg}} = -\frac{1}{N}\sum_{i=1}^{N} \sum_{c=1}^{M} y_{ic} \log \hat{y}_{ic}\]其中 $M$ 是语义类别数。
形状补全损失:Chamfer距离
用于从部分点云重建完整形状。
Chamfer距离(CD):
\[d_{\text{CD}}(S_1, S_2) = \frac{1}{|S_1|}\sum_{\mathbf{x} \in S_1} \min_{\mathbf{y} \in S_2} \|\mathbf{x} - \mathbf{y}\|_2^2 + \frac{1}{|S_2|}\sum_{\mathbf{y} \in S_2} \min_{\mathbf{x} \in S_1} \|\mathbf{x} - \mathbf{y}\|_2^2\]第一项:$S_1$ 中每个点到 $S_2$ 的最近距离平均 第二项:$S_2$ 中每个点到 $S_1$ 的最近距离平均
性质:
- 对称性:$d_{\text{CD}}(S_1, S_2) = d_{\text{CD}}(S_2, S_1)$
- 平衡性:两个方向的距离都考虑
地球移动距离(EMD)
定义:
\[d_{\text{EMD}}(S_1, S_2) = \min_{\phi: S_1 \to S_2} \sum_{\mathbf{x} \in S_1} \|\mathbf{x} - \phi(\mathbf{x})\|_2\]其中 $\phi$ 是双射(一一对应)。
直观理解:将一个点云”搬运”成另一个点云的最小总距离。
对比CD与EMD:
| 性质 | Chamfer距离 | EMD |
|---|---|---|
| 计算 | 高效(可并行) | 较慢(需要匹配) |
| 对称性 | ✓ | ✓ |
| 鲁棒性 | 对噪声敏感 | 更稳定 |
| 稀疏点云 | 可能有问题 | 更好 |
正则化损失:T-Net约束
正交正则化:
\[L_{\text{ortho}} = \|\mathbf{R}^T\mathbf{R} - \mathbf{I}\|_F^2\]其中 $|\cdot|_F$ 是Frobenius范数。
完整损失函数:
\[L_{\text{total}} = L_{\text{task}} + \lambda_1 L_{\text{ortho}}^{(1)} + \lambda_2 L_{\text{ortho}}^{(2)}\]其中 $\lambda_1, \lambda_2$ 是正则化权重(通常取0.001)。
PointNet++:层次化点云学习
核心思想
PointNet的局限性:无法捕捉局部几何结构。
解决方案:层次化采样 + 局部特征聚合。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
┌─────────────────────────────────────────────────────────────────┐
│ PointNet++ 层次化结构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 输入点云 (N × 3) │
│ ↓ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Sampling + Grouping + PointNet → 局部特征 (N₁ × C₁) │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Sampling + Grouping + PointNet → 局部特征 (N₂ × C₂) │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Global PointNet → 全局特征 │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ↓ │
│ 分类/分割 │
│ │
└─────────────────────────────────────────────────────────────────┘
采样策略:最远点采样(FPS)
Farthest Point Sampling算法:
1
2
3
4
5
6
7
8
9
输入:点集 P = {p₁, ..., pN},目标数量 K
输出:采样点索引 {i₁, ..., iK}
1. 随机选择第一个点:i₁ ← argmax_j ||p_j|| (或随机)
2. 对于 k = 2 到 K:
对于每个未采样点 p_j:
d_j ← min_{i ∈ {i₁, ..., i_{k-1}}} ||p_j - p_i||
选择距离最大的点:i_k ← argmax_j d_j
3. 返回 {i₁, ..., iK}
数学性质:
- 时间复杂度:$O(NK)$(使用球树可优化到 $O(N \log K)$)
- 覆盖性:保证采样点均匀分布
分组策略:球查询(Ball Query)
对于每个采样点 $c_i$,查询半径 $r$ 内的 $K$ 个最近邻:
\[\mathcal{N}(c_i) = \{\mathbf{p}_j : \|\mathbf{p}_j - \mathbf{c}_i\|_2 < r\}\]特点:
- 保证局部性(球形区域)
- 点数量可变(通过K限制最大数量)
Set Abstraction模块
1
2
3
4
5
6
7
8
9
10
输入:点集 P (N × C_in)
1. Sampling: 使用FPS选择 N' 个中心点 {c₁, ..., c_{N'}}
2. Grouping: 对每个中心点 c_i,找到邻域点 N(c_i)
3. PointNet: 对每个邻域 N(c_i) 应用PointNet:
- 相对坐标归一化:p_j' = p_j - c_i
- 拼接特征:(p_j', f_j) 或仅f_j
- 输出局部特征 f_i'
输出:聚合后的点集 {f'_₁, ..., f'_{N'}} (N' × C_out)
关键洞察:相对坐标的重要性
\[\mathbf{p}_j^{\text{norm}} = \mathbf{p}_j - \mathbf{c}_i\]这个归一化使得特征学习平移不变——无论物体在空间中的什么位置,局部几何关系保持不变。
代码实现与实践
核心模块实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import torch
import torch.nn as nn
import torch.nn.functional as F
class TNet(nn.Module):
"""T-Net: 空间变换网络"""
def __init__(self, k=3):
super(TNet, self).__init__()
self.k = k
# 共享MLP层
self.conv1 = nn.Conv1d(k, 64, 1)
self.conv2 = nn.Conv1d(64, 128, 1)
self.conv3 = nn.Conv1d(128, 1024, 1)
# 全连接层
self.fc1 = nn.Linear(1024, 512)
self.fc2 = nn.Linear(512, 256)
self.fc3 = nn.Linear(256, k * k)
def forward(self, x):
# x: (B, 3, N)
batch_size = x.size(0)
# 共享MLP
x = F.relu(self.conv1(x))
x = F.relu(self.conv2(x))
x = F.relu(self.conv3(x))
# Max Pooling
x = torch.max(x, 2)[0] # (B, 1024)
# 全连接层
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
# 初始化为单位矩阵
identity = torch.eye(self.k, dtype=torch.float32, device=x.device)
identity = identity.view(1, self.k * self.k).repeat(batch_size, 1)
# 变换矩阵
transform = x + identity
transform = transform.view(-1, self.k, self.k)
return transform
class PointNetEncoder(nn.Module):
"""PointNet编码器"""
def __init__(self, global_feat=True, feature_transform=False, channel=3):
super(PointNetEncoder, self).__init__()
self.global_feat = global_feat
self.feature_transform = feature_transform
# 空间变换
self.stn = TNet(k=channel)
# 第一个MLP
self.conv1 = nn.Conv1d(channel, 64, 1)
self.conv2 = nn.Conv1d(64, 64, 1)
# 特征空间变换
self.stn_feature = TNet(k=64)
# 第二个MLP
self.conv3 = nn.Conv1d(64, 64, 1)
self.conv4 = nn.Conv1d(64, 128, 1)
self.conv5 = nn.Conv1d(128, 1024, 1)
# 特征变换正则化
self.feature_transform_reg = 0
def forward(self, x):
# x: (B, C, N)
B, D, N = x.size()
# 输入空间变换
trans = self.stn(x)
x = x.transpose(2, 1)
x = torch.bmm(x, trans)
x = x.transpose(2, 1)
# 第一个MLP
x = F.relu(self.conv1(x))
x = F.relu(self.conv2(x))
# 特征空间变换
trans_feat = self.stn_feature(x)
x = x.transpose(2, 1)
x = torch.bmm(x, trans_feat)
x = x.transpose(2, 1)
# 存储特征用于正则化
if self.feature_transform:
self.feature_transform_reg = feature_transform_loss(trans_feat)
# 第二个MLP
x = F.relu(self.conv3(x))
x = F.relu(self.conv4(x))
x = self.conv5(x)
# Max Pooling
x = torch.max(x, 2)[0] # (B, 1024)
if self.global_feat:
return x, trans
else:
x = x.view(-1, 1024, 1).repeat(1, 1, N)
return torch.cat([x, x], 1), trans
def feature_transform_loss(feat_trans):
"""特征变换的正交正则化损失"""
d = feat_trans.size(1)
I = torch.eye(d, device=feat_trans.device)[None, :, :]
loss = torch.mean(torch.norm(torch.bmm(feat_trans, feat_trans.transpose(2, 1)) - I, dim=(1, 2)))
return loss
class PointNet(nn.Module):
"""完整的PointNet分类网络"""
def __init__(self, k=40, feature_transform=False):
super(PointNet, self).__init__()
self.feature_transform = feature_transform
self.encoder = PointNetEncoder(
global_feat=True,
feature_transform=feature_transform,
channel=3
)
# 全连接层
self.fc1 = nn.Linear(1024, 512)
self.fc2 = nn.Linear(512, 256)
self.fc3 = nn.Linear(256, k)
self.dropout = nn.Dropout(p=0.3)
self.bn1 = nn.BatchNorm1d(512)
self.bn2 = nn.BatchNorm1d(256)
def forward(self, x):
x, trans = self.encoder(x)
x = F.relu(self.bn1(self.fc1(x)))
x = self.dropout(x)
x = F.relu(self.bn2(self.fc2(x)))
x = self.dropout(x)
x = self.fc3(x)
x = F.log_softmax(x, dim=1)
return x, trans
语义分割扩展
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class PointNetDenseCls(nn.Module):
"""PointNet语义分割网络"""
def __init__(self, k=2, feature_transform=False):
super(PointNetDenseCls, self).__init__()
self.k = k
self.encoder = PointNetEncoder(
global_feat=False,
feature_transform=feature_transform,
channel=3
)
# 融合全局和局部特征的MLP
self.conv1 = nn.Conv1d(1088, 512, 1)
self.conv2 = nn.Conv1d(512, 256, 1)
self.conv3 = nn.Conv1d(256, 128, 1)
self.conv4 = nn.Conv1d(128, self.k, 1)
def forward(self, x):
# x: (B, 3, N)
B, D, N = x.size()
# 编码器输出:局部特征和全局特征
x, trans = self.encoder(x) # x: (B, 1088, N)
x = F.relu(self.conv1(x))
x = F.relu(self.conv2(x))
x = F.relu(self.conv3(x))
x = self.conv4(x)
x = x.transpose(2, 1).contiguous() # (B, N, K)
x = F.log_softmax(x.view(-1, self.k), dim=-1)
x = x.view(B, N, self.k) # (B, N, K)
return x, trans
应用场景与前沿发展
PointNet的应用领域
| 应用场景 | 任务类型 | 典型数据集 |
|---|---|---|
| 自动驾驶 | 3D目标检测 | KITTI, Waymo |
| 机器人抓取 | 姿态估计 | YCB |
| 室内导航 | 语义分割 | S3DIS, ScanNet |
| 工业检测 | 缺陷检测 | 自定义 |
| 医学影像 | 器官分割 | LiTS |
从PointNet到现代3D深度学习
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
┌─────────────────────────────────────────────────────────────────┐
│ 3D深度学习演进图谱 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 2017 PointNet ──┬─ PointNet++ (2017) │
│ │ ↓ │
│ │ VoteNet (2019) ──→ 目标检测 │
│ │ ↓ │
│ │ PAConv, PointMLP │
│ │ │
│ ┌───────────────┴─────────────────────────────────────────────┐ │
│ │ │ │
│ │ 2020+ 基于体素的改进 │ │
│ │ ↓ │ │
│ │ SparseConvNet → MinkowskiNet │ │
│ │ ↓ │ │
│ │ 3D U-Net, UNet3D │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ ↓ │
│ 2023+ Transformer │
│ ↓ │
│ Point Transformer, Point Cloud Transformer │
│ Swin3D, Voxel Transformer │
│ │
└─────────────────────────────────────────────────────────────────┘
PointNet的局限性
| 局限性 | 原因 | 解决方案 |
|---|---|---|
| 局部结构捕捉不足 | 单层MLP + Max Pooling | PointNet++层次化 |
| 对噪声敏感 | 点的独立处理 | 加入图结构 |
| 旋转敏感性 | 刚体变换未对齐 | 增强数据augmentation |
| 推理速度 | 共享MLP的冗余 | PointMLP高效变体 |
小结:PointNet的数学本质
| 核心概念 | 数学表述 | 意义 |
|---|---|---|
| 置换不变性 | $f({\mathbf{p}1,…\mathbf{p}_N}) = f({\mathbf{p}{\sigma(1)},…})$ | 点序无关 |
| 对称函数 | $g = \max_i \text{MLP}(\mathbf{p}_i)$ | 聚合不变特征 |
| 空间变换 | $\mathbf{p}’ = \mathbf{R}\mathbf{p} + \mathbf{t}$ | 几何归一化 |
| 全局特征 | $\mathbf{g} = \max_i \mathbf{h}_i$ | 形状表示 |
| 局部-全局融合 | $\mathbf{f}{\text{fused}} = [\mathbf{f}{\text{local}}; \mathbf{g}]$ | 细粒度分割 |
PointNet的成功在于它直面三维数据的本质特征,用简洁的数学工具(对称函数、空间变换)解决了核心问题。这种”第一性原理”的思维方式,值得所有深度学习研究者学习。
参考论文:
- Qi et al. “PointNet: Deep Learning on Point Sets for 3D Classification and Segmentation”, CVPR 2017
- Qi et al. “PointNet++: Deep Hierarchical Feature Learning on Point Sets in a Metric Space”, NeurIPS 2017
- Jaderberg et al. “Spatial Transformer Networks”, NeurIPS 2015