This commit is contained in:
erikwijmans
2018-01-06 12:13:52 -05:00
parent 7e746ba72a
commit 5a5adc2b77
20 changed files with 650 additions and 494 deletions
+2 -2
View File
@@ -11,7 +11,7 @@ cuda_compile(cuda_kernels SHARED ${cuda_kernels_src} OPTIONS -O3)
set(BUILD_CMD python "${CMAKE_CURRENT_SOURCE_DIR}/utils/build_ffi.py") set(BUILD_CMD python "${CMAKE_CURRENT_SOURCE_DIR}/utils/build_ffi.py")
file(GLOB wrapper_headers "${CMAKE_CURRENT_SOURCE_DIR}/utils/cinclude/*wrapper.h") file(GLOB wrapper_headers "${CMAKE_CURRENT_SOURCE_DIR}/utils/cinclude/*wrapper.h")
file(GLOB wrapper_sources "${CMAKE_CURRENT_SOURCE_DIR}/utils/csrs/*.c") file(GLOB wrapper_sources "${CMAKE_CURRENT_SOURCE_DIR}/utils/csrs/*.c")
add_custom_command(OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/utils/_ext/__ext.so" add_custom_command(OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/utils/_ext/pointnet2/_pointnet2.so"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/utils WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/utils
COMMAND ${BUILD_CMD} --build --objs ${cuda_kernels} COMMAND ${BUILD_CMD} --build --objs ${cuda_kernels}
DEPENDS ${cuda_kernels} DEPENDS ${cuda_kernels}
@@ -20,6 +20,6 @@ add_custom_command(OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/utils/_ext/__ext.so"
VERBATIM) VERBATIM)
add_custom_target(ext ALL add_custom_target(ext ALL
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/utils/_ext/__ext.so") DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/utils/_ext/pointnet2/_pointnet2.so")
set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${CMAKE_CURRENT_SOURCE_DIR}/utils/_ext") set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${CMAKE_CURRENT_SOURCE_DIR}/utils/_ext")
+17 -12
View File
@@ -16,12 +16,10 @@ def _load_data_file(name):
class Indoor3DSemSeg(data.Dataset): class Indoor3DSemSeg(data.Dataset):
def __init__(self,
num_points, def __init__(
root, self, num_points, root, train=True, download=True, data_precent=1.0
train=True, ):
download=True,
data_precent=1.0):
super().__init__() super().__init__()
self.data_precent = data_precent self.data_precent = data_precent
root = os.path.abspath(root) root = os.path.abspath(root)
@@ -32,18 +30,23 @@ class Indoor3DSemSeg(data.Dataset):
if download and not os.path.exists(self.data_dir): if download and not os.path.exists(self.data_dir):
zipfile = os.path.join(root, os.path.basename(self.url)) zipfile = os.path.join(root, os.path.basename(self.url))
subprocess.check_call( subprocess.check_call(
shlex.split("curl {} -o {}".format(self.url, zipfile))) shlex.split("curl {} -o {}".format(self.url, zipfile))
)
subprocess.check_call(shlex.split("unzip {} -d {}".format(zipfile, root))) subprocess.check_call(
shlex.split("unzip {} -d {}".format(zipfile, root))
)
subprocess.check_call(shlex.split("rm {}".format(zipfile))) subprocess.check_call(shlex.split("rm {}".format(zipfile)))
self.train, self.num_points = train, num_points self.train, self.num_points = train, num_points
all_files = _get_data_files( all_files = _get_data_files(
os.path.join(self.data_dir, "all_files.txt")) os.path.join(self.data_dir, "all_files.txt")
)
room_filelist = _get_data_files( room_filelist = _get_data_files(
os.path.join(self.data_dir, "room_filelist.txt")) os.path.join(self.data_dir, "room_filelist.txt")
)
data_batchlist, label_batchlist = [], [] data_batchlist, label_batchlist = [], []
for f in all_files: for f in all_files:
@@ -74,9 +77,11 @@ class Indoor3DSemSeg(data.Dataset):
np.random.shuffle(pt_idxs) np.random.shuffle(pt_idxs)
current_points = torch.from_numpy(self.points[idx, pt_idxs, :]).type( current_points = torch.from_numpy(self.points[idx, pt_idxs, :]).type(
torch.FloatTensor) torch.FloatTensor
)
current_labels = torch.from_numpy(self.labels[idx, pt_idxs]).type( current_labels = torch.from_numpy(self.labels[idx, pt_idxs]).type(
torch.LongTensor) torch.LongTensor
)
return current_points, current_labels return current_points, current_labels
+13 -11
View File
@@ -19,12 +19,10 @@ def _load_data_file(name):
class ModelNet40Cls(data.Dataset): class ModelNet40Cls(data.Dataset):
def __init__(self,
num_points, def __init__(
root, self, num_points, root, transforms=None, train=True, download=True
transforms=None, ):
train=True,
download=True):
super().__init__() super().__init__()
self.transforms = transforms self.transforms = transforms
@@ -37,9 +35,12 @@ class ModelNet40Cls(data.Dataset):
if download and not os.path.exists(self.data_dir): if download and not os.path.exists(self.data_dir):
zipfile = os.path.join(root, os.path.basename(self.url)) zipfile = os.path.join(root, os.path.basename(self.url))
subprocess.check_call( subprocess.check_call(
shlex.split("curl {} -o {}".format(self.url, zipfile))) shlex.split("curl {} -o {}".format(self.url, zipfile))
)
subprocess.check_call(shlex.split("unzip {} -d {}".format(zipfile, root))) subprocess.check_call(
shlex.split("unzip {} -d {}".format(zipfile, root))
)
subprocess.check_call(shlex.split("rm {}".format(zipfile))) subprocess.check_call(shlex.split("rm {}".format(zipfile)))
@@ -83,9 +84,10 @@ class ModelNet40Cls(data.Dataset):
def randomize(self): def randomize(self):
self.actual_number_of_points = min( self.actual_number_of_points = min(
max( max(
np.random.randint(self.num_points * 0.8, np.random.randint(self.num_points * 0.8, self.num_points * 1.2),
self.num_points * 1.2), 1), 1
self.points.shape[1]) ), self.points.shape[1]
)
if __name__ == "__main__": if __name__ == "__main__":
+21 -8
View File
@@ -39,6 +39,7 @@ def model_fn_decorator(criterion):
class Pointnet2SSG(nn.Module): class Pointnet2SSG(nn.Module):
def __init__(self, num_classes, input_channels=9): def __init__(self, num_classes, input_channels=9):
super().__init__() super().__init__()
@@ -48,13 +49,17 @@ class Pointnet2SSG(nn.Module):
npoint=512, npoint=512,
radius=0.2, radius=0.2,
nsample=64, nsample=64,
mlp=[input_channels, 64, 64, 128])) mlp=[input_channels, 64, 64, 128]
)
)
self.SA_modules.append( self.SA_modules.append(
PointnetSAModule( PointnetSAModule(
npoint=128, npoint=128,
radius=0.4, radius=0.4,
nsample=64, nsample=64,
mlp=[128 + 3, 128, 128, 256])) mlp=[128 + 3, 128, 128, 256]
)
)
self.SA_modules.append(PointnetSAModule(mlp=[256 + 3, 256, 512, 1024])) self.SA_modules.append(PointnetSAModule(mlp=[256 + 3, 256, 512, 1024]))
self.FC_layer = nn.Sequential( self.FC_layer = nn.Sequential(
@@ -62,7 +67,8 @@ class Pointnet2SSG(nn.Module):
nn.Dropout(p=0.5), nn.Dropout(p=0.5),
pt_utils.FC(512, 256, bn=True), pt_utils.FC(512, 256, bn=True),
nn.Dropout(p=0.5), nn.Dropout(p=0.5),
pt_utils.FC(256, num_classes, activation=None)) pt_utils.FC(256, num_classes, activation=None)
)
def forward(self, xyz, points=None): def forward(self, xyz, points=None):
for module in self.SA_modules: for module in self.SA_modules:
@@ -72,6 +78,7 @@ class Pointnet2SSG(nn.Module):
class Pointnet2MSG(nn.Module): class Pointnet2MSG(nn.Module):
def __init__(self, num_classes, input_channels=9): def __init__(self, num_classes, input_channels=9):
super().__init__() super().__init__()
@@ -83,7 +90,9 @@ class Pointnet2MSG(nn.Module):
nsamples=[32, 64, 128], nsamples=[32, 64, 128],
mlps=[[input_channels, 32, 32, mlps=[[input_channels, 32, 32,
64], [input_channels, 64, 64, 128], 64], [input_channels, 64, 64, 128],
[input_channels, 64, 96, 128]])) [input_channels, 64, 96, 128]]
)
)
input_channels = 64 + 128 + 128 + 3 input_channels = 64 + 128 + 128 + 3
self.SA_modules.append( self.SA_modules.append(
@@ -92,17 +101,21 @@ class Pointnet2MSG(nn.Module):
radii=[0.2, 0.4, 0.8], radii=[0.2, 0.4, 0.8],
nsamples=[16, 32, 64], nsamples=[16, 32, 64],
mlps=[[input_channels, 64, 64, mlps=[[input_channels, 64, 64,
128], [input_channels, 128, 128, 256], 128], [input_channels, 128, 128, 256],
[input_channels, 128, 128, 256]])) [input_channels, 128, 128, 256]]
)
)
self.SA_modules.append( self.SA_modules.append(
PointnetSAModule(mlp=[128 + 256 + 256 + 3, 256, 512, 1024])) PointnetSAModule(mlp=[128 + 256 + 256 + 3, 256, 512, 1024])
)
self.FC_layer = nn.Sequential( self.FC_layer = nn.Sequential(
pt_utils.FC(1024, 512, bn=True), pt_utils.FC(1024, 512, bn=True),
nn.Dropout(p=0.5), nn.Dropout(p=0.5),
pt_utils.FC(512, 256, bn=True), pt_utils.FC(512, 256, bn=True),
nn.Dropout(p=0.5), nn.Dropout(p=0.5),
pt_utils.FC(256, num_classes, activation=None)) pt_utils.FC(256, num_classes, activation=None)
)
def forward(self, xyz, points=None): def forward(self, xyz, points=None):
for module in self.SA_modules: for module in self.SA_modules:
+49 -25
View File
@@ -38,6 +38,7 @@ def model_fn_decorator(criterion):
class Pointnet2SSG(nn.Module): class Pointnet2SSG(nn.Module):
def __init__(self, num_classes, input_channels=9): def __init__(self, num_classes, input_channels=9):
super().__init__() super().__init__()
@@ -49,32 +50,37 @@ class Pointnet2SSG(nn.Module):
npoint=1024, npoint=1024,
radius=0.1, radius=0.1,
nsample=32, nsample=32,
mlp=[input_channels, 32, 32, 64])) mlp=[input_channels, 32, 32, 64]
)
)
self.SA_modules.append( self.SA_modules.append(
PointnetSAModule( PointnetSAModule(
npoint=256, radius=0.2, nsample=32, mlp=[64 + 3, 64, 64, 128])) npoint=256, radius=0.2, nsample=32, mlp=[64 + 3, 64, 64, 128]
)
)
self.SA_modules.append( self.SA_modules.append(
PointnetSAModule( PointnetSAModule(
npoint=64, npoint=64, radius=0.4, nsample=32, mlp=[128 + 3, 128, 128, 256]
radius=0.4, )
nsample=32, )
mlp=[128 + 3, 128, 128, 256]))
self.SA_modules.append( self.SA_modules.append(
PointnetSAModule( PointnetSAModule(
npoint=16, npoint=16, radius=0.8, nsample=32, mlp=[256 + 3, 256, 256, 512]
radius=0.8, )
nsample=32, )
mlp=[256 + 3, 256, 256, 512]))
self.FP_modules = nn.ModuleList() self.FP_modules = nn.ModuleList()
self.FP_modules.append(PointnetFPModule(mlp=[128 + input_channels - 3, 128, 128, 128])) self.FP_modules.append(
PointnetFPModule(mlp=[128 + input_channels - 3, 128, 128, 128])
)
self.FP_modules.append(PointnetFPModule(mlp=[256 + 64, 256, 128])) self.FP_modules.append(PointnetFPModule(mlp=[256 + 64, 256, 128]))
self.FP_modules.append(PointnetFPModule(mlp=[256 + 128, 256, 256])) self.FP_modules.append(PointnetFPModule(mlp=[256 + 128, 256, 256]))
self.FP_modules.append(PointnetFPModule(mlp=[512 + 256, 256, 256])) self.FP_modules.append(PointnetFPModule(mlp=[512 + 256, 256, 256]))
self.FC_layer = nn.Sequential( self.FC_layer = nn.Sequential(
pt_utils.Conv1d(128, 128, bn=True), nn.Dropout(), pt_utils.Conv1d(128, 128, bn=True), nn.Dropout(),
pt_utils.Conv1d(128, num_classes, activation=None)) pt_utils.Conv1d(128, num_classes, activation=None)
)
def forward(self, xyz, points=None): def forward(self, xyz, points=None):
if points is not None: if points is not None:
@@ -91,13 +97,17 @@ class Pointnet2SSG(nn.Module):
l_points.append(li_points) l_points.append(li_points)
for i in range(-1, -(len(self.FP_modules + 1) - 1), -1): for i in range(-1, -(len(self.FP_modules + 1) - 1), -1):
l_points[i - 1] = self.FP_modules[i](l_xyz[i - 1], l_xyz[i], l_points[i - 1] = self.FP_modules[i](
l_points[i - 1], l_points[i]) l_xyz[i - 1], l_xyz[i], l_points[i - 1], l_points[i]
)
return self.FC_layer(l_points[0].transpose(1, 2)).transpose(1, 2).contiguous() return self.FC_layer(l_points[0].transpose(1,
2)).transpose(1,
2).contiguous()
class Pointnet2MSG(nn.Module): class Pointnet2MSG(nn.Module):
def __init__(self, num_classes, input_channels=9): def __init__(self, num_classes, input_channels=9):
super().__init__() super().__init__()
@@ -111,7 +121,9 @@ class Pointnet2MSG(nn.Module):
npoint=1024, npoint=1024,
radii=[0.05, 0.1], radii=[0.05, 0.1],
nsamples=[16, 32], nsamples=[16, 32],
mlps=[[c_in, 16, 16, 32], [c_in, 32, 32, 64]])) mlps=[[c_in, 16, 16, 32], [c_in, 32, 32, 64]]
)
)
c_out_0 = 32 + 64 c_out_0 = 32 + 64
c_in = c_out_0 + 3 c_in = c_out_0 + 3
@@ -120,7 +132,9 @@ class Pointnet2MSG(nn.Module):
npoint=256, npoint=256,
radii=[0.1, 0.2], radii=[0.1, 0.2],
nsamples=[16, 32], nsamples=[16, 32],
mlps=[[c_in, 64, 64, 128], [c_in, 64, 96, 128]])) mlps=[[c_in, 64, 64, 128], [c_in, 64, 96, 128]]
)
)
c_out_1 = 128 + 128 c_out_1 = 128 + 128
c_in = c_out_1 + 3 c_in = c_out_1 + 3
@@ -129,7 +143,9 @@ class Pointnet2MSG(nn.Module):
npoint=64, npoint=64,
radii=[0.2, 0.4], radii=[0.2, 0.4],
nsamples=[16, 32], nsamples=[16, 32],
mlps=[[c_in, 128, 196, 256], [c_in, 128, 196, 256]])) mlps=[[c_in, 128, 196, 256], [c_in, 128, 196, 256]]
)
)
c_out_2 = 256 + 256 c_out_2 = 256 + 256
c_in = c_out_2 + 3 c_in = c_out_2 + 3
@@ -138,20 +154,25 @@ class Pointnet2MSG(nn.Module):
npoint=16, npoint=16,
radii=[0.4, 0.8], radii=[0.4, 0.8],
nsamples=[16, 32], nsamples=[16, 32],
mlps=[[c_in, 256, 256, 512], [c_in, 256, 384, 512]])) mlps=[[c_in, 256, 256, 512], [c_in, 256, 384, 512]]
)
)
c_out_3 = 512 + 512 c_out_3 = 512 + 512
self.FP_modules = nn.ModuleList() self.FP_modules = nn.ModuleList()
self.FP_modules.append( self.FP_modules.append(
PointnetFPModule(mlp=[256 + input_channels - 3, 128, 128])) PointnetFPModule(mlp=[256 + input_channels - 3, 128, 128])
)
self.FP_modules.append(PointnetFPModule(mlp=[512 + c_out_0, 256, 256])) self.FP_modules.append(PointnetFPModule(mlp=[512 + c_out_0, 256, 256]))
self.FP_modules.append(PointnetFPModule(mlp=[512 + c_out_1, 512, 512])) self.FP_modules.append(PointnetFPModule(mlp=[512 + c_out_1, 512, 512]))
self.FP_modules.append( self.FP_modules.append(
PointnetFPModule(mlp=[c_out_3 + c_out_2, 512, 512])) PointnetFPModule(mlp=[c_out_3 + c_out_2, 512, 512])
)
self.FC_layer = nn.Sequential( self.FC_layer = nn.Sequential(
pt_utils.Conv1d(128, 128, bn=True), nn.Dropout(), pt_utils.Conv1d(128, 128, bn=True), nn.Dropout(),
pt_utils.Conv1d(128, num_classes, activation=None)) pt_utils.Conv1d(128, num_classes, activation=None)
)
def forward(self, xyz, points=None): def forward(self, xyz, points=None):
if points is not None and self.initial_dropout is not None: if points is not None and self.initial_dropout is not None:
@@ -167,10 +188,13 @@ class Pointnet2MSG(nn.Module):
l_points.append(li_points) l_points.append(li_points)
for i in range(-1, -(len(self.FP_modules) + 1), -1): for i in range(-1, -(len(self.FP_modules) + 1), -1):
l_points[i - 1] = self.FP_modules[i](l_xyz[i - 1], l_xyz[i], l_points[i - 1] = self.FP_modules[i](
l_points[i - 1], l_points[i]) l_xyz[i - 1], l_xyz[i], l_points[i - 1], l_points[i]
)
return self.FC_layer(l_points[0].transpose(1, 2)).transpose(1, 2).contiguous() return self.FC_layer(l_points[0].transpose(1,
2)).transpose(1,
2).contiguous()
if __name__ == "__main__": if __name__ == "__main__":
+46 -31
View File
@@ -16,56 +16,62 @@ import utils.pytorch_utils as pt_utils
import utils.data_utils as d_utils import utils.data_utils as d_utils
import argparse import argparse
def parse_args(): def parse_args():
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="Arguments for cls training", description="Arguments for cls training",
formatter_class=argparse.ArgumentDefaultsHelpFormatter) formatter_class=argparse.ArgumentDefaultsHelpFormatter
parser.add_argument( )
"-batch_size", type=int, default=16, help="Batch size") parser.add_argument("-batch_size", type=int, default=16, help="Batch size")
parser.add_argument( parser.add_argument(
"-num_points", "-num_points",
type=int, type=int,
default=1024, default=1024,
help="Number of points to train with") help="Number of points to train with"
)
parser.add_argument( parser.add_argument(
"-weight_decay", type=float, default=1e-5, help="L2 regularization coeff") "-weight_decay",
parser.add_argument(
"-lr",
type=float, type=float,
default=1e-2, default=1e-5,
help="Initial learning rate") help="L2 regularization coeff"
)
parser.add_argument( parser.add_argument(
"-lr_decay", "-lr", type=float, default=1e-2, help="Initial learning rate"
type=float, )
default=0.7,
help="Learning rate decay gamma")
parser.add_argument( parser.add_argument(
"-decay_step", "-lr_decay", type=float, default=0.7, help="Learning rate decay gamma"
type=int, )
default=20, parser.add_argument(
help="Learning rate decay step") "-decay_step", type=int, default=20, help="Learning rate decay step"
)
parser.add_argument( parser.add_argument(
"-bn_momentum", "-bn_momentum",
type=float, type=float,
default=0.5, default=0.5,
help="Initial batch norm momentum") help="Initial batch norm momentum"
)
parser.add_argument( parser.add_argument(
"-bnm_decay", "-bnm_decay",
type=float, type=float,
default=0.5, default=0.5,
help="Batch norm momentum decay gamma") help="Batch norm momentum decay gamma"
)
parser.add_argument( parser.add_argument(
"-checkpoint", type=str, default=None, help="Checkpoint to start from") "-checkpoint", type=str, default=None, help="Checkpoint to start from"
)
parser.add_argument( parser.add_argument(
"-epochs", type=int, default=200, help="Number of epochs to train for") "-epochs", type=int, default=200, help="Number of epochs to train for"
)
parser.add_argument( parser.add_argument(
"-run_name", "-run_name",
type=str, type=str,
default="cls_run_1", default="cls_run_1",
help="Name for run in tensorboard_logger") help="Name for run in tensorboard_logger"
)
return parser.parse_args() return parser.parse_args()
lr_clip = 1e-5 lr_clip = 1e-5
bnm_clip = 1e-2 bnm_clip = 1e-2
@@ -82,13 +88,15 @@ if __name__ == "__main__":
]) ])
test_set = ModelNet40Cls( test_set = ModelNet40Cls(
args.num_points, BASE_DIR, transforms=transforms, train=False) args.num_points, BASE_DIR, transforms=transforms, train=False
)
test_loader = DataLoader( test_loader = DataLoader(
test_set, test_set,
batch_size=args.batch_size, batch_size=args.batch_size,
shuffle=True, shuffle=True,
num_workers=2, num_workers=2,
pin_memory=True) pin_memory=True
)
train_set = ModelNet40Cls(args.num_points, BASE_DIR, transforms=transforms) train_set = ModelNet40Cls(args.num_points, BASE_DIR, transforms=transforms)
train_loader = DataLoader( train_loader = DataLoader(
@@ -96,25 +104,30 @@ if __name__ == "__main__":
batch_size=args.batch_size, batch_size=args.batch_size,
shuffle=True, shuffle=True,
num_workers=2, num_workers=2,
pin_memory=True) pin_memory=True
)
tb_log.configure('runs/{}'.format(args.run_name)) tb_log.configure('runs/{}'.format(args.run_name))
model = Pointnet(input_channels=3, num_classes=40) model = Pointnet(input_channels=3, num_classes=40)
model.cuda() model.cuda()
optimizer = optim.Adam( optimizer = optim.Adam(
model.parameters(), lr=args.lr, weight_decay=args.weight_decay) model.parameters(), lr=args.lr, weight_decay=args.weight_decay
)
lr_lbmd = lambda e: max(args.lr_decay**(e // args.decay_step), lr_clip / args.lr) lr_lbmd = lambda e: max(args.lr_decay**(e // args.decay_step), lr_clip / args.lr)
bn_lbmd = lambda e: max(args.bn_momentum * args.bnm_decay**(e // args.decay_step), bnm_clip) bn_lbmd = lambda e: max(args.bn_momentum * args.bnm_decay**(e // args.decay_step), bnm_clip)
if args.checkpoint is not None: if args.checkpoint is not None:
start_epoch, best_loss = pt_utils.load_checkpoint( start_epoch, best_loss = pt_utils.load_checkpoint(
model, optimizer, filename=args.checkpoint.split(".")[0]) model, optimizer, filename=args.checkpoint.split(".")[0]
)
lr_scheduler = lr_sched.LambdaLR( lr_scheduler = lr_sched.LambdaLR(
optimizer, lr_lambda=lr_lbmd, last_epoch=start_epoch) optimizer, lr_lambda=lr_lbmd, last_epoch=start_epoch
)
bnm_scheduler = pt_utils.BNMomentumScheduler( bnm_scheduler = pt_utils.BNMomentumScheduler(
model, bn_lambda=bn_lbmd, last_epoch=start_epoch) model, bn_lambda=bn_lbmd, last_epoch=start_epoch
)
else: else:
lr_scheduler = lr_sched.LambdaLR(optimizer, lr_lambda=lr_lbmd) lr_scheduler = lr_sched.LambdaLR(optimizer, lr_lambda=lr_lbmd)
bnm_scheduler = pt_utils.BNMomentumScheduler(model, bn_lambda=bn_lbmd) bnm_scheduler = pt_utils.BNMomentumScheduler(model, bn_lambda=bn_lbmd)
@@ -131,14 +144,16 @@ if __name__ == "__main__":
checkpoint_name="cls_checkpoint", checkpoint_name="cls_checkpoint",
best_name="cls_best", best_name="cls_best",
lr_scheduler=lr_scheduler, lr_scheduler=lr_scheduler,
bnm_scheduler=bnm_scheduler) bnm_scheduler=bnm_scheduler
)
trainer.train( trainer.train(
start_epoch, start_epoch,
args.epochs, args.epochs,
train_loader, train_loader,
test_loader, test_loader,
best_loss=best_loss) best_loss=best_loss
)
if start_epoch == args.epochs: if start_epoch == args.epochs:
_ = trainer.eval_epoch(start_epoch, test_loader) _ = trainer.eval_epoch(start_epoch, test_loader)
+40 -20
View File
@@ -18,51 +18,62 @@ import argparse
parser = argparse.ArgumentParser(description="Arg parser") parser = argparse.ArgumentParser(description="Arg parser")
parser.add_argument( parser.add_argument(
"-batch_size", type=int, default=32, help="Batch size [default: 32]") "-batch_size", type=int, default=32, help="Batch size [default: 32]"
)
parser.add_argument( parser.add_argument(
"-num_points", "-num_points",
type=int, type=int,
default=2048, default=2048,
help="Number of points to train with [default: 2048]") help="Number of points to train with [default: 2048]"
)
parser.add_argument( parser.add_argument(
"-weight_decay", "-weight_decay",
type=float, type=float,
default=0, default=0,
help="L2 regularization coeff [default: 0.0]") help="L2 regularization coeff [default: 0.0]"
)
parser.add_argument( parser.add_argument(
"-lr", "-lr",
type=float, type=float,
default=1e-2, default=1e-2,
help="Initial learning rate [default: 1e-2]") help="Initial learning rate [default: 1e-2]"
)
parser.add_argument( parser.add_argument(
"-lr_decay", "-lr_decay",
type=float, type=float,
default=0.5, default=0.5,
help="Learning rate decay gamma [default: 0.5]") help="Learning rate decay gamma [default: 0.5]"
)
parser.add_argument( parser.add_argument(
"-decay_step", "-decay_step",
type=int, type=int,
default=20, default=20,
help="Learning rate decay step [default: 20]") help="Learning rate decay step [default: 20]"
)
parser.add_argument( parser.add_argument(
"-bn_momentum", "-bn_momentum",
type=float, type=float,
default=0.9, default=0.9,
help="Initial batch norm momentum [default: 0.9]") help="Initial batch norm momentum [default: 0.9]"
)
parser.add_argument( parser.add_argument(
"-bn_decay", "-bn_decay",
type=float, type=float,
default=0.5, default=0.5,
help="Batch norm momentum decay gamma [default: 0.5]") help="Batch norm momentum decay gamma [default: 0.5]"
)
parser.add_argument( parser.add_argument(
"-checkpoint", type=str, default=None, help="Checkpoint to start from") "-checkpoint", type=str, default=None, help="Checkpoint to start from"
)
parser.add_argument( parser.add_argument(
"-epochs", type=int, default=200, help="Number of epochs to train for") "-epochs", type=int, default=200, help="Number of epochs to train for"
)
parser.add_argument( parser.add_argument(
"-run_name", "-run_name",
type=str, type=str,
default="sem_seg_run_1", default="sem_seg_run_1",
help="Name for run in tensorboard_logger") help="Name for run in tensorboard_logger"
)
BASE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data') BASE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data')
@@ -74,13 +85,15 @@ if __name__ == "__main__":
tb_log.configure('runs/{}'.format(args.run_name)) tb_log.configure('runs/{}'.format(args.run_name))
test_set = Indoor3DSemSeg( test_set = Indoor3DSemSeg(
args.num_points, BASE_DIR, train=False, data_precent=0.01) args.num_points, BASE_DIR, train=False, data_precent=0.01
)
test_loader = DataLoader( test_loader = DataLoader(
test_set, test_set,
batch_size=args.batch_size, batch_size=args.batch_size,
shuffle=True, shuffle=True,
pin_memory=True, pin_memory=True,
num_workers=2) num_workers=2
)
train_set = Indoor3DSemSeg(args.num_points, BASE_DIR, data_precent=1.0) train_set = Indoor3DSemSeg(args.num_points, BASE_DIR, data_precent=1.0)
train_loader = DataLoader( train_loader = DataLoader(
@@ -88,12 +101,14 @@ if __name__ == "__main__":
batch_size=args.batch_size, batch_size=args.batch_size,
pin_memory=True, pin_memory=True,
num_workers=2, num_workers=2,
shuffle=True) shuffle=True
)
model = Pointnet(num_classes=13) model = Pointnet(num_classes=13)
model.cuda() model.cuda()
optimizer = optim.Adam( optimizer = optim.Adam(
model.parameters(), lr=args.lr, weight_decay=args.weight_decay) model.parameters(), lr=args.lr, weight_decay=args.weight_decay
)
lr_lbmd = lambda e: max(args.lr_decay**(e // args.decay_step), lr_clip / args.lr) lr_lbmd = lambda e: max(args.lr_decay**(e // args.decay_step), lr_clip / args.lr)
bnm_lmbd = lambda e: max(args.bn_momentum * args.bn_decay**(e // args.decay_step), bnm_clip) bnm_lmbd = lambda e: max(args.bn_momentum * args.bn_decay**(e // args.decay_step), bnm_clip)
@@ -106,12 +121,15 @@ if __name__ == "__main__":
best_loss = 1e10 best_loss = 1e10
else: else:
start_epoch, best_loss = pt_utils.load_checkpoint( start_epoch, best_loss = pt_utils.load_checkpoint(
model, optimizer, filename=args.checkpoint.split(".")[0]) model, optimizer, filename=args.checkpoint.split(".")[0]
)
lr_scheduler = lr_sched.LambdaLR( lr_scheduler = lr_sched.LambdaLR(
optimizer, lr_lbmd, last_epoch=start_epoch) optimizer, lr_lbmd, last_epoch=start_epoch
)
bnm_scheduler = pt_utils.BNMomentumScheduler( bnm_scheduler = pt_utils.BNMomentumScheduler(
model, bnm_lmbd, last_epoch=start_epoch) model, bnm_lmbd, last_epoch=start_epoch
)
model_fn = model_fn_decorator(nn.CrossEntropyLoss()) model_fn = model_fn_decorator(nn.CrossEntropyLoss())
@@ -123,14 +141,16 @@ if __name__ == "__main__":
best_name="sem_seg_best", best_name="sem_seg_best",
lr_scheduler=lr_scheduler, lr_scheduler=lr_scheduler,
bnm_scheduler=bnm_scheduler, bnm_scheduler=bnm_scheduler,
eval_frequency=10) eval_frequency=10
)
trainer.train( trainer.train(
start_epoch, start_epoch,
args.epochs, args.epochs,
train_loader, train_loader,
test_loader, test_loader,
best_loss=best_loss) best_loss=best_loss
)
if start_epoch == args.epochs: if start_epoch == args.epochs:
test_loader.dataset.data_precent = 1.0 test_loader.dataset.data_precent = 1.0
+7 -3
View File
@@ -9,7 +9,8 @@ base_dir = osp.dirname(osp.abspath(__file__))
def parse_args(): def parse_args():
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="Arguments for building pointnet2 ffi extension") description="Arguments for building pointnet2 ffi extension"
)
parser.add_argument("--objs", nargs="*") parser.add_argument("--objs", nargs="*")
clean_arg = parser.add_mutually_exclusive_group() clean_arg = parser.add_mutually_exclusive_group()
clean_arg.add_argument("--build", dest='build', action="store_true") clean_arg.add_argument("--build", dest='build', action="store_true")
@@ -27,7 +28,7 @@ def build(args):
extra_objects += [a for a in glob.glob('/usr/local/cuda/lib64/*.a')] extra_objects += [a for a in glob.glob('/usr/local/cuda/lib64/*.a')]
ffi = create_extension( ffi = create_extension(
'_ext', '_ext.pointnet2',
headers=[a for a in glob.glob("cinclude/*_wrapper.h")], headers=[a for a in glob.glob("cinclude/*_wrapper.h")],
sources=[a for a in glob.glob("csrc/*.c")], sources=[a for a in glob.glob("csrc/*.c")],
define_macros=[('WITH_CUDA', None)], define_macros=[('WITH_CUDA', None)],
@@ -36,12 +37,15 @@ def build(args):
extra_objects=extra_objects, extra_objects=extra_objects,
include_dirs=[osp.join(base_dir, 'cinclude')], include_dirs=[osp.join(base_dir, 'cinclude')],
verbose=False, verbose=False,
package=False) package=False
)
ffi.build() ffi.build()
def clean(args): def clean(args):
shutil.rmtree(osp.join(base_dir, "_ext")) shutil.rmtree(osp.join(base_dir, "_ext"))
if __name__ == "__main__": if __name__ == "__main__":
args = parse_args() args = parse_args()
if args.clean: if args.clean:
+7 -7
View File
@@ -8,13 +8,13 @@ int ball_query_wrapper(int b, int n, int m, float radius, int nsample,
THCudaTensor *new_xyz_tensor, THCudaTensor *xyz_tensor, THCudaTensor *new_xyz_tensor, THCudaTensor *xyz_tensor,
THCudaIntTensor *idx_tensor) { THCudaIntTensor *idx_tensor) {
const float *new_xyz = THCudaTensor_data(state, new_xyz_tensor); const float *new_xyz = THCudaTensor_data(state, new_xyz_tensor);
const float *xyz = THCudaTensor_data(state, xyz_tensor); const float *xyz = THCudaTensor_data(state, xyz_tensor);
int *idx = THCudaIntTensor_data(state, idx_tensor); int *idx = THCudaIntTensor_data(state, idx_tensor);
cudaStream_t stream = THCState_getCurrentStream(state); cudaStream_t stream = THCState_getCurrentStream(state);
query_ball_point_kernel_wrapper(b, n, m, radius, nsample, new_xyz, xyz, query_ball_point_kernel_wrapper(b, n, m, radius, nsample, new_xyz, xyz, idx,
idx, stream); stream);
return 1; return 1;
} }
+35 -37
View File
@@ -11,38 +11,37 @@ __global__ void query_ball_point_kernel(int b, int n, int m, float radius,
int nsample, int nsample,
const float *__restrict__ new_xyz, const float *__restrict__ new_xyz,
const float *__restrict__ xyz, const float *__restrict__ xyz,
int * __restrict__ idx) { int *__restrict__ idx) {
int batch_index = blockIdx.x; int batch_index = blockIdx.x;
xyz += batch_index * n * 3; xyz += batch_index * n * 3;
new_xyz += batch_index * m * 3; new_xyz += batch_index * m * 3;
idx += m * nsample * batch_index; idx += m * nsample * batch_index;
int index = threadIdx.x; int index = threadIdx.x;
int stride = blockDim.x; int stride = blockDim.x;
float radius2 = radius * radius; float radius2 = radius * radius;
for (int j = index; j < m; j += stride) { for (int j = index; j < m; j += stride) {
float new_x = new_xyz[j * 3 + 0]; float new_x = new_xyz[j * 3 + 0];
float new_y = new_xyz[j * 3 + 1]; float new_y = new_xyz[j * 3 + 1];
float new_z = new_xyz[j * 3 + 2]; float new_z = new_xyz[j * 3 + 2];
for (int k = 0, cnt = 0; k < n && cnt < nsample; ++k) { for (int k = 0, cnt = 0; k < n && cnt < nsample; ++k) {
float x = xyz[k * 3 + 0]; float x = xyz[k * 3 + 0];
float y = xyz[k * 3 + 1]; float y = xyz[k * 3 + 1];
float z = xyz[k * 3 + 2]; float z = xyz[k * 3 + 2];
float d2 = (new_x - x) * (new_x - x) + float d2 = (new_x - x) * (new_x - x) + (new_y - y) * (new_y - y) +
(new_y - y) * (new_y - y) + (new_z - z) * (new_z - z);
(new_z - z) * (new_z - z); if (d2 < radius2) {
if (d2 < radius2) { if (cnt == 0) {
if (cnt == 0) { for (int l = 0; l < nsample; ++l) {
for (int l = 0; l < nsample; ++l) { idx[j * nsample + l] = k;
idx[j * nsample + l] = k; }
}
}
idx[j * nsample + cnt] = k;
++cnt;
}
} }
idx[j * nsample + cnt] = k;
++cnt;
}
} }
}
} }
void query_ball_point_kernel_wrapper(int b, int n, int m, float radius, void query_ball_point_kernel_wrapper(int b, int n, int m, float radius,
@@ -50,14 +49,13 @@ void query_ball_point_kernel_wrapper(int b, int n, int m, float radius,
const float *xyz, int *idx, const float *xyz, int *idx,
cudaStream_t stream) { cudaStream_t stream) {
cudaError_t err; cudaError_t err;
query_ball_point_kernel<<<b, opt_n_threads(m), 0, stream>>>( query_ball_point_kernel<<<b, opt_n_threads(m), 0, stream>>>(
b, n, m, radius, nsample, new_xyz, xyz, idx); b, n, m, radius, nsample, new_xyz, xyz, idx);
err = cudaGetLastError(); err = cudaGetLastError();
if (cudaSuccess != err) { if (cudaSuccess != err) {
fprintf(stderr, "CUDA kernel failed : %s\n", fprintf(stderr, "CUDA kernel failed : %s\n", cudaGetErrorString(err));
cudaGetErrorString(err)); exit(-1);
exit(-1); }
}
} }
+14 -14
View File
@@ -9,15 +9,15 @@ int group_points_wrapper(int b, int n, int c, int npoints, int nsample,
THCudaIntTensor *idx_tensor, THCudaIntTensor *idx_tensor,
THCudaTensor *out_tensor) { THCudaTensor *out_tensor) {
const float *points = THCudaTensor_data(state, points_tensor); const float *points = THCudaTensor_data(state, points_tensor);
const int *idx = THCudaIntTensor_data(state, idx_tensor); const int *idx = THCudaIntTensor_data(state, idx_tensor);
float *out = THCudaTensor_data(state, out_tensor); float *out = THCudaTensor_data(state, out_tensor);
cudaStream_t stream = THCState_getCurrentStream(state); cudaStream_t stream = THCState_getCurrentStream(state);
group_points_kernel_wrapper(b, n, c, npoints, nsample, points, idx, out, group_points_kernel_wrapper(b, n, c, npoints, nsample, points, idx, out,
stream); stream);
return 1; return 1;
} }
int group_points_grad_wrapper(int b, int n, int c, int npoints, int nsample, int group_points_grad_wrapper(int b, int n, int c, int npoints, int nsample,
@@ -25,13 +25,13 @@ int group_points_grad_wrapper(int b, int n, int c, int npoints, int nsample,
THCudaIntTensor *idx_tensor, THCudaIntTensor *idx_tensor,
THCudaTensor *grad_points_tensor) { THCudaTensor *grad_points_tensor) {
float *grad_points = THCudaTensor_data(state, grad_points_tensor); float *grad_points = THCudaTensor_data(state, grad_points_tensor);
const int *idx = THCudaIntTensor_data(state, idx_tensor); const int *idx = THCudaIntTensor_data(state, idx_tensor);
const float *grad_out = THCudaTensor_data(state, grad_out_tensor); const float *grad_out = THCudaTensor_data(state, grad_out_tensor);
cudaStream_t stream = THCState_getCurrentStream(state); cudaStream_t stream = THCState_getCurrentStream(state);
group_points_grad_kernel_wrapper(b, n, c, npoints, nsample, grad_out, group_points_grad_kernel_wrapper(b, n, c, npoints, nsample, grad_out, idx,
idx, grad_points, stream); grad_points, stream);
return 1; return 1;
} }
+49 -52
View File
@@ -1,86 +1,83 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include "group_points_gpu.h"
#include "cuda_utils.h" #include "cuda_utils.h"
#include "group_points_gpu.h"
// input: points(b, n, c) idx(b, npoints, nsample) // input: points(b, n, c) idx(b, npoints, nsample)
// output: out(b, npoints, nsample, c) // output: out(b, npoints, nsample, c)
__global__ void group_points_kernel(int b, int n, int c, int npoints, __global__ void group_points_kernel(int b, int n, int c, int npoints,
int nsample, int nsample,
const float *__restrict__ points, const float *__restrict__ points,
const int *__restrict__ idx, const int *__restrict__ idx,
float *__restrict__ out) { float *__restrict__ out) {
int batch_index = blockIdx.x; int batch_index = blockIdx.x;
points += batch_index * n * c; points += batch_index * n * c;
idx += batch_index * npoints * nsample; idx += batch_index * npoints * nsample;
out += batch_index * npoints * nsample * c; out += batch_index * npoints * nsample * c;
int index = threadIdx.x; int index = threadIdx.x;
int stride = blockDim.x; int stride = blockDim.x;
for (int j = index; j < npoints; j += stride) { for (int j = index; j < npoints; j += stride) {
for (int k = 0; k < nsample; ++k) { for (int k = 0; k < nsample; ++k) {
int ii = idx[j * nsample + k]; int ii = idx[j * nsample + k];
memcpy(out + j * nsample * c + k * c, points + ii * c, memcpy(out + j * nsample * c + k * c, points + ii * c,
sizeof(float) * c); sizeof(float) * c);
}
} }
}
} }
void group_points_kernel_wrapper(int b, int n, int c, int npoints, int nsample, void group_points_kernel_wrapper(int b, int n, int c, int npoints, int nsample,
const float *points, const int *idx, const float *points, const int *idx,
float *out, cudaStream_t stream) { float *out, cudaStream_t stream) {
cudaError_t err; cudaError_t err;
group_points_kernel<<<b, opt_n_threads(npoints), 0, stream>>>( group_points_kernel<<<b, opt_n_threads(npoints), 0, stream>>>(
b, n, c, npoints, nsample, points, idx, out); b, n, c, npoints, nsample, points, idx, out);
err = cudaGetLastError(); err = cudaGetLastError();
if (cudaSuccess != err) { if (cudaSuccess != err) {
fprintf(stderr, "CUDA kernel failed : %s\n", fprintf(stderr, "CUDA kernel failed : %s\n", cudaGetErrorString(err));
cudaGetErrorString(err)); exit(-1);
exit(-1); }
}
} }
// input: grad_out(b, npoints, nsample, c), idx(b, npoints, nsample) // input: grad_out(b, npoints, nsample, c), idx(b, npoints, nsample)
// output: grad_points(b, n, c) // output: grad_points(b, n, c)
__global__ void group_points_grad_kernel(int b, int n, int c, int npoints, __global__ void group_points_grad_kernel(int b, int n, int c, int npoints,
int nsample, int nsample,
const float *__restrict__ grad_out, const float *__restrict__ grad_out,
const int *__restrict__ idx, const int *__restrict__ idx,
float *__restrict__ grad_points) { float *__restrict__ grad_points) {
int batch_index = blockIdx.x; int batch_index = blockIdx.x;
grad_points += batch_index * n * c; grad_points += batch_index * n * c;
idx += batch_index * npoints * nsample; idx += batch_index * npoints * nsample;
grad_out += batch_index * npoints * nsample * c; grad_out += batch_index * npoints * nsample * c;
int index = threadIdx.x; int index = threadIdx.x;
int stride = blockDim.x; int stride = blockDim.x;
for (int j = index; j < npoints; j += stride) { for (int j = index; j < npoints; j += stride) {
for (int k = 0; k < nsample; ++k) { for (int k = 0; k < nsample; ++k) {
int ii = idx[j * nsample + k]; int ii = idx[j * nsample + k];
for (int l = 0; l < c; ++l) { for (int l = 0; l < c; ++l) {
atomicAdd( atomicAdd(grad_points + ii * c + l,
grad_points + ii * c + l, grad_out[j * nsample * c + k * c + l]);
grad_out[j * nsample * c + k * c + l]); }
}
}
} }
}
} }
void group_points_grad_kernel_wrapper(int b, int n, int c, int npoints, void group_points_grad_kernel_wrapper(int b, int n, int c, int npoints,
int nsample, const float *grad_out, int nsample, const float *grad_out,
const int *idx, float *grad_points, const int *idx, float *grad_points,
cudaStream_t stream) { cudaStream_t stream) {
cudaError_t err; cudaError_t err;
group_points_grad_kernel<<<b, opt_n_threads(npoints), 0, stream>>>( group_points_grad_kernel<<<b, opt_n_threads(npoints), 0, stream>>>(
b, n, c, npoints, nsample, grad_out, idx, grad_points); b, n, c, npoints, nsample, grad_out, idx, grad_points);
err = cudaGetLastError(); err = cudaGetLastError();
if (cudaSuccess != err) { if (cudaSuccess != err) {
fprintf(stderr, "CUDA kernel failed : %s\n", fprintf(stderr, "CUDA kernel failed : %s\n", cudaGetErrorString(err));
cudaGetErrorString(err)); exit(-1);
exit(-1); }
}
} }
+1 -1
View File
@@ -2,8 +2,8 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include "interpolate_gpu.h"
#include "cuda_utils.h" #include "cuda_utils.h"
#include "interpolate_gpu.h"
// input: unknown(b, n, 3) known(b, m, 3) // input: unknown(b, n, 3) known(b, m, 3)
// output: dist2(b, n, 3), idx(b, n, 3) // output: dist2(b, n, 3), idx(b, n, 3)
+28 -29
View File
@@ -9,17 +9,17 @@ int roi_mask_wrapper(int n_roi, int b, int n, THCudaTensor *rois_tensor,
THCudaTensor *data_xyz_tensor, THCudaTensor *data_xyz_tensor,
THCudaByteTensor *mask_tensor) { THCudaByteTensor *mask_tensor) {
const float *rois = THCudaTensor_data(state, rois_tensor); const float *rois = THCudaTensor_data(state, rois_tensor);
const long *batch_indices = const long *batch_indices =
THCudaLongTensor_data(state, batch_indices_tensor); THCudaLongTensor_data(state, batch_indices_tensor);
const float *data_xyz = THCudaTensor_data(state, data_xyz_tensor); const float *data_xyz = THCudaTensor_data(state, data_xyz_tensor);
unsigned char *mask = THCudaByteTensor_data(state, mask_tensor); unsigned char *mask = THCudaByteTensor_data(state, mask_tensor);
cudaStream_t stream = THCState_getCurrentStream(state); cudaStream_t stream = THCState_getCurrentStream(state);
roi_mask_kernel_wrapper(n_roi, b, n, rois, batch_indices, data_xyz, roi_mask_kernel_wrapper(n_roi, b, n, rois, batch_indices, data_xyz, mask,
mask, stream); stream);
return 1; return 1;
} }
int roi_avg_pool_forward_wrapper(int n_roi, int b, int n, int d, int roi_avg_pool_forward_wrapper(int n_roi, int b, int n, int d,
@@ -28,17 +28,17 @@ int roi_avg_pool_forward_wrapper(int n_roi, int b, int n, int d,
THCudaTensor *points_tensor, THCudaTensor *points_tensor,
THCudaTensor *descriptors_tensor) { THCudaTensor *descriptors_tensor) {
const long *batch_indices = const long *batch_indices =
THCudaLongTensor_data(state, batch_indices_tensor); THCudaLongTensor_data(state, batch_indices_tensor);
const unsigned char *mask = THCudaByteTensor_data(state, mask_tensor); const unsigned char *mask = THCudaByteTensor_data(state, mask_tensor);
const float *points = THCudaTensor_data(state, points_tensor); const float *points = THCudaTensor_data(state, points_tensor);
float *descriptors = THCudaTensor_data(state, descriptors_tensor); float *descriptors = THCudaTensor_data(state, descriptors_tensor);
cudaStream_t stream = THCState_getCurrentStream(state); cudaStream_t stream = THCState_getCurrentStream(state);
roi_avg_pool_kernel_forward_wrapper(n_roi, b, n, d, mask, batch_indices, roi_avg_pool_kernel_forward_wrapper(n_roi, b, n, d, mask, batch_indices,
points, descriptors, stream); points, descriptors, stream);
return 1; return 1;
} }
int roi_avg_pool_backward_wrapper(int n_roi, int b, int n, int d, int roi_avg_pool_backward_wrapper(int n_roi, int b, int n, int d,
@@ -47,17 +47,16 @@ int roi_avg_pool_backward_wrapper(int n_roi, int b, int n, int d,
THCudaTensor *grad_descriptors_tensor, THCudaTensor *grad_descriptors_tensor,
THCudaTensor *grad_points_tensor) { THCudaTensor *grad_points_tensor) {
const long *batch_indices = const long *batch_indices =
THCudaLongTensor_data(state, batch_indices_tensor); THCudaLongTensor_data(state, batch_indices_tensor);
const unsigned char *mask = THCudaByteTensor_data(state, mask_tensor); const unsigned char *mask = THCudaByteTensor_data(state, mask_tensor);
const float *grad_descriptors = const float *grad_descriptors =
THCudaTensor_data(state, grad_descriptors_tensor); THCudaTensor_data(state, grad_descriptors_tensor);
float *grad_points = THCudaTensor_data(state, grad_points_tensor); float *grad_points = THCudaTensor_data(state, grad_points_tensor);
cudaStream_t stream = THCState_getCurrentStream(state); cudaStream_t stream = THCState_getCurrentStream(state);
roi_avg_pool_kernel_backward_wrapper(n_roi, b, n, d, mask, roi_avg_pool_kernel_backward_wrapper(n_roi, b, n, d, mask, batch_indices,
batch_indices, grad_descriptors, grad_descriptors, grad_points, stream);
grad_points, stream);
return 1; return 1;
} }
+12 -14
View File
@@ -9,15 +9,14 @@ int gather_points_wrapper(int b, int n, int c, int npoints,
THCudaIntTensor *idx_tensor, THCudaIntTensor *idx_tensor,
THCudaTensor *out_tensor) { THCudaTensor *out_tensor) {
const float *points = THCudaTensor_data(state, points_tensor); const float *points = THCudaTensor_data(state, points_tensor);
const int *idx = THCudaIntTensor_data(state, idx_tensor); const int *idx = THCudaIntTensor_data(state, idx_tensor);
float *out = THCudaTensor_data(state, out_tensor); float *out = THCudaTensor_data(state, out_tensor);
cudaStream_t stream = THCState_getCurrentStream(state); cudaStream_t stream = THCState_getCurrentStream(state);
gather_points_kernel_wrapper(b, n, c, npoints, points, idx, out, gather_points_kernel_wrapper(b, n, c, npoints, points, idx, out, stream);
stream); return 1;
return 1;
} }
int furthest_point_sampling_wrapper(int b, int n, int m, int furthest_point_sampling_wrapper(int b, int n, int m,
@@ -25,13 +24,12 @@ int furthest_point_sampling_wrapper(int b, int n, int m,
THCudaTensor *temp_tensor, THCudaTensor *temp_tensor,
THCudaIntTensor *idx_tensor) { THCudaIntTensor *idx_tensor) {
const float *points = THCudaTensor_data(state, points_tensor); const float *points = THCudaTensor_data(state, points_tensor);
float *temp = THCudaTensor_data(state, temp_tensor); float *temp = THCudaTensor_data(state, temp_tensor);
int *idx = THCudaIntTensor_data(state, idx_tensor); int *idx = THCudaIntTensor_data(state, idx_tensor);
cudaStream_t stream = THCState_getCurrentStream(state); cudaStream_t stream = THCState_getCurrentStream(state);
furthest_point_sampling_kernel_wrapper(b, n, m, points, temp, idx, furthest_point_sampling_kernel_wrapper(b, n, m, points, temp, idx, stream);
stream); return 1;
return 1;
} }
+11 -4
View File
@@ -3,17 +3,19 @@ import numpy as np
class PointcloudScale(object): class PointcloudScale(object):
def __init__(self, mean=2.0, std=1.0, clip=1.8): def __init__(self, mean=2.0, std=1.0, clip=1.8):
self.mean, self.std, self.clip = mean, std, clip self.mean, self.std, self.clip = mean, std, clip
def __call__(self, points): def __call__(self, points):
scaler = points.new(1).normal_( scaler = points.new(1).normal_(
mean=self.mean, std=self.std).clamp_( mean=self.mean, std=self.std
max(self.mean - self.clip, 0.01), self.mean + self.clip) ).clamp_(max(self.mean - self.clip, 0.01), self.mean + self.clip)
return scaler * points return scaler * points
class PointcloudRotate(object): class PointcloudRotate(object):
def __init__(self, x_axis=False, z_axis=True): def __init__(self, x_axis=False, z_axis=True):
assert x_axis or z_axis assert x_axis or z_axis
self.x, self.z = x_axis, z_axis self.x, self.z = x_axis, z_axis
@@ -46,25 +48,30 @@ class PointcloudRotate(object):
class PointcloudJitter(object): class PointcloudJitter(object):
def __init__(self, std=0.01, clip=0.03): def __init__(self, std=0.01, clip=0.03):
self.std, self.clip = std, clip self.std, self.clip = std, clip
def __call__(self, points): def __call__(self, points):
jittered_data = points.new(*points.size()).normal_( jittered_data = points.new(*points.size()).normal_(
mean=0.0, std=self.std).clamp_(-self.clip, self.clip) mean=0.0, std=self.std
).clamp_(-self.clip, self.clip)
return points + jittered_data return points + jittered_data
class PointcloudTranslate(object): class PointcloudTranslate(object):
def __init__(self, std=1.0, clip=3.0): def __init__(self, std=1.0, clip=3.0):
self.std, self.clip = std, clip self.std, self.clip = std, clip
def __call__(self, points): def __call__(self, points):
translation = points.new(3).normal_( translation = points.new(3).normal_(
mean=0.0, std=self.std).clamp_(-self.clip, self.clip) mean=0.0, std=self.std
).clamp_(-self.clip, self.clip)
return points + translation return points + translation
class PointcloudToTensor(object): class PointcloudToTensor(object):
def __call__(self, points): def __call__(self, points):
return torch.from_numpy(points).float() return torch.from_numpy(points).float()
+5 -3
View File
@@ -4,9 +4,11 @@ from enum import Enum
PDist2Order = Enum('PDist2Order', 'd_first d_second') PDist2Order = Enum('PDist2Order', 'd_first d_second')
def pdist2(X: torch.Tensor, def pdist2(
Z: torch.Tensor = None, X: torch.Tensor,
order: PDist2Order = PDist2Order.d_second) -> torch.Tensor: Z: torch.Tensor = None,
order: PDist2Order = PDist2Order.d_second
) -> torch.Tensor:
r""" Calculates the pairwise distance between X and Z r""" Calculates the pairwise distance between X and Z
D[b, i, j] = l2 distance X[b, i] and Z[b, j] D[b, i, j] = l2 distance X[b, i] and Z[b, j]
+54 -44
View File
@@ -24,13 +24,15 @@ class PointnetSAModuleMSG(nn.Module):
Use batchnorm Use batchnorm
""" """
def __init__(self, def __init__(
*, self,
npoint: int, *,
radii: List[float], npoint: int,
nsamples: List[int], radii: List[float],
mlps: List[List[int]], nsamples: List[int],
bn: bool = True): mlps: List[List[int]],
bn: bool = True
):
super().__init__() super().__init__()
assert len(radii) == len(nsamples) == len(mlps) assert len(radii) == len(nsamples) == len(mlps)
@@ -41,8 +43,7 @@ class PointnetSAModuleMSG(nn.Module):
for i in range(len(radii)): for i in range(len(radii)):
radius = radii[i] radius = radii[i]
nsample = nsamples[i] nsample = nsamples[i]
self.groupers.append( self.groupers.append(pointnet2_utils.QueryAndGroup(radius, nsample))
pointnet2_utils.QueryAndGroup(radius, nsample))
mlp_spec = mlps[i] mlp_spec = mlps[i]
self.mlps.append(pt_utils.SharedMLP(mlp_spec, bn=bn)) self.mlps.append(pt_utils.SharedMLP(mlp_spec, bn=bn))
@@ -66,18 +67,20 @@ class PointnetSAModuleMSG(nn.Module):
new_points_list = [] new_points_list = []
new_xyz = pointnet2_utils.gather_points( new_xyz = pointnet2_utils.gather_points(
xyz, pointnet2_utils.furthest_point_sample(xyz, self.npoint)) xyz, pointnet2_utils.furthest_point_sample(xyz, self.npoint)
)
for i in range(len(self.groupers)): for i in range(len(self.groupers)):
new_points = self.groupers[i](xyz, new_xyz, points) new_points = self.groupers[i](xyz, new_xyz, points)
new_points = self.mlps[i](new_points.permute( new_points = self.mlps[i](new_points.permute(0, 3, 1, 2)
0, 3, 1, 2)) # (B, mlp[-1], npoint, nsample) ) # (B, mlp[-1], npoint, nsample)
new_points = F.max_pool2d( new_points = F.max_pool2d(
new_points, new_points, kernel_size=[1, new_points.size(3)]
kernel_size=[1, new_points.size(3)]) # (B, mlp[-1], npoint, 1) ) # (B, mlp[-1], npoint, 1)
new_points = new_points.squeeze(-1) # (B, mlp[-1], npoint) new_points = new_points.squeeze(-1) # (B, mlp[-1], npoint)
new_points = new_points.transpose( new_points = new_points.transpose(
1, 2).contiguous() # (B, npoint, mlp[-1]) 1, 2
).contiguous() # (B, npoint, mlp[-1])
new_points_list.append(new_points) new_points_list.append(new_points)
@@ -101,13 +104,15 @@ class PointnetSAModule(nn.Module):
Use batchnorm Use batchnorm
""" """
def __init__(self, def __init__(
*, self,
mlp: List[int], *,
npoint: int = None, mlp: List[int],
radius: float = None, npoint: int = None,
nsample: int = None, radius: float = None,
bn: bool = True): nsample: int = None,
bn: bool = True
):
super().__init__() super().__init__()
self.npoint = npoint self.npoint = npoint
@@ -140,21 +145,23 @@ class PointnetSAModule(nn.Module):
if self.npoint is not None: if self.npoint is not None:
new_xyz = pointnet2_utils.gather_points( new_xyz = pointnet2_utils.gather_points(
xyz, pointnet2_utils.furthest_point_sample(xyz, self.npoint)) xyz, pointnet2_utils.furthest_point_sample(xyz, self.npoint)
)
else: else:
new_xyz = xyz.data.new([[[0, 0, 0]]]).expand(xyz.size(0), 1, 3) new_xyz = xyz.data.new([[[0, 0, 0]]]).expand(xyz.size(0), 1, 3)
new_points = self.grouper(xyz, new_xyz, new_points = self.grouper(
points) # (B, npoint, nsample, 3 + C) xyz, new_xyz, points
) # (B, npoint, nsample, 3 + C)
new_points = self.mlp(new_points.permute( new_points = self.mlp(new_points.permute(0, 3, 1, 2)
0, 3, 1, 2)) # (B, mlp[-1], npoint, nsample) ) # (B, mlp[-1], npoint, nsample)
new_points = F.max_pool2d( new_points = F.max_pool2d(
new_points, new_points, kernel_size=[1, new_points.size(3)]
kernel_size=[1, new_points.size(3)]) # (B, mlp[-1], npoint, 1) ) # (B, mlp[-1], npoint, 1)
new_points = new_points.squeeze(-1) # (B, mlp[-1], npoint) new_points = new_points.squeeze(-1) # (B, mlp[-1], npoint)
new_points = new_points.transpose( new_points = new_points.transpose(1, 2
1, 2).contiguous() # (B, npoint, mlp[-1]) ).contiguous() # (B, npoint, mlp[-1])
return new_xyz, new_points return new_xyz, new_points
@@ -174,9 +181,10 @@ class PointnetFPModule(nn.Module):
super().__init__() super().__init__()
self.mlp = pt_utils.SharedMLP(mlp, bn=bn) self.mlp = pt_utils.SharedMLP(mlp, bn=bn)
def forward(self, unknown: torch.Tensor, known: torch.Tensor, def forward(
unknow_feats: torch.Tensor, self, unknown: torch.Tensor, known: torch.Tensor,
known_feats: torch.Tensor) -> torch.Tensor: unknow_feats: torch.Tensor, known_feats: torch.Tensor
) -> torch.Tensor:
r""" r"""
Parameters Parameters
---------- ----------
@@ -201,19 +209,21 @@ class PointnetFPModule(nn.Module):
weight = dist_recip / norm weight = dist_recip / norm
interpolated_feats = pointnet2_utils.three_interpolate( interpolated_feats = pointnet2_utils.three_interpolate(
known_feats, idx, weight) known_feats, idx, weight
)
if unknow_feats is not None: if unknow_feats is not None:
new_points = torch.cat( new_points = torch.cat([interpolated_feats, unknow_feats],
[interpolated_feats, unknow_feats], dim=-1) #(B, n, C2 + C1) dim=-1) #(B, n, C2 + C1)
else: else:
new_points = interpolated_feats new_points = interpolated_feats
new_points = new_points.unsqueeze(-1).transpose(1, new_points = new_points.unsqueeze(-1).transpose(
2) #(B, C2 + C1, n, 1) 1, 2
) #(B, C2 + C1, n, 1)
new_points = self.mlp(new_points) new_points = self.mlp(new_points)
return new_points.squeeze(-1).transpose( return new_points.squeeze(-1).transpose(1, 2
1, 2).contiguous() #(B, n, mlp[-1]) ).contiguous() #(B, n, mlp[-1])
if __name__ == "__main__": if __name__ == "__main__":
@@ -224,7 +234,8 @@ if __name__ == "__main__":
xyz_feats = Variable(torch.randn(2, 10, 6).cuda(), requires_grad=True) xyz_feats = Variable(torch.randn(2, 10, 6).cuda(), requires_grad=True)
test_module = PointnetSAModuleMSG( test_module = PointnetSAModuleMSG(
npoint=2, radii=[5.0, 10.0], nsamples=[6, 3], mlps=[[9, 3], [9, 6]]) npoint=2, radii=[5.0, 10.0], nsamples=[6, 3], mlps=[[9, 3], [9, 6]]
)
test_module.cuda() test_module.cuda()
print(test_module(xyz, xyz_feats)) print(test_module(xyz, xyz_feats))
@@ -237,7 +248,6 @@ if __name__ == "__main__":
for _ in range(1): for _ in range(1):
_, new_points = test_module(xyz, xyz_feats) _, new_points = test_module(xyz, xyz_feats)
new_points.backward( new_points.backward(torch.cuda.FloatTensor(*new_points.size()).fill_(1))
torch.cuda.FloatTensor(*new_points.size()).fill_(1))
print(new_points) print(new_points)
print(xyz.grad) print(xyz.grad)
+57 -36
View File
@@ -5,12 +5,14 @@ import torch.nn.functional as F
import torch.nn as nn import torch.nn as nn
from linalg_utils import pdist2, PDist2Order from linalg_utils import pdist2, PDist2Order
from collections import namedtuple from collections import namedtuple
import _ext as pointnet2
import pytorch_utils as pt_utils import pytorch_utils as pt_utils
from typing import List, Tuple from typing import List, Tuple
from _ext import pointnet2
class RandomDropout(nn.Module): class RandomDropout(nn.Module):
def __init__(self, p=0.5, inplace=False): def __init__(self, p=0.5, inplace=False):
super().__init__() super().__init__()
self.p = p self.p = p
@@ -18,11 +20,13 @@ class RandomDropout(nn.Module):
def forward(self, X): def forward(self, X):
theta = torch.Tensor(1).uniform_(0, self.p)[0] theta = torch.Tensor(1).uniform_(0, self.p)[0]
return pt_utils.feature_dropout_no_scaling(X, theta, self.train, return pt_utils.feature_dropout_no_scaling(
self.inplace) X, theta, self.train, self.inplace
)
class FurthestPointSampling(Function): class FurthestPointSampling(Function):
@staticmethod @staticmethod
def forward(ctx, xyz: torch.Tensor, npoint: int) -> torch.Tensor: def forward(ctx, xyz: torch.Tensor, npoint: int) -> torch.Tensor:
r""" r"""
@@ -30,16 +34,16 @@ class FurthestPointSampling(Function):
minimum distance minimum distance
Parameters Parameters
--------- ----------
xyz : torch.Tensor xyz : torch.Tensor
(B, N, 3) tensor where N > npoint (B, N, 3) tensor where N > npoint
npoint : int32 npoint : int32
number of points in the sampled set number of points in the sampled set
Returns Returns
-------
torch.Tensor torch.Tensor
(B, npoint) tensor containing the set (B, npoint) tensor containing the set
------
""" """
B, N, _ = xyz.size() B, N, _ = xyz.size()
@@ -50,8 +54,9 @@ class FurthestPointSampling(Function):
temp = temp.contiguous() temp = temp.contiguous()
output = output.contiguous() output = output.contiguous()
pointnet2.furthest_point_sampling_wrapper(B, N, npoint, xyz, temp, pointnet2.furthest_point_sampling_wrapper(
output) B, N, npoint, xyz, temp, output
)
return output return output
@@ -64,6 +69,7 @@ furthest_point_sample = FurthestPointSampling.apply
class GatherPoints(Function): class GatherPoints(Function):
@staticmethod @staticmethod
def forward(ctx, points: torch.Tensor, idx: torch.Tensor) -> torch.Tensor: def forward(ctx, points: torch.Tensor, idx: torch.Tensor) -> torch.Tensor:
r""" r"""
@@ -71,7 +77,7 @@ class GatherPoints(Function):
minimum distance minimum distance
Parameters Parameters
--------- ----------
points : torch.Tensor points : torch.Tensor
(B, N, 3) tensor (B, N, 3) tensor
@@ -79,9 +85,9 @@ class GatherPoints(Function):
(B, npoint) tensor of the points to gather (B, npoint) tensor of the points to gather
Returns Returns
-------
torch.Tensor torch.Tensor
(B, npoint, 3) tensor (B, npoint, 3) tensor
------
""" """
B, N, C = points.size() B, N, C = points.size()
@@ -106,6 +112,7 @@ gather_points = GatherPoints.apply
class ThreeNN(Function): class ThreeNN(Function):
@staticmethod @staticmethod
def forward(ctx, unknown: torch.Tensor, def forward(ctx, unknown: torch.Tensor,
known: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]: known: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
@@ -147,9 +154,11 @@ three_nn = ThreeNN.apply
class ThreeInterpolate(Function): class ThreeInterpolate(Function):
@staticmethod @staticmethod
def forward(ctx, points: torch.Tensor, idx: torch.Tensor, def forward(
weight: torch.Tensor) -> torch.Tensor: ctx, points: torch.Tensor, idx: torch.Tensor, weight: torch.Tensor
) -> torch.Tensor:
r""" r"""
Performs weight linear interpolation on 3 points Performs weight linear interpolation on 3 points
Parameters Parameters
@@ -178,14 +187,15 @@ class ThreeInterpolate(Function):
idx = idx.contiguous() idx = idx.contiguous()
weight = weight.contiguous() weight = weight.contiguous()
output = output.contiguous() output = output.contiguous()
pointnet2.three_interpolate_wrapper(B, m, c, n, points, idx, weight, pointnet2.three_interpolate_wrapper(
output) B, m, c, n, points, idx, weight, output
)
return output return output
@staticmethod @staticmethod
def backward(ctx, grad_out: torch.Tensor def backward(ctx, grad_out: torch.Tensor
) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]: ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
r""" r"""
Parameters Parameters
---------- ----------
@@ -196,6 +206,7 @@ class ThreeInterpolate(Function):
------- -------
grad_points : torch.Tensor grad_points : torch.Tensor
(B, m, c) tensor with gradients of points (B, m, c) tensor with gradients of points
None None
None None
@@ -209,8 +220,9 @@ class ThreeInterpolate(Function):
idx = idx.contiguous() idx = idx.contiguous()
weight = weight.contiguous() weight = weight.contiguous()
grad_points = grad_points.contiguous() grad_points = grad_points.contiguous()
pointnet2.three_interpolate_grad_wrapper(B, n, c, m, grad_out.data, pointnet2.three_interpolate_grad_wrapper(
idx, weight, grad_points.data) B, n, c, m, grad_out.data, idx, weight, grad_points.data
)
return grad_points, None, None return grad_points, None, None
@@ -219,6 +231,7 @@ three_interpolate = ThreeInterpolate.apply
class GroupPoints(Function): class GroupPoints(Function):
@staticmethod @staticmethod
def forward(ctx, points: torch.Tensor, idx: torch.Tensor) -> torch.Tensor: def forward(ctx, points: torch.Tensor, idx: torch.Tensor) -> torch.Tensor:
r""" r"""
@@ -243,8 +256,9 @@ class GroupPoints(Function):
points = points.contiguous() points = points.contiguous()
idx = idx.contiguous() idx = idx.contiguous()
output = output.contiguous() output = output.contiguous()
pointnet2.group_points_wrapper(B, N, C, npoints, nsample, points, idx, pointnet2.group_points_wrapper(
output) B, N, C, npoints, nsample, points, idx, output
)
ctx.idx_N_C_for_backward = (idx, N, C) ctx.idx_N_C_for_backward = (idx, N, C)
return output return output
@@ -273,7 +287,8 @@ class GroupPoints(Function):
grad_out = grad_out.contiguous() grad_out = grad_out.contiguous()
grad_points = grad_points.contiguous() grad_points = grad_points.contiguous()
pointnet2.group_points_grad_wrapper( pointnet2.group_points_grad_wrapper(
B, N, C, npoint, nsample, grad_out.data, idx, grad_points.data) B, N, C, npoint, nsample, grad_out.data, idx, grad_points.data
)
return grad_points, None return grad_points, None
@@ -282,13 +297,16 @@ group_points = GroupPoints.apply
class BallQuery(Function): class BallQuery(Function):
@staticmethod @staticmethod
def forward(ctx, radius: float, nsample: int, xyz: torch.Tensor, def forward(
new_xyz: torch.Tensor) -> torch.Tensor: ctx, radius: float, nsample: int, xyz: torch.Tensor,
new_xyz: torch.Tensor
) -> torch.Tensor:
r""" r"""
Parameters Parameters
--------- ----------
radius : float radius : float
radius of the balls radius of the balls
nsample : int nsample : int
@@ -299,7 +317,7 @@ class BallQuery(Function):
(B, npoint, 3) centers of the ball query (B, npoint, 3) centers of the ball query
Returns Returns
------ -------
torch.Tensor torch.Tensor
(B, npoint, nsample) tensor with the indicies of the points that form the query balls (B, npoint, nsample) tensor with the indicies of the points that form the query balls
""" """
@@ -311,8 +329,9 @@ class BallQuery(Function):
new_xyz = new_xyz.contiguous() new_xyz = new_xyz.contiguous()
xyz = xyz.contiguous() xyz = xyz.contiguous()
idx = idx.contiguous() idx = idx.contiguous()
pointnet2.ball_query_wrapper(B, N, npoint, radius, nsample, new_xyz, pointnet2.ball_query_wrapper(
xyz, idx) B, N, npoint, radius, nsample, new_xyz, xyz, idx
)
return idx return idx
@@ -344,10 +363,11 @@ class QueryAndGroup(nn.Module):
self, self,
xyz: torch.Tensor, xyz: torch.Tensor,
new_xyz: torch.Tensor, new_xyz: torch.Tensor,
points: torch.Tensor = None) -> Tuple[torch.Tensor]: points: torch.Tensor = None
) -> Tuple[torch.Tensor]:
r""" r"""
Parameters Parameters
--------- ----------
xyz : torch.Tensor xyz : torch.Tensor
xyz coordinates of the points (B, N, 3) xyz coordinates of the points (B, N, 3)
new_xyz : torch.Tensor new_xyz : torch.Tensor
@@ -368,9 +388,8 @@ class QueryAndGroup(nn.Module):
if points is not None: if points is not None:
grouped_points = group_points(points, idx) grouped_points = group_points(points, idx)
if self.use_xyz: if self.use_xyz:
new_points = torch.cat( new_points = torch.cat([grouped_xyz, grouped_points],
[grouped_xyz, grouped_points], dim=-1) # (B, npoint, nsample, 3 + C)
dim=-1) # (B, npoint, nsample, 3 + C)
else: else:
new_points = group_points new_points = group_points
else: else:
@@ -395,10 +414,11 @@ class GroupAll(nn.Module):
self, self,
xyz: torch.Tensor, xyz: torch.Tensor,
new_xyz: torch.Tensor, new_xyz: torch.Tensor,
points: torch.Tensor = None) -> Tuple[torch.Tensor]: points: torch.Tensor = None
) -> Tuple[torch.Tensor]:
r""" r"""
Parameters Parameters
--------- ----------
xyz : torch.Tensor xyz : torch.Tensor
xyz coordinates of the points (B, N, 3) xyz coordinates of the points (B, N, 3)
new_xyz : torch.Tensor new_xyz : torch.Tensor
@@ -414,11 +434,12 @@ class GroupAll(nn.Module):
grouped_xyz = xyz.view(xyz.size(0), 1, xyz.size(1), xyz.size(2)) grouped_xyz = xyz.view(xyz.size(0), 1, xyz.size(1), xyz.size(2))
if points is not None: if points is not None:
grouped_points = points.view(points.size(0), 1, points.size(1), points.size(2)) grouped_points = points.view(
points.size(0), 1, points.size(1), points.size(2)
)
if self.use_xyz: if self.use_xyz:
new_points = torch.cat( new_points = torch.cat([grouped_xyz, grouped_points],
[grouped_xyz, grouped_points], dim=-1) # (B, npoint, nsample, 3 + C)
dim=-1) # (B, npoint, nsample, 3 + C)
else: else:
new_points = group_points new_points = group_points
else: else:
+182 -141
View File
@@ -16,48 +16,55 @@ import math
class SharedMLP(nn.Sequential): class SharedMLP(nn.Sequential):
def __init__(self,
args: List[int], def __init__(
*, self,
bn: bool = False, args: List[int],
activation=nn.ReLU(inplace=True), *,
name: str = ""): bn: bool = False,
activation=nn.ReLU(inplace=True),
name: str = ""
):
super().__init__() super().__init__()
for i in range(len(args) - 1): for i in range(len(args) - 1):
self.add_module(name + 'layer{}'.format(i), self.add_module(
Conv2d( name + 'layer{}'.format(i),
args[i], Conv2d(args[i], args[i + 1], bn=bn, activation=activation)
args[i + 1], )
bn=bn,
activation=activation))
class _ConvBase(nn.Sequential): class _ConvBase(nn.Sequential):
def __init__(self,
in_size, def __init__(
out_size, self,
kernel_size, in_size,
stride, out_size,
padding, kernel_size,
activation, stride,
bn, padding,
init, activation,
conv=None, bn,
batch_norm=None, init,
bias=True, conv=None,
name=""): batch_norm=None,
bias=True,
name=""
):
super().__init__() super().__init__()
bias = bias and (not bn) bias = bias and (not bn)
self.add_module(name + 'conv', self.add_module(
conv( name + 'conv',
in_size, conv(
out_size, in_size,
kernel_size=kernel_size, out_size,
stride=stride, kernel_size=kernel_size,
padding=padding, stride=stride,
bias=bias)) padding=padding,
bias=bias
)
)
init(self[0].weight) init(self[0].weight)
if bias: if bias:
@@ -73,18 +80,21 @@ class _ConvBase(nn.Sequential):
class Conv1d(_ConvBase): class Conv1d(_ConvBase):
def __init__(self,
in_size: int, def __init__(
out_size: int, self,
*, in_size: int,
kernel_size: int = 1, out_size: int,
stride: int = 1, *,
padding: int = 0, kernel_size: int = 1,
activation=nn.ReLU(inplace=True), stride: int = 1,
bn: bool = False, padding: int = 0,
init=nn.init.kaiming_normal, activation=nn.ReLU(inplace=True),
bias: bool = True, bn: bool = False,
name: str = ""): init=nn.init.kaiming_normal,
bias: bool = True,
name: str = ""
):
super().__init__( super().__init__(
in_size, in_size,
out_size, out_size,
@@ -97,22 +107,26 @@ class Conv1d(_ConvBase):
conv=nn.Conv1d, conv=nn.Conv1d,
batch_norm=nn.BatchNorm1d, batch_norm=nn.BatchNorm1d,
bias=bias, bias=bias,
name=name) name=name
)
class Conv2d(_ConvBase): class Conv2d(_ConvBase):
def __init__(self,
in_size: int, def __init__(
out_size: int, self,
*, in_size: int,
kernel_size: Tuple[int, int] = (1, 1), out_size: int,
stride: Tuple[int, int] = (1, 1), *,
padding: Tuple[int, int] = (0, 0), kernel_size: Tuple[int, int] = (1, 1),
activation=nn.ReLU(inplace=True), stride: Tuple[int, int] = (1, 1),
bn: bool = False, padding: Tuple[int, int] = (0, 0),
init=nn.init.kaiming_normal, activation=nn.ReLU(inplace=True),
bias: bool = True, bn: bool = False,
name: str = ""): init=nn.init.kaiming_normal,
bias: bool = True,
name: str = ""
):
super().__init__( super().__init__(
in_size, in_size,
out_size, out_size,
@@ -125,22 +139,26 @@ class Conv2d(_ConvBase):
conv=nn.Conv2d, conv=nn.Conv2d,
batch_norm=nn.BatchNorm2d, batch_norm=nn.BatchNorm2d,
bias=bias, bias=bias,
name=name) name=name
)
class Conv3d(_ConvBase): class Conv3d(_ConvBase):
def __init__(self,
in_size: int, def __init__(
out_size: int, self,
*, in_size: int,
kernel_size: Tuple[int, int, int] = (1, 1, 1), out_size: int,
stride: Tuple[int, int, int] = (1, 1, 1), *,
padding: Tuple[int, int, int] = (0, 0, 0), kernel_size: Tuple[int, int, int] = (1, 1, 1),
activation=nn.ReLU(inplace=True), stride: Tuple[int, int, int] = (1, 1, 1),
bn: bool = False, padding: Tuple[int, int, int] = (0, 0, 0),
init=nn.init.kaiming_normal, activation=nn.ReLU(inplace=True),
bias: bool = True, bn: bool = False,
name: str = ""): init=nn.init.kaiming_normal,
bias: bool = True,
name: str = ""
):
super().__init__( super().__init__(
in_size, in_size,
out_size, out_size,
@@ -153,18 +171,22 @@ class Conv3d(_ConvBase):
conv=nn.Conv3d, conv=nn.Conv3d,
batch_norm=nn.BatchNorm3d, batch_norm=nn.BatchNorm3d,
bias=bias, bias=bias,
name=name) name=name
)
class FC(nn.Sequential): class FC(nn.Sequential):
def __init__(self,
in_size: int, def __init__(
out_size: int, self,
*, in_size: int,
activation=nn.ReLU(inplace=True), out_size: int,
bn: bool = False, *,
init=None, activation=nn.ReLU(inplace=True),
name: str = ""): bn: bool = False,
init=None,
name: str = ""
):
super().__init__() super().__init__()
self.add_module(name + 'fc', nn.Linear(in_size, out_size, bias=not bn)) self.add_module(name + 'fc', nn.Linear(in_size, out_size, bias=not bn))
if init is not None: if init is not None:
@@ -183,6 +205,7 @@ class FC(nn.Sequential):
class _DropoutNoScaling(InplaceFunction): class _DropoutNoScaling(InplaceFunction):
@staticmethod @staticmethod
def _make_noise(input): def _make_noise(input):
return input.new().resize_as_(input) return input.new().resize_as_(input)
@@ -192,8 +215,9 @@ class _DropoutNoScaling(InplaceFunction):
if inplace: if inplace:
return None return None
n = g.appendNode( n = g.appendNode(
g.create("Dropout", [input]).f_("ratio", p).i_( g.create("Dropout", [input]).f_("ratio",
"is_test", not train)) p).i_("is_test", not train)
)
real = g.appendNode(g.createSelect(n, 0)) real = g.appendNode(g.createSelect(n, 0))
g.appendNode(g.createSelect(n, 1)) g.appendNode(g.createSelect(n, 1))
return real return real
@@ -201,8 +225,10 @@ class _DropoutNoScaling(InplaceFunction):
@classmethod @classmethod
def forward(cls, ctx, input, p=0.5, train=False, inplace=False): def forward(cls, ctx, input, p=0.5, train=False, inplace=False):
if p < 0 or p > 1: if p < 0 or p > 1:
raise ValueError("dropout probability has to be between 0 and 1, " raise ValueError(
"but got {}".format(p)) "dropout probability has to be between 0 and 1, "
"but got {}".format(p)
)
ctx.p = p ctx.p = p
ctx.train = train ctx.train = train
ctx.inplace = inplace ctx.inplace = inplace
@@ -236,6 +262,7 @@ dropout_no_scaling = _DropoutNoScaling.apply
class _FeatureDropoutNoScaling(_DropoutNoScaling): class _FeatureDropoutNoScaling(_DropoutNoScaling):
@staticmethod @staticmethod
def symbolic(input, p=0.5, train=False, inplace=False): def symbolic(input, p=0.5, train=False, inplace=False):
return None return None
@@ -244,7 +271,8 @@ class _FeatureDropoutNoScaling(_DropoutNoScaling):
def _make_noise(input): def _make_noise(input):
return input.new().resize_( return input.new().resize_(
input.size(0), input.size(1), *repeat(1, input.size(0), input.size(1), *repeat(1,
input.dim() - 2)) input.dim() - 2)
)
feature_dropout_no_scaling = _FeatureDropoutNoScaling.apply feature_dropout_no_scaling = _FeatureDropoutNoScaling.apply
@@ -252,21 +280,17 @@ feature_dropout_no_scaling = _FeatureDropoutNoScaling.apply
def checkpoint_state(model=None, optimizer=None, best_prec=None, epoch=None): def checkpoint_state(model=None, optimizer=None, best_prec=None, epoch=None):
return { return {
'epoch': 'epoch': epoch,
epoch, 'best_prec': best_prec,
'best_prec': 'model_state': model.state_dict() if model is not None else None,
best_prec, 'optimizer_state': optimizer.state_dict()
'model_state': if optimizer is not None else None
model.state_dict() if model is not None else None,
'optimizer_state':
optimizer.state_dict() if optimizer is not None else None
} }
def save_checkpoint(state, def save_checkpoint(
is_best, state, is_best, filename='checkpoint', bestname='model_best'
filename='checkpoint', ):
bestname='model_best'):
filename = '{}.pth.tar'.format(filename) filename = '{}.pth.tar'.format(filename)
torch.save(state, filename) torch.save(state, filename)
if is_best: if is_best:
@@ -325,7 +349,8 @@ def variable_size_collate(pad_val=0, use_shared_memory=True):
out = out.view( out = out.view(
len(batch), max_len, len(batch), max_len,
*[batch[0].size(i) for i in range(1, batch[0].dim())]) *[batch[0].size(i) for i in range(1, batch[0].dim())]
)
out.fill_(pad_val) out.fill_(pad_val)
for i in range(len(batch)): for i in range(len(batch)):
out[i, 0:batch[i].size(0)] = batch[i] out[i, 0:batch[i].size(0)] = batch[i]
@@ -342,8 +367,9 @@ def variable_size_collate(pad_val=0, use_shared_memory=True):
return wrapped([torch.from_numpy(b) for b in batch]) return wrapped([torch.from_numpy(b) for b in batch])
if elem.shape == (): # scalars if elem.shape == (): # scalars
py_type = float if elem.dtype.name.startswith('float') else int py_type = float if elem.dtype.name.startswith('float') else int
return _numpy_type_map[elem.dtype.name](list( return _numpy_type_map[elem.dtype.name](
map(py_type, batch))) list(map(py_type, batch))
)
elif isinstance(batch[0], int): elif isinstance(batch[0], int):
return torch.LongTensor(batch) return torch.LongTensor(batch)
elif isinstance(batch[0], float): elif isinstance(batch[0], float):
@@ -372,19 +398,19 @@ class TrainValSplitter():
Whether or not shuffle which data goes to which split Whether or not shuffle which data goes to which split
""" """
def __init__(self, def __init__(
*, self, *, numel: int, percent_train: float, shuffled: bool = False
numel: int, ):
percent_train: float,
shuffled: bool = False):
indicies = np.array([i for i in range(numel)]) indicies = np.array([i for i in range(numel)])
if shuffled: if shuffled:
np.random.shuffle(indicies) np.random.shuffle(indicies)
self.train = torch.utils.data.sampler.SubsetRandomSampler( self.train = torch.utils.data.sampler.SubsetRandomSampler(
indicies[0:int(percent_train * numel)]) indicies[0:int(percent_train * numel)]
)
self.val = torch.utils.data.sampler.SubsetRandomSampler( self.val = torch.utils.data.sampler.SubsetRandomSampler(
indicies[int(percent_train * numel):-1]) indicies[int(percent_train * numel):-1]
)
class CrossValSplitter(): class CrossValSplitter():
@@ -413,7 +439,8 @@ class CrossValSplitter():
self.val = torch.utils.data.sampler.SubsetRandomSampler(self.folds[0]) self.val = torch.utils.data.sampler.SubsetRandomSampler(self.folds[0])
self.train = torch.utils.data.sampler.SubsetRandomSampler( self.train = torch.utils.data.sampler.SubsetRandomSampler(
np.concatenate(self.folds[1:], axis=0)) np.concatenate(self.folds[1:], axis=0)
)
self.metrics = {} self.metrics = {}
@@ -428,7 +455,8 @@ class CrossValSplitter():
assert idx >= 0 and idx < len(self) assert idx >= 0 and idx < len(self)
self.val.inidicies = self.folds[idx] self.val.inidicies = self.folds[idx]
self.train.inidicies = np.concatenate( self.train.inidicies = np.concatenate(
self.folds[np.arange(len(self)) != idx], axis=0) self.folds[np.arange(len(self)) != idx], axis=0
)
def __next__(self): def __next__(self):
self.current_v_ind += 1 self.current_v_ind += 1
@@ -454,6 +482,7 @@ class CrossValSplitter():
def set_bn_momentum_default(bn_momentum): def set_bn_momentum_default(bn_momentum):
def fn(m): def fn(m):
if isinstance(m, (nn.BatchNorm1d, nn.BatchNorm2d, nn.BatchNorm3d)): if isinstance(m, (nn.BatchNorm1d, nn.BatchNorm2d, nn.BatchNorm3d)):
m.momentum = bn_momentum m.momentum = bn_momentum
@@ -462,14 +491,17 @@ def set_bn_momentum_default(bn_momentum):
class BNMomentumScheduler(object): class BNMomentumScheduler(object):
def __init__(self,
model, def __init__(
bn_lambda, self, model, bn_lambda, last_epoch=-1,
last_epoch=-1, setter=set_bn_momentum_default
setter=set_bn_momentum_default): ):
if not isinstance(model, nn.Module): if not isinstance(model, nn.Module):
raise RuntimeError("Class '{}' is not a PyTorch nn Module".format( raise RuntimeError(
type(model).__name__)) "Class '{}' is not a PyTorch nn Module".format(
type(model).__name__
)
)
self.model = model self.model = model
self.setter = setter self.setter = setter
@@ -511,18 +543,21 @@ class Trainer(object):
Name of file to output tensorboard_logger to Name of file to output tensorboard_logger to
""" """
def __init__(self, def __init__(
model, self,
model_fn, model,
optimizer, model_fn,
checkpoint_name="ckpt", optimizer,
best_name="best", checkpoint_name="ckpt",
lr_scheduler=None, best_name="best",
bnm_scheduler=None, lr_scheduler=None,
eval_frequency=1, bnm_scheduler=None,
log_name=None): eval_frequency=1,
log_name=None
):
self.model, self.model_fn, self.optimizer, self.lr_scheduler, self.bnm_scheduler = ( self.model, self.model_fn, self.optimizer, self.lr_scheduler, self.bnm_scheduler = (
model, model_fn, optimizer, lr_scheduler, bnm_scheduler) model, model_fn, optimizer, lr_scheduler, bnm_scheduler
)
self.checkpoint_name, self.best_name = checkpoint_name, best_name self.checkpoint_name, self.best_name = checkpoint_name, best_name
self.eval_frequency = eval_frequency self.eval_frequency = eval_frequency
@@ -536,7 +571,8 @@ class Trainer(object):
@staticmethod @staticmethod
def _print(mode, epoch, loss, eval_dict, count): def _print(mode, epoch, loss, eval_dict, count):
to_print = "[{:d}] {}\tMean Loss: {:.4e}".format( to_print = "[{:d}] {}\tMean Loss: {:.4e}".format(
epoch, mode, loss / count) epoch, mode, loss / count
)
for k, v in natsorted(eval_dict.items(), key=itemgetter(0)): for k, v in natsorted(eval_dict.items(), key=itemgetter(0)):
to_print += "\tMean {}: {:2.3f}%".format(k, stats.mean(v) * 1e2) to_print += "\tMean {}: {:2.3f}%".format(k, stats.mean(v) * 1e2)
@@ -574,7 +610,8 @@ class Trainer(object):
for k, v in eval_res.items(): for k, v in eval_res.items():
if v is not None: if v is not None:
tb_log.log_value( tb_log.log_value(
"Training {}".format(k), 1.0 - v, step=idx) "Training {}".format(k), 1.0 - v, step=idx
)
d_loader.dataset.randomize() d_loader.dataset.randomize()
@@ -593,7 +630,8 @@ class Trainer(object):
self.optimizer.zero_grad() self.optimizer.zero_grad()
_, loss, eval_res = self.model_fn( _, loss, eval_res = self.model_fn(
self.model, data, eval=True, epoch=epoch) self.model, data, eval=True, epoch=epoch
)
total_loss += loss.data[0] total_loss += loss.data[0]
count += 1 count += 1
@@ -606,8 +644,7 @@ class Trainer(object):
tb_log.log_value("Eval loss", loss.data[0], step=idx) tb_log.log_value("Eval loss", loss.data[0], step=idx)
for k, v in eval_res.items(): for k, v in eval_res.items():
if v is not None: if v is not None:
tb_log.log_value( tb_log.log_value("Eval {}".format(k), 1.0 - v, step=idx)
"Eval {}".format(k), 1.0 - v, step=idx)
d_loader.dataset.randomize() d_loader.dataset.randomize()
@@ -615,12 +652,14 @@ class Trainer(object):
return total_loss / count, eval_dict return total_loss / count, eval_dict
def train(self, def train(
start_epoch, self,
n_epochs, start_epoch,
train_loader, n_epochs,
test_loader=None, train_loader,
best_loss=0.0): test_loader=None,
best_loss=0.0
):
r""" r"""
Call to begin training the model Call to begin training the model
@@ -649,10 +688,12 @@ class Trainer(object):
is_best = val_loss < best_loss is_best = val_loss < best_loss
best_loss = min(best_loss, val_loss) best_loss = min(best_loss, val_loss)
save_checkpoint( save_checkpoint(
checkpoint_state(self.model, self.optimizer, val_loss, checkpoint_state(
epoch), self.model, self.optimizer, val_loss, epoch
),
is_best, is_best,
filename=self.checkpoint_name, filename=self.checkpoint_name,
bestname=self.best_name) bestname=self.best_name
)
return best_loss return best_loss