3939from typing import Literal , assert_never
4040from utils import AppearanceOptModule , CameraOptModule , knn , rgb_to_sh , set_random_seed
4141
42+ from numpy .core .multiarray import scalar as npscalar
43+ from numpy .dtypes import Float64DType as npFloat64DType
44+ from numpy import dtype as npdtype
45+
4246from gsplat import export_splats
4347from gsplat .distributed import cli
4448from gsplat .optimizers import SelectiveAdam
@@ -58,6 +62,9 @@ class Config:
5862 # Path to the .pt files to load and resume training
5963 resume_ckpt : Optional [str ] = ""
6064
65+ # Whether to retrieve optimizer state from the checkpoint
66+ retrieve_optimizer_state : bool = True
67+
6168 # Path to the dataset
6269 sfm_file : str = "sfm.json"
6370 image_alpha : bool = False
@@ -127,6 +134,9 @@ class Config:
127134 # Use random background for training to discourage transparency
128135 random_bkgd : bool = False
129136
137+ # Whether to use schedulers for the learning rates (only for MCMC)
138+ use_scheduler : bool = True
139+
130140 # LR for 3D point positions
131141 means_lr : float = 1.6e-4
132142 # LR for Gaussian scale factors
@@ -222,6 +232,7 @@ def create_splats_with_optimizers(
222232 device : str = "cuda" ,
223233 world_rank : int = 0 ,
224234 world_size : int = 1 ,
235+ strategy : Union [DefaultStrategy , MCMCStrategy ] = DefaultStrategy ,
225236) -> Tuple [torch .nn .ParameterDict , Dict [str , torch .optim .Optimizer ]]:
226237
227238 #initialize from sfm
@@ -328,6 +339,12 @@ def __init__(
328339
329340 logging .info (f"Scene scale: { self .scene_scale } " )
330341
342+ retrieve_optimizer_state = cfg .retrieve_optimizer_state and cfg .resume_ckpt != ""
343+ if retrieve_optimizer_state and isinstance (cfg .strategy , MCMCStrategy ):
344+ # In case of MCMC, the scheduler reduces the effective learning rate by 100
345+ # This line updates the learning rate accordingly
346+ cfg .means_lr = 0.01 * cfg .means_lr
347+
331348 # Model
332349 feature_dim = 32 if cfg .app_opt else None
333350 self .splats , self .optimizers = create_splats_with_optimizers (
@@ -349,6 +366,7 @@ def __init__(
349366 device = self .device ,
350367 world_rank = world_rank ,
351368 world_size = world_size ,
369+ strategy = cfg .strategy ,
352370 )
353371 logging .info (f"Model initialized. Number of GS: { len (self .splats ['means' ])} " )
354372
@@ -517,37 +535,42 @@ def train(self):
517535 #Compute effective steps
518536 max_steps = cfg .max_epochs * len (self .trainset )
519537
520- schedulers = [
521- # means has a learning rate schedule, that end at 0.01 of the initial value
522- torch .optim .lr_scheduler .ExponentialLR (
523- self .optimizers ["means" ], gamma = 0.01 ** (1.0 / max_steps )
524- ),
525- ]
538+ schedulers = []
539+ if isinstance (cfg .strategy , MCMCStrategy ) and cfg .use_scheduler :
526540
527- if cfg .pose_opt :
528- # pose optimization has a learning rate schedule
529- schedulers .append (
541+ logging .info ("Create schedulers" )
542+
543+ schedulers = [
544+ # means has a learning rate schedule that ends at 0.01 of the initial value
530545 torch .optim .lr_scheduler .ExponentialLR (
531- self .pose_optimizers [0 ], gamma = 0.01 ** (1.0 / max_steps )
546+ self .optimizers ["means" ], gamma = 0.01 ** (1.0 / max_steps )
547+ ),
548+ ]
549+
550+ if cfg .pose_opt :
551+ # pose optimization has a learning rate schedule
552+ schedulers .append (
553+ torch .optim .lr_scheduler .ExponentialLR (
554+ self .pose_optimizers [0 ], gamma = 0.01 ** (1.0 / max_steps )
555+ )
532556 )
533- )
534557
535- if cfg .use_bilateral_grid :
536- # bilateral grid has a learning rate schedule. Linear warmup for 1000 steps.
537- schedulers .append (
538- torch .optim .lr_scheduler .ChainedScheduler (
539- [
540- torch .optim .lr_scheduler .LinearLR (
541- self .bil_grid_optimizers [0 ],
542- start_factor = 0.01 ,
543- total_iters = 1000 ,
544- ),
545- torch .optim .lr_scheduler .ExponentialLR (
546- self .bil_grid_optimizers [0 ], gamma = 0.01 ** (1.0 / max_steps )
547- ),
548- ]
558+ if cfg .use_bilateral_grid :
559+ # bilateral grid has a learning rate schedule. Linear warmup for 1000 steps.
560+ schedulers .append (
561+ torch .optim .lr_scheduler .ChainedScheduler (
562+ [
563+ torch .optim .lr_scheduler .LinearLR (
564+ self .bil_grid_optimizers [0 ],
565+ start_factor = 0.01 ,
566+ total_iters = 1000 ,
567+ ),
568+ torch .optim .lr_scheduler .ExponentialLR (
569+ self .bil_grid_optimizers [0 ], gamma = 0.01 ** (1.0 / max_steps )
570+ ),
571+ ]
572+ )
549573 )
550- )
551574
552575 trainloader = torch .utils .data .DataLoader (
553576 self .trainset ,
@@ -560,6 +583,19 @@ def train(self):
560583
561584 step = - 1
562585
586+ if cfg .resume_ckpt != "" :
587+ torch .serialization .add_safe_globals ([npscalar , npdtype , npFloat64DType ])
588+ logging .info ("Loading Gaussians from checkpoint" )
589+ ckpt = torch .load (cfg .resume_ckpt , map_location = device , weights_only = True )
590+ for k in self .splats .keys ():
591+ self .splats [k ].data = torch .cat ([ckpt ["splats" ][k ]])
592+ if cfg .retrieve_optimizer_state :
593+ logging .info ("Loading optimizer state from checkpoint" )
594+ self .strategy_state = ckpt ["strategy_state" ]
595+ for name , opt_state_dict in ckpt ["optimizer_state_dicts" ].items ():
596+ self .optimizers [name ].load_state_dict (opt_state_dict )
597+ step = ckpt ["step" ] - 1
598+
563599 if not cfg .use_progress_bar :
564600 logging .info ("Training started" )
565601
@@ -763,7 +799,10 @@ def train(self):
763799 if epoch in [i - 1 for i in cfg .save_epochs ] or epoch == cfg .max_epochs - 1 :
764800
765801 path = f"{ self .ckpt_dir } /ckpt_{ epoch } _rank{ self .world_rank } .pt"
766- data = {"step" : step , "splats" : self .splats .state_dict ()}
802+ data = {"step" : step ,
803+ "splats" : self .splats .state_dict (),
804+ "strategy_state" : self .strategy_state ,
805+ "optimizer_state_dicts" : {name : opt .state_dict () for name , opt in self .optimizers .items ()}}
767806
768807 if cfg .pose_opt :
769808 if world_size > 1 :
@@ -784,13 +823,6 @@ def main(local_rank: int, world_rank, world_size: int, cfg: Config):
784823 #Create object for computation
785824 runner = Runner (local_rank , world_rank , world_size , cfg )
786825
787- #if a checkpoint was passed, will load the gaussians from there
788- if cfg .resume_ckpt != "" :
789- logging .info ("Loading from checkpoint" )
790- ckpt = torch .load (cfg .resume_ckpt , map_location = runner .device , weights_only = True )
791- for k in runner .splats .keys ():
792- runner .splats [k ].data = torch .cat ([ckpt ["splats" ][k ]])
793-
794826 #Launch training
795827 runner .train ()
796828
0 commit comments