| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| import os | |
| import copy | |
| import math | |
| import numpy as np | |
| from torch_geometric.data import Data, DataLoader | |
| from torch_geometric.nn import ( | |
| GCNConv, | |
| GATConv, | |
| global_mean_pool, | |
| global_max_pool, | |
| JumpingKnowledge, | |
| ) | |
| from torch.nn import Sequential, Linear, ReLU, BatchNorm1d, Dropout | |
| from torch_geometric.nn import BatchNorm | |
| def knn(x, k): | |
| """ | |
| Computes the k-nearest neighbors for each point in x. | |
| Args: | |
| x (torch.Tensor): The input tensor of shape (batch_size, num_dims, num_points). | |
| k (int): The number of nearest neighbors to find. | |
| Returns: | |
| torch.Tensor: Indices of the k-nearest neighbors for each point, shape (batch_size, num_points, k). | |
| """ | |
| # Calculate pairwise distance, shape (batch_size, num_points, num_points) | |
| inner = -2 * torch.matmul(x.transpose(2, 1), x) | |
| xx = torch.sum(x**2, dim=1, keepdim=True) | |
| pairwise_distance = -xx - inner - xx.transpose(2, 1) | |
| # Retrieve the indices of the k nearest neighbors | |
| idx = pairwise_distance.topk(k=k, dim=-1)[1] | |
| return idx | |
| def get_graph_feature(x, k=20, idx=None): | |
| """ | |
| Constructs local graph features for each point by finding its k-nearest neighbors and | |
| concatenating the relative position vectors. | |
| Args: | |
| x (torch.Tensor): The input tensor of shape (batch_size, num_dims, num_points). | |
| k (int): The number of neighbors to consider for graph construction. | |
| idx (torch.Tensor, optional): Precomputed k-nearest neighbor indices. | |
| Returns: | |
| torch.Tensor: The constructed graph features of shape (batch_size, 2*num_dims, num_points, k). | |
| """ | |
| batch_size = x.size(0) | |
| num_points = x.size(2) | |
| x = x.view(batch_size, -1, num_points) | |
| # Compute k-nearest neighbors if not provided | |
| if idx is None: | |
| idx = knn(x, k=k) | |
| # Prepare indices for gathering | |
| device = x.device | |
| idx_base = torch.arange(0, batch_size, device=device).view(-1, 1, 1) * num_points | |
| idx = idx + idx_base | |
| idx = idx.view(-1) | |
| _, num_dims, _ = x.size() | |
| x = x.transpose(2, 1).contiguous() | |
| # Gather neighbors for each point to construct local regions | |
| feature = x.view(batch_size * num_points, -1)[idx, :] | |
| feature = feature.view(batch_size, num_points, k, num_dims) | |
| # Expand x to match the dimensions for broadcasting subtraction | |
| x = x.view(batch_size, num_points, 1, num_dims).repeat(1, 1, k, 1) | |
| # Concatenate the original point features with the relative positions to form the graph features | |
| feature = torch.cat((feature - x, x), dim=3).permute(0, 3, 1, 2).contiguous() | |
| return feature | |
| # class Model(nn.Module): | |
| # """ | |
| # Deep Graph Convolutional Neural Network for Regression Tasks (RegDGCNN) for processing 3D point cloud data. | |
| # This network architecture extracts hierarchical features from point clouds using graph-based convolutions, | |
| # enabling effective learning of spatial structures. | |
| # """ | |
| # def __init__(self, args, device): | |
| # """ | |
| # Initializes the RegDGCNN model with specified configurations. | |
| # Args: | |
| # args (dict): Configuration parameters including 'k' for the number of neighbors, 'emb_dims' for embedding | |
| # dimensions, and 'dropout' rate. | |
| # output_channels (int): Number of output channels (e.g., for drag prediction, this is 1). | |
| # """ | |
| # super(Model, self).__init__() | |
| # self.__name__ = 'RegDGCNN' | |
| # self.args = args | |
| # self.k = 20 # Number of nearest neighbors | |
| # # Batch normalization layers to stabilize and accelerate training | |
| # self.bn1 = nn.BatchNorm2d(args.n_hidden) | |
| # self.bn2 = nn.BatchNorm2d(args.n_hidden * 2) | |
| # self.bn3 = nn.BatchNorm2d(args.n_hidden * 2) | |
| # self.bn4 = nn.BatchNorm2d(args.n_hidden * 4) | |
| # self.bn5 = nn.BatchNorm1d(args.emb_dims) | |
| # # EdgeConv layers: Convolutional layers leveraging local neighborhood information | |
| # self.conv1 = nn.Sequential(nn.Conv2d(2*(args.fun_dim + args.space_dim), args.n_hidden, kernel_size=1, bias=False), | |
| # self.bn1, | |
| # nn.LeakyReLU(negative_slope=0.2)) | |
| # self.conv2 = nn.Sequential(nn.Conv2d(args.n_hidden * 2, args.n_hidden * 2, kernel_size=1, bias=False), | |
| # self.bn2, | |
| # nn.LeakyReLU(negative_slope=0.2)) | |
| # self.conv3 = nn.Sequential(nn.Conv2d(args.n_hidden * 4, args.n_hidden * 2, kernel_size=1, bias=False), | |
| # self.bn3, | |
| # nn.LeakyReLU(negative_slope=0.2)) | |
| # self.conv4 = nn.Sequential(nn.Conv2d(args.n_hidden * 4, args.n_hidden * 4, kernel_size=1, bias=False), | |
| # self.bn4, | |
| # nn.LeakyReLU(negative_slope=0.2)) | |
| # self.conv5 = nn.Sequential(nn.Conv1d(args.n_hidden * 9, args.emb_dims, kernel_size=1, bias=False), | |
| # self.bn5, | |
| # nn.LeakyReLU(negative_slope=0.2)) | |
| # # Fully connected layers to interpret the extracted features and make predictions | |
| # self.linear1 = nn.Linear(args.emb_dims*2, 128, bias=False) | |
| # self.bn6 = nn.LayerNorm(128) | |
| # self.dp1 = nn.Dropout(p=args.dropout) | |
| # self.linear2 = nn.Linear(128, 64) | |
| # self.bn7 = nn.LayerNorm(64) | |
| # self.dp2 = nn.Dropout(p=args.dropout) | |
| # self.linear3 = nn.Linear(64, 32) | |
| # self.bn8 = nn.LayerNorm(32) | |
| # self.dp3 = nn.Dropout(p=args.dropout) | |
| # self.linear4 = nn.Linear(32, 16) | |
| # self.bn9 = nn.LayerNorm(16) | |
| # self.dp4 = nn.Dropout(p=args.dropout) | |
| # self.linear5 = nn.Linear(16, args.out_dim) # The final output layer | |
| # def _make_norm_layer(self, channels): | |
| # """创建归一化层,支持batch size=1""" | |
| # return nn.GroupNorm(num_groups=min(32, channels), num_channels=channels) | |
| # def forward(self, x, fx, T=None, geo=None): | |
| # """ | |
| # Forward pass of the model to process input data and predict outputs. | |
| # Args: | |
| # x (torch.Tensor): Input tensor representing a batch of point clouds. | |
| # Returns: | |
| # torch.Tensor: Model predictions for the input batch. | |
| # """ | |
| # batch_size = x.size(0) | |
| # # 兼容 batch_size = 1 输入:去除 batch 维度 | |
| # x = torch.cat([x, fx], dim=-1) | |
| # x = x.permute(0, 2, 1) | |
| # print(f"x:{x.shape}") | |
| # # Extract graph features and apply EdgeConv blocks | |
| # x = get_graph_feature(x, k=self.k) # (batch_size, 3, num_points) -> (batch_size, 3*2, num_points, k) | |
| # print(f"x:{x.shape}") | |
| # x = self.conv1(x) # (batch_size, 3*2, num_points, k) -> (batch_size, 256, num_points, k) | |
| # # Global max pooling | |
| # x1 = x.max(dim=-1, keepdim=False)[0] # (batch_size, 64, num_points, k) -> (batch_size, 64, num_points) | |
| # # Repeat the process for subsequent EdgeConv blocks | |
| # x = get_graph_feature(x1, k=self.k) # (batch_size, 256, num_points) -> (batch_size, 256*2, num_points, k) | |
| # x = self.conv2(x) # (batch_size, 256*2, num_points, k) -> (batch_size, 512, num_points, k) | |
| # x2 = x.max(dim=-1, keepdim=False)[0] # (batch_size, 512, num_points, k) -> (batch_size, 512, num_points) | |
| # x = get_graph_feature(x2, k=self.k) # (batch_size, 512, num_points) -> (batch_size, 512*2, num_points, k) | |
| # x = self.conv3(x) # (batch_size, 512*2, num_points, k) -> (batch_size, 512, num_points, k) | |
| # x3 = x.max(dim=-1, keepdim=False)[0] # (batch_size, 512, num_points, k) -> (batch_size, 512, num_points) | |
| # x = get_graph_feature(x3, k=self.k) # (batch_size, 512, num_points) -> (batch_size, 512*2, num_points, k) | |
| # x = self.conv4(x) # (batch_size, 512*2, num_points, k) -> (batch_size, 1024, num_points, k) | |
| # x4 = x.max(dim=-1, keepdim=False)[0] # (batch_size, 1024, num_points, k) -> (batch_size, 1024, num_points) | |
| # # Concatenate features from all EdgeConv blocks | |
| # x = torch.cat((x1, x2, x3, x4), dim=1) # (batch_size, 256+512+512+1024, num_points) | |
| # # Apply the final convolutional block | |
| # x = self.conv5(x) # (batch_size, 256+512+512+1024, num_points) -> (batch_size, emb_dims, num_points) | |
| # # Combine global max and average pooling features | |
| # # (batch_size, emb_dims, num_points) -> (batch_size, emb_dims) | |
| # x1 = F.adaptive_max_pool1d(x, 1).view(batch_size, -1) | |
| # # (batch_size, emb_dims, num_points) -> (batch_size, emb_dims) | |
| # x2 = F.adaptive_avg_pool1d(x, 1).view(batch_size, -1) | |
| # x = torch.cat((x1, x2), 1) # (batch_size, emb_dims*2) | |
| # print(f"x:{x.shape}") | |
| # # Process features through fully connected layers with dropout and batch normalization | |
| # x = F.leaky_relu(self.bn6(self.linear1(x)), negative_slope=0.2) # (batch_size, emb_dims*2) -> (batch_size, 128) | |
| # x = self.dp1(x) | |
| # x = F.leaky_relu(self.bn7(self.linear2(x)), negative_slope=0.2) # (batch_size, 128) -> (batch_size, 64) | |
| # x = self.dp2(x) | |
| # x = F.leaky_relu(self.bn8(self.linear3(x)), negative_slope=0.2) # (batch_size, 64) -> (batch_size, 32) | |
| # x = self.dp3(x) | |
| # x = F.leaky_relu(self.bn9(self.linear4(x)), negative_slope=0.2) # (batch_size, 32) -> (batch_size, 16) | |
| # x = self.dp4(x) | |
| # # Final linear layer to produce the output | |
| # x = self.linear5(x) # (batch_size, 16) -> (batch_size, 1) | |
| # print(f"x:{x.shape}") | |
| # x = x.permute(0, 2, 1) | |
| # return x | |
| class Model(nn.Module): | |
| """ | |
| Deep Graph Convolutional Neural Network for Regression Tasks (RegDGCNN) for processing 3D point cloud data. | |
| This network architecture extracts hierarchical features from point clouds using graph-based convolutions, | |
| enabling effective learning of spatial structures. | |
| """ | |
| def __init__(self, args, device): | |
| """ | |
| Initializes the RegDGCNN model with specified configurations. | |
| Args: | |
| args (dict): Configuration parameters including 'k' for the number of neighbors, 'emb_dims' for embedding | |
| dimensions, and 'dropout' rate. | |
| output_channels (int): Number of output channels (e.g., for drag prediction, this is 1). | |
| """ | |
| super(Model, self).__init__() | |
| self.__name__ = "RegDGCNN" | |
| self.args = args | |
| self.k = 40 # Number of nearest neighbors | |
| # Batch normalization layers to stabilize and accelerate training | |
| self.bn1 = nn.BatchNorm2d(args.n_hidden) | |
| self.bn2 = nn.BatchNorm2d(args.n_hidden * 2) | |
| self.bn3 = nn.BatchNorm2d(args.n_hidden * 2) | |
| self.bn4 = nn.BatchNorm2d(args.n_hidden * 4) | |
| self.bn5 = nn.BatchNorm1d(args.emb_dims) | |
| # EdgeConv layers: Convolutional layers leveraging local neighborhood information | |
| self.conv1 = nn.Sequential( | |
| nn.Conv2d( | |
| 2 * (args.fun_dim + args.space_dim), | |
| args.n_hidden, | |
| kernel_size=1, | |
| bias=False, | |
| ), | |
| self.bn1, | |
| nn.LeakyReLU(negative_slope=0.2), | |
| ) | |
| self.conv2 = nn.Sequential( | |
| nn.Conv2d(args.n_hidden * 2, args.n_hidden * 2, kernel_size=1, bias=False), | |
| self.bn2, | |
| nn.LeakyReLU(negative_slope=0.2), | |
| ) | |
| self.conv3 = nn.Sequential( | |
| nn.Conv2d(args.n_hidden * 4, args.n_hidden * 2, kernel_size=1, bias=False), | |
| self.bn3, | |
| nn.LeakyReLU(negative_slope=0.2), | |
| ) | |
| self.conv4 = nn.Sequential( | |
| nn.Conv2d(args.n_hidden * 4, args.n_hidden * 4, kernel_size=1, bias=False), | |
| self.bn4, | |
| nn.LeakyReLU(negative_slope=0.2), | |
| ) | |
| self.conv5 = nn.Sequential( | |
| nn.Conv1d(args.n_hidden * 9, args.emb_dims, kernel_size=1, bias=False), | |
| self.bn5, | |
| nn.LeakyReLU(negative_slope=0.2), | |
| ) | |
| # Fully connected layers to interpret the extracted features and make predictions | |
| self.point_pred = nn.Sequential( | |
| nn.Linear(args.emb_dims, 64), nn.ReLU(), nn.Linear(64, args.out_dim) | |
| ) | |
| def _make_norm_layer(self, channels): | |
| return nn.GroupNorm(num_groups=min(32, channels), num_channels=channels) | |
| def forward(self, x, fx, T=None, geo=None): | |
| """ | |
| Forward pass of the model to process input data and predict outputs. | |
| Args: | |
| x (torch.Tensor): Input tensor representing a batch of point clouds. | |
| Returns: | |
| torch.Tensor: Model predictions for the input batch. | |
| """ | |
| batch_size = x.size(0) | |
| # 兼容 batch_size = 1 输入:去除 batch 维度 | |
| x = torch.cat([x, fx], dim=-1) | |
| x = x.permute(0, 2, 1) | |
| # Extract graph features and apply EdgeConv blocks | |
| x = get_graph_feature( | |
| x, k=self.k | |
| ) # (batch_size, 3, num_points) -> (batch_size, 3*2, num_points, k) | |
| x = self.conv1( | |
| x | |
| ) # (batch_size, 3*2, num_points, k) -> (batch_size, 256, num_points, k) | |
| # Global max pooling | |
| x1 = x.max(dim=-1, keepdim=False)[ | |
| 0 | |
| ] # (batch_size, 64, num_points, k) -> (batch_size, 64, num_points) | |
| # Repeat the process for subsequent EdgeConv blocks | |
| x = get_graph_feature( | |
| x1, k=self.k | |
| ) # (batch_size, 256, num_points) -> (batch_size, 256*2, num_points, k) | |
| x = self.conv2( | |
| x | |
| ) # (batch_size, 256*2, num_points, k) -> (batch_size, 512, num_points, k) | |
| x2 = x.max(dim=-1, keepdim=False)[ | |
| 0 | |
| ] # (batch_size, 512, num_points, k) -> (batch_size, 512, num_points) | |
| x = get_graph_feature( | |
| x2, k=self.k | |
| ) # (batch_size, 512, num_points) -> (batch_size, 512*2, num_points, k) | |
| x = self.conv3( | |
| x | |
| ) # (batch_size, 512*2, num_points, k) -> (batch_size, 512, num_points, k) | |
| x3 = x.max(dim=-1, keepdim=False)[ | |
| 0 | |
| ] # (batch_size, 512, num_points, k) -> (batch_size, 512, num_points) | |
| x = get_graph_feature( | |
| x3, k=self.k | |
| ) # (batch_size, 512, num_points) -> (batch_size, 512*2, num_points, k) | |
| x = self.conv4( | |
| x | |
| ) # (batch_size, 512*2, num_points, k) -> (batch_size, 1024, num_points, k) | |
| x4 = x.max(dim=-1, keepdim=False)[ | |
| 0 | |
| ] # (batch_size, 1024, num_points, k) -> (batch_size, 1024, num_points) | |
| # Concatenate features from all EdgeConv blocks | |
| x = torch.cat( | |
| (x1, x2, x3, x4), dim=1 | |
| ) # (batch_size, 256+512+512+1024, num_points) | |
| # Apply the final convolutional block | |
| x = self.conv5( | |
| x | |
| ) # (batch_size, 256+512+512+1024, num_points) -> (batch_size, emb_dims, num_points) | |
| x = x.permute(0, 2, 1) # (batch_size, num_points, emb_dims) | |
| out = self.point_pred(x) # 点级预测层,输出 (batch_size, num_points, out_dim) | |
| return out | |