KPConv-PyTorch/utils/config.py

377 lines
13 KiB
Python
Raw Normal View History

2020-03-31 19:42:35 +00:00
#
#
# 0=================================0
# | Kernel Point Convolutions |
# 0=================================0
#
#
# ----------------------------------------------------------------------------------------------------------------------
#
# Configuration class
#
# ----------------------------------------------------------------------------------------------------------------------
#
# Hugues THOMAS - 11/06/2018
#
from os.path import join
import numpy as np
# Colors for printing
class bcolors:
HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
class Config:
"""
Class containing the parameters you want to modify for this dataset
"""
##################
# Input parameters
##################
# Dataset name
dataset = ''
# Type of network model
dataset_task = ''
# Number of classes in the dataset
num_classes = 0
# Dimension of input points
in_points_dim = 3
# Dimension of input features
in_features_dim = 1
# Radius of the input sphere (ignored for models, only used for point clouds)
in_radius = 1.0
# Number of CPU threads for the input pipeline
input_threads = 8
##################
# Model parameters
##################
# Architecture definition. List of blocks
architecture = []
# Decide the mode of equivariance and invariance
equivar_mode = ''
invar_mode = ''
# Dimension of the first feature maps
first_features_dim = 64
# Batch normalization parameters
use_batch_norm = True
batch_norm_momentum = 0.99
# For segmentation models : ratio between the segmented area and the input area
segmentation_ratio = 1.0
###################
# KPConv parameters
###################
# Number of kernel points
num_kernel_points = 15
# Size of the first subsampling grid in meter
first_subsampling_dl = 0.02
# Radius of convolution in "number grid cell". (2.5 is the standard value)
conv_radius = 2.5
# Radius of deformable convolution in "number grid cell". Larger so that deformed kernel can spread out
deform_radius = 5.0
# Kernel point influence radius
KP_extent = 1.0
# Influence function when d < KP_extent. ('constant', 'linear', 'gaussian') When d > KP_extent, always zero
KP_influence = 'linear'
# Aggregation function of KPConv in ('closest', 'sum')
# Decide if you sum all kernel point influences, or if you only take the influence of the closest KP
aggregation_mode = 'sum'
# Fixed points in the kernel : 'none', 'center' or 'verticals'
fixed_kernel_points = 'center'
# Use modulateion in deformable convolutions
modulated = False
# For SLAM datasets like SemanticKitti number of frames used (minimum one)
n_frames = 1
2020-04-09 21:13:27 +00:00
# For SLAM datasets like SemanticKitti max number of point in input cloud + validation
2020-03-31 19:42:35 +00:00
max_in_points = 0
2020-04-09 21:13:27 +00:00
val_radius = 51.0
max_val_points = 50000
2020-03-31 19:42:35 +00:00
#####################
# Training parameters
#####################
# Network optimizer parameters (learning rate and momentum)
learning_rate = 1e-3
momentum = 0.9
# Learning rate decays. Dictionary of all decay values with their epoch {epoch: decay}.
lr_decays = {200: 0.2, 300: 0.2}
# Gradient clipping value (negative means no clipping)
grad_clip_norm = 100.0
# Augmentation parameters
augment_scale_anisotropic = True
augment_scale_min = 0.9
augment_scale_max = 1.1
augment_symmetries = [False, False, False]
augment_rotation = 'vertical'
augment_noise = 0.005
augment_color = 0.7
# Augment with occlusions (not implemented yet)
2020-04-02 21:31:35 +00:00
augment_occlusion = 'none'
2020-03-31 19:42:35 +00:00
augment_occlusion_ratio = 0.2
augment_occlusion_num = 1
# Regularization loss importance
weight_decay = 1e-3
2020-04-09 21:13:27 +00:00
# The way we balance segmentation loss DEPRECATED
2020-03-31 19:42:35 +00:00
segloss_balance = 'none'
2020-04-09 21:13:27 +00:00
# Choose weights for class (used in segmentation loss). Empty list for no weights
class_w = []
2020-03-31 19:42:35 +00:00
# Offset regularization loss
offsets_loss = 'permissive'
offsets_decay = 1e-2
# Number of batch
batch_num = 10
2020-04-09 21:13:27 +00:00
val_batch_num = 10
2020-03-31 19:42:35 +00:00
# Maximal number of epochs
max_epoch = 1000
# Number of steps per epochs
epoch_steps = 1000
# Number of validation examples per epoch
validation_size = 100
# Number of epoch between each checkpoint
checkpoint_gap = 50
# Do we nee to save convergence
saving = True
saving_path = None
def __init__(self):
"""
Class Initialyser
"""
# Number of layers
self.num_layers = len([block for block in self.architecture if 'pool' in block or 'strided' in block]) + 1
###################
# Deform layer list
###################
#
# List of boolean indicating which layer has a deformable convolution
#
layer_blocks = []
self.deform_layers = []
arch = self.architecture
for block_i, block in enumerate(arch):
# Get all blocks of the layer
if not ('pool' in block or 'strided' in block or 'global' in block or 'upsample' in block):
layer_blocks += [block]
continue
# Convolution neighbors indices
# *****************************
deform_layer = False
if layer_blocks:
if np.any(['deformable' in blck for blck in layer_blocks]):
deform_layer = True
if 'pool' in block or 'strided' in block:
if 'deformable' in block:
deform_layer = True
self.deform_layers += [deform_layer]
layer_blocks = []
# Stop when meeting a global pooling or upsampling
if 'global' in block or 'upsample' in block:
break
def load(self, path):
filename = join(path, 'parameters.txt')
with open(filename, 'r') as f:
lines = f.readlines()
# Class variable dictionary
for line in lines:
line_info = line.split()
2020-04-02 21:31:35 +00:00
if len(line_info) > 2 and line_info[0] != '#':
2020-03-31 19:42:35 +00:00
if line_info[2] == 'None':
setattr(self, line_info[0], None)
elif line_info[0] == 'lr_decay_epochs':
self.lr_decays = {int(b.split(':')[0]): float(b.split(':')[1]) for b in line_info[2:]}
elif line_info[0] == 'architecture':
self.architecture = [b for b in line_info[2:]]
elif line_info[0] == 'augment_symmetries':
self.augment_symmetries = [bool(int(b)) for b in line_info[2:]]
elif line_info[0] == 'num_classes':
if len(line_info) > 3:
self.num_classes = [int(c) for c in line_info[2:]]
else:
self.num_classes = int(line_info[2])
2020-04-09 21:13:27 +00:00
elif line_info[0] == 'class_w':
self.class_w = [float(w) for w in line_info[2:]]
2020-03-31 19:42:35 +00:00
else:
attr_type = type(getattr(self, line_info[0]))
if attr_type == bool:
setattr(self, line_info[0], attr_type(int(line_info[2])))
else:
setattr(self, line_info[0], attr_type(line_info[2]))
self.saving = True
self.saving_path = path
self.__init__()
def save(self):
with open(join(self.saving_path, 'parameters.txt'), "w") as text_file:
text_file.write('# -----------------------------------#\n')
text_file.write('# Parameters of the training session #\n')
text_file.write('# -----------------------------------#\n\n')
# Input parameters
text_file.write('# Input parameters\n')
text_file.write('# ****************\n\n')
text_file.write('dataset = {:s}\n'.format(self.dataset))
text_file.write('dataset_task = {:s}\n'.format(self.dataset_task))
if type(self.num_classes) is list:
text_file.write('num_classes =')
for n in self.num_classes:
text_file.write(' {:d}'.format(n))
text_file.write('\n')
else:
text_file.write('num_classes = {:d}\n'.format(self.num_classes))
text_file.write('in_points_dim = {:d}\n'.format(self.in_points_dim))
text_file.write('in_features_dim = {:d}\n'.format(self.in_features_dim))
text_file.write('in_radius = {:.3f}\n'.format(self.in_radius))
text_file.write('input_threads = {:d}\n\n'.format(self.input_threads))
# Model parameters
text_file.write('# Model parameters\n')
text_file.write('# ****************\n\n')
text_file.write('architecture =')
for a in self.architecture:
text_file.write(' {:s}'.format(a))
text_file.write('\n')
text_file.write('equivar_mode = {:s}\n'.format(self.equivar_mode))
text_file.write('invar_mode = {:s}\n'.format(self.invar_mode))
text_file.write('num_layers = {:d}\n'.format(self.num_layers))
text_file.write('first_features_dim = {:d}\n'.format(self.first_features_dim))
text_file.write('use_batch_norm = {:d}\n'.format(int(self.use_batch_norm)))
text_file.write('batch_norm_momentum = {:.3f}\n\n'.format(self.batch_norm_momentum))
text_file.write('segmentation_ratio = {:.3f}\n\n'.format(self.segmentation_ratio))
# KPConv parameters
text_file.write('# KPConv parameters\n')
text_file.write('# *****************\n\n')
text_file.write('first_subsampling_dl = {:.3f}\n'.format(self.first_subsampling_dl))
text_file.write('num_kernel_points = {:d}\n'.format(self.num_kernel_points))
text_file.write('conv_radius = {:.3f}\n'.format(self.conv_radius))
text_file.write('deform_radius = {:.3f}\n'.format(self.deform_radius))
text_file.write('fixed_kernel_points = {:s}\n'.format(self.fixed_kernel_points))
text_file.write('KP_extent = {:.3f}\n'.format(self.KP_extent))
text_file.write('KP_influence = {:s}\n'.format(self.KP_influence))
text_file.write('aggregation_mode = {:s}\n'.format(self.aggregation_mode))
text_file.write('modulated = {:d}\n'.format(int(self.modulated)))
text_file.write('n_frames = {:d}\n'.format(self.n_frames))
text_file.write('max_in_points = {:d}\n\n'.format(self.max_in_points))
2020-04-09 21:13:27 +00:00
text_file.write('max_val_points = {:d}\n\n'.format(self.max_val_points))
text_file.write('val_radius = {:.3f}\n\n'.format(self.val_radius))
2020-03-31 19:42:35 +00:00
# Training parameters
text_file.write('# Training parameters\n')
text_file.write('# *******************\n\n')
text_file.write('learning_rate = {:f}\n'.format(self.learning_rate))
text_file.write('momentum = {:f}\n'.format(self.momentum))
text_file.write('lr_decay_epochs =')
for e, d in self.lr_decays.items():
text_file.write(' {:d}:{:f}'.format(e, d))
text_file.write('\n')
text_file.write('grad_clip_norm = {:f}\n\n'.format(self.grad_clip_norm))
text_file.write('augment_symmetries =')
for a in self.augment_symmetries:
text_file.write(' {:d}'.format(int(a)))
text_file.write('\n')
text_file.write('augment_rotation = {:s}\n'.format(self.augment_rotation))
text_file.write('augment_noise = {:f}\n'.format(self.augment_noise))
text_file.write('augment_occlusion = {:s}\n'.format(self.augment_occlusion))
text_file.write('augment_occlusion_ratio = {:.3f}\n'.format(self.augment_occlusion_ratio))
text_file.write('augment_occlusion_num = {:d}\n'.format(self.augment_occlusion_num))
text_file.write('augment_scale_anisotropic = {:d}\n'.format(int(self.augment_scale_anisotropic)))
text_file.write('augment_scale_min = {:.3f}\n'.format(self.augment_scale_min))
text_file.write('augment_scale_max = {:.3f}\n'.format(self.augment_scale_max))
text_file.write('augment_color = {:.3f}\n\n'.format(self.augment_color))
text_file.write('weight_decay = {:f}\n'.format(self.weight_decay))
text_file.write('segloss_balance = {:s}\n'.format(self.segloss_balance))
2020-04-09 21:13:27 +00:00
text_file.write('class_w =')
for a in self.class_w:
text_file.write(' {:.3f}'.format(a))
text_file.write('\n')
2020-03-31 19:42:35 +00:00
text_file.write('offsets_loss = {:s}\n'.format(self.offsets_loss))
text_file.write('offsets_decay = {:f}\n'.format(self.offsets_decay))
text_file.write('batch_num = {:d}\n'.format(self.batch_num))
2020-04-09 21:13:27 +00:00
text_file.write('val_batch_num = {:d}\n'.format(self.val_batch_num))
2020-03-31 19:42:35 +00:00
text_file.write('max_epoch = {:d}\n'.format(self.max_epoch))
if self.epoch_steps is None:
text_file.write('epoch_steps = None\n')
else:
text_file.write('epoch_steps = {:d}\n'.format(self.epoch_steps))
text_file.write('validation_size = {:d}\n'.format(self.validation_size))
text_file.write('checkpoint_gap = {:d}\n'.format(self.checkpoint_gap))