| |
| |
| |
| |
| |
|
|
| import unittest |
| from typing import Any, Dict |
| from unittest.mock import patch |
|
|
| import torch |
| from pytorch3d.implicitron.models.generic_model import GenericModel |
| from pytorch3d.implicitron.models.overfit_model import OverfitModel |
| from pytorch3d.implicitron.models.renderer.base import EvaluationMode |
| from pytorch3d.implicitron.tools.config import expand_args_fields |
| from pytorch3d.renderer.cameras import look_at_view_transform, PerspectiveCameras |
|
|
| DEVICE = torch.device("cuda:0") |
|
|
|
|
| def _generate_fake_inputs(N: int, H: int, W: int) -> Dict[str, Any]: |
| R, T = look_at_view_transform(azim=torch.rand(N) * 360) |
| return { |
| "camera": PerspectiveCameras(R=R, T=T, device=DEVICE), |
| "fg_probability": torch.randint( |
| high=2, size=(N, 1, H, W), device=DEVICE |
| ).float(), |
| "depth_map": torch.rand((N, 1, H, W), device=DEVICE) + 0.1, |
| "mask_crop": torch.randint(high=2, size=(N, 1, H, W), device=DEVICE).float(), |
| "sequence_name": ["sequence"] * N, |
| "image_rgb": torch.rand((N, 1, H, W), device=DEVICE), |
| } |
|
|
|
|
| def mock_safe_multinomial(input: torch.Tensor, num_samples: int) -> torch.Tensor: |
| """Return non deterministic indexes to mock safe_multinomial |
| |
| Args: |
| input: tensor of shape [B, n] containing non-negative values; |
| rows are interpreted as unnormalized event probabilities |
| in categorical distributions. |
| num_samples: number of samples to take. |
| |
| Returns: |
| Tensor of shape [B, num_samples] |
| """ |
| batch_size = input.shape[0] |
| return torch.arange(num_samples).repeat(batch_size, 1).to(DEVICE) |
|
|
|
|
| class TestOverfitModel(unittest.TestCase): |
| def setUp(self): |
| torch.manual_seed(42) |
|
|
| def test_overfit_model_vs_generic_model_with_batch_size_one(self): |
| """In this test we compare OverfitModel to GenericModel behavior. |
| |
| We use a Nerf setup (2 rendering passes). |
| |
| OverfitModel is a specific case of GenericModel. Hence, with the same inputs, |
| they should provide the exact same results. |
| """ |
| expand_args_fields(OverfitModel) |
| expand_args_fields(GenericModel) |
| batch_size, image_height, image_width = 1, 80, 80 |
| assert batch_size == 1 |
| overfit_model = OverfitModel( |
| render_image_height=image_height, |
| render_image_width=image_width, |
| coarse_implicit_function_class_type="NeuralRadianceFieldImplicitFunction", |
| |
| |
| raysampler_AdaptiveRaySampler_args={ |
| "stratified_point_sampling_training": False |
| }, |
| global_encoder_class_type="SequenceAutodecoder", |
| global_encoder_SequenceAutodecoder_args={ |
| "autodecoder_args": { |
| "n_instances": 1000, |
| "init_scale": 1.0, |
| "encoding_dim": 64, |
| } |
| }, |
| ) |
| generic_model = GenericModel( |
| render_image_height=image_height, |
| render_image_width=image_width, |
| n_train_target_views=batch_size, |
| num_passes=2, |
| |
| |
| raysampler_AdaptiveRaySampler_args={ |
| "stratified_point_sampling_training": False |
| }, |
| global_encoder_class_type="SequenceAutodecoder", |
| global_encoder_SequenceAutodecoder_args={ |
| "autodecoder_args": { |
| "n_instances": 1000, |
| "init_scale": 1.0, |
| "encoding_dim": 64, |
| } |
| }, |
| ) |
|
|
| |
| num_params_mvm = sum(p.numel() for p in overfit_model.parameters()) |
| num_params_gm = sum(p.numel() for p in generic_model.parameters()) |
| self.assertEqual(num_params_mvm, num_params_gm) |
|
|
| |
| mapping_om_from_gm = { |
| key.replace( |
| "_implicit_functions.0._fn", "coarse_implicit_function" |
| ).replace("_implicit_functions.1._fn", "implicit_function"): val |
| for key, val in generic_model.state_dict().items() |
| } |
| |
| overfit_model.load_state_dict(mapping_om_from_gm) |
|
|
| overfit_model.to(DEVICE) |
| generic_model.to(DEVICE) |
| inputs_ = _generate_fake_inputs(batch_size, image_height, image_width) |
|
|
| |
| overfit_model.train() |
| generic_model.train() |
|
|
| with patch( |
| "pytorch3d.renderer.implicit.raysampling._safe_multinomial", |
| side_effect=mock_safe_multinomial, |
| ): |
| train_preds_om = overfit_model( |
| **inputs_, |
| evaluation_mode=EvaluationMode.TRAINING, |
| ) |
| train_preds_gm = generic_model( |
| **inputs_, |
| evaluation_mode=EvaluationMode.TRAINING, |
| ) |
|
|
| self.assertTrue(len(train_preds_om) == len(train_preds_gm)) |
|
|
| self.assertTrue(train_preds_om["objective"].isfinite().item()) |
| |
| |
| self.assertTrue( |
| torch.allclose(train_preds_om["objective"], train_preds_gm["objective"]) |
| ) |
|
|
| |
| overfit_model.eval() |
| generic_model.eval() |
| with torch.no_grad(): |
| eval_preds_om = overfit_model( |
| **inputs_, |
| evaluation_mode=EvaluationMode.EVALUATION, |
| ) |
| eval_preds_gm = generic_model( |
| **inputs_, |
| evaluation_mode=EvaluationMode.EVALUATION, |
| ) |
|
|
| self.assertEqual( |
| eval_preds_om["images_render"].shape, |
| (batch_size, 3, image_height, image_width), |
| ) |
| self.assertTrue( |
| torch.allclose(eval_preds_om["objective"], eval_preds_gm["objective"]) |
| ) |
| self.assertTrue( |
| torch.allclose( |
| eval_preds_om["images_render"], eval_preds_gm["images_render"] |
| ) |
| ) |
|
|
| def test_overfit_model_check_share_weights(self): |
| model = OverfitModel(share_implicit_function_across_passes=True) |
| for p1, p2 in zip( |
| model.implicit_function.parameters(), |
| model.coarse_implicit_function.parameters(), |
| ): |
| self.assertEqual(id(p1), id(p2)) |
|
|
| model.to(DEVICE) |
| inputs_ = _generate_fake_inputs(2, 80, 80) |
| model(**inputs_, evaluation_mode=EvaluationMode.TRAINING) |
|
|
| def test_overfit_model_check_no_share_weights(self): |
| model = OverfitModel( |
| share_implicit_function_across_passes=False, |
| coarse_implicit_function_class_type="NeuralRadianceFieldImplicitFunction", |
| coarse_implicit_function_NeuralRadianceFieldImplicitFunction_args={ |
| "transformer_dim_down_factor": 1.0, |
| "n_hidden_neurons_xyz": 256, |
| "n_layers_xyz": 8, |
| "append_xyz": (5,), |
| }, |
| ) |
| for p1, p2 in zip( |
| model.implicit_function.parameters(), |
| model.coarse_implicit_function.parameters(), |
| ): |
| self.assertNotEqual(id(p1), id(p2)) |
|
|
| model.to(DEVICE) |
| inputs_ = _generate_fake_inputs(2, 80, 80) |
| model(**inputs_, evaluation_mode=EvaluationMode.TRAINING) |
|
|
| def test_overfit_model_coarse_implicit_function_is_none(self): |
| model = OverfitModel( |
| share_implicit_function_across_passes=False, |
| coarse_implicit_function_NeuralRadianceFieldImplicitFunction_args=None, |
| ) |
| self.assertIsNone(model.coarse_implicit_function) |
| model.to(DEVICE) |
| inputs_ = _generate_fake_inputs(2, 80, 80) |
| model(**inputs_, evaluation_mode=EvaluationMode.TRAINING) |
|
|