CoolFace
Apppublic

robometer/rewardeval_ui

sourceHugging Faceupdated 7mo agoView on Hugging Face
4likes
progress_policy_ranking.py232 linesDownload Raw Back to eval
1from typing import Dict, List, Any, Optional2from itertools import cycle3 4import numpy as np5from collections import defaultdict6from rfm.data.dataset_types import ProgressSample7from rfm.data.samplers.base import RFMBaseSampler8from rfm.utils.logger import get_logger9 10logger = get_logger()11 12 13class ProgressPolicyRankingSampler(RFMBaseSampler):14    """Dataset that generates progress samples for policy ranking by selecting N trajectories per quality label for tasks with multiple quality labels."""15 16    def __init__(17        self,18        num_examples_per_quality_pr: int = 5,19        num_partial_successes: Optional[int] = None,20        frame_step: int = 1,21        use_frame_steps: bool = True,22        max_tasks: Optional[int] = None,23        **kwargs,24    ):25        super().__init__(**kwargs)26 27        self.num_examples_per_quality_pr = num_examples_per_quality_pr28        self.num_partial_successes = num_partial_successes29        self.frame_step = frame_step30        self.use_frame_steps = use_frame_steps31        self.max_tasks = max_tasks32        logger.info(f"ProgressPolicyRankingSampler initialized with {len(self.robot_trajectories)} trajectories")33 34        self.sample_indices = self._generate_all_sample_indices()35 36        logger.info(f"Generated {len(self.sample_indices)} sample indices")37 38    def _generate_all_sample_indices(self) -> List[Dict[str, Any]]:39        """Generate sample indices by selecting tasks with multiple quality labels/partial_success values and sampling N trajectories per group.40 41        For non-RoboArena: Groups by task and quality_label.42        For RoboArena: Groups by task and partial_success values.43 44        If use_frame_steps=True, generates subsequence samples like reward_alignment (0:frame_step, 0:2*frame_step, etc.).45        If use_frame_steps=False, generates one sample per trajectory (whole trajectory).46        """47 48        # Check if this is RoboArena (has partial_success)49        is_roboarena = False50        if self.robot_trajectories:51            first_traj = self.dataset[self.robot_trajectories[0]]52            is_roboarena = first_traj.get("partial_success") is not None53 54        # Group trajectories by task and grouping key (quality_label or partial_success)55        task_to_key_to_trajs = defaultdict(lambda: defaultdict(list))56 57        for traj_idx in self.robot_trajectories:58            traj = self.dataset[traj_idx]59            task = traj["task"]60 61            if is_roboarena:62                # RoboArena: Use rounded partial_success as key to group similar values63                partial_success_val = traj.get("partial_success")64                if partial_success_val is not None:65                    partial_success = round(float(partial_success_val), 2)66                    task_to_key_to_trajs[task][partial_success].append(traj_idx)67            else:68                # Non-RoboArena: Use quality_label69                quality = traj["quality_label"]70                task_to_key_to_trajs[task][quality].append(traj_idx)71 72        # Filter to tasks that have multiple grouping values73        tasks_with_multiple_values = {74            task: key_to_trajs for task, key_to_trajs in task_to_key_to_trajs.items() if len(key_to_trajs) > 175        }76 77        dataset_type_str = "partial_success values" if is_roboarena else "quality labels"78        logger.info(f"Found {len(tasks_with_multiple_values)} tasks with multiple {dataset_type_str}")79 80        # Limit number of tasks if max_tasks is specified81        if self.max_tasks is not None and self.max_tasks > 0:82            # Convert to list, shuffle, and take first max_tasks83            # Sort tasks first to ensure deterministic ordering before shuffling84            tasks_list = sorted(tasks_with_multiple_values.items())85            self._local_random.shuffle(tasks_list)86            tasks_with_multiple_values = dict(tasks_list[: self.max_tasks])87            logger.info(f"Limited to {len(tasks_with_multiple_values)} tasks (max_tasks={self.max_tasks})")88 89        # Sample trajectories for each task90        sample_indices = []91        all_sampled_traj_indices = []92        # Sort tasks to ensure deterministic processing order93        for task, key_to_trajs in sorted(tasks_with_multiple_values.items()):94            if is_roboarena:95                # RoboArena: Use num_partial_successes for circular sampling96                num_to_sample_total = self.num_partial_successes97 98                # Build lists of available indices per partial_success (sorted for deterministic sampling)99                available_lists = []100                for partial_success in sorted(key_to_trajs.keys()):101                    traj_indices = sorted(key_to_trajs[partial_success])102                    if traj_indices:103                        available_lists.append(traj_indices)104 105                # Circular sampling: cycle through partial_success groups until we reach max106                sampled_traj_indices = []107                for available_indices in cycle(available_lists):108                    if len(sampled_traj_indices) >= num_to_sample_total:109                        break110                    if not available_indices:111                        # If all lists are empty, stop112                        if all(not lst for lst in available_lists):113                            break114                        continue115 116                    # Sample one trajectory from this group117                    sampled_idx = self._local_random.choice(available_indices)118                    sampled_traj_indices.append(sampled_idx)119                    # Remove the sampled index from this list120                    available_indices.remove(sampled_idx)121 122                # Generate samples for all sampled trajectories123                for traj_idx in sampled_traj_indices:124                    traj = self.dataset[traj_idx]125                    sample_indices.extend(self._generate_indices_for_trajectory(traj_idx, traj))126                    all_sampled_traj_indices.append(traj_idx)127            else:128                # Non-RoboArena: Sample N trajectories per quality label129                # Sort quality labels to ensure deterministic order130                for quality in sorted(key_to_trajs.keys()):131                    traj_indices = key_to_trajs[quality]132                    # Sort trajectory indices to ensure deterministic sampling133                    traj_indices = sorted(traj_indices)134                    # Sample up to num_examples_per_quality_pr trajectories for this quality label135                    num_to_sample = min(self.num_examples_per_quality_pr, len(traj_indices))136                    sampled_traj_indices = self._local_random.sample(traj_indices, num_to_sample)137                    for traj_idx in sampled_traj_indices:138                        traj = self.dataset[traj_idx]139                        sample_indices.extend(self._generate_indices_for_trajectory(traj_idx, traj))140                        all_sampled_traj_indices.append(traj_idx)141 142        logger.info(f"Sampled {len(sample_indices)} samples across {len(tasks_with_multiple_values)} tasks")143        logger.info(f"Sampled trajectory indices: {all_sampled_traj_indices}")144 145        return sample_indices146 147    def _generate_indices_for_trajectory(self, traj_idx: int, traj: Dict[str, Any]) -> List[Dict[str, Any]]:148        """Generate sample indices for a single trajectory.149 150        Args:151            traj_idx: Index of the trajectory in the dataset152            traj: Trajectory dictionary153 154        Returns:155            List of sample index dictionaries156        """157        num_frames = traj["num_frames"]158        indices = []159 160        if self.use_frame_steps:161            # Generate subsequence indices like reward_alignment: 0:frame_step, 0:2*frame_step, etc.162            for end_idx in range(self.frame_step, num_frames + 1, self.frame_step):163                frame_indices = list(range(end_idx))164                indices.append({165                    "traj_idx": traj_idx,166                    "frame_indices": frame_indices,167                    "num_frames": num_frames,168                    "video_path": traj["frames"],169                    "id": traj["id"],170                    "use_frame_steps": True,171                })172        else:173            # Generate one sample per trajectory (whole trajectory)174            indices.append({175                "traj_idx": traj_idx,176                "video_path": traj["frames"],177                "id": traj["id"],178                "use_frame_steps": False,179            })180 181        return indices182 183    def _generate_sample_from_indices(self, sample_idx_info: dict) -> ProgressSample:184        """Generate a single progress sample from trajectory index."""185        traj_idx = sample_idx_info["traj_idx"]186        use_frame_steps = sample_idx_info.get("use_frame_steps", True)187 188        traj = self.dataset[traj_idx]189 190        if use_frame_steps:191            # Frame steps mode: create subsequence like reward_alignment192            frame_indices = sample_idx_info["frame_indices"]193            num_frames = sample_idx_info["num_frames"]194 195            metadata = {196                "quality_label": traj["quality_label"],197                "data_source": traj["data_source"],198                "task": traj["task"],199                "id": traj["id"],200                "video_path": sample_idx_info["video_path"],201                "frame_step": frame_indices[-1] if frame_indices else 0,202            }203 204            trajectory = self._get_traj_from_data(205                traj=traj,206                frame_indices=frame_indices,207                metadata=metadata,208            )209        else:210            # Whole trajectory mode211            metadata = {212                "quality_label": traj["quality_label"],213                "data_source": traj["data_source"],214                "task": traj["task"],215                "id": traj["id"],216                "video_path": sample_idx_info["video_path"],217            }218 219            trajectory = self._get_traj_from_data(220                traj=traj,221                metadata=metadata,222            )223 224        sample = ProgressSample(trajectory=trajectory)225        return sample226 227    def __len__(self):228        return len(self.sample_indices)229 230    def __getitem__(self, idx):231        return self._generate_sample_from_indices(self.sample_indices[idx])232