BIMStruct3D-segmentation / pointcept /models /stratified_transformer /stratified_transformer_v1m2_refine.py
Download pointcept/models/stratified_transformer/stratified_transformer_v1m2_refine.py from dfki-av/BIMStruct3D-segmentation: direct link, hf CLI and curl.
- Browser
- Download file 25.8 kB
-
https://huggingface.co/dfki-av/BIMStruct3D-segmentation/resolve/7ab05dd198d38ea08e60bb1e4f3f04aa369975c2/pointcept/models/stratified_transformer/stratified_transformer_v1m2_refine.py
- Command line
-
hf download hf://dfki-av/BIMStruct3D-segmentation@7ab05dd198d38ea08e60bb1e4f3f04aa369975c2/pointcept/models/stratified_transformer/stratified_transformer_v1m2_refine.py
-
curl -L -o stratified_transformer_v1m2_refine.py https://huggingface.co/dfki-av/BIMStruct3D-segmentation/resolve/7ab05dd198d38ea08e60bb1e4f3f04aa369975c2/pointcept/models/stratified_transformer/stratified_transformer_v1m2_refine.py
25.8 kB
| """ | |
| Stratified Transformer | |
| Modified from https://github.com/dvlab-research/Stratified-Transformer | |
| Author: Xiaoyang Wu (xiaoyang.wu.cs@gmail.com) | |
| Please cite our work if the code is helpful to you. | |
| """ | |
| from copy import deepcopy | |
| import torch | |
| import torch.nn as nn | |
| try: | |
| import torch_points_kernels as tp | |
| except ImportError: | |
| tp = None | |
| try: | |
| from torch_points3d.modules.KPConv.kernels import KPConvLayer | |
| from torch_points3d.core.common_modules import FastBatchNorm1d | |
| except ImportError: | |
| KPConvLayer = None | |
| FastBatchNorm1d = None | |
| from torch_scatter import scatter_softmax | |
| from timm.layers import DropPath, trunc_normal_ | |
| from torch_geometric.nn.pool import voxel_grid | |
| try: | |
| import pointops2.pointops as pointops | |
| except ImportError: | |
| pointops = None | |
| from pointcept.models.builder import MODELS | |
| def offset2batch(offset): | |
| return ( | |
| torch.cat( | |
| [ | |
| ( | |
| torch.tensor([i] * (o - offset[i - 1])) | |
| if i > 0 | |
| else torch.tensor([i] * o) | |
| ) | |
| for i, o in enumerate(offset) | |
| ], | |
| dim=0, | |
| ) | |
| .long() | |
| .to(offset.device) | |
| ) | |
| def grid_sample(coords, batch, size, start, return_p2v=True): | |
| cluster = voxel_grid(coords, batch, size, start=start) | |
| if not return_p2v: | |
| unique, cluster = torch.unique(cluster, sorted=True, return_inverse=True) | |
| return cluster | |
| else: | |
| unique, cluster, counts = torch.unique( | |
| cluster, sorted=True, return_inverse=True, return_counts=True | |
| ) | |
| # obtain p2v_map | |
| n = unique.shape[0] | |
| k = counts.max().item() | |
| p2v_map = cluster.new_zeros(n, k) | |
| mask = torch.arange(k).cuda().unsqueeze(0) < counts.unsqueeze(-1) | |
| p2v_map[mask] = torch.argsort(cluster) | |
| return cluster, p2v_map, counts | |
| class WindowAttention(nn.Module): | |
| """Window based multi-head self attention (W-MSA) module with relative position bias. | |
| It supports both of shifted and non-shifted window. | |
| """ | |
| def __init__( | |
| self, | |
| embed_channels, | |
| num_heads, | |
| window_size, | |
| quant_size, | |
| attn_drop=0.0, | |
| proj_drop=0.0, | |
| scale=None, | |
| rel_query=True, | |
| rel_key=True, | |
| rel_value=True, | |
| qkv_bias=True, | |
| ): | |
| super().__init__() | |
| self.embed_channels = embed_channels | |
| self.head_channels = embed_channels // num_heads | |
| self.num_heads = num_heads | |
| self.scale = scale or self.head_channels**-0.5 | |
| self.window_size = window_size | |
| self.quant_size = quant_size | |
| self.rel_query = rel_query | |
| self.rel_key = rel_key | |
| self.rel_value = rel_value | |
| self.quant_grid_length = int((2 * window_size + 1e-4) // quant_size) | |
| assert self.rel_query and self.rel_key | |
| if rel_query: | |
| self.relative_pos_query_table = nn.Parameter( | |
| torch.zeros( | |
| 2 * self.quant_grid_length, self.num_heads, self.head_channels, 3 | |
| ) | |
| ) | |
| trunc_normal_(self.relative_pos_query_table, std=0.02) | |
| if rel_key: | |
| self.relative_pos_key_table = nn.Parameter( | |
| torch.zeros( | |
| 2 * self.quant_grid_length, self.num_heads, self.head_channels, 3 | |
| ) | |
| ) | |
| trunc_normal_(self.relative_pos_query_table, std=0.02) | |
| if rel_value: | |
| self.relative_pos_value_table = nn.Parameter( | |
| torch.zeros( | |
| 2 * self.quant_grid_length, self.num_heads, self.head_channels, 3 | |
| ) | |
| ) | |
| trunc_normal_(self.relative_pos_query_table, std=0.02) | |
| self.qkv = nn.Linear(embed_channels, embed_channels * 3, bias=qkv_bias) | |
| self.attn_drop = nn.Dropout(attn_drop, inplace=True) | |
| self.proj = nn.Linear(embed_channels, embed_channels) | |
| self.proj_drop = nn.Dropout(proj_drop, inplace=True) | |
| self.softmax = nn.Softmax(dim=-1) | |
| def forward(self, feats, coords, index_0, index_1, index_0_offsets, n_max): | |
| n, c = feats.shape | |
| m = index_0.shape[0] | |
| assert index_0.shape[0] == index_1.shape[0] | |
| qkv = ( | |
| self.qkv(feats) | |
| .reshape(n, 3, self.num_heads, c // self.num_heads) | |
| .permute(1, 0, 2, 3) | |
| .contiguous() | |
| ) | |
| query, key, value = qkv[0], qkv[1], qkv[2] | |
| query = query * self.scale | |
| attn_flat = pointops.attention_step1_v2( | |
| query.float(), key.float(), index_1.int(), index_0_offsets.int(), n_max | |
| ) | |
| # Position embedding | |
| relative_position = coords[index_0] - coords[index_1] | |
| relative_position = torch.round(relative_position * 100000) / 100000 | |
| relative_position_index = torch.div( | |
| relative_position + 2 * self.window_size - 1e-4, | |
| self.quant_size, | |
| rounding_mode="trunc", | |
| ) | |
| # relative_position_index = (relative_position + 2 * self.window_size - 1e-4) // self.quant_size | |
| assert (relative_position_index >= 0).all() | |
| assert (relative_position_index <= 2 * self.quant_grid_length - 1).all() | |
| if self.rel_query and self.rel_key: | |
| relative_position_bias = pointops.dot_prod_with_idx_v3( | |
| query.float(), | |
| index_0_offsets.int(), | |
| n_max, | |
| key.float(), | |
| index_1.int(), | |
| self.relative_pos_query_table.float(), | |
| self.relative_pos_key_table.float(), | |
| relative_position_index.int(), | |
| ) | |
| elif self.rel_query: | |
| relative_position_bias = pointops.dot_prod_with_idx( | |
| query.float(), | |
| index_0.int(), | |
| self.relative_pos_query_table.float(), | |
| relative_position_index.int(), | |
| ) # [M, num_heads] | |
| elif self.rel_key: | |
| relative_position_bias = pointops.dot_prod_with_idx( | |
| key.float(), | |
| index_1.int(), | |
| self.relative_pos_key_table.float(), | |
| relative_position_index.int(), | |
| ) # [M, num_heads] | |
| else: | |
| relative_position_bias = 0.0 | |
| attn_flat += relative_position_bias | |
| softmax_attn_flat = scatter_softmax(src=attn_flat, index=index_0, dim=0) | |
| if self.rel_value: | |
| x = pointops.attention_step2_with_rel_pos_value_v2( | |
| softmax_attn_flat.float(), | |
| value.float(), | |
| index_0_offsets.int(), | |
| n_max, | |
| index_1.int(), | |
| self.relative_pos_value_table.float(), | |
| relative_position_index.int(), | |
| ) | |
| else: | |
| x = pointops.attention_step2( | |
| softmax_attn_flat.float(), value.float(), index_0.int(), index_1.int() | |
| ) | |
| x = x.view(n, c) | |
| x = self.proj(x) | |
| x = self.proj_drop(x) | |
| return x | |
| class MLP(nn.Module): | |
| def __init__(self, in_channels, hidden_channels=None, out_channels=None, drop=0.0): | |
| super().__init__() | |
| out_channels = out_channels or in_channels | |
| hidden_channels = hidden_channels or in_channels | |
| self.fc1 = nn.Linear(in_channels, hidden_channels) | |
| self.act = nn.GELU() | |
| self.fc2 = nn.Linear(hidden_channels, out_channels) | |
| self.drop = nn.Dropout(drop, inplace=True) | |
| def forward(self, x): | |
| x = self.fc1(x) | |
| x = self.act(x) | |
| x = self.drop(x) | |
| x = self.fc2(x) | |
| x = self.drop(x) | |
| return x | |
| class Block(nn.Module): | |
| def __init__( | |
| self, | |
| embed_channels, | |
| num_heads, | |
| window_size, | |
| quant_size, | |
| mlp_expend_ratio=4.0, | |
| drop_path=0.0, | |
| qk_scale=None, | |
| rel_query=True, | |
| rel_key=True, | |
| rel_value=True, | |
| qkv_bias=True, | |
| ): | |
| super().__init__() | |
| self.norm1 = nn.LayerNorm(embed_channels) | |
| self.attn = WindowAttention( | |
| embed_channels, | |
| num_heads, | |
| window_size, | |
| quant_size, | |
| scale=qk_scale, | |
| rel_query=rel_query, | |
| rel_key=rel_key, | |
| rel_value=rel_value, | |
| qkv_bias=qkv_bias, | |
| ) | |
| self.drop_path = DropPath(drop_path) if drop_path > 0.0 else nn.Identity() | |
| self.norm2 = nn.LayerNorm(embed_channels) | |
| self.mlp = MLP( | |
| in_channels=embed_channels, | |
| hidden_channels=int(embed_channels * mlp_expend_ratio), | |
| ) | |
| def forward(self, feats, coords, index_0, index_1, index_0_offsets, n_max): | |
| short_cut = feats | |
| feats = self.norm1(feats) | |
| feats = self.attn(feats, coords, index_0, index_1, index_0_offsets, n_max) | |
| feats = short_cut + self.drop_path(feats) | |
| feats += self.drop_path(self.mlp(self.norm2(feats))) | |
| return feats | |
| class BasicLayer(nn.Module): | |
| def __init__( | |
| self, | |
| embed_channels, | |
| out_channels, | |
| depth, | |
| num_heads, | |
| window_size, | |
| quant_size, | |
| mlp_expend_ratio=4.0, | |
| down_ratio=0.25, | |
| down_num_sample=16, | |
| drop_path=None, | |
| qk_scale=None, | |
| down=True, | |
| rel_query=True, | |
| rel_key=True, | |
| rel_value=True, | |
| qkv_bias=True, | |
| ): | |
| super().__init__() | |
| self.depth = depth | |
| self.window_size = window_size | |
| self.quant_size = quant_size | |
| self.down_ratio = down_ratio | |
| if isinstance(drop_path, list): | |
| drop_path = drop_path | |
| assert len(drop_path) == depth | |
| elif isinstance(drop_path, float): | |
| drop_path = [deepcopy(drop_path) for _ in range(depth)] | |
| else: | |
| drop_path = [0.0 for _ in range(depth)] | |
| self.blocks = nn.ModuleList() | |
| for i in range(depth): | |
| block = Block( | |
| embed_channels, | |
| num_heads, | |
| window_size, | |
| quant_size, | |
| mlp_expend_ratio=mlp_expend_ratio, | |
| drop_path=drop_path[i], | |
| qk_scale=qk_scale, | |
| rel_query=rel_query, | |
| rel_key=rel_key, | |
| rel_value=rel_value, | |
| qkv_bias=qkv_bias, | |
| ) | |
| self.blocks.append(block) | |
| self.down = ( | |
| TransitionDown(embed_channels, out_channels, down_ratio, down_num_sample) | |
| if down | |
| else None | |
| ) | |
| def forward(self, feats, coords, offset): | |
| # window_size -> [window_size, window_size, window_size] | |
| window_size = torch.tensor( | |
| [self.window_size] * 3, dtype=coords.dtype, device=coords.device | |
| ) | |
| new_window_size = 2 * torch.tensor( | |
| [self.window_size] * 3, dtype=coords.dtype, device=coords.device | |
| ) | |
| batch = offset2batch(offset) | |
| # compute new offset | |
| new_offset = [int(offset[0].item() * self.down_ratio) + 1] | |
| count = int(offset[0].item() * self.down_ratio) + 1 | |
| for i in range(1, offset.shape[0]): | |
| count += ( | |
| int((offset[i].item() - offset[i - 1].item()) * self.down_ratio) + 1 | |
| ) | |
| new_offset.append(count) | |
| new_offset = torch.cuda.IntTensor(new_offset) | |
| down_idx = pointops.furthestsampling(coords, offset.int(), new_offset.int()) | |
| # compute window mapping | |
| coords_min = coords.min(0).values | |
| v2p_map, p2v_map, counts = grid_sample(coords, batch, window_size, start=None) | |
| shift_size = window_size * 1 / 2 | |
| shift_v2p_map, shift_p2v_map, shift_counts = grid_sample( | |
| coords + shift_size, batch, window_size, start=coords_min | |
| ) | |
| new_v2p_map, new_p2v_map, new_counts = grid_sample( | |
| coords, batch, new_window_size, start=None | |
| ) | |
| shift_size = new_window_size * 1 / 2 | |
| shift_new_v2p_map, shift_new_p2v_map, shift_new_counts = grid_sample( | |
| coords + shift_size, batch, new_window_size, start=coords_min | |
| ) | |
| # stratified attention | |
| for i, blk in enumerate(self.blocks): | |
| p2v_map_blk = p2v_map if i % 2 == 0 else shift_p2v_map | |
| counts_blk = counts if i % 2 == 0 else shift_counts | |
| new_p2v_map_blk = new_p2v_map if i % 2 == 0 else shift_new_p2v_map | |
| new_counts_blk = new_counts if i % 2 == 0 else shift_new_counts | |
| n, k = p2v_map_blk.shape | |
| mask = torch.arange(k).unsqueeze(0).cuda() < counts_blk.unsqueeze(-1) | |
| mask_mat = mask.unsqueeze(-1) & mask.unsqueeze(-2) | |
| index_0 = p2v_map_blk.unsqueeze(-1).expand(-1, -1, k)[mask_mat] | |
| index_1 = p2v_map_blk.unsqueeze(1).expand(-1, k, -1)[mask_mat] | |
| down_mask = torch.zeros_like(batch).bool() | |
| down_mask[down_idx.long()] = True | |
| down_mask = down_mask[new_p2v_map_blk] # [n, k], down sample mask | |
| n, k = new_p2v_map_blk.shape | |
| mask = torch.arange(k).unsqueeze(0).cuda() < new_counts_blk.unsqueeze( | |
| -1 | |
| ) # [n, k] | |
| down_mask = down_mask & mask # down sample and window mask | |
| # [n, k, k] query: dense point in large windows; key: sparse point in large windows | |
| mask_mat = mask.unsqueeze(-1) & down_mask.unsqueeze(-2) | |
| if i % 2 == 0: | |
| # [n, k, 3] | |
| # window_coord = (coords[new_p2v_map_blk] - coords_min) // window_size | |
| window_coord = torch.div( | |
| coords[new_p2v_map_blk] - coords_min, | |
| window_size, | |
| rounding_mode="trunc", | |
| ) | |
| else: | |
| # [n, k, 3] | |
| # window_coord = (coords[new_p2v_map_blk] - coords_min + 1/2 * window_size) // window_size | |
| window_coord = torch.div( | |
| coords[new_p2v_map_blk] - coords_min + 1 / 2 * window_size, | |
| window_size, | |
| rounding_mode="trunc", | |
| ) | |
| # [n, k, k], whether pair points are in same small windows | |
| mask_mat_prev = ( | |
| window_coord.unsqueeze(2) != window_coord.unsqueeze(1) | |
| ).any(-1) | |
| mask_mat = mask_mat & mask_mat_prev | |
| new_index_0 = new_p2v_map_blk.unsqueeze(-1).expand(-1, -1, k)[mask_mat] | |
| new_index_1 = new_p2v_map_blk.unsqueeze(1).expand(-1, k, -1)[mask_mat] | |
| index_0 = torch.cat([index_0, new_index_0], 0) | |
| index_1 = torch.cat([index_1, new_index_1], 0) | |
| # rearrange index for acceleration | |
| index_0, indices = torch.sort(index_0) | |
| index_1 = index_1[indices] | |
| index_0_counts = index_0.bincount() | |
| n_max = index_0_counts.max() | |
| index_0_offsets = index_0_counts.cumsum(dim=-1) | |
| index_0_offsets = torch.cat( | |
| [torch.zeros(1, dtype=torch.long).cuda(), index_0_offsets], 0 | |
| ) | |
| feats = blk(feats, coords, index_0, index_1, index_0_offsets, n_max) | |
| if self.down: | |
| feats_down, coords_down, offset_down = self.down(feats, coords, offset) | |
| else: | |
| feats_down, coords_down, offset_down = None, None, None | |
| return feats, coords, offset, feats_down, coords_down, offset_down | |
| class TransitionDown(nn.Module): | |
| def __init__(self, in_channels, out_channels, ratio, k, norm_layer=nn.LayerNorm): | |
| super().__init__() | |
| self.ratio = ratio | |
| self.k = k | |
| self.norm = norm_layer(in_channels) if norm_layer else None | |
| self.linear = nn.Linear(in_channels, out_channels, bias=False) | |
| self.pool = nn.MaxPool1d(k) | |
| def forward(self, feats, coords, offset): | |
| new_offset, count = [int(offset[0].item() * self.ratio) + 1], int( | |
| offset[0].item() * self.ratio | |
| ) + 1 | |
| for i in range(1, offset.shape[0]): | |
| count += ((offset[i].item() - offset[i - 1].item()) * self.ratio) + 1 | |
| new_offset.append(count) | |
| new_offset = torch.cuda.IntTensor(new_offset) | |
| idx = pointops.furthestsampling(coords, offset, new_offset) # (m) | |
| new_coords = coords[idx.long(), :] # (m, 3) | |
| feats = pointops.queryandgroup( | |
| self.k, coords, new_coords, feats, None, offset, new_offset, use_xyz=False | |
| ) # (m, nsample, 3+c) | |
| m, k, c = feats.shape | |
| feats = ( | |
| self.linear(self.norm(feats.view(m * k, c)).view(m, k, c)) | |
| .transpose(1, 2) | |
| .contiguous() | |
| ) | |
| feats = self.pool(feats).squeeze(-1) # (m, c) | |
| return feats, new_coords, new_offset | |
| class TransitionUp(nn.Module): | |
| def __init__(self, in_channels, out_channels): | |
| super().__init__() | |
| self.in_channels = in_channels | |
| self.out_channels = out_channels | |
| self.linear1 = nn.Sequential( | |
| nn.LayerNorm(out_channels), nn.Linear(out_channels, out_channels) | |
| ) | |
| self.linear2 = nn.Sequential( | |
| nn.LayerNorm(in_channels), nn.Linear(in_channels, out_channels) | |
| ) | |
| def forward(self, feats, coords, offset, skip_feats, skip_coords, skip_offset): | |
| feats = self.linear1(skip_feats) + pointops.interpolation( | |
| coords, skip_coords, self.linear2(feats), offset, skip_offset | |
| ) | |
| return feats, skip_coords, skip_offset | |
| class KPConvSimpleBlock(nn.Module): | |
| def __init__( | |
| self, | |
| in_channels, | |
| out_channels, | |
| prev_grid_size, | |
| sigma=1.0, | |
| negative_slope=0.2, | |
| bn_momentum=0.02, | |
| ): | |
| super().__init__() | |
| self.kpconv = KPConvLayer( | |
| in_channels, | |
| out_channels, | |
| point_influence=prev_grid_size * sigma, | |
| add_one=False, | |
| ) | |
| self.bn = FastBatchNorm1d(out_channels, momentum=bn_momentum) | |
| self.activation = nn.LeakyReLU(negative_slope=negative_slope) | |
| def forward(self, feats, xyz, batch, neighbor_idx): | |
| # feats: [N, C] | |
| # coords: [N, 3] | |
| # batch: [N,] | |
| # neighbor_idx: [N, M] | |
| feats = self.kpconv(xyz, xyz, neighbor_idx, feats) | |
| feats = self.activation(self.bn(feats)) | |
| return feats | |
| class KPConvResBlock(nn.Module): | |
| def __init__( | |
| self, | |
| in_channels, | |
| out_channels, | |
| prev_grid_size, | |
| sigma=1.0, | |
| negative_slope=0.2, | |
| bn_momentum=0.02, | |
| ): | |
| super().__init__() | |
| d_2 = out_channels // 4 | |
| activation = nn.LeakyReLU(negative_slope=negative_slope) | |
| self.unary_1 = torch.nn.Sequential( | |
| nn.Linear(in_channels, d_2, bias=False), | |
| FastBatchNorm1d(d_2, momentum=bn_momentum), | |
| activation, | |
| ) | |
| self.unary_2 = torch.nn.Sequential( | |
| nn.Linear(d_2, out_channels, bias=False), | |
| FastBatchNorm1d(out_channels, momentum=bn_momentum), | |
| activation, | |
| ) | |
| self.kpconv = KPConvLayer( | |
| d_2, d_2, point_influence=prev_grid_size * sigma, add_one=False | |
| ) | |
| self.bn = FastBatchNorm1d(out_channels, momentum=bn_momentum) | |
| self.activation = activation | |
| if in_channels != out_channels: | |
| self.shortcut_op = torch.nn.Sequential( | |
| nn.Linear(in_channels, out_channels, bias=False), | |
| FastBatchNorm1d(out_channels, momentum=bn_momentum), | |
| ) | |
| else: | |
| self.shortcut_op = nn.Identity() | |
| def forward(self, feats, xyz, batch, neighbor_idx): | |
| # feats: [N, C] | |
| # coords: [N, 3] | |
| # batch: [N,] | |
| # neighbor_idx: [N, M] | |
| shortcut = feats | |
| feats = self.unary_1(feats) | |
| feats = self.kpconv(xyz, xyz, neighbor_idx, feats) | |
| feats = self.unary_2(feats) | |
| shortcut = self.shortcut_op(shortcut) | |
| feats += shortcut | |
| return feats | |
| class StratifiedTransformer(nn.Module): | |
| def __init__( | |
| self, | |
| in_channels, | |
| num_classes, | |
| channels=(48, 96, 192, 384, 384), | |
| num_heads=(6, 12, 24, 24), | |
| depths=(3, 9, 3, 3), | |
| window_size=(0.2, 0.4, 0.8, 1.6), | |
| quant_size=(0.01, 0.02, 0.04, 0.08), | |
| mlp_expend_ratio=4.0, | |
| down_ratio=0.25, | |
| down_num_sample=16, | |
| kp_ball_radius=2.5 * 0.02, | |
| kp_max_neighbor=34, | |
| kp_grid_size=0.02, | |
| kp_sigma=1.0, | |
| drop_path_rate=0.2, | |
| rel_query=True, | |
| rel_key=True, | |
| rel_value=True, | |
| qkv_bias=True, | |
| stem=True, | |
| ): | |
| super().__init__() | |
| assert ( | |
| KPConvLayer is not None and FastBatchNorm1d is not None | |
| ), "Please make sure torch_points3d is installed" | |
| assert tp is not None, "Please make sure torch_points_kernels is installed" | |
| assert pointops is not None, "Please make sure pointops2 is installed" | |
| # stochastic depth decay rule | |
| dpr = [x.item() for x in torch.linspace(0, drop_path_rate, sum(depths))] | |
| self.kp_ball_radius = kp_ball_radius | |
| self.kp_max_neighbor = kp_max_neighbor | |
| self.stem = stem | |
| if stem: | |
| self.point_embed = nn.ModuleList( | |
| [ | |
| KPConvSimpleBlock( | |
| in_channels, channels[0], kp_grid_size, sigma=kp_sigma | |
| ), | |
| KPConvResBlock( | |
| channels[0], channels[0], kp_grid_size, sigma=kp_sigma | |
| ), | |
| ] | |
| ) | |
| self.down = TransitionDown( | |
| channels[0], channels[1], down_ratio, down_num_sample | |
| ) | |
| else: | |
| assert channels[0] == channels[1] | |
| self.point_embed = nn.ModuleList( | |
| [ | |
| KPConvSimpleBlock( | |
| in_channels, channels[1], kp_grid_size, sigma=kp_sigma | |
| ), | |
| ] | |
| ) | |
| num_layers = len(depths) | |
| self.layers = nn.ModuleList() | |
| for i in range(num_layers): | |
| layer = BasicLayer( | |
| embed_channels=channels[i + 1], | |
| out_channels=channels[i + 2] if i < num_layers - 1 else channels[i + 1], | |
| depth=depths[i], | |
| num_heads=num_heads[i], | |
| window_size=window_size[i], | |
| quant_size=quant_size[i], | |
| mlp_expend_ratio=mlp_expend_ratio, | |
| down_ratio=down_ratio, | |
| down_num_sample=down_num_sample, | |
| drop_path=dpr[sum(depths[:i]) : sum(depths[: i + 1])], | |
| rel_query=rel_query, | |
| rel_key=rel_key, | |
| rel_value=rel_value, | |
| qkv_bias=qkv_bias, | |
| down=True if i < num_layers - 1 else False, | |
| ) | |
| self.layers.append(layer) | |
| self.up = nn.ModuleList( | |
| [ | |
| TransitionUp(channels[i + 1], channels[i]) | |
| for i in reversed(range(1, num_layers)) | |
| ] | |
| ) | |
| if self.stem: | |
| self.up.append(TransitionUp(channels[1], channels[0])) | |
| self.classifier = nn.Sequential( | |
| nn.Linear(channels[0], channels[0]), | |
| nn.BatchNorm1d(channels[0]), | |
| nn.ReLU(inplace=True), | |
| nn.Linear(channels[0], num_classes), | |
| ) | |
| self.init_weights() | |
| def forward(self, data_dict): | |
| feats = data_dict["feat"] | |
| coords = data_dict["coord"] | |
| offset = data_dict["offset"].int() | |
| batch = offset2batch(offset) | |
| neighbor_idx = tp.ball_query( | |
| self.kp_ball_radius, | |
| self.kp_max_neighbor, | |
| coords, | |
| coords, | |
| mode="partial_dense", | |
| batch_x=batch, | |
| batch_y=batch, | |
| )[0] | |
| feats_stack = [] | |
| coords_stack = [] | |
| offset_stack = [] | |
| for i, layer in enumerate(self.point_embed): | |
| feats = layer(feats, coords, batch, neighbor_idx) | |
| feats = feats.contiguous() | |
| if self.stem: | |
| feats_stack.append(feats) | |
| coords_stack.append(coords) | |
| offset_stack.append(offset) | |
| feats, coords, offset = self.down(feats, coords, offset) | |
| for i, layer in enumerate(self.layers): | |
| feats, coords, offset, feats_down, coords_down, offset_down = layer( | |
| feats, coords, offset | |
| ) | |
| feats_stack.append(feats) | |
| coords_stack.append(coords) | |
| offset_stack.append(offset) | |
| feats = feats_down | |
| coords = coords_down | |
| offset = offset_down | |
| feats = feats_stack.pop() | |
| coords = coords_stack.pop() | |
| offset = offset_stack.pop() | |
| for i, up in enumerate(self.up): | |
| feats, coords, offset = up( | |
| feats, | |
| coords, | |
| offset, | |
| feats_stack.pop(), | |
| coords_stack.pop(), | |
| offset_stack.pop(), | |
| ) | |
| out = self.classifier(feats) | |
| return out | |
| def init_weights(self): | |
| """Initialize the weights in backbone.""" | |
| def _init_weights(m): | |
| if isinstance(m, nn.Linear): | |
| trunc_normal_(m.weight, std=0.02) | |
| if isinstance(m, nn.Linear) and m.bias is not None: | |
| nn.init.constant_(m.bias, 0) | |
| elif isinstance(m, nn.LayerNorm) or isinstance(m, nn.BatchNorm1d): | |
| nn.init.constant_(m.bias, 0) | |
| nn.init.constant_(m.weight, 1.0) | |
| self.apply(_init_weights) | |