4
0
mirror of https://github.com/nerfstudio-project/nerfstudio.git synced 2026-11-11 02:24:38 +08:00

Merge branch 'perf' of github.com:nerfstudio-project/nerfstudio into main

This commit is contained in:
Jake Austin 2022-11-30 02:02:04 +00:00
commit 2af29d00a5
4 changed files with 177 additions and 4 deletions

View File

@ -30,7 +30,7 @@ from typing_extensions import Literal, assert_never
from nerfstudio.cameras.lie_groups import exp_map_SE3, exp_map_SO3xR3
from nerfstudio.configs import base_config as cfg
from nerfstudio.engine.optimizers import AdamOptimizerConfig
from nerfstudio.engine.optimizers import AdamOptimizerConfig, OptimizerConfig
from nerfstudio.engine.schedulers import SchedulerConfig
from nerfstudio.utils import poses as pose_utils
@ -50,7 +50,7 @@ class CameraOptimizerConfig(cfg.InstantiateConfig):
orientation_noise_std: float = 0.0
"""Noise to add to initial orientations. Useful for debugging."""
optimizer: AdamOptimizerConfig = AdamOptimizerConfig(lr=6e-4, eps=1e-15)
optimizer: OptimizerConfig = AdamOptimizerConfig(lr=6e-4, eps=1e-15)
"""ADAM parameters for camera optimization."""
scheduler: SchedulerConfig = SchedulerConfig(max_steps=10000)

View File

@ -0,0 +1,149 @@
# 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.
"""
Data manager for ray level caching
"""
from __future__ import annotations
import random
from abc import abstractmethod
from dataclasses import dataclass, field
from typing import Dict, List, Optional, Tuple, Type, Union
import torch
import tyro
from rich.progress import Console
from torch import nn
from torch.nn import Parameter
from torch.utils.data import Dataset
from torch.utils.data.distributed import DistributedSampler
from typing_extensions import Literal
import nerfstudio.utils.profiler as profiler
from nerfstudio.cameras.camera_optimizers import CameraOptimizerConfig
from nerfstudio.cameras.rays import RayBundle
from nerfstudio.configs.base_config import InstantiateConfig
from nerfstudio.data.datamanagers.base_datamanager import (
DataManager,
VanillaDataManager,
VanillaDataManagerConfig,
)
from nerfstudio.data.dataparsers.blender_dataparser import BlenderDataParserConfig
from nerfstudio.data.dataparsers.dnerf_dataparser import DNeRFDataParserConfig
from nerfstudio.data.dataparsers.friends_dataparser import FriendsDataParserConfig
from nerfstudio.data.dataparsers.instant_ngp_dataparser import (
InstantNGPDataParserConfig,
)
from nerfstudio.data.dataparsers.nerfstudio_dataparser import NerfstudioDataParserConfig
from nerfstudio.data.dataparsers.nuscenes_dataparser import NuScenesDataParserConfig
from nerfstudio.data.dataparsers.phototourism_dataparser import (
PhototourismDataParserConfig,
)
from nerfstudio.data.dataparsers.record3d_dataparser import Record3DDataParserConfig
from nerfstudio.data.datasets.base_dataset import InputDataset
from nerfstudio.data.pixel_samplers import PixelSampler
from nerfstudio.data.utils.dataloaders import (
CacheDataloader,
FixedIndicesEvalDataloader,
RandIndicesEvalDataloader,
)
from nerfstudio.data.utils.nerfstudio_collate import nerfstudio_collate
from nerfstudio.engine.callbacks import TrainingCallback, TrainingCallbackAttributes
from nerfstudio.engine.optimizers import AdamOptimizerConfig
from nerfstudio.model_components.ray_generators import RayGenerator
from nerfstudio.utils.misc import IterableWrapper
CONSOLE = Console(width=120)
@dataclass
class RayCacheDataManagerConfig(VanillaDataManagerConfig):
"""A datamanager for optimized caching of ray level data."""
_target: Type = field(default_factory=lambda: RayCacheDataManager)
# train_num_images_to_sample_from: int = 40
# """Number of images to sample during training iteration."""
# train_num_times_to_repeat_images: int = 100
# """When not training on all images, number of iterations before picking new
# images. If -1, never pick new images."""
# eval_num_images_to_sample_from: int = 40
# """Number of images to sample during eval iteration."""
# eval_num_times_to_repeat_images: int = 100
# """When not training on all images, number of iterations before picking new"""
# ---------- RayCacheDataManager Specific Args ----------#
num_rays_cached: int = 100000
"""Number of rays to cache / refresh buffer with."""
train_num_times_to_repeat_ray_cache: int = 50
"""Number of iterations before refreshing the ray cache.
Also ends up being number of steps in between camera updates."""
camera_optimizer: CameraOptimizerConfig = CameraOptimizerConfig(optimizer=AdamOptimizerConfig(weight_decay=0))
"""Camera optimizer config."""
class RayCacheDataManager(VanillaDataManager): # pylint: disable=abstract-method
"""
Wrapper around the vanilla data manager that will cache raw rays
Nothing is fundamentally flawed or incorrect about the original datamanager... it just could be quicker.
"""
config: RayCacheDataManagerConfig
_ray_cache = None
_batch_cache = None
def _refresh_cache(self):
"""Refresh the ray cache and return the next batch."""
def setup_train(self):
"""Sets up the data loaders for training"""
assert self.train_dataset is not None
CONSOLE.print("Setting up training dataset...")
self.train_image_dataloader = CacheDataloader(
self.train_dataset,
num_images_to_sample_from=self.config.train_num_images_to_sample_from,
num_times_to_repeat_images=self.config.train_num_times_to_repeat_images,
device=self.device,
num_workers=self.world_size * 4,
pin_memory=True,
collate_fn=self.config.collate_fn,
)
self.iter_train_image_dataloader = iter(self.train_image_dataloader)
self.train_pixel_sampler = PixelSampler(self.config.num_rays_cached)
self.train_camera_optimizer = self.config.camera_optimizer.setup(
num_cameras=self.train_dataset.cameras.size, device=self.device
)
self.train_ray_generator = RayGenerator(
self.train_dataset.cameras.to(self.device),
self.train_camera_optimizer,
)
def next_train(self, step: int):
"""Get the next batch of training data."""
if self._ray_cache is None or step % self.config.train_num_times_to_repeat_ray_cache == 0:
self._ray_cache, self._batch_cache = super().next_train(step)
start = random.randint(0, self.config.num_rays_cached - self.config.train_num_rays_per_batch)
rays = self._ray_cache[start : start + self.config.train_num_rays_per_batch]
batch = {k: v[start : start + self.config.train_num_rays_per_batch] for k, v in self._batch_cache.items()}
if step % self.config.train_num_times_to_repeat_ray_cache == 0:
self._ray_cache = self._ray_cache.detach()
self._batch_cache = {k: v.detach() for k, v in self._batch_cache.items()}
return rays, batch

View File

@ -35,7 +35,6 @@ class OptimizerConfig(base_config.PrintableConfig):
_target: Type = torch.optim.Adam
lr: float = 0.0005
eps: float = 1e-08
# TODO: somehow make this more generic. i dont like the idea of overriding the setup function
# but also not sure how to go about passing things into predefined torch objects.
@ -46,12 +45,28 @@ class OptimizerConfig(base_config.PrintableConfig):
return self._target(params, **kwargs)
@dataclass
class SGDOptimizerConfig(OptimizerConfig):
"""Basic optimizer config with RAdam"""
_target: Type = torch.optim.SGD
lr: float = 0.0005
weight_decay: float = 0.0
def setup(self, params) -> Any:
"""Returns the instantiated object using the config."""
kwargs = vars(self).copy()
kwargs.pop("_target")
return self._target(params, **kwargs)
@dataclass
class AdamOptimizerConfig(OptimizerConfig):
"""Basic optimizer config with Adam"""
_target: Type = torch.optim.Adam
weight_decay: float = 0
eps: float = 1e-08
@dataclass
@ -59,6 +74,7 @@ class RAdamOptimizerConfig(OptimizerConfig):
"""Basic optimizer config with RAdam"""
_target: Type = torch.optim.RAdam
eps: float = 1e-08
def setup_optimizers(config: base_config.Config, param_groups: Dict[str, List[Parameter]]) -> "Optimizers":

View File

@ -147,7 +147,7 @@ class TensorDataclass:
return new_dict
def __getitem__(self: TensorDataclassT, indices) -> TensorDataclassT:
if isinstance(indices, (torch.Tensor)):
if isinstance(indices, (torch.Tensor, list)):
return self._apply_fn_to_fields(lambda x: x[indices])
if isinstance(indices, (int, slice, type(Ellipsis))):
indices = (indices,)
@ -256,6 +256,14 @@ class TensorDataclass:
"""
return self._apply_fn_to_fields(lambda x: x.to(device))
def detach(self: TensorDataclassT) -> TensorDataclassT:
"""Returns a new TensorDataclass with the same data but detached from the computation graph.
Returns:
A new TensorDataclass with the same data but detached from the computation graph.
"""
return self._apply_fn_to_fields(lambda x: x.detach())
def _apply_fn_to_fields(
self: TensorDataclassT,
fn: Callable,