openpoints 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- openpoints/__init__.py +3 -0
- openpoints/cpp/__init__.py +6 -0
- openpoints/cpp/chamfer_dist/__init__.py +110 -0
- openpoints/cpp/chamfer_dist/setup.py +19 -0
- openpoints/cpp/chamfer_dist/test.py +34 -0
- openpoints/cpp/emd/__init__.py +3 -0
- openpoints/cpp/emd/emd.py +88 -0
- openpoints/cpp/emd/setup.py +27 -0
- openpoints/cpp/emd/test_emd_loss.py +45 -0
- openpoints/cpp/pointnet2_batch/__init__.py +24 -0
- openpoints/cpp/pointnet2_batch/setup.py +23 -0
- openpoints/cpp/pointops/__init__.py +0 -0
- openpoints/cpp/pointops/functions/__init__.py +0 -0
- openpoints/cpp/pointops/functions/pointops.py +314 -0
- openpoints/cpp/pointops/setup.py +37 -0
- openpoints/cpp/pointops/src/__init__.py +0 -0
- openpoints/dataset/__init__.py +10 -0
- openpoints/dataset/atom3d/__init__.py +1 -0
- openpoints/dataset/atom3d/psr.py +38 -0
- openpoints/dataset/build.py +98 -0
- openpoints/dataset/data_util.py +192 -0
- openpoints/dataset/datalist.py +67 -0
- openpoints/dataset/dataset_base.py +96 -0
- openpoints/dataset/graph_dataset/__init__.py +3 -0
- openpoints/dataset/graph_dataset/graph_dataset.py +93 -0
- openpoints/dataset/graph_dataset/stack_with_pad.py +91 -0
- openpoints/dataset/graph_dataset/structural_dataset.py +73 -0
- openpoints/dataset/graph_dataset/svd_encodings_dataset.py +110 -0
- openpoints/dataset/grid_sample.py +21 -0
- openpoints/dataset/matterport3d/__init__.py +1 -0
- openpoints/dataset/matterport3d/category_mapping.tsv +1660 -0
- openpoints/dataset/matterport3d/matterport3d.py +210 -0
- openpoints/dataset/matterport3d/matterport3d_dataprocessing.py +105 -0
- openpoints/dataset/modelnet/__init__.py +3 -0
- openpoints/dataset/modelnet/modelnet40_normal_resampled_loader.py +124 -0
- openpoints/dataset/modelnet/modelnet40_ply_2048_loader.py +160 -0
- openpoints/dataset/molhiv/__init__.py +1 -0
- openpoints/dataset/molhiv/data.py +59 -0
- openpoints/dataset/molpcba/__init__.py +1 -0
- openpoints/dataset/molpcba/data.py +59 -0
- openpoints/dataset/parsers/__init__.py +1 -0
- openpoints/dataset/parsers/class_map.py +19 -0
- openpoints/dataset/parsers/constants.py +1 -0
- openpoints/dataset/parsers/parser.py +17 -0
- openpoints/dataset/parsers/parser_factory.py +29 -0
- openpoints/dataset/parsers/parser_image_folder.py +69 -0
- openpoints/dataset/parsers/parser_image_in_tar.py +222 -0
- openpoints/dataset/parsers/parser_image_tar.py +72 -0
- openpoints/dataset/parsers/parser_tfds.py +297 -0
- openpoints/dataset/pcqm4m/__init__.py +1 -0
- openpoints/dataset/pcqm4m/data.py +62 -0
- openpoints/dataset/pcqm4mv2/__init__.py +1 -0
- openpoints/dataset/pcqm4mv2/data.py +87 -0
- openpoints/dataset/s3dis/__init__.py +2 -0
- openpoints/dataset/s3dis/s3dis.py +156 -0
- openpoints/dataset/s3dis/s3dis_block.py +96 -0
- openpoints/dataset/s3dis/s3dis_sphere.py +349 -0
- openpoints/dataset/scannetv2/__init__.py +1 -0
- openpoints/dataset/scannetv2/scannet.py +176 -0
- openpoints/dataset/scanobjectnn/__init__.py +3 -0
- openpoints/dataset/scanobjectnn/scanobjectnn.py +110 -0
- openpoints/dataset/semantic_kitti/__init__.py +1 -0
- openpoints/dataset/semantic_kitti/helper_tool.py +286 -0
- openpoints/dataset/semantic_kitti/label_mapping.yaml +211 -0
- openpoints/dataset/semantic_kitti/semantickitti.py +229 -0
- openpoints/dataset/semantic_kitti/utils/meta/anno_paths.txt +272 -0
- openpoints/dataset/semantic_kitti/utils/meta/class_names.txt +13 -0
- openpoints/dataset/semantic_kitti/utils/semantic-kitti.yaml +211 -0
- openpoints/dataset/shapenet/__init__.py +1 -0
- openpoints/dataset/shapenet/shapenet55.py +76 -0
- openpoints/dataset/shapenet/shapenetpart.py +121 -0
- openpoints/dataset/shapenetpart/__init__.py +1 -0
- openpoints/dataset/shapenetpart/shapenet55.py +75 -0
- openpoints/dataset/shapenetpart/shapenetpart.py +388 -0
- openpoints/dataset/vis2d.py +17 -0
- openpoints/dataset/vis3d.py +153 -0
- openpoints/loss/__init__.py +3 -0
- openpoints/loss/build.py +281 -0
- openpoints/loss/cross_entropy.py +38 -0
- openpoints/loss/distill_loss.py +76 -0
- openpoints/models/__init__.py +10 -0
- openpoints/models/backbone/Stratified_transformer.py +558 -0
- openpoints/models/backbone/__init__.py +11 -0
- openpoints/models/backbone/baafnet.py +527 -0
- openpoints/models/backbone/ball_dgcnn.py +123 -0
- openpoints/models/backbone/curvenet.py +793 -0
- openpoints/models/backbone/debug_invvit.py +114 -0
- openpoints/models/backbone/deepgcn.py +143 -0
- openpoints/models/backbone/dgcnn.py +119 -0
- openpoints/models/backbone/graphvit3d.py +134 -0
- openpoints/models/backbone/grouppointnet.py +100 -0
- openpoints/models/backbone/pct.py +163 -0
- openpoints/models/backbone/pointmlp.py +417 -0
- openpoints/models/backbone/pointnet.py +199 -0
- openpoints/models/backbone/pointnetv2.py +511 -0
- openpoints/models/backbone/pointnext.py +663 -0
- openpoints/models/backbone/pointnextPyG.py +555 -0
- openpoints/models/backbone/pointtransformer.py +293 -0
- openpoints/models/backbone/pointvector.py +853 -0
- openpoints/models/backbone/pointvit.py +392 -0
- openpoints/models/backbone/pointvit_inv.py +942 -0
- openpoints/models/backbone/pointvit_inv_old.py +784 -0
- openpoints/models/backbone/randlenet.py +318 -0
- openpoints/models/backbone/resnet.py +342 -0
- openpoints/models/backbone/simpleview.py +153 -0
- openpoints/models/backbone/simpleview_util.py +292 -0
- openpoints/models/build.py +13 -0
- openpoints/models/classification/__init__.py +5 -0
- openpoints/models/classification/cls_base.py +136 -0
- openpoints/models/classification/point_bert.py +154 -0
- openpoints/models/layers/__init__.py +14 -0
- openpoints/models/layers/activation.py +57 -0
- openpoints/models/layers/attention.py +103 -0
- openpoints/models/layers/conv.py +167 -0
- openpoints/models/layers/drop.py +164 -0
- openpoints/models/layers/graph_conv.py +122 -0
- openpoints/models/layers/group.py +415 -0
- openpoints/models/layers/group_embed.py +286 -0
- openpoints/models/layers/helpers.py +43 -0
- openpoints/models/layers/kmeans.py +119 -0
- openpoints/models/layers/knn.py +110 -0
- openpoints/models/layers/local_aggregation.py +286 -0
- openpoints/models/layers/mlp.py +129 -0
- openpoints/models/layers/norm.py +106 -0
- openpoints/models/layers/padding.py +56 -0
- openpoints/models/layers/patch_embed.py +37 -0
- openpoints/models/layers/registry.py +168 -0
- openpoints/models/layers/subsample.py +185 -0
- openpoints/models/layers/upsampling.py +106 -0
- openpoints/models/layers/weight_init.py +89 -0
- openpoints/models/reconstruction/__init__.py +8 -0
- openpoints/models/reconstruction/base_recontruct.py +216 -0
- openpoints/models/reconstruction/maskedpoint.py +116 -0
- openpoints/models/reconstruction/maskedpointgroup.py +168 -0
- openpoints/models/reconstruction/maskedpointvit.py +253 -0
- openpoints/models/reconstruction/nodeshuffle.py +11 -0
- openpoints/models/registry.py +149 -0
- openpoints/models/segmentation/__init__.py +6 -0
- openpoints/models/segmentation/base_seg.py +278 -0
- openpoints/models/segmentation/vit_seg.py +126 -0
- openpoints/optim/__init__.py +15 -0
- openpoints/optim/adabelief.py +201 -0
- openpoints/optim/adafactor.py +167 -0
- openpoints/optim/adahessian.py +156 -0
- openpoints/optim/adamp.py +105 -0
- openpoints/optim/adamw.py +122 -0
- openpoints/optim/lamb.py +192 -0
- openpoints/optim/lars.py +135 -0
- openpoints/optim/lookahead.py +61 -0
- openpoints/optim/madgrad.py +184 -0
- openpoints/optim/nadam.py +92 -0
- openpoints/optim/nvnovograd.py +120 -0
- openpoints/optim/optim_factory.py +306 -0
- openpoints/optim/radam.py +89 -0
- openpoints/optim/rmsprop_tf.py +139 -0
- openpoints/optim/sgdp.py +70 -0
- openpoints/scheduler/__init__.py +8 -0
- openpoints/scheduler/cosine_lr.py +124 -0
- openpoints/scheduler/multistep_lr.py +65 -0
- openpoints/scheduler/plateau_lr.py +113 -0
- openpoints/scheduler/poly_lr.py +116 -0
- openpoints/scheduler/scheduler.py +110 -0
- openpoints/scheduler/scheduler_factory.py +118 -0
- openpoints/scheduler/step_lr.py +65 -0
- openpoints/scheduler/tanh_lr.py +117 -0
- openpoints/transforms/__init__.py +7 -0
- openpoints/transforms/point_transform_cpu.py +332 -0
- openpoints/transforms/point_transformer_gpu.py +764 -0
- openpoints/transforms/transforms_factory.py +60 -0
- openpoints/utils/__init__.py +8 -0
- openpoints/utils/ckpt_util.py +438 -0
- openpoints/utils/config.py +113 -0
- openpoints/utils/dist_utils.py +54 -0
- openpoints/utils/logger.py +170 -0
- openpoints/utils/metrics.py +311 -0
- openpoints/utils/random.py +16 -0
- openpoints/utils/registry.py +294 -0
- openpoints/utils/str2bool.py +11 -0
- openpoints/utils/wandb.py +88 -0
- openpoints-0.1.0.dist-info/METADATA +150 -0
- openpoints-0.1.0.dist-info/RECORD +184 -0
- openpoints-0.1.0.dist-info/WHEEL +5 -0
- openpoints-0.1.0.dist-info/licenses/LICENSE +21 -0
- openpoints-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,784 @@
|
|
|
1
|
+
""" Vision Transformer (ViT) for Point Cloud Understanding in PyTorch
|
|
2
|
+
Hacked together by / Copyright 2020, Ross Wightman
|
|
3
|
+
Modified to 3D application by / Copyright 2022@Pix4Point team
|
|
4
|
+
"""
|
|
5
|
+
import logging
|
|
6
|
+
from typing import List
|
|
7
|
+
import torch
|
|
8
|
+
import torch.nn as nn
|
|
9
|
+
from ..layers import create_norm, create_linearblock, create_convblock1d, three_interpolation, \
|
|
10
|
+
furthest_point_sample, random_sample
|
|
11
|
+
from ..layers.attention import Block
|
|
12
|
+
from .pointnext import FeaturePropogation
|
|
13
|
+
from ..build import MODELS, build_model_from_cfg
|
|
14
|
+
from torch.cuda.amp import custom_fwd, custom_bwd
|
|
15
|
+
import math
|
|
16
|
+
import sys
|
|
17
|
+
|
|
18
|
+
from ..layers import Mlp, DropPath, trunc_normal_, lecun_normal_
|
|
19
|
+
|
|
20
|
+
use_inv = True
|
|
21
|
+
use_customized_backprop = True
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class RevBackProp(torch.autograd.Function):
|
|
25
|
+
@staticmethod
|
|
26
|
+
@custom_fwd
|
|
27
|
+
def forward(ctx, x, blocks, pos_embed=None, alpha=0., lambd=1.):
|
|
28
|
+
# print('during rev, alpha, lambda:', alpha, lambd) # TODO: DEBUG
|
|
29
|
+
|
|
30
|
+
for block in blocks:
|
|
31
|
+
x = block(x, pos_embed, alpha=alpha, lambd=lambd)
|
|
32
|
+
|
|
33
|
+
all_tensors = [x.detach(), pos_embed.detach() if pos_embed is not None else pos_embed]
|
|
34
|
+
|
|
35
|
+
ctx.save_for_backward(*all_tensors)
|
|
36
|
+
ctx.blocks = blocks
|
|
37
|
+
ctx.lambd = lambd
|
|
38
|
+
ctx.alpha = alpha
|
|
39
|
+
|
|
40
|
+
return x
|
|
41
|
+
|
|
42
|
+
@staticmethod
|
|
43
|
+
@custom_bwd
|
|
44
|
+
def backward(ctx, dy): # pragma: no cover
|
|
45
|
+
|
|
46
|
+
# retrieve params from ctx for backward
|
|
47
|
+
Y, pos_embed = ctx.saved_tensors
|
|
48
|
+
blocks = ctx.blocks
|
|
49
|
+
lambd = ctx.lambd
|
|
50
|
+
alpha = ctx.alpha
|
|
51
|
+
|
|
52
|
+
dY_1, dY_2 = torch.chunk(dy, 2, dim=-1)
|
|
53
|
+
Y_1, Y_2 = torch.chunk(Y, 2, dim=-1)
|
|
54
|
+
|
|
55
|
+
for _, blk in enumerate(blocks[::-1]):
|
|
56
|
+
Y_1, Y_2, dY_1, dY_2 = blk.backward_pass(
|
|
57
|
+
Y_1=Y_1,
|
|
58
|
+
Y_2=Y_2,
|
|
59
|
+
dY_1=dY_1,
|
|
60
|
+
dY_2=dY_2,
|
|
61
|
+
pos_embed=pos_embed,
|
|
62
|
+
alpha=alpha,
|
|
63
|
+
lambd=lambd,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
dx = torch.cat([dY_1, dY_2], dim=-1)
|
|
67
|
+
del Y_1, Y_2, dY_1, dY_2
|
|
68
|
+
return dx, None, None, None, None
|
|
69
|
+
|
|
70
|
+
def seed_cuda():
|
|
71
|
+
|
|
72
|
+
# randomize seeds
|
|
73
|
+
# use cuda generator if available
|
|
74
|
+
if (
|
|
75
|
+
hasattr(torch.cuda, "default_generators")
|
|
76
|
+
and len(torch.cuda.default_generators) > 0
|
|
77
|
+
):
|
|
78
|
+
# GPU
|
|
79
|
+
device_idx = torch.cuda.current_device()
|
|
80
|
+
seed = torch.cuda.default_generators[device_idx].seed()
|
|
81
|
+
else:
|
|
82
|
+
# CPU
|
|
83
|
+
seed = int(torch.seed() % sys.maxsize)
|
|
84
|
+
|
|
85
|
+
return seed
|
|
86
|
+
|
|
87
|
+
class InvFuncWrapper(nn.Module):
|
|
88
|
+
def __init__(self, Fm, Gm, split_dim=-1):
|
|
89
|
+
|
|
90
|
+
super(InvFuncWrapper, self).__init__()
|
|
91
|
+
|
|
92
|
+
self.Fm = Fm
|
|
93
|
+
self.Gm = Gm
|
|
94
|
+
|
|
95
|
+
self.split_dim = split_dim
|
|
96
|
+
|
|
97
|
+
def forward(self, x, pos_embed=None, alpha=0., lambd=1.):
|
|
98
|
+
|
|
99
|
+
# torch.manual_seed(2022)
|
|
100
|
+
x1, x2 = torch.chunk(x, 2, dim=self.split_dim)
|
|
101
|
+
# x1, x2 = x1.contiguous(), x2.contiguous()
|
|
102
|
+
fmd = self.Fm(x2, pos_embed, alpha)
|
|
103
|
+
y1 = lambd * x1 + fmd
|
|
104
|
+
del x1
|
|
105
|
+
|
|
106
|
+
# torch.manual_seed(2022)
|
|
107
|
+
gmd = self.Gm(y1, alpha)
|
|
108
|
+
y2 = lambd * x2 + gmd
|
|
109
|
+
del x2
|
|
110
|
+
|
|
111
|
+
out = torch.cat([y1, y2], dim=self.split_dim)
|
|
112
|
+
return out
|
|
113
|
+
|
|
114
|
+
def backward_pass(self, Y_1, Y_2, dY_1, dY_2, pos_embed=None, alpha=0., lambd=1.):
|
|
115
|
+
|
|
116
|
+
"""
|
|
117
|
+
equations:
|
|
118
|
+
Y_1 = X_1 + F(X_2), F = Attention
|
|
119
|
+
Y_2 = X_2 + G(Y_1), G = MLP
|
|
120
|
+
|
|
121
|
+
equations for recompuation of activations:
|
|
122
|
+
X_2 = Y_2 - G(Y_1), G = MLP
|
|
123
|
+
X_1 = Y_1 - F(X_2), F = Attention
|
|
124
|
+
"""
|
|
125
|
+
# device = Y_1.device
|
|
126
|
+
# print(f'Before Gm: {torch.cuda.memory_allocated(device)}')
|
|
127
|
+
with torch.enable_grad():
|
|
128
|
+
Y_1.requires_grad = True
|
|
129
|
+
g_Y_1 = self.Gm(Y_1, alpha)
|
|
130
|
+
# print(f'After Gm: {torch.cuda.memory_allocated(device)}')
|
|
131
|
+
g_Y_1.backward(dY_2, retain_graph=True)
|
|
132
|
+
# print(f'After backward: {torch.cuda.memory_allocated(device)}')
|
|
133
|
+
# print(f'After 1: {torch.cuda.memory_allocated(device)}')
|
|
134
|
+
|
|
135
|
+
with torch.no_grad():
|
|
136
|
+
X_2 = (Y_2 - g_Y_1) / lambd
|
|
137
|
+
del g_Y_1
|
|
138
|
+
dY_1 = dY_1 + Y_1.grad
|
|
139
|
+
Y_1.grad = None
|
|
140
|
+
# print(f'After 2: {torch.cuda.memory_allocated(device)}\n')
|
|
141
|
+
# print('*************************************************')
|
|
142
|
+
|
|
143
|
+
# print(f'Before Fm: {torch.cuda.memory_allocated(device)}')
|
|
144
|
+
with torch.enable_grad():
|
|
145
|
+
X_2.requires_grad = True
|
|
146
|
+
f_X_2 = self.Fm(X_2, pos_embed, alpha)
|
|
147
|
+
# print(f'After Fm: {torch.cuda.memory_allocated(device)}')
|
|
148
|
+
f_X_2.backward(dY_1, retain_graph=True)
|
|
149
|
+
# print(f'After backward: {torch.cuda.memory_allocated(device)}')
|
|
150
|
+
|
|
151
|
+
with torch.no_grad():
|
|
152
|
+
X_1 = (Y_1 - f_X_2) / lambd
|
|
153
|
+
del f_X_2, Y_1
|
|
154
|
+
dY_2 = lambd * dY_2 + X_2.grad
|
|
155
|
+
X_2.grad = None
|
|
156
|
+
X_2 = X_2.detach()
|
|
157
|
+
|
|
158
|
+
dY_1 = lambd * dY_1
|
|
159
|
+
# print(f'After 4: {torch.cuda.memory_allocated(device)}\n')
|
|
160
|
+
# print('*************************************************')
|
|
161
|
+
|
|
162
|
+
return X_1, X_2, dY_1, dY_2
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
'''
|
|
166
|
+
File Description: attention layer for transformer. borrowed from TIMM
|
|
167
|
+
'''
|
|
168
|
+
import torch
|
|
169
|
+
import torch.nn as nn
|
|
170
|
+
import torch.nn.functional as F
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
class Attention(nn.Module):
|
|
175
|
+
def __init__(self, dim, num_heads=8, qkv_bias=False, attn_drop=0., proj_drop=0.):
|
|
176
|
+
super().__init__()
|
|
177
|
+
self.num_heads = num_heads
|
|
178
|
+
# this is different from what I understand before.
|
|
179
|
+
# the num_heads here actually works as groups for shared attentions. it partition the channels to different groups.
|
|
180
|
+
head_dim = dim // num_heads
|
|
181
|
+
self.scale = head_dim ** -0.5
|
|
182
|
+
|
|
183
|
+
self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)
|
|
184
|
+
self.attn_drop = nn.Dropout(attn_drop)
|
|
185
|
+
self.proj = nn.Linear(dim, dim)
|
|
186
|
+
self.proj_drop = nn.Dropout(proj_drop)
|
|
187
|
+
|
|
188
|
+
def forward(self, x):
|
|
189
|
+
B, N, C = x.shape
|
|
190
|
+
qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4)
|
|
191
|
+
q, k, v = qkv.unbind(0) # make torchscript happy (cannot use tensor as tuple), shape [B, #Heads, N, C]
|
|
192
|
+
|
|
193
|
+
attn = (q @ k.transpose(-2, -1)) * self.scale
|
|
194
|
+
attn = attn.softmax(dim=-1)
|
|
195
|
+
attn = self.attn_drop(attn)
|
|
196
|
+
|
|
197
|
+
x = (attn @ v).transpose(1, 2).reshape(B, N, C)
|
|
198
|
+
x = self.proj(x)
|
|
199
|
+
x = self.proj_drop(x)
|
|
200
|
+
return x
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
class Block_inv_F(nn.Module):
|
|
204
|
+
|
|
205
|
+
def __init__(self, dim, num_heads, qkv_bias=False, drop=0., attn_drop=0., norm_args={'norm': 'ln'}, drop_path=None):
|
|
206
|
+
super().__init__()
|
|
207
|
+
|
|
208
|
+
self.drop_path = drop_path
|
|
209
|
+
self.norm1 = create_norm(norm_args, dim)
|
|
210
|
+
self.attn = Attention(dim, num_heads=num_heads, qkv_bias=qkv_bias, attn_drop=attn_drop, proj_drop=drop)
|
|
211
|
+
self.seeds = {}
|
|
212
|
+
self.seeds['droppath'] = seed_cuda()
|
|
213
|
+
|
|
214
|
+
def forward(self, x, pos_embed=None, alpha=0):
|
|
215
|
+
torch.manual_seed(self.seeds['droppath'])
|
|
216
|
+
# print('L216:', pos_embed)
|
|
217
|
+
if pos_embed is not None:
|
|
218
|
+
x = x + pos_embed
|
|
219
|
+
# print('L219:', pos_embed)
|
|
220
|
+
x = alpha*x + self.drop_path(self.attn(self.norm1(x)))
|
|
221
|
+
return x
|
|
222
|
+
|
|
223
|
+
class Block_inv_G(nn.Module):
|
|
224
|
+
|
|
225
|
+
def __init__(self, dim, mlp_ratio=4., drop=0., act_args={'act': 'gelu'}, norm_args={'norm': 'ln'}, drop_path=None):
|
|
226
|
+
super().__init__()
|
|
227
|
+
|
|
228
|
+
self.drop_path = drop_path
|
|
229
|
+
self.norm2 = create_norm(norm_args, dim)
|
|
230
|
+
mlp_hidden_dim = int(dim * mlp_ratio)
|
|
231
|
+
# invertable bottleneck layer
|
|
232
|
+
self.mlp = Mlp(in_features=dim, hidden_features=mlp_hidden_dim, act_args=act_args, drop=drop)
|
|
233
|
+
self.seeds = {}
|
|
234
|
+
self.seeds['droppath'] = seed_cuda()
|
|
235
|
+
|
|
236
|
+
def forward(self, x, alpha=0):
|
|
237
|
+
torch.manual_seed(self.seeds['droppath'])
|
|
238
|
+
x = alpha*x + self.drop_path(self.mlp(self.norm2(x)))
|
|
239
|
+
return x
|
|
240
|
+
|
|
241
|
+
class Block(nn.Module):
|
|
242
|
+
|
|
243
|
+
def __init__(self, dim, num_heads, mlp_ratio=4., qkv_bias=False, drop=0., attn_drop=0.,
|
|
244
|
+
drop_path=0., act_args={'act': 'gelu'}, norm_args={'norm': 'ln'}):
|
|
245
|
+
super().__init__()
|
|
246
|
+
|
|
247
|
+
# NOTE: drop path for stochastic depth, we shall see if this is better than dropout here
|
|
248
|
+
drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity()
|
|
249
|
+
Fm = Block_inv_F(dim=dim, num_heads=num_heads, qkv_bias=qkv_bias, drop=drop, attn_drop=attn_drop, norm_args=norm_args, drop_path=drop_path)
|
|
250
|
+
Gm = Block_inv_G(dim=dim, mlp_ratio=mlp_ratio, drop=drop, act_args=act_args, norm_args=norm_args, drop_path=drop_path)
|
|
251
|
+
|
|
252
|
+
if use_inv:
|
|
253
|
+
self.inv_block = InvFuncWrapper(Fm=Fm, Gm=Gm, split_dim=-1)
|
|
254
|
+
else:
|
|
255
|
+
self.Fm = Fm
|
|
256
|
+
self.Gm = Gm
|
|
257
|
+
|
|
258
|
+
def forward(self, x, pos_embed=None, alpha=0., lambd=1.):
|
|
259
|
+
if use_inv:
|
|
260
|
+
x = self.inv_block(x, pos_embed, alpha=alpha, lambd=lambd)
|
|
261
|
+
else:
|
|
262
|
+
x = self.Fm(x, pos_embed, alpha=1)
|
|
263
|
+
x = self.Gm(x)
|
|
264
|
+
|
|
265
|
+
return x
|
|
266
|
+
|
|
267
|
+
def backward_pass(self, Y_1, Y_2, dY_1, dY_2, pos_embed=None, alpha=0., lambd=1.):
|
|
268
|
+
return self.inv_block.backward_pass(
|
|
269
|
+
Y_1=Y_1,
|
|
270
|
+
Y_2=Y_2,
|
|
271
|
+
dY_1=dY_1,
|
|
272
|
+
dY_2=dY_2,
|
|
273
|
+
pos_embed=pos_embed,
|
|
274
|
+
alpha=alpha, lambd=lambd)
|
|
275
|
+
|
|
276
|
+
class TransformerEncoder(nn.Module):
|
|
277
|
+
""" Transformer Encoder without hierarchical structure
|
|
278
|
+
"""
|
|
279
|
+
|
|
280
|
+
def __init__(self, embed_dim=768, depth=12, num_heads=12, mlp_ratio=4., qkv_bias=False,
|
|
281
|
+
drop_rate=0., attn_drop_rate=0., drop_path_rate=0.,
|
|
282
|
+
act_args={'act': 'gelu'}, norm_args={'norm': 'ln'}
|
|
283
|
+
):
|
|
284
|
+
super().__init__()
|
|
285
|
+
self.blocks = nn.ModuleList([
|
|
286
|
+
Block(
|
|
287
|
+
dim=embed_dim, num_heads=num_heads, mlp_ratio=mlp_ratio, qkv_bias=qkv_bias,
|
|
288
|
+
drop=drop_rate, attn_drop=attn_drop_rate,
|
|
289
|
+
drop_path=drop_path_rate[i] if isinstance(drop_path_rate, list) else drop_path_rate,
|
|
290
|
+
norm_args=norm_args, act_args=act_args
|
|
291
|
+
)
|
|
292
|
+
for i in range(depth)])
|
|
293
|
+
self.depth = depth
|
|
294
|
+
# dilation = depth//3
|
|
295
|
+
# self.out_depth = list(range(depth))[dilation-1::dilation]
|
|
296
|
+
|
|
297
|
+
def forward(self, x, pos):
|
|
298
|
+
for _, block in enumerate(self.blocks):
|
|
299
|
+
# x = block(x)
|
|
300
|
+
|
|
301
|
+
# Injecting the positional information at each block.
|
|
302
|
+
# (Dehghani et al., 2018) and (Lan et al.,2019) observe better performance
|
|
303
|
+
# by further injecting the position information at each block
|
|
304
|
+
# Reference: Learning to Encode Position for Transformer with Continuous Dynamical Model.
|
|
305
|
+
# http://proceedings.mlr.press/v119/liu20n/liu20n.pdf
|
|
306
|
+
x = block(x + pos)
|
|
307
|
+
return x
|
|
308
|
+
|
|
309
|
+
def forward_features(self, x, pos, num_outs=None):
|
|
310
|
+
dilation = self.depth // num_outs
|
|
311
|
+
out_depth = list(range(self.depth))[(self.depth - (num_outs-1)*dilation -1) :: dilation]
|
|
312
|
+
|
|
313
|
+
out = []
|
|
314
|
+
for i, block in enumerate(self.blocks):
|
|
315
|
+
x = block(x + pos)
|
|
316
|
+
if i in out_depth:
|
|
317
|
+
out.append(x)
|
|
318
|
+
return out
|
|
319
|
+
|
|
320
|
+
@MODELS.register_module()
|
|
321
|
+
class InvPointViT(nn.Module):
|
|
322
|
+
""" Point Vision Transformer ++: with early convolutions
|
|
323
|
+
"""
|
|
324
|
+
def __init__(self,
|
|
325
|
+
in_channels=3,
|
|
326
|
+
embed_dim=384, depth=12,
|
|
327
|
+
num_heads=6, mlp_ratio=4., qkv_bias=False,
|
|
328
|
+
drop_rate=0., attn_drop_rate=0., drop_path_rate=0.,
|
|
329
|
+
embed_args={'NAME': 'PointPatchEmbed',
|
|
330
|
+
'num_groups': 256,
|
|
331
|
+
'group_size': 32,
|
|
332
|
+
'subsample': 'fps',
|
|
333
|
+
'group': 'knn',
|
|
334
|
+
'feature_type': 'fj',
|
|
335
|
+
'norm_args': {'norm': 'in2d'},
|
|
336
|
+
},
|
|
337
|
+
norm_args={'norm': 'ln', 'eps': 1.0e-6},
|
|
338
|
+
act_args={'act': 'gelu'},
|
|
339
|
+
add_pos_each_block=True,
|
|
340
|
+
global_feat='cls,max',
|
|
341
|
+
distill=False,
|
|
342
|
+
init_epoch = 0,
|
|
343
|
+
start_epoch = 1,
|
|
344
|
+
end_epoch = 200,
|
|
345
|
+
freq=2,
|
|
346
|
+
init_lambd = 0.1,
|
|
347
|
+
lambd = 1.,
|
|
348
|
+
alpha = 0.,
|
|
349
|
+
option = -1,
|
|
350
|
+
**kwargs
|
|
351
|
+
):
|
|
352
|
+
"""
|
|
353
|
+
Args:
|
|
354
|
+
in_channels (int): number of input channels. Default: 6. (p + rgb)
|
|
355
|
+
embed_dim (int): embedding dimension
|
|
356
|
+
depth (int): depth of transformer
|
|
357
|
+
num_heads (int): number of attention heads
|
|
358
|
+
mlp_ratio (int): ratio of mlp hidden dim to embedding dim
|
|
359
|
+
qkv_bias (bool): enable bias for qkv if True
|
|
360
|
+
representation_size (Optional[int]): enable and set representation layer (pre-logits) to this value if set
|
|
361
|
+
drop_rate (float): dropout rate
|
|
362
|
+
attn_drop_rate (float): attention dropout rate
|
|
363
|
+
drop_path_rate (float): stochastic depth rate
|
|
364
|
+
embed_layer (nn.Module): patch embedding layer
|
|
365
|
+
norm_layer: (nn.Module): normalization layer
|
|
366
|
+
"""
|
|
367
|
+
super().__init__()
|
|
368
|
+
if kwargs:
|
|
369
|
+
logging.warning(f"kwargs: {kwargs} are not used in {__class__.__name__}")
|
|
370
|
+
self.num_features = self.embed_dim = embed_dim # num_features for consistency with other models
|
|
371
|
+
embed_args.in_channels = in_channels
|
|
372
|
+
embed_args.embed_dim = embed_dim
|
|
373
|
+
self.patch_embed = build_model_from_cfg(embed_args)
|
|
374
|
+
self.cls_token = nn.Parameter(torch.randn(1, 1, self.embed_dim))
|
|
375
|
+
self.cls_pos = nn.Parameter(torch.randn(1, 1, self.embed_dim))
|
|
376
|
+
self.pos_embed = nn.Sequential(
|
|
377
|
+
create_linearblock(3, 128, norm_args=None, act_args=act_args),
|
|
378
|
+
nn.Linear(128, self.embed_dim)
|
|
379
|
+
)
|
|
380
|
+
if self.patch_embed.out_channels != self.embed_dim:
|
|
381
|
+
self.proj = nn.Linear(self.patch_embed.out_channels, self.embed_dim)
|
|
382
|
+
else:
|
|
383
|
+
self.proj = nn.Identity()
|
|
384
|
+
self.add_pos_each_block = add_pos_each_block
|
|
385
|
+
self.pos_drop = nn.Dropout(p=drop_rate)
|
|
386
|
+
dpr = [x.item() for x in torch.linspace(0, drop_path_rate, depth)] # stochastic depth decay rule
|
|
387
|
+
self.depth = depth
|
|
388
|
+
self.blocks = nn.ModuleList([
|
|
389
|
+
Block(
|
|
390
|
+
dim=self.embed_dim, num_heads=num_heads, mlp_ratio=mlp_ratio, qkv_bias=qkv_bias,
|
|
391
|
+
drop=drop_rate, attn_drop=attn_drop_rate, drop_path=dpr[i],
|
|
392
|
+
norm_args=norm_args, act_args=act_args
|
|
393
|
+
)
|
|
394
|
+
for i in range(depth)])
|
|
395
|
+
self.norm = create_norm(norm_args, self.embed_dim) # Norm layer is extremely important here!
|
|
396
|
+
self.global_feat = global_feat.split(',')
|
|
397
|
+
self.out_channels = len(self.global_feat)*embed_dim
|
|
398
|
+
self.distill_channels = embed_dim
|
|
399
|
+
self.channel_list = self.patch_embed.channel_list
|
|
400
|
+
self.channel_list[-1] = embed_dim
|
|
401
|
+
|
|
402
|
+
# distill
|
|
403
|
+
if distill:
|
|
404
|
+
self.dist_token = nn.Parameter(torch.randn(1, 1, self.embed_dim))
|
|
405
|
+
self.dist_pos = nn.Parameter(torch.randn(1, 1, self.embed_dim))
|
|
406
|
+
self.n_tokens = 2
|
|
407
|
+
else:
|
|
408
|
+
self.dist_token = None
|
|
409
|
+
self.n_tokens = 1
|
|
410
|
+
self.initialize_weights()
|
|
411
|
+
|
|
412
|
+
self.init_epoch = init_epoch
|
|
413
|
+
self.start_epoch = start_epoch
|
|
414
|
+
self.end_epoch = end_epoch
|
|
415
|
+
self.freq = freq
|
|
416
|
+
self.alpha = alpha
|
|
417
|
+
self.init_lambd = init_lambd
|
|
418
|
+
self.lambd = lambd
|
|
419
|
+
self.option = option
|
|
420
|
+
|
|
421
|
+
def initialize_weights(self):
|
|
422
|
+
torch.nn.init.normal_(self.cls_token, std=.02)
|
|
423
|
+
torch.nn.init.normal_(self.cls_pos, std=.02)
|
|
424
|
+
if self.dist_token is not None:
|
|
425
|
+
torch.nn.init.normal_(self.dist_token, std=.02)
|
|
426
|
+
torch.nn.init.normal_(self.dist_pos, std=.02)
|
|
427
|
+
self.apply(self._init_weights)
|
|
428
|
+
|
|
429
|
+
@staticmethod
|
|
430
|
+
def _init_weights(m):
|
|
431
|
+
if isinstance(m, nn.Linear):
|
|
432
|
+
torch.nn.init.xavier_uniform_(m.weight)
|
|
433
|
+
if isinstance(m, nn.Linear) and m.bias is not None:
|
|
434
|
+
nn.init.constant_(m.bias, 0)
|
|
435
|
+
elif isinstance(m, (nn.LayerNorm, nn.GroupNorm, nn.BatchNorm2d, nn.BatchNorm1d)):
|
|
436
|
+
nn.init.constant_(m.bias, 0)
|
|
437
|
+
nn.init.constant_(m.weight, 1.0)
|
|
438
|
+
|
|
439
|
+
@torch.jit.ignore
|
|
440
|
+
def no_weight_decay(self):
|
|
441
|
+
return {'cls_token', 'dist_token', 'dist_token'}
|
|
442
|
+
|
|
443
|
+
def get_num_layers(self):
|
|
444
|
+
return self.depth
|
|
445
|
+
|
|
446
|
+
def get_alpha_lambd(self, epoch=-1):
|
|
447
|
+
|
|
448
|
+
if self.option != -1:
|
|
449
|
+
###################### alpha ######################
|
|
450
|
+
if epoch < self.init_epoch: # Inference
|
|
451
|
+
alpha = 0.0
|
|
452
|
+
elif epoch < self.start_epoch:
|
|
453
|
+
alpha = 1.0
|
|
454
|
+
elif epoch >= self.start_epoch and epoch < self.end_epoch:
|
|
455
|
+
# Option 0: linear, interleave alpha and lambda
|
|
456
|
+
if self.option == 0.0:
|
|
457
|
+
alpha = round(min(max(-round(self.freq/(self.end_epoch-self.start_epoch), 2) * math.ceil((epoch-self.start_epoch-1-self.freq//2) / self.freq) + 1, 0), 1.), 2)
|
|
458
|
+
# self.option 1: linear alpha = (1/(A+1-N))*x + N/(N-A-1), lambd = 1 - alpha + 0.01
|
|
459
|
+
elif self.option == 1:
|
|
460
|
+
alpha = round(1/(self.start_epoch+1-self.end_epoch) * epoch + self.end_epoch/(self.end_epoch-self.start_epoch-1), 2)
|
|
461
|
+
# self.option 2: logarithmic
|
|
462
|
+
elif self.option == 2:
|
|
463
|
+
base = math.e
|
|
464
|
+
a = 1/(math.log((self.start_epoch+1) / self.end_epoch, base))
|
|
465
|
+
c = - a * math.log(self.end_epoch, base)
|
|
466
|
+
alpha = round(max(min(a * math.log(epoch, base) + c, 1.), 0), 2)
|
|
467
|
+
# self.option 3: exponential
|
|
468
|
+
elif self.option == 3:
|
|
469
|
+
base = math.e
|
|
470
|
+
a = 1/(pow(base, self.start_epoch+1) - pow(base, self.end_epoch))
|
|
471
|
+
c = - a * pow(base, self.end_epoch)
|
|
472
|
+
alpha = round(max(min(a * pow(base, epoch) + c, 1.), 0), 2)
|
|
473
|
+
# self.option 4: hand-crafted, increasing
|
|
474
|
+
elif self.option == 4:
|
|
475
|
+
alpha_list = [1., 1., 0.8, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.]
|
|
476
|
+
alpha = alpha_list[epoch - 1]
|
|
477
|
+
|
|
478
|
+
elif epoch >= self.end_epoch:
|
|
479
|
+
alpha = 0.0
|
|
480
|
+
|
|
481
|
+
###################### lambda #####################
|
|
482
|
+
if epoch < self.init_epoch: # Inference
|
|
483
|
+
lambd = 1.0
|
|
484
|
+
elif epoch < self.start_epoch:
|
|
485
|
+
lambd = 0.0
|
|
486
|
+
elif epoch >= self.start_epoch and epoch < self.end_epoch:
|
|
487
|
+
if self.option == 0:
|
|
488
|
+
lambd = round(max(min(self.freq/(self.end_epoch-self.start_epoch) * math.ceil((epoch-self.start_epoch-1) / self.freq) + self.init_lambd, 1.), self.init_lambd), 2)
|
|
489
|
+
else:
|
|
490
|
+
lambd = round(min(1 - alpha + 0.1, 1.), 2)
|
|
491
|
+
elif epoch >= self.end_epoch:
|
|
492
|
+
lambd = 1.0
|
|
493
|
+
else:
|
|
494
|
+
alpha, lambd = self.alpha, self.lambd
|
|
495
|
+
# print('during rev, alpha, lambda:', alpha, lambd) # TODO: DEBUG
|
|
496
|
+
return alpha, lambd
|
|
497
|
+
|
|
498
|
+
def forward(self, p, x=None, epoch=-1):
|
|
499
|
+
if hasattr(p, 'keys'):
|
|
500
|
+
p, x, epoch = p['pos'], p['x'] if 'x' in p.keys() else None, p['epoch'] if 'epoch' in p.keys() else -1
|
|
501
|
+
if x is None:
|
|
502
|
+
x = p.clone().transpose(1, 2).contiguous()
|
|
503
|
+
p_list, x_list = self.patch_embed(p, x)
|
|
504
|
+
center_p, x = p_list[-1], self.proj(x_list[-1].transpose(1, 2))
|
|
505
|
+
pos_embed = self.pos_embed(center_p)
|
|
506
|
+
|
|
507
|
+
pos_embed = [self.cls_pos.expand(x.shape[0], -1, -1), pos_embed]
|
|
508
|
+
tokens = [self.cls_token.expand(x.shape[0], -1, -1), x]
|
|
509
|
+
if self.dist_token is not None:
|
|
510
|
+
pos_embed.insert(1, self.dist_pos.expand(x.shape[0], -1, -1))
|
|
511
|
+
tokens.insert(1, self.dist_token.expand(x.shape[0], -1, -1))
|
|
512
|
+
pos_embed = torch.cat(pos_embed, dim=1)
|
|
513
|
+
x = torch.cat(tokens, dim=1)
|
|
514
|
+
|
|
515
|
+
with torch.set_grad_enabled(epoch>=self.start_epoch):
|
|
516
|
+
alpha, lambd = self.get_alpha_lambd(epoch)
|
|
517
|
+
if self.add_pos_each_block:
|
|
518
|
+
if use_inv:
|
|
519
|
+
x = self._use_inv(x, pos_embed, alpha, lambd)
|
|
520
|
+
else:
|
|
521
|
+
for block in self.blocks:
|
|
522
|
+
x = block(x, pos_embed)
|
|
523
|
+
else:
|
|
524
|
+
x = self.pos_drop(x + pos_embed)
|
|
525
|
+
if use_inv:
|
|
526
|
+
x = self._use_inv(x, None, alpha, lambd)
|
|
527
|
+
else:
|
|
528
|
+
for block in self.blocks:
|
|
529
|
+
x = block(x)
|
|
530
|
+
x = self.norm(x)
|
|
531
|
+
return p_list, x_list, x
|
|
532
|
+
|
|
533
|
+
def _use_inv(self, x, pos_embed=None, alpha=0., lambd=1.):
|
|
534
|
+
x = torch.cat((x, x), dim=-1)
|
|
535
|
+
if use_customized_backprop:
|
|
536
|
+
x = RevBackProp.apply(x, self.blocks, pos_embed, alpha, lambd)
|
|
537
|
+
else:
|
|
538
|
+
for block in self.blocks:
|
|
539
|
+
x = block(x, pos_embed)
|
|
540
|
+
C = int(x.shape[-1] / 2)
|
|
541
|
+
x = (x[:,:,:C] + x[:,:,C:]) / 2
|
|
542
|
+
return x
|
|
543
|
+
|
|
544
|
+
def forward_cls_feat(self, p, x=None): # p: p, x: features
|
|
545
|
+
_, _, x = self.forward(p, x)
|
|
546
|
+
token_features = x[:, self.n_tokens:, :]
|
|
547
|
+
cls_feats = []
|
|
548
|
+
for token_type in self.global_feat:
|
|
549
|
+
if 'cls' in token_type:
|
|
550
|
+
cls_feats.append(x[:, 0, :])
|
|
551
|
+
elif 'max' in token_type:
|
|
552
|
+
cls_feats.append(torch.max(token_features, dim=1, keepdim=False)[0])
|
|
553
|
+
elif token_type in ['avg', 'mean']:
|
|
554
|
+
cls_feats.append(torch.mean(token_features, dim=1, keepdim=False))
|
|
555
|
+
global_features = torch.cat(cls_feats, dim=1)
|
|
556
|
+
|
|
557
|
+
if self.dist_token is not None and self.training:
|
|
558
|
+
return global_features, x[:, 1, :]
|
|
559
|
+
else:
|
|
560
|
+
return global_features
|
|
561
|
+
|
|
562
|
+
def forward_seg_feat(self, p, x=None, epoch=-1): # p: p, x: features
|
|
563
|
+
p_list, x_list, x = self.forward(p, x, epoch)
|
|
564
|
+
x_list[-1] = x.transpose(1, 2)
|
|
565
|
+
return p_list, x_list
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
@MODELS.register_module()
|
|
569
|
+
class InvPointViTDecoder(nn.Module):
|
|
570
|
+
""" Decoder of Point Vision Transformer for segmentation.
|
|
571
|
+
"""
|
|
572
|
+
def __init__(self,
|
|
573
|
+
encoder_channel_list: List[int],
|
|
574
|
+
decoder_layers: int = 2,
|
|
575
|
+
n_decoder_stages: int = 2, # TODO: ablate this
|
|
576
|
+
scale: int = 4,
|
|
577
|
+
channel_scaling: int = 1,
|
|
578
|
+
sampler: str = 'fps',
|
|
579
|
+
global_feat=None,
|
|
580
|
+
progressive_input=False,
|
|
581
|
+
**kwargs
|
|
582
|
+
):
|
|
583
|
+
super().__init__()
|
|
584
|
+
self.decoder_layers = decoder_layers
|
|
585
|
+
if global_feat is not None:
|
|
586
|
+
self.global_feat = global_feat.split(',')
|
|
587
|
+
num_global_feat = len(self.global_feat)
|
|
588
|
+
else:
|
|
589
|
+
self.global_feat = None
|
|
590
|
+
num_global_feat = 0
|
|
591
|
+
|
|
592
|
+
self.in_channels = encoder_channel_list[-1]
|
|
593
|
+
self.scale = scale
|
|
594
|
+
self.n_decoder_stages = n_decoder_stages
|
|
595
|
+
|
|
596
|
+
if progressive_input:
|
|
597
|
+
skip_dim = [self.in_channels//2**i for i in range(n_decoder_stages-1, 0, -1)]
|
|
598
|
+
else:
|
|
599
|
+
skip_dim = [0 for i in range(n_decoder_stages-1)]
|
|
600
|
+
skip_channels = [encoder_channel_list[0]] + skip_dim
|
|
601
|
+
|
|
602
|
+
fp_channels = [self.in_channels*channel_scaling]
|
|
603
|
+
for _ in range(n_decoder_stages-1):
|
|
604
|
+
fp_channels.insert(0, fp_channels[0] * channel_scaling)
|
|
605
|
+
|
|
606
|
+
decoder = [[] for _ in range(n_decoder_stages)]
|
|
607
|
+
for i in range(-1, -n_decoder_stages-1, -1):
|
|
608
|
+
decoder[i] = self._make_dec(
|
|
609
|
+
skip_channels[i], fp_channels[i])
|
|
610
|
+
self.decoder = nn.Sequential(*decoder)
|
|
611
|
+
self.out_channels = fp_channels[-n_decoder_stages] * (num_global_feat + 1)
|
|
612
|
+
|
|
613
|
+
if sampler.lower() == 'fps':
|
|
614
|
+
self.sample_fn = furthest_point_sample
|
|
615
|
+
elif sampler.lower() == 'random':
|
|
616
|
+
self.sample_fn = random_sample
|
|
617
|
+
|
|
618
|
+
def _make_dec(self, skip_channels, fp_channels):
|
|
619
|
+
layers = []
|
|
620
|
+
mlp = [skip_channels + self.in_channels] + \
|
|
621
|
+
[fp_channels] * self.decoder_layers
|
|
622
|
+
layers.append(FeaturePropogation(mlp))
|
|
623
|
+
self.in_channels = fp_channels
|
|
624
|
+
return nn.Sequential(*layers)
|
|
625
|
+
|
|
626
|
+
def forward(self, p, f):
|
|
627
|
+
"""
|
|
628
|
+
Args:
|
|
629
|
+
p (List(Tensor)): List of tensor for p, length 2, input p and center p
|
|
630
|
+
f (List(Tensor)): List of tensor for feature maps, input features and out features
|
|
631
|
+
"""
|
|
632
|
+
if len(p) != (self.n_decoder_stages + 1):
|
|
633
|
+
for i in range(self.n_decoder_stages - 1):
|
|
634
|
+
pos = p[i]
|
|
635
|
+
idx = self.sample_fn(pos, pos.shape[1] // self.scale).long()
|
|
636
|
+
new_p = torch.gather(pos, 1, idx.unsqueeze(-1).expand(-1, -1, 3))
|
|
637
|
+
p.insert(1, new_p)
|
|
638
|
+
f.insert(1, None)
|
|
639
|
+
cls_token = f[-1][:, :, 0:1]
|
|
640
|
+
f[-1] = f[-1][:, :, 1:].contiguous()
|
|
641
|
+
|
|
642
|
+
for i in range(-1, -len(self.decoder) - 1, -1):
|
|
643
|
+
f[i - 1] = self.decoder[i][1:](
|
|
644
|
+
[p[i], self.decoder[i][0]([p[i - 1], f[i - 1]], [p[i], f[i]])])[1]
|
|
645
|
+
f_out = f[-len(self.decoder) - 1]
|
|
646
|
+
|
|
647
|
+
if self.global_feat is not None:
|
|
648
|
+
global_feats = []
|
|
649
|
+
for token_type in self.global_feat:
|
|
650
|
+
if 'cls' in token_type:
|
|
651
|
+
global_feats.append(cls_token)
|
|
652
|
+
elif 'max' in token_type:
|
|
653
|
+
global_feats.append(torch.max(f_out, dim=2, keepdim=True)[0])
|
|
654
|
+
elif token_type in ['avg', 'mean']:
|
|
655
|
+
global_feats.append(torch.mean(f_out, dim=2, keepdim=True))
|
|
656
|
+
global_feats = torch.cat(global_feats, dim=1).expand(-1, -1, f_out.shape[-1])
|
|
657
|
+
f_out = torch.cat((global_feats, f_out), dim=1)
|
|
658
|
+
return f_out
|
|
659
|
+
|
|
660
|
+
|
|
661
|
+
@MODELS.register_module()
|
|
662
|
+
class InvPointViTPartDecoder(nn.Module):
|
|
663
|
+
""" Decoder of Point Vision Transformer for segmentation.
|
|
664
|
+
"""
|
|
665
|
+
def __init__(self,
|
|
666
|
+
encoder_channel_list: List[int],
|
|
667
|
+
decoder_layers: int = 2,
|
|
668
|
+
n_decoder_stages: int = 2,
|
|
669
|
+
scale: int = 4,
|
|
670
|
+
channel_scaling: int = 1,
|
|
671
|
+
sampler: str = 'fps',
|
|
672
|
+
global_feat=None,
|
|
673
|
+
progressive_input=False,
|
|
674
|
+
cls_map='pointnet2',
|
|
675
|
+
num_classes: int = 16,
|
|
676
|
+
**kwargs
|
|
677
|
+
):
|
|
678
|
+
super().__init__()
|
|
679
|
+
self.decoder_layers = decoder_layers
|
|
680
|
+
if global_feat is not None:
|
|
681
|
+
self.global_feat = global_feat.split(',')
|
|
682
|
+
num_global_feat = len(self.global_feat)
|
|
683
|
+
else:
|
|
684
|
+
self.global_feat = None
|
|
685
|
+
num_global_feat = 0
|
|
686
|
+
|
|
687
|
+
self.in_channels = encoder_channel_list[-1]
|
|
688
|
+
self.scale = scale
|
|
689
|
+
self.n_decoder_stages = n_decoder_stages
|
|
690
|
+
|
|
691
|
+
if progressive_input:
|
|
692
|
+
skip_dim = [self.in_channels//2**i for i in range(n_decoder_stages-1, 0, -1)]
|
|
693
|
+
else:
|
|
694
|
+
skip_dim = [0 for i in range(n_decoder_stages-1)]
|
|
695
|
+
skip_channels = [encoder_channel_list[0]] + skip_dim
|
|
696
|
+
|
|
697
|
+
fp_channels = [self.in_channels*channel_scaling]
|
|
698
|
+
for _ in range(n_decoder_stages-1):
|
|
699
|
+
fp_channels.insert(0, fp_channels[0] * channel_scaling)
|
|
700
|
+
|
|
701
|
+
self.cls_map = cls_map
|
|
702
|
+
self.num_classes = num_classes
|
|
703
|
+
act_args = kwargs.get('act_args', {'act': 'relu'})
|
|
704
|
+
if self.cls_map == 'curvenet':
|
|
705
|
+
# global features
|
|
706
|
+
self.global_conv2 = nn.Sequential(
|
|
707
|
+
create_convblock1d(fp_channels[-1] * 2, 128,
|
|
708
|
+
norm_args=None,
|
|
709
|
+
act_args=act_args))
|
|
710
|
+
self.global_conv1 = nn.Sequential(
|
|
711
|
+
create_convblock1d(fp_channels[-2] * 2, 64,
|
|
712
|
+
norm_args=None,
|
|
713
|
+
act_args=act_args))
|
|
714
|
+
skip_channels[0] += 64 + 128 + 16 # shape categories labels
|
|
715
|
+
elif self.cls_map == 'pointnet2':
|
|
716
|
+
self.convc = nn.Sequential(create_convblock1d(16, 64,
|
|
717
|
+
norm_args=None,
|
|
718
|
+
act_args=act_args))
|
|
719
|
+
skip_channels[0] += 64 # shape categories labels
|
|
720
|
+
|
|
721
|
+
decoder = [[] for _ in range(n_decoder_stages)]
|
|
722
|
+
for i in range(-1, -n_decoder_stages-1, -1):
|
|
723
|
+
decoder[i] = self._make_dec(
|
|
724
|
+
skip_channels[i], fp_channels[i])
|
|
725
|
+
self.decoder = nn.Sequential(*decoder)
|
|
726
|
+
self.out_channels = fp_channels[-n_decoder_stages] * (num_global_feat + 1)
|
|
727
|
+
|
|
728
|
+
if sampler.lower() == 'fps':
|
|
729
|
+
self.sample_fn = furthest_point_sample
|
|
730
|
+
elif sampler.lower() == 'random':
|
|
731
|
+
self.sample_fn = random_sample
|
|
732
|
+
|
|
733
|
+
def _make_dec(self, skip_channels, fp_channels):
|
|
734
|
+
layers = []
|
|
735
|
+
mlp = [skip_channels + self.in_channels] + \
|
|
736
|
+
[fp_channels] * self.decoder_layers
|
|
737
|
+
layers.append(FeaturePropogation(mlp))
|
|
738
|
+
self.in_channels = fp_channels
|
|
739
|
+
return nn.Sequential(*layers)
|
|
740
|
+
|
|
741
|
+
def forward(self, p, f, cls_label):
|
|
742
|
+
"""
|
|
743
|
+
Args:
|
|
744
|
+
p (List(Tensor)): List of tensor for p, length 2, input p and center p
|
|
745
|
+
f (List(Tensor)): List of tensor for feature maps, input features and out features
|
|
746
|
+
"""
|
|
747
|
+
if len(p) != (self.n_decoder_stages + 1):
|
|
748
|
+
for i in range(self.n_decoder_stages - 1):
|
|
749
|
+
pos = p[i]
|
|
750
|
+
idx = self.sample_fn(pos, pos.shape[1] // self.scale).long()
|
|
751
|
+
new_p = torch.gather(pos, 1, idx.unsqueeze(-1).expand(-1, -1, 3))
|
|
752
|
+
p.insert(1, new_p)
|
|
753
|
+
f.insert(1, None)
|
|
754
|
+
cls_token = f[-1][:, :, 0:1]
|
|
755
|
+
f[-1] = f[-1][:, :, 1:].contiguous()
|
|
756
|
+
|
|
757
|
+
B, N = p[0].shape[0:2]
|
|
758
|
+
if self.cls_map == 'pointnet2':
|
|
759
|
+
cls_one_hot = torch.zeros((B, self.num_classes), device=p[0].device)
|
|
760
|
+
cls_one_hot = cls_one_hot.scatter_(1, cls_label, 1).unsqueeze(-1).repeat(1, 1, N)
|
|
761
|
+
cls_one_hot = self.convc(cls_one_hot)
|
|
762
|
+
|
|
763
|
+
for i in range(-1, -len(self.decoder), -1):
|
|
764
|
+
f[i - 1] = self.decoder[i][1:](
|
|
765
|
+
[p[i], self.decoder[i][0]([p[i - 1], f[i - 1]], [p[i], f[i]])])[1]
|
|
766
|
+
|
|
767
|
+
i = -len(self.decoder)
|
|
768
|
+
f[i - 1] = self.decoder[i][1:](
|
|
769
|
+
[p[i], self.decoder[i][0]([p[i - 1], torch.cat([cls_one_hot, f[i - 1]], 1)], [p[i], f[i]])])[1]
|
|
770
|
+
|
|
771
|
+
f_out = f[-len(self.decoder) - 1]
|
|
772
|
+
|
|
773
|
+
if self.global_feat is not None:
|
|
774
|
+
global_feats = []
|
|
775
|
+
for token_type in self.global_feat:
|
|
776
|
+
if 'cls' in token_type:
|
|
777
|
+
global_feats.append(cls_token)
|
|
778
|
+
elif 'max' in token_type:
|
|
779
|
+
global_feats.append(torch.max(f_out, dim=2, keepdim=True)[0])
|
|
780
|
+
elif token_type in ['avg', 'mean']:
|
|
781
|
+
global_feats.append(torch.mean(f_out, dim=2, keepdim=True))
|
|
782
|
+
global_feats = torch.cat(global_feats, dim=1).expand(-1, -1, f_out.shape[-1])
|
|
783
|
+
f_out = torch.cat((global_feats, f_out), dim=1)
|
|
784
|
+
return f_out
|