2025-09-08 18:44:15 +02:00
|
|
|
#!/usr/bin/env python
|
|
|
|
|
|
2025-07-23 18:41:53 +02:00
|
|
|
# Copyright 2025 The HuggingFace Inc. 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.
|
2025-09-08 18:44:15 +02:00
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
This script defines processor steps for adding a batch dimension to various components of an environment transition.
|
|
|
|
|
|
|
|
|
|
These steps are designed to process actions, observations, and complementary data, making them suitable for batch processing by adding a leading dimension. This is a common requirement before feeding data into a neural network model.
|
|
|
|
|
"""
|
|
|
|
|
|
2025-08-31 20:38:52 +02:00
|
|
|
from dataclasses import dataclass, field
|
2025-07-23 18:41:53 +02:00
|
|
|
|
|
|
|
|
from torch import Tensor
|
|
|
|
|
|
2025-09-09 18:27:30 +02:00
|
|
|
from lerobot.configs.types import PipelineFeatureType, PolicyFeature
|
2025-07-23 18:41:53 +02:00
|
|
|
from lerobot.constants import OBS_ENV_STATE, OBS_IMAGE, OBS_IMAGES, OBS_STATE
|
2025-09-02 18:26:59 +02:00
|
|
|
|
2025-09-10 22:40:37 +02:00
|
|
|
from .core import EnvTransition, PolicyAction
|
2025-09-02 18:26:59 +02:00
|
|
|
from .pipeline import (
|
2025-09-03 17:30:47 +02:00
|
|
|
ComplementaryDataProcessorStep,
|
|
|
|
|
ObservationProcessorStep,
|
2025-09-10 22:40:37 +02:00
|
|
|
PolicyActionProcessorStep,
|
2025-08-31 20:38:52 +02:00
|
|
|
ProcessorStep,
|
|
|
|
|
ProcessorStepRegistry,
|
|
|
|
|
)
|
2025-07-23 18:41:53 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
2025-08-31 20:38:52 +02:00
|
|
|
@ProcessorStepRegistry.register(name="to_batch_processor_action")
|
2025-09-10 22:40:37 +02:00
|
|
|
class AddBatchDimensionActionStep(PolicyActionProcessorStep):
|
2025-09-08 18:44:15 +02:00
|
|
|
"""
|
|
|
|
|
Processor step to add a batch dimension to a 1D tensor action.
|
2025-07-23 18:41:53 +02:00
|
|
|
|
2025-09-08 18:44:15 +02:00
|
|
|
This is useful for creating a batch of size 1 from a single action sample.
|
|
|
|
|
"""
|
|
|
|
|
|
2025-09-10 22:40:37 +02:00
|
|
|
def action(self, action: PolicyAction) -> PolicyAction:
|
2025-09-08 18:44:15 +02:00
|
|
|
"""
|
|
|
|
|
Adds a batch dimension to the action if it's a 1D tensor.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
action: The action tensor.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
The action tensor with an added batch dimension.
|
|
|
|
|
"""
|
2025-09-10 22:40:37 +02:00
|
|
|
if action.dim() != 1:
|
2025-08-31 20:38:52 +02:00
|
|
|
return action
|
|
|
|
|
return action.unsqueeze(0)
|
2025-07-23 18:41:53 +02:00
|
|
|
|
2025-09-09 18:27:30 +02:00
|
|
|
def transform_features(
|
|
|
|
|
self, features: dict[PipelineFeatureType, dict[str, PolicyFeature]]
|
|
|
|
|
) -> dict[PipelineFeatureType, dict[str, PolicyFeature]]:
|
2025-09-08 18:44:15 +02:00
|
|
|
"""
|
|
|
|
|
Returns the input features unchanged.
|
|
|
|
|
|
|
|
|
|
Adding a batch dimension does not alter the feature definition.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
features: A dictionary of policy features.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
The original dictionary of policy features.
|
|
|
|
|
"""
|
2025-09-02 17:15:01 +02:00
|
|
|
return features
|
|
|
|
|
|
2025-07-23 18:41:53 +02:00
|
|
|
|
2025-08-31 20:38:52 +02:00
|
|
|
@dataclass
|
|
|
|
|
@ProcessorStepRegistry.register(name="to_batch_processor_observation")
|
2025-09-03 18:12:11 +02:00
|
|
|
class AddBatchDimensionObservationStep(ObservationProcessorStep):
|
2025-09-08 18:44:15 +02:00
|
|
|
"""
|
|
|
|
|
Processor step to add a batch dimension to observations.
|
|
|
|
|
|
|
|
|
|
It handles different types of observations:
|
|
|
|
|
- State vectors (1D tensors).
|
|
|
|
|
- Single images (3D tensors).
|
|
|
|
|
- Dictionaries of multiple images (3D tensors).
|
|
|
|
|
"""
|
2025-07-23 18:41:53 +02:00
|
|
|
|
2025-09-08 18:44:15 +02:00
|
|
|
def observation(self, observation: dict[str, Tensor]) -> dict[str, Tensor]:
|
|
|
|
|
"""
|
|
|
|
|
Adds a batch dimension to tensor-based observations in the observation dictionary.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
observation: The observation dictionary.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
The observation dictionary with batch dimensions added to tensors.
|
|
|
|
|
"""
|
2025-07-23 18:41:53 +02:00
|
|
|
# Process state observations - add batch dim if 1D
|
|
|
|
|
for state_key in [OBS_STATE, OBS_ENV_STATE]:
|
|
|
|
|
if state_key in observation:
|
|
|
|
|
state_value = observation[state_key]
|
|
|
|
|
if isinstance(state_value, Tensor) and state_value.dim() == 1:
|
|
|
|
|
observation[state_key] = state_value.unsqueeze(0)
|
|
|
|
|
|
|
|
|
|
# Process single image observation - add batch dim if 3D
|
|
|
|
|
if OBS_IMAGE in observation:
|
|
|
|
|
image_value = observation[OBS_IMAGE]
|
|
|
|
|
if isinstance(image_value, Tensor) and image_value.dim() == 3:
|
|
|
|
|
observation[OBS_IMAGE] = image_value.unsqueeze(0)
|
|
|
|
|
|
|
|
|
|
# Process multiple image observations - add batch dim if 3D
|
|
|
|
|
for key, value in observation.items():
|
|
|
|
|
if key.startswith(f"{OBS_IMAGES}.") and isinstance(value, Tensor) and value.dim() == 3:
|
|
|
|
|
observation[key] = value.unsqueeze(0)
|
2025-08-31 20:38:52 +02:00
|
|
|
return observation
|
2025-07-23 18:41:53 +02:00
|
|
|
|
2025-09-09 18:27:30 +02:00
|
|
|
def transform_features(
|
|
|
|
|
self, features: dict[PipelineFeatureType, dict[str, PolicyFeature]]
|
|
|
|
|
) -> dict[PipelineFeatureType, dict[str, PolicyFeature]]:
|
2025-09-08 18:44:15 +02:00
|
|
|
"""
|
|
|
|
|
Returns the input features unchanged.
|
|
|
|
|
|
|
|
|
|
Adding a batch dimension does not alter the feature definition.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
features: A dictionary of policy features.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
The original dictionary of policy features.
|
|
|
|
|
"""
|
2025-09-02 17:15:01 +02:00
|
|
|
return features
|
|
|
|
|
|
2025-07-23 18:41:53 +02:00
|
|
|
|
2025-08-31 20:38:52 +02:00
|
|
|
@dataclass
|
|
|
|
|
@ProcessorStepRegistry.register(name="to_batch_processor_complementary_data")
|
2025-09-03 18:12:11 +02:00
|
|
|
class AddBatchDimensionComplementaryDataStep(ComplementaryDataProcessorStep):
|
2025-09-08 18:44:15 +02:00
|
|
|
"""
|
|
|
|
|
Processor step to add a batch dimension to complementary data fields.
|
|
|
|
|
|
|
|
|
|
Handles specific keys like 'task', 'index', and 'task_index' to make them batched.
|
|
|
|
|
- 'task' (str) is wrapped in a list.
|
|
|
|
|
- 'index' and 'task_index' (0D tensors) get a batch dimension.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def complementary_data(self, complementary_data: dict) -> dict:
|
|
|
|
|
"""
|
|
|
|
|
Adds a batch dimension to specific fields in the complementary data dictionary.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
complementary_data: The complementary data dictionary.
|
2025-07-25 19:05:44 +02:00
|
|
|
|
2025-09-08 18:44:15 +02:00
|
|
|
Returns:
|
|
|
|
|
The complementary data dictionary with batch dimensions added.
|
|
|
|
|
"""
|
2025-07-25 19:05:44 +02:00
|
|
|
# Process task field - wrap string in list to add batch dimension
|
|
|
|
|
if "task" in complementary_data:
|
|
|
|
|
task_value = complementary_data["task"]
|
|
|
|
|
if isinstance(task_value, str):
|
|
|
|
|
complementary_data["task"] = [task_value]
|
|
|
|
|
|
feature(pipeline): port tokenizer pipeline for VLA (#1645)
* feat(tokenizer): Introduce TokenizerProcessor for text tokenization
- Added TokenizerProcessor class to handle tokenization of task strings using Hugging Face's AutoTokenizer.
- Supports both string and list inputs, with customizable parameters for task key, output key, and tokenization settings.
- Implemented comprehensive unit tests to validate functionality, including handling of various input scenarios and integration with RobotProcessor.
- Updated types.py to include LANGUAGE feature type and modified __init__.py to register the new processor.
* feat(language): Enhance language processing in TokenizerProcessor
- Added OBS_LANGUAGE constant to define the observation language key.
- Updated TokenizerProcessor to store tokenized task data in the observation dictionary, ensuring compatibility with the new language feature.
- Introduced Pi0NewLineProcessor to append newlines to tasks for proper tokenization.
- Modified tests to validate the integration of language tokens and attention masks in the observation structure.
* feat(tokenizer): Add padding configuration to TokenizerProcessor
- Introduced `padding_side` parameter to the TokenizerProcessor for customizable padding direction.
- Updated the `make_pi0_processor` function to include the new padding configuration.
- Enhanced unit tests to validate the functionality of the `padding_side` parameter in various scenarios.
* feat(processor): Add state management methods to Pi0NewLineProcessor
* feat(normalization): Track normalization and unnormalization info in complementary data
- Updated NormalizerProcessor and UnnormalizerProcessor to accept additional parameters for tracking normalization modes.
- Enhanced the __call__ methods to store normalization and unnormalization information in the complementary data of transitions.
- Added unit tests to verify the correct tracking of normalization info, including scenarios with missing stats and selective normalization keys.
* feat(factory): Add preprocessor and postprocessor overrides to ProcessorConfigKwargs
- Updated ProcessorConfigKwargs to include optional overrides for preprocessor and postprocessor configurations.
- Enhanced the make_processor function to utilize the new overrides, allowing for more flexible processor initialization.
* feat(processors): Integrate RenameProcessor into various processor configurations
- Added RenameProcessor to the input steps of multiple processor functions, including make_act_processor, make_diffusion_processor, make_pi0_processor, make_sac_processor, make_tdmpc_processor, make_vqbet_processor, and make_smolvla_processor.
- Consolidated normalization features from input and output into a single NormalizerProcessor for improved efficiency.
- Updated the input steps to ensure compatibility with the new RenameProcessor integration.
* feat(smolvla): Refactor language processing and introduce new line processor (#1658)
- Removed the prepare_language method and directly accessed language tokens and masks from the batch using the OBS_LANGUAGE constant.
- Added SmolVLANewLineProcessor to ensure tasks end with a newline, enhancing tokenization compatibility.
- Updated the make_smolvla_processor function to include the new line processor and tokenizer processor for improved input handling.
* feture(policies): add device processor (#1659)
* feat(processors): Integrate DeviceProcessor into multiple processor configurations
- Added DeviceProcessor to the input and output steps of various processor functions, including make_act_processor, make_diffusion_processor, make_pi0_processor, make_pi0fast_processor, make_sac_processor, make_tdmpc_processor, make_vqbet_processor, and make_smolvla_processor.
- Enhanced the DeviceProcessor class with state management methods and ensured compatibility with existing processor pipelines.
- Introduced unit tests for DeviceProcessor to validate functionality across different scenarios, including CPU and CUDA operations.
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* refactor(pipeline): Remove to() method for device management
- Eliminated the to() method from RobotProcessor, which was responsible for moving tensor states to specified devices.
- Removed associated unit tests that validated the functionality of the to() method across various scenarios.
- Streamlined the pipeline code by focusing on other device management strategies.
* feat(processor): Enhance DeviceProcessor with float dtype conversion
- Added support for optional float dtype conversion in DeviceProcessor, allowing tensors to be converted to specified floating-point types while preserving non-float types.
- Implemented validation for float dtype input and updated the processor's configuration methods to include float dtype.
- Refactored tensor processing logic to streamline device movement and dtype conversion.
- Introduced comprehensive unit tests to validate the new float dtype functionality across various scenarios.
* feat(policies): Add new line processors and update module exports
* feat(processor): Enhance batch and device processors to handle index and task_index fields
- Added logic to ToBatchProcessor for unsqueezing 0D tensors for index and task_index fields, ensuring they are processed as 1D tensors.
- Updated DeviceProcessor to process index and task_index fields in complementary data, preserving their tensor types and ensuring non-tensor fields remain unchanged.
- Enhanced unit tests to validate the correct handling of index and task_index fields across various scenarios, including device compatibility and dtype preservation.
2025-08-05 10:53:08 +02:00
|
|
|
# Process index field - add batch dim if 0D
|
|
|
|
|
if "index" in complementary_data:
|
|
|
|
|
index_value = complementary_data["index"]
|
|
|
|
|
if isinstance(index_value, Tensor) and index_value.dim() == 0:
|
|
|
|
|
complementary_data["index"] = index_value.unsqueeze(0)
|
|
|
|
|
|
|
|
|
|
# Process task_index field - add batch dim if 0D
|
|
|
|
|
if "task_index" in complementary_data:
|
|
|
|
|
task_index_value = complementary_data["task_index"]
|
|
|
|
|
if isinstance(task_index_value, Tensor) and task_index_value.dim() == 0:
|
|
|
|
|
complementary_data["task_index"] = task_index_value.unsqueeze(0)
|
2025-08-31 20:38:52 +02:00
|
|
|
return complementary_data
|
feature(pipeline): port tokenizer pipeline for VLA (#1645)
* feat(tokenizer): Introduce TokenizerProcessor for text tokenization
- Added TokenizerProcessor class to handle tokenization of task strings using Hugging Face's AutoTokenizer.
- Supports both string and list inputs, with customizable parameters for task key, output key, and tokenization settings.
- Implemented comprehensive unit tests to validate functionality, including handling of various input scenarios and integration with RobotProcessor.
- Updated types.py to include LANGUAGE feature type and modified __init__.py to register the new processor.
* feat(language): Enhance language processing in TokenizerProcessor
- Added OBS_LANGUAGE constant to define the observation language key.
- Updated TokenizerProcessor to store tokenized task data in the observation dictionary, ensuring compatibility with the new language feature.
- Introduced Pi0NewLineProcessor to append newlines to tasks for proper tokenization.
- Modified tests to validate the integration of language tokens and attention masks in the observation structure.
* feat(tokenizer): Add padding configuration to TokenizerProcessor
- Introduced `padding_side` parameter to the TokenizerProcessor for customizable padding direction.
- Updated the `make_pi0_processor` function to include the new padding configuration.
- Enhanced unit tests to validate the functionality of the `padding_side` parameter in various scenarios.
* feat(processor): Add state management methods to Pi0NewLineProcessor
* feat(normalization): Track normalization and unnormalization info in complementary data
- Updated NormalizerProcessor and UnnormalizerProcessor to accept additional parameters for tracking normalization modes.
- Enhanced the __call__ methods to store normalization and unnormalization information in the complementary data of transitions.
- Added unit tests to verify the correct tracking of normalization info, including scenarios with missing stats and selective normalization keys.
* feat(factory): Add preprocessor and postprocessor overrides to ProcessorConfigKwargs
- Updated ProcessorConfigKwargs to include optional overrides for preprocessor and postprocessor configurations.
- Enhanced the make_processor function to utilize the new overrides, allowing for more flexible processor initialization.
* feat(processors): Integrate RenameProcessor into various processor configurations
- Added RenameProcessor to the input steps of multiple processor functions, including make_act_processor, make_diffusion_processor, make_pi0_processor, make_sac_processor, make_tdmpc_processor, make_vqbet_processor, and make_smolvla_processor.
- Consolidated normalization features from input and output into a single NormalizerProcessor for improved efficiency.
- Updated the input steps to ensure compatibility with the new RenameProcessor integration.
* feat(smolvla): Refactor language processing and introduce new line processor (#1658)
- Removed the prepare_language method and directly accessed language tokens and masks from the batch using the OBS_LANGUAGE constant.
- Added SmolVLANewLineProcessor to ensure tasks end with a newline, enhancing tokenization compatibility.
- Updated the make_smolvla_processor function to include the new line processor and tokenizer processor for improved input handling.
* feture(policies): add device processor (#1659)
* feat(processors): Integrate DeviceProcessor into multiple processor configurations
- Added DeviceProcessor to the input and output steps of various processor functions, including make_act_processor, make_diffusion_processor, make_pi0_processor, make_pi0fast_processor, make_sac_processor, make_tdmpc_processor, make_vqbet_processor, and make_smolvla_processor.
- Enhanced the DeviceProcessor class with state management methods and ensured compatibility with existing processor pipelines.
- Introduced unit tests for DeviceProcessor to validate functionality across different scenarios, including CPU and CUDA operations.
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* refactor(pipeline): Remove to() method for device management
- Eliminated the to() method from RobotProcessor, which was responsible for moving tensor states to specified devices.
- Removed associated unit tests that validated the functionality of the to() method across various scenarios.
- Streamlined the pipeline code by focusing on other device management strategies.
* feat(processor): Enhance DeviceProcessor with float dtype conversion
- Added support for optional float dtype conversion in DeviceProcessor, allowing tensors to be converted to specified floating-point types while preserving non-float types.
- Implemented validation for float dtype input and updated the processor's configuration methods to include float dtype.
- Refactored tensor processing logic to streamline device movement and dtype conversion.
- Introduced comprehensive unit tests to validate the new float dtype functionality across various scenarios.
* feat(policies): Add new line processors and update module exports
* feat(processor): Enhance batch and device processors to handle index and task_index fields
- Added logic to ToBatchProcessor for unsqueezing 0D tensors for index and task_index fields, ensuring they are processed as 1D tensors.
- Updated DeviceProcessor to process index and task_index fields in complementary data, preserving their tensor types and ensuring non-tensor fields remain unchanged.
- Enhanced unit tests to validate the correct handling of index and task_index fields across various scenarios, including device compatibility and dtype preservation.
2025-08-05 10:53:08 +02:00
|
|
|
|
2025-09-09 18:27:30 +02:00
|
|
|
def transform_features(
|
|
|
|
|
self, features: dict[PipelineFeatureType, dict[str, PolicyFeature]]
|
|
|
|
|
) -> dict[PipelineFeatureType, dict[str, PolicyFeature]]:
|
2025-09-08 18:44:15 +02:00
|
|
|
"""
|
|
|
|
|
Returns the input features unchanged.
|
|
|
|
|
|
|
|
|
|
Adding a batch dimension does not alter the feature definition.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
features: A dictionary of policy features.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
The original dictionary of policy features.
|
|
|
|
|
"""
|
2025-09-02 17:15:01 +02:00
|
|
|
return features
|
|
|
|
|
|
2025-07-23 18:41:53 +02:00
|
|
|
|
2025-08-31 20:38:52 +02:00
|
|
|
@dataclass
|
|
|
|
|
@ProcessorStepRegistry.register(name="to_batch_processor")
|
2025-09-03 18:12:11 +02:00
|
|
|
class AddBatchDimensionProcessorStep(ProcessorStep):
|
2025-09-08 18:44:15 +02:00
|
|
|
"""
|
|
|
|
|
A composite processor step that adds a batch dimension to the entire environment transition.
|
2025-08-31 20:38:52 +02:00
|
|
|
|
2025-09-08 18:44:15 +02:00
|
|
|
This step combines individual processors for actions, observations, and complementary data
|
|
|
|
|
to create a batched transition (batch size 1) from a single-instance transition.
|
2025-08-31 20:38:52 +02:00
|
|
|
|
2025-09-08 18:44:15 +02:00
|
|
|
Attributes:
|
|
|
|
|
to_batch_action_processor: Processor for the action component.
|
|
|
|
|
to_batch_observation_processor: Processor for the observation component.
|
|
|
|
|
to_batch_complementary_data_processor: Processor for the complementary data component.
|
2025-08-31 20:38:52 +02:00
|
|
|
"""
|
2025-07-23 18:41:53 +02:00
|
|
|
|
2025-09-03 18:12:11 +02:00
|
|
|
to_batch_action_processor: AddBatchDimensionActionStep = field(
|
|
|
|
|
default_factory=AddBatchDimensionActionStep
|
2025-08-31 20:38:52 +02:00
|
|
|
)
|
2025-09-03 18:12:11 +02:00
|
|
|
to_batch_observation_processor: AddBatchDimensionObservationStep = field(
|
|
|
|
|
default_factory=AddBatchDimensionObservationStep
|
|
|
|
|
)
|
|
|
|
|
to_batch_complementary_data_processor: AddBatchDimensionComplementaryDataStep = field(
|
|
|
|
|
default_factory=AddBatchDimensionComplementaryDataStep
|
2025-08-31 20:38:52 +02:00
|
|
|
)
|
2025-08-01 09:18:16 +02:00
|
|
|
|
2025-08-31 20:38:52 +02:00
|
|
|
def __call__(self, transition: EnvTransition) -> EnvTransition:
|
2025-09-08 18:44:15 +02:00
|
|
|
"""
|
|
|
|
|
Applies the batching process to all relevant parts of an environment transition.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
transition: The environment transition to process.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
The environment transition with a batch dimension added.
|
|
|
|
|
"""
|
2025-08-31 20:38:52 +02:00
|
|
|
transition = self.to_batch_action_processor(transition)
|
|
|
|
|
transition = self.to_batch_observation_processor(transition)
|
|
|
|
|
transition = self.to_batch_complementary_data_processor(transition)
|
|
|
|
|
return transition
|
2025-09-02 17:15:01 +02:00
|
|
|
|
2025-09-09 18:27:30 +02:00
|
|
|
def transform_features(
|
|
|
|
|
self, features: dict[PipelineFeatureType, dict[str, PolicyFeature]]
|
|
|
|
|
) -> dict[PipelineFeatureType, dict[str, PolicyFeature]]:
|
2025-09-08 18:44:15 +02:00
|
|
|
"""
|
|
|
|
|
Returns the input features unchanged.
|
|
|
|
|
|
|
|
|
|
Adding a batch dimension does not alter the feature definition.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
features: A dictionary of policy features.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
The original dictionary of policy features.
|
|
|
|
|
"""
|
2025-09-03 16:54:41 +02:00
|
|
|
# NOTE: We ignore the batch dimension when transforming features
|
2025-09-02 17:15:01 +02:00
|
|
|
return features
|