From 2d3a605b3c9e70f833e3cca791152efce052a321 Mon Sep 17 00:00:00 2001 From: Adil Zouitine Date: Mon, 29 Sep 2025 16:55:52 +0200 Subject: [PATCH] Revert feat(normalization): add validation for empty features in NormalizerProcessorStep and UnnormalizerProcessorStep (#2087) Revert "feat(normalization): add validation for empty features in NormalizerProcessorStep and UnnormalizerProcessorStep (#2087)" This reverts commit f173265354166547414ff922faa7b014de761481. --- src/lerobot/processor/normalize_processor.py | 20 +++++++++----------- tests/policies/test_policies.py | 1 + tests/processor/test_normalize_processor.py | 12 ------------ 3 files changed, 10 insertions(+), 23 deletions(-) diff --git a/src/lerobot/processor/normalize_processor.py b/src/lerobot/processor/normalize_processor.py index 885911ff..ce69a103 100644 --- a/src/lerobot/processor/normalize_processor.py +++ b/src/lerobot/processor/normalize_processor.py @@ -108,18 +108,16 @@ class _NormalizationMixin: """ # Track if stats were explicitly provided (not None and not empty) self._stats_explicitly_provided = self.stats is not None and bool(self.stats) - # Check if self.features is not empty - if not self.features: - raise ValueError("Normalization features cannot be empty") # Robust JSON deserialization handling (guard empty maps). - first_val = next(iter(self.features.values())) - if isinstance(first_val, dict): - reconstructed = {} - for key, ft_dict in self.features.items(): - reconstructed[key] = PolicyFeature( - type=FeatureType(ft_dict["type"]), shape=tuple(ft_dict["shape"]) - ) - self.features = reconstructed + if self.features: + first_val = next(iter(self.features.values())) + if isinstance(first_val, dict): + reconstructed = {} + for key, ft_dict in self.features.items(): + reconstructed[key] = PolicyFeature( + type=FeatureType(ft_dict["type"]), shape=tuple(ft_dict["shape"]) + ) + self.features = reconstructed # if keys are strings (JSON), rebuild enum map if self.norm_map and all(isinstance(k, str) for k in self.norm_map): diff --git a/tests/policies/test_policies.py b/tests/policies/test_policies.py index 07e80d59..34fa8939 100644 --- a/tests/policies/test_policies.py +++ b/tests/policies/test_policies.py @@ -234,6 +234,7 @@ def test_act_backbone_lr(): assert cfg.policy.optimizer_lr_backbone == 0.001 dataset = make_dataset(cfg) + preprocessor, _ = make_pre_post_processors(cfg.policy, None) policy = make_policy(cfg.policy, ds_meta=dataset.meta) optimizer, _ = make_optimizer_and_scheduler(cfg, policy) assert len(optimizer.param_groups) == 2 diff --git a/tests/processor/test_normalize_processor.py b/tests/processor/test_normalize_processor.py index 80ac58df..98c9e0b2 100644 --- a/tests/processor/test_normalize_processor.py +++ b/tests/processor/test_normalize_processor.py @@ -534,18 +534,6 @@ def test_empty_observation(): assert result == transition -def test_empty_features_raises_error(): - """Test that empty features dict raises ValueError.""" - norm_map = {FeatureType.VISUAL: NormalizationMode.MEAN_STD} - stats = {OBS_IMAGE: {"mean": [0.5], "std": [0.2]}} - - with pytest.raises(ValueError, match="Normalization features cannot be empty"): - NormalizerProcessorStep(features={}, norm_map=norm_map, stats=stats) - - with pytest.raises(ValueError, match="Normalization features cannot be empty"): - UnnormalizerProcessorStep(features={}, norm_map=norm_map, stats=stats) - - def test_empty_stats(): features = {OBS_IMAGE: PolicyFeature(FeatureType.VISUAL, (3, 96, 96))} norm_map = {FeatureType.VISUAL: NormalizationMode.MEAN_STD}