diff --git a/nerfstudio/configs/base_config.py b/nerfstudio/configs/base_config.py index 53bc7b4c4..a74f07ed7 100644 --- a/nerfstudio/configs/base_config.py +++ b/nerfstudio/configs/base_config.py @@ -20,23 +20,14 @@ from __future__ import annotations import warnings from dataclasses import dataclass -from datetime import datetime from pathlib import Path -from typing import Any, Dict, List, Optional, Tuple, Type - -import yaml -from rich.console import Console -from typing_extensions import Literal - -from nerfstudio.configs.config_utils import to_immutable_dict +from typing import Any, List, Optional, Tuple, Type # model instances from nerfstudio.utils import writer warnings.filterwarnings("ignore", module="torchvision") -CONSOLE = Console(width=120) - # Pretty printing class class PrintableConfig: # pylint: disable=too-few-public-methods """Printable Config defining str function""" @@ -186,93 +177,3 @@ class ViewerConfig(PrintableConfig): skip_openrelay: bool = False """Avoid using openrelay to communicate with the viewer. Try disabling if you have trouble connecting to the viewer""" - - -from nerfstudio.engine.optimizers import OptimizerConfig -from nerfstudio.engine.schedulers import SchedulerConfig -from nerfstudio.pipelines.base_pipeline import VanillaPipelineConfig - - -@dataclass -class Config(PrintableConfig): - """Full config contents""" - - output_dir: Path = Path("outputs") - """relative or absolute output directory to save all checkpoints and logging""" - method_name: Optional[str] = None - """Method name. Required to set in python or via cli""" - experiment_name: Optional[str] = None - """Experiment name. If None, will automatically be set to dataset name""" - timestamp: str = "{timestamp}" - """Experiment timestamp.""" - machine: MachineConfig = MachineConfig() - """Machine configuration""" - logging: LoggingConfig = LoggingConfig() - """Logging configuration""" - viewer: ViewerConfig = ViewerConfig() - """Viewer configuration""" - trainer: TrainerConfig = TrainerConfig() - """Trainer configuration""" - pipeline: VanillaPipelineConfig = VanillaPipelineConfig() - """Pipeline configuration""" - optimizers: Dict[str, Any] = to_immutable_dict( - { - "fields": { - "optimizer": OptimizerConfig(), - "scheduler": SchedulerConfig(), - } - } - ) - """Dictionary of optimizer groups and their schedulers""" - vis: Literal["viewer", "wandb", "tensorboard"] = "wandb" - """Which visualizer to use.""" - data: Optional[Path] = None - """Alias for --pipeline.datamanager.dataparser.data""" - - def is_viewer_enabled(self) -> bool: - """Checks if a viewer is enabled.""" - return "viewer" == self.vis - - def is_wandb_enabled(self) -> bool: - """Checks if wandb is enabled.""" - return "wandb" == self.vis - - def is_tensorboard_enabled(self) -> bool: - """Checks if tensorboard is enabled.""" - return "tensorboard" == self.vis - - def set_timestamp(self) -> None: - """Dynamically set the experiment timestamp""" - if self.timestamp == "{timestamp}": - self.timestamp = datetime.now().strftime("%Y-%m-%d_%H%M%S") - - def set_experiment_name(self) -> None: - """Dynamically set the experiment name""" - if self.experiment_name is None: - self.experiment_name = str(self.pipeline.datamanager.dataparser.data).replace("../", "").replace("/", "-") - - def get_base_dir(self) -> Path: - """Retrieve the base directory to set relative paths""" - # check the experiment and method names - assert self.method_name is not None, "Please set method name in config or via the cli" - self.set_experiment_name() - return Path(f"{self.output_dir}/{self.experiment_name}/{self.method_name}/{self.timestamp}") - - def get_checkpoint_dir(self) -> Path: - """Retrieve the checkpoint directory""" - return Path(self.get_base_dir() / self.trainer.relative_model_dir) - - def print_to_terminal(self) -> None: - """Helper to pretty print config to terminal""" - CONSOLE.rule("Config") - CONSOLE.print(self) - CONSOLE.rule("") - - def save_config(self) -> None: - """Save config to base directory""" - base_dir = self.get_base_dir() - assert base_dir is not None - base_dir.mkdir(parents=True, exist_ok=True) - config_yaml_path = base_dir / "config.yml" - CONSOLE.log(f"Saving config to: {config_yaml_path}") - config_yaml_path.write_text(yaml.dump(self), "utf8") diff --git a/nerfstudio/configs/experiment_config.py b/nerfstudio/configs/experiment_config.py new file mode 100644 index 000000000..e78c8de59 --- /dev/null +++ b/nerfstudio/configs/experiment_config.py @@ -0,0 +1,123 @@ +# Copyright 2022 The Nerfstudio Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Config used for running an experiment""" + +from dataclasses import dataclass +from datetime import datetime +from pathlib import Path +from typing import Any, Dict, Optional + +import yaml +from rich.console import Console +from typing_extensions import Literal + +from nerfstudio.configs.base_config import ( + LoggingConfig, + MachineConfig, + PrintableConfig, + TrainerConfig, + ViewerConfig, +) +from nerfstudio.configs.config_utils import to_immutable_dict +from nerfstudio.engine.optimizers import OptimizerConfig +from nerfstudio.engine.schedulers import SchedulerConfig +from nerfstudio.pipelines.base_pipeline import VanillaPipelineConfig + +CONSOLE = Console(width=120) + + +@dataclass +class ExperimentConfig(PrintableConfig): + """Full config contents""" + + output_dir: Path = Path("outputs") + """relative or absolute output directory to save all checkpoints and logging""" + method_name: Optional[str] = None + """Method name. Required to set in python or via cli""" + experiment_name: Optional[str] = None + """Experiment name. If None, will automatically be set to dataset name""" + timestamp: str = "{timestamp}" + """Experiment timestamp.""" + machine: MachineConfig = MachineConfig() + """Machine configuration""" + logging: LoggingConfig = LoggingConfig() + """Logging configuration""" + viewer: ViewerConfig = ViewerConfig() + """Viewer configuration""" + trainer: TrainerConfig = TrainerConfig() + """Trainer configuration""" + pipeline: VanillaPipelineConfig = VanillaPipelineConfig() + """Pipeline configuration""" + optimizers: Dict[str, Any] = to_immutable_dict( + { + "fields": { + "optimizer": OptimizerConfig(), + "scheduler": SchedulerConfig(), + } + } + ) + """Dictionary of optimizer groups and their schedulers""" + vis: Literal["viewer", "wandb", "tensorboard"] = "wandb" + """Which visualizer to use.""" + data: Optional[Path] = None + """Alias for --pipeline.datamanager.dataparser.data""" + + def is_viewer_enabled(self) -> bool: + """Checks if a viewer is enabled.""" + return "viewer" == self.vis + + def is_wandb_enabled(self) -> bool: + """Checks if wandb is enabled.""" + return "wandb" == self.vis + + def is_tensorboard_enabled(self) -> bool: + """Checks if tensorboard is enabled.""" + return "tensorboard" == self.vis + + def set_timestamp(self) -> None: + """Dynamically set the experiment timestamp""" + if self.timestamp == "{timestamp}": + self.timestamp = datetime.now().strftime("%Y-%m-%d_%H%M%S") + + def set_experiment_name(self) -> None: + """Dynamically set the experiment name""" + if self.experiment_name is None: + self.experiment_name = str(self.pipeline.datamanager.dataparser.data).replace("../", "").replace("/", "-") + + def get_base_dir(self) -> Path: + """Retrieve the base directory to set relative paths""" + # check the experiment and method names + assert self.method_name is not None, "Please set method name in config or via the cli" + self.set_experiment_name() + return Path(f"{self.output_dir}/{self.experiment_name}/{self.method_name}/{self.timestamp}") + + def get_checkpoint_dir(self) -> Path: + """Retrieve the checkpoint directory""" + return Path(self.get_base_dir() / self.trainer.relative_model_dir) + + def print_to_terminal(self) -> None: + """Helper to pretty print config to terminal""" + CONSOLE.rule("Config") + CONSOLE.print(self) + CONSOLE.rule("") + + def save_config(self) -> None: + """Save config to base directory""" + base_dir = self.get_base_dir() + assert base_dir is not None + base_dir.mkdir(parents=True, exist_ok=True) + config_yaml_path = base_dir / "config.yml" + CONSOLE.log(f"Saving config to: {config_yaml_path}") + config_yaml_path.write_text(yaml.dump(self), "utf8") diff --git a/nerfstudio/configs/method_configs.py b/nerfstudio/configs/method_configs.py index 6fb532e14..b02d782b9 100644 --- a/nerfstudio/configs/method_configs.py +++ b/nerfstudio/configs/method_configs.py @@ -23,12 +23,8 @@ from typing import Dict import tyro from nerfstudio.cameras.camera_optimizers import CameraOptimizerConfig -from nerfstudio.configs.base_config import ( - Config, - SchedulerConfig, - TrainerConfig, - ViewerConfig, -) +from nerfstudio.configs.base_config import TrainerConfig, ViewerConfig +from nerfstudio.configs.experiment_config import ExperimentConfig from nerfstudio.data.datamanagers.base_datamanager import VanillaDataManagerConfig from nerfstudio.data.datamanagers.semantic_datamanager import SemanticDataManagerConfig from nerfstudio.data.datamanagers.variable_res_datamanager import ( @@ -42,6 +38,7 @@ from nerfstudio.data.dataparsers.phototourism_dataparser import ( PhototourismDataParserConfig, ) from nerfstudio.engine.optimizers import AdamOptimizerConfig, RAdamOptimizerConfig +from nerfstudio.engine.schedulers import SchedulerConfig from nerfstudio.field_components.temporal_distortions import TemporalDistortionKind from nerfstudio.models.instant_ngp import InstantNGPModelConfig from nerfstudio.models.mipnerf import MipNerfModel @@ -52,7 +49,7 @@ from nerfstudio.models.vanilla_nerf import NeRFModel, VanillaModelConfig from nerfstudio.pipelines.base_pipeline import VanillaPipelineConfig from nerfstudio.pipelines.dynamic_batch import DynamicBatchPipelineConfig -method_configs: Dict[str, Config] = {} +method_configs: Dict[str, ExperimentConfig] = {} descriptions = { "nerfacto": "Recommended real-time model tuned for real captures. This model will be continually updated.", "instant-ngp": "Implementation of Instant-NGP. Recommended real-time model for bounded synthetic data.", @@ -64,7 +61,7 @@ descriptions = { "phototourism": "Uses the Phototourism data.", } -method_configs["nerfacto"] = Config( +method_configs["nerfacto"] = ExperimentConfig( method_name="nerfacto", trainer=TrainerConfig( steps_per_eval_batch=500, steps_per_save=2000, max_num_iterations=30000, mixed_precision=True @@ -94,7 +91,7 @@ method_configs["nerfacto"] = Config( vis="viewer", ) -method_configs["instant-ngp"] = Config( +method_configs["instant-ngp"] = ExperimentConfig( method_name="instant-ngp", trainer=TrainerConfig( steps_per_eval_batch=500, steps_per_save=2000, max_num_iterations=30000, mixed_precision=True @@ -113,7 +110,7 @@ method_configs["instant-ngp"] = Config( vis="viewer", ) -method_configs["mipnerf"] = Config( +method_configs["mipnerf"] = ExperimentConfig( method_name="mipnerf", pipeline=VanillaPipelineConfig( datamanager=VanillaDataManagerConfig(dataparser=NerfstudioDataParserConfig(), train_num_rays_per_batch=1024), @@ -133,7 +130,7 @@ method_configs["mipnerf"] = Config( }, ) -method_configs["semantic-nerfw"] = Config( +method_configs["semantic-nerfw"] = ExperimentConfig( method_name="semantic-nerfw", trainer=TrainerConfig( steps_per_eval_batch=500, steps_per_save=2000, max_num_iterations=30000, mixed_precision=True @@ -158,7 +155,7 @@ method_configs["semantic-nerfw"] = Config( vis="viewer", ) -method_configs["vanilla-nerf"] = Config( +method_configs["vanilla-nerf"] = ExperimentConfig( method_name="vanilla-nerf", pipeline=VanillaPipelineConfig( datamanager=VanillaDataManagerConfig( @@ -178,7 +175,7 @@ method_configs["vanilla-nerf"] = Config( }, ) -method_configs["tensorf"] = Config( +method_configs["tensorf"] = ExperimentConfig( method_name="tensorf", trainer=TrainerConfig(mixed_precision=False), pipeline=VanillaPipelineConfig( @@ -199,7 +196,7 @@ method_configs["tensorf"] = Config( }, ) -method_configs["dnerf"] = Config( +method_configs["dnerf"] = ExperimentConfig( method_name="dnerf", pipeline=VanillaPipelineConfig( datamanager=VanillaDataManagerConfig(dataparser=DNeRFDataParserConfig()), @@ -221,7 +218,7 @@ method_configs["dnerf"] = Config( }, ) -method_configs["phototourism"] = Config( +method_configs["phototourism"] = ExperimentConfig( method_name="phototourism", trainer=TrainerConfig( steps_per_eval_batch=500, steps_per_save=2000, max_num_iterations=30000, mixed_precision=True diff --git a/nerfstudio/engine/optimizers.py b/nerfstudio/engine/optimizers.py index 5bae109d0..c97b10580 100644 --- a/nerfstudio/engine/optimizers.py +++ b/nerfstudio/engine/optimizers.py @@ -61,29 +61,6 @@ class RAdamOptimizerConfig(OptimizerConfig): _target: Type = torch.optim.RAdam -def setup_optimizers(config: base_config.Config, param_groups: Dict[str, List[Parameter]]) -> "Optimizers": - """Helper to set up the optimizers - - Args: - config: The trainer configuration object. - param_groups: A dictionary of parameter groups to optimize. - - Returns: - The optimizers object. - """ - optimizer_config = config.optimizers.copy() - - # Add the camera optimizer if enabled. - camera_optimizer_config = config.pipeline.datamanager.camera_optimizer - if camera_optimizer_config.mode != "off": - assert camera_optimizer_config.param_group not in optimizer_config - optimizer_config[camera_optimizer_config.param_group] = { - "optimizer": config.pipeline.datamanager.camera_optimizer.optimizer, - "scheduler": config.pipeline.datamanager.camera_optimizer.scheduler, - } - return Optimizers(optimizer_config, param_groups) - - class Optimizers: """A set of optimizers. diff --git a/nerfstudio/engine/trainer.py b/nerfstudio/engine/trainer.py index 445788fbf..513fb194e 100644 --- a/nerfstudio/engine/trainer.py +++ b/nerfstudio/engine/trainer.py @@ -28,13 +28,13 @@ from rich.console import Console from torch.cuda.amp.grad_scaler import GradScaler from typing_extensions import Literal -from nerfstudio.configs import base_config as cfg +from nerfstudio.configs.experiment_config import ExperimentConfig from nerfstudio.engine.callbacks import ( TrainingCallback, TrainingCallbackAttributes, TrainingCallbackLocation, ) -from nerfstudio.engine.optimizers import Optimizers, setup_optimizers +from nerfstudio.engine.optimizers import Optimizers from nerfstudio.pipelines.base_pipeline import VanillaPipeline from nerfstudio.utils import profiler, writer from nerfstudio.utils.decorators import ( @@ -71,7 +71,7 @@ class Trainer: optimizers: Optimizers callbacks: List[TrainingCallback] - def __init__(self, config: cfg.Config, local_rank: int = 0, world_size: int = 1): + def __init__(self, config: ExperimentConfig, local_rank: int = 0, world_size: int = 1): self.config = config self.local_rank = local_rank self.world_size = world_size @@ -96,7 +96,7 @@ class Trainer: self._check_viewer_warnings() # set up writers/profilers if enabled writer_log_path = self.base_dir / config.logging.relative_log_dir - writer.setup_event_writer(config, log_dir=writer_log_path) + writer.setup_event_writer(config.is_wandb_enabled(), config.is_tensorboard_enabled(), log_dir=writer_log_path) writer.setup_local_writer( config.logging, max_iter=config.trainer.max_num_iterations, banner_messages=banner_messages ) @@ -115,7 +115,7 @@ class Trainer: self.pipeline = self.config.pipeline.setup( device=self.device, test_mode=test_mode, world_size=self.world_size, local_rank=self.local_rank ) - self.optimizers = setup_optimizers(self.config, self.pipeline.get_param_groups()) + self.optimizers = self.setup_optimizers() self._load_checkpoint() @@ -128,6 +128,23 @@ class Trainer: ) ) + def setup_optimizers(self) -> Optimizers: + """Helper to set up the optimizers + + Returns: + The optimizers object given the experiment config. + """ + optimizer_config = self.config.optimizers.copy() + camera_optimizer_config = self.config.pipeline.datamanager.camera_optimizer + param_groups = self.pipeline.get_param_groups() + if camera_optimizer_config.mode != "off": + assert camera_optimizer_config.param_group not in optimizer_config + optimizer_config[camera_optimizer_config.param_group] = { + "optimizer": camera_optimizer_config.optimizer, + "scheduler": camera_optimizer_config.scheduler, + } + return Optimizers(optimizer_config, param_groups) + def train(self) -> None: """Train the model.""" assert self.pipeline.datamanager.train_dataset is not None, "Missing DatsetInputs" diff --git a/nerfstudio/exporter/exporter_utils.py b/nerfstudio/exporter/exporter_utils.py index c3db219ed..3e5f4425e 100644 --- a/nerfstudio/exporter/exporter_utils.py +++ b/nerfstudio/exporter/exporter_utils.py @@ -39,7 +39,6 @@ from rich.progress import ( from torchtyping import TensorType from nerfstudio.cameras.cameras import Cameras -from nerfstudio.configs.base_config import Config # pylint: disable=unused-import from nerfstudio.pipelines.base_pipeline import Pipeline from nerfstudio.utils.rich_utils import ItersPerSecColumn diff --git a/nerfstudio/utils/eval_utils.py b/nerfstudio/utils/eval_utils.py index 7401a916c..8d780cc82 100644 --- a/nerfstudio/utils/eval_utils.py +++ b/nerfstudio/utils/eval_utils.py @@ -27,13 +27,14 @@ import yaml from rich.console import Console from typing_extensions import Literal -from nerfstudio.configs import base_config as cfg +from nerfstudio.configs.base_config import TrainerConfig +from nerfstudio.configs.experiment_config import ExperimentConfig from nerfstudio.pipelines.base_pipeline import Pipeline CONSOLE = Console(width=120) -def eval_load_checkpoint(config: cfg.TrainerConfig, pipeline: Pipeline) -> Path: +def eval_load_checkpoint(config: TrainerConfig, pipeline: Pipeline) -> Path: ## TODO: ideally eventually want to get this to be the same as whatever is used to load train checkpoint too """Helper function to load checkpointed pipeline @@ -68,7 +69,7 @@ def eval_setup( config_path: Path, eval_num_rays_per_chunk: Optional[int] = None, test_mode: Literal["test", "val", "inference"] = "test", -) -> Tuple[cfg.Config, Pipeline, Path]: +) -> Tuple[ExperimentConfig, Pipeline, Path]: """Shared setup for loading a saved pipeline for evaluation. Args: @@ -85,7 +86,7 @@ def eval_setup( """ # load save config config = yaml.load(config_path.read_text(), Loader=yaml.Loader) - assert isinstance(config, cfg.Config) + assert isinstance(config, ExperimentConfig) if eval_num_rays_per_chunk: config.pipeline.model.eval_num_rays_per_chunk = eval_num_rays_per_chunk diff --git a/nerfstudio/utils/writer.py b/nerfstudio/utils/writer.py index 76d7a3aa9..d85c75bcf 100644 --- a/nerfstudio/utils/writer.py +++ b/nerfstudio/utils/writer.py @@ -188,7 +188,7 @@ def setup_local_writer(config: cfg.LoggingConfig, max_iter: int, banner_messages @check_main_thread -def setup_event_writer(config: cfg.Config, log_dir: Path) -> None: +def setup_event_writer(is_wandb_enabled: bool, is_tensorboard_enabled: bool, log_dir: Path) -> None: """Initialization of all event writers specified in config Args: @@ -197,11 +197,11 @@ def setup_event_writer(config: cfg.Config, log_dir: Path) -> None: banner_messages: list of messages to always display at bottom of screen """ using_event_writer = False - if config.is_wandb_enabled(): + if is_wandb_enabled: curr_writer = WandbWriter(log_dir=log_dir) EVENT_WRITERS.append(curr_writer) using_event_writer = True - if config.is_tensorboard_enabled(): + if is_tensorboard_enabled: curr_writer = TensorboardWriter(log_dir=log_dir) EVENT_WRITERS.append(curr_writer) using_event_writer = True diff --git a/scripts/render.py b/scripts/render.py index 60154f8b7..c8bb4fdae 100644 --- a/scripts/render.py +++ b/scripts/render.py @@ -26,7 +26,6 @@ from typing_extensions import Literal, assert_never from nerfstudio.cameras.camera_paths import get_path_from_json, get_spiral_path from nerfstudio.cameras.cameras import Cameras -from nerfstudio.configs.base_config import Config # pylint: disable=unused-import from nerfstudio.pipelines.base_pipeline import Pipeline from nerfstudio.utils import install_checks from nerfstudio.utils.eval_utils import eval_setup diff --git a/scripts/train.py b/scripts/train.py index 37717a11c..910dbff1c 100755 --- a/scripts/train.py +++ b/scripts/train.py @@ -45,8 +45,8 @@ import tyro import yaml from rich.console import Console -from nerfstudio.configs import base_config as cfg from nerfstudio.configs.config_utils import convert_markup_to_ansi +from nerfstudio.configs.experiment_config import ExperimentConfig from nerfstudio.configs.method_configs import AnnotatedBaseConfigUnion from nerfstudio.engine.trainer import Trainer from nerfstudio.utils import comms, profiler @@ -74,7 +74,7 @@ def _set_random_seed(seed) -> None: torch.manual_seed(seed) -def train_loop(local_rank: int, world_size: int, config: cfg.Config, global_rank: int = 0): +def train_loop(local_rank: int, world_size: int, config: ExperimentConfig, global_rank: int = 0): """Main training function that sets up and runs the trainer per process Args: @@ -95,7 +95,7 @@ def _distributed_worker( num_gpus_per_machine: int, machine_rank: int, dist_url: str, - config: cfg.Config, + config: ExperimentConfig, timeout: timedelta = DEFAULT_TIMEOUT, ) -> Any: """Spawned distributed worker that handles the initialization of process group and handles the @@ -110,7 +110,7 @@ def _distributed_worker( dist_url: URL to connect to for distributed jobs, including protocol E.g., "tcp://127.0.0.1:8686". It can be set to "auto" to automatically select a free port on localhost. - config: Config specifying training regimen. + config: ExperimentConfig specifying training regimen. timeout: Timeout of the distributed workers. Raises: @@ -150,7 +150,7 @@ def launch( num_machines: int = 1, machine_rank: int = 0, dist_url: str = "auto", - config: Optional[cfg.Config] = None, + config: Optional[ExperimentConfig] = None, timeout: timedelta = DEFAULT_TIMEOUT, ) -> None: """Function that spawns muliple processes to call on main_func @@ -161,7 +161,7 @@ def launch( num_machines (int, optional): total number of machines machine_rank (int, optional): rank of this machine. dist_url (str, optional): url to connect to for distributed jobs. - config (Config, optional): config file specifying training regimen. + config (ExperimentConfig, optional): config file specifying training regimen. timeout (timedelta, optional): timeout of the distributed workers. """ assert config is not None @@ -215,7 +215,7 @@ def launch( profiler.flush_profiler(config.logging) -def main(config: cfg.Config) -> None: +def main(config: ExperimentConfig) -> None: """Main function.""" config.set_timestamp() diff --git a/tests/test_train.py b/tests/test_train.py index 6332ba260..dfc2d651e 100644 --- a/tests/test_train.py +++ b/tests/test_train.py @@ -9,7 +9,7 @@ from pathlib import Path import pytest -from nerfstudio.configs.base_config import Config +from nerfstudio.configs.experiment_config import ExperimentConfig from nerfstudio.configs.method_configs import method_configs from nerfstudio.data.dataparsers.blender_dataparser import BlenderDataParserConfig from scripts.train import train_loop @@ -17,7 +17,7 @@ from scripts.train import train_loop BLACKLIST = ["base", "semantic-nerfw", "instant-ngp", "nerfacto", "phototourism"] -def set_reduced_config(config: Config): +def set_reduced_config(config: ExperimentConfig): """Reducing the config settings to speedup test""" config.machine.num_gpus = 0 config.trainer.max_num_iterations = 2