codecsplat / src /main.py
multimodalart's picture
multimodalart HF Staff
CodecSplat demo
c9ad6b5 verified
Raw History Blame Contribute Delete
11.9 kB
import os
from pathlib import Path
import copy
import hydra
import torch
import wandb
from colorama import Fore
from jaxtyping import install_import_hook
from omegaconf import DictConfig, OmegaConf
from pytorch_lightning import Trainer
from pytorch_lightning.callbacks import (
LearningRateMonitor,
ModelCheckpoint,
)
from pytorch_lightning.loggers.wandb import WandbLogger
from pytorch_lightning.plugins.environments import LightningEnvironment
# Configure beartype and jaxtyping.
with install_import_hook(
("src",),
("beartype", "beartype"),
):
from src.config import load_typed_root_config
from src.dataset.data_module import DataModule
from src.global_cfg import set_cfg
from src.loss import get_losses
from src.misc.LocalLogger import LocalLogger
from src.misc.step_tracker import StepTracker
from src.misc.wandb_tools import update_checkpoint_path
from src.misc.resume_ckpt import find_latest_ckpt
from src.model.decoder import get_decoder
from src.model.encoder import get_encoder
from src.model.model_wrapper import ModelWrapper
def cyan(text: str) -> str:
return f"{Fore.CYAN}{text}{Fore.RESET}"
def load_state_dict_with_shape_filter(
module: torch.nn.Module,
state_dict: dict[str, torch.Tensor],
strict: bool,
label: str,
) -> None:
if strict:
module.load_state_dict(state_dict, strict=True)
return
module_state = module.state_dict()
filtered_state_dict = {}
shape_mismatches = []
for key, value in state_dict.items():
if key not in module_state:
continue
if module_state[key].shape != value.shape:
shape_mismatches.append(
(key, tuple(value.shape), tuple(module_state[key].shape))
)
continue
filtered_state_dict[key] = value
incompatible = module.load_state_dict(filtered_state_dict, strict=False)
missing_keys = sorted(incompatible.missing_keys)
unexpected_keys = sorted(incompatible.unexpected_keys)
print(cyan(f"{label}: strict=False, filtered checkpoint loading summary"))
print(f" loaded tensors: {len(filtered_state_dict)}")
print(f" missing model tensors: {len(missing_keys)}")
for key in missing_keys:
print(f" [missing] {key}")
print(f" shape-mismatched checkpoint tensors: {len(shape_mismatches)}")
for key, ckpt_shape, model_shape in shape_mismatches:
print(
f" [shape-mismatch] {key}: checkpoint {ckpt_shape} vs model {model_shape}"
)
print(f" unexpected checkpoint tensors: {len(unexpected_keys)}")
for key in unexpected_keys:
print(f" [unexpected] {key}")
@hydra.main(
version_base=None,
config_path="../config",
config_name="main",
)
def train(cfg_dict: DictConfig):
if cfg_dict["mode"] == "train" and cfg_dict["train"]["eval_model_every_n_val"] > 0:
eval_cfg_dict = copy.deepcopy(cfg_dict)
dataset_dir = str(cfg_dict["dataset"]["roots"]).lower()
eval_num_context_views = cfg_dict["dataset"]["view_sampler"][
"num_context_views"
]
if "re10k" in dataset_dir:
if cfg_dict["dataset"]["view_sampler"]["num_context_views"] == 2:
eval_path = "assets/evaluation_index_re10k.json"
else:
raise ValueError("unsupported number of views for re10k")
elif "dl3dv" in dataset_dir:
if eval_num_context_views == 8:
eval_path = (
"assets/dl3dv_evaluation/"
"dl3dv_start_0_distance_40_ctx_8v_tgt_8v.json"
)
else:
raise ValueError("unsupported number of views for dl3dv")
else:
raise Exception("Fail to load eval index path")
eval_cfg_dict["dataset"]["view_sampler"] = {
"name": "evaluation",
"index_path": eval_path,
"num_context_views": eval_num_context_views,
}
eval_cfg = load_typed_root_config(eval_cfg_dict)
else:
eval_cfg = None
cfg = load_typed_root_config(cfg_dict)
set_cfg(cfg_dict)
# Set up the output directory.
if cfg_dict.output_dir is None:
output_dir = Path(
hydra.core.hydra_config.HydraConfig.get()["runtime"]["output_dir"]
)
else: # for resuming
output_dir = Path(cfg_dict.output_dir)
os.makedirs(output_dir, exist_ok=True)
print(cyan(f"Saving outputs to {output_dir}."))
# Set up logging with wandb.
callbacks = []
if cfg_dict.wandb.mode != "disabled" and cfg.mode == "train":
wandb_extra_kwargs = {}
if cfg_dict.wandb.id is not None:
wandb_extra_kwargs.update({'id': cfg_dict.wandb.id,
'resume': "must"})
logger = WandbLogger(
entity=cfg_dict.wandb.entity,
project=cfg_dict.wandb.project,
mode=cfg_dict.wandb.mode,
name=os.path.basename(cfg_dict.output_dir),
tags=cfg_dict.wandb.get("tags", None),
log_model=False,
save_dir=output_dir,
config=OmegaConf.to_container(cfg_dict),
**wandb_extra_kwargs,
)
callbacks.append(LearningRateMonitor("step", True))
if wandb.run is not None:
wandb.run.log_code("src")
else:
logger = LocalLogger(output_dir / "local")
# Set up checkpointing.
callbacks.append(
ModelCheckpoint(
output_dir / "checkpoints",
every_n_train_steps=cfg.checkpointing.every_n_train_steps,
save_top_k=cfg.checkpointing.save_top_k,
monitor="info/global_step",
mode="max",
)
)
for cb in callbacks:
cb.CHECKPOINT_EQUALS_CHAR = '_'
# Prepare the checkpoint for loading.
if cfg.checkpointing.resume:
if not os.path.exists(output_dir / 'checkpoints'):
checkpoint_path = None
else:
checkpoint_path = find_latest_ckpt(output_dir / 'checkpoints')
print(f'resume from {checkpoint_path}')
else:
checkpoint_path = update_checkpoint_path(cfg.checkpointing.load, cfg.wandb)
# This allows the current step to be shared with the data loader processes.
step_tracker = StepTracker()
if cfg.mode == "test":
trainer_devices = 1
trainer_strategy = "auto"
trainer_num_nodes = 1
else:
trainer_devices = torch.cuda.device_count()
trainer_strategy = "ddp" if trainer_devices > 1 else "auto"
trainer_num_nodes = cfg.trainer.num_nodes
trainer = Trainer(
max_epochs=-1,
accelerator="gpu",
logger=logger,
devices=trainer_devices,
strategy=trainer_strategy,
callbacks=callbacks,
val_check_interval=cfg.trainer.val_check_interval,
enable_progress_bar=cfg.mode == "test",
gradient_clip_val=cfg.trainer.gradient_clip_val,
accumulate_grad_batches=cfg.trainer.accumulate_grad_batches,
precision=cfg.trainer.precision,
max_steps=cfg.trainer.max_steps,
num_sanity_val_steps=cfg.trainer.num_sanity_val_steps,
num_nodes=trainer_num_nodes,
plugins=LightningEnvironment() if cfg.use_plugins else None,
)
torch.manual_seed(cfg_dict.seed + trainer.global_rank)
encoder = get_encoder(cfg.model.encoder)
model_wrapper = ModelWrapper(
cfg.optimizer,
cfg.test,
cfg.train,
encoder,
get_decoder(cfg.model.decoder, cfg.dataset),
get_losses(cfg.loss),
step_tracker,
eval_data_cfg=(
None if eval_cfg is None else eval_cfg.dataset
),
)
data_module = DataModule(
cfg.dataset,
cfg.data_loader,
step_tracker,
global_rank=trainer.global_rank,
)
if cfg.mode == "train":
print("train:", len(data_module.train_dataloader()))
print("val:", len(data_module.val_dataloader()))
print("test:", len(data_module.test_dataloader()))
strict_load = not cfg.checkpointing.no_strict_load
if cfg.mode == "train":
# only load monodepth
if cfg.checkpointing.pretrained_monodepth is not None:
strict_load = False
pretrained_model = torch.load(cfg.checkpointing.pretrained_monodepth, map_location='cpu')
if 'state_dict' in pretrained_model:
pretrained_model = pretrained_model['state_dict']
model_wrapper.encoder.depth_predictor.load_state_dict(pretrained_model, strict=strict_load)
print(
cyan(
f"Loaded pretrained monodepth: {cfg.checkpointing.pretrained_monodepth}"
)
)
# load pretrained mvdepth
if cfg.checkpointing.pretrained_mvdepth is not None:
pretrained_model = torch.load(cfg.checkpointing.pretrained_mvdepth, map_location='cpu')['model']
model_wrapper.encoder.depth_predictor.load_state_dict(pretrained_model, strict=False)
print(
cyan(
f"Loaded pretrained mvdepth: {cfg.checkpointing.pretrained_mvdepth}"
)
)
# load full model
if cfg.checkpointing.pretrained_model is not None:
pretrained_model = torch.load(cfg.checkpointing.pretrained_model, map_location='cpu')
if 'state_dict' in pretrained_model:
pretrained_model = pretrained_model['state_dict']
load_state_dict_with_shape_filter(
model_wrapper,
pretrained_model,
strict=strict_load,
label="pretrained_model",
)
print(
cyan(
f"Loaded pretrained weights: {cfg.checkpointing.pretrained_model}"
)
)
# load pretrained depth
if cfg.checkpointing.pretrained_depth is not None:
pretrained_model = torch.load(cfg.checkpointing.pretrained_depth, map_location='cpu')['model']
strict_load = True
model_wrapper.encoder.depth_predictor.load_state_dict(pretrained_model, strict=strict_load)
print(
cyan(
f"Loaded pretrained depth: {cfg.checkpointing.pretrained_depth}"
)
)
trainer.fit(model_wrapper, datamodule=data_module, ckpt_path=checkpoint_path)
else:
# load full model
if cfg.checkpointing.pretrained_model is not None:
pretrained_model = torch.load(cfg.checkpointing.pretrained_model, map_location='cpu')
if 'state_dict' in pretrained_model:
pretrained_model = pretrained_model['state_dict']
load_state_dict_with_shape_filter(
model_wrapper,
pretrained_model,
strict=strict_load,
label="pretrained_model",
)
print(
cyan(
f"Loaded pretrained weights: {cfg.checkpointing.pretrained_model}"
)
)
# load pretrained depth model only
if cfg.checkpointing.pretrained_depth is not None:
pretrained_model = torch.load(cfg.checkpointing.pretrained_depth, map_location='cpu')['model']
strict_load = True
model_wrapper.encoder.depth_predictor.load_state_dict(pretrained_model, strict=strict_load)
print(
cyan(
f"Loaded pretrained depth: {cfg.checkpointing.pretrained_depth}"
)
)
trainer.test(
model_wrapper,
datamodule=data_module,
ckpt_path=checkpoint_path,
)
if __name__ == "__main__":
torch.set_float32_matmul_precision('high')
train()