Download code/datasets/core.py from szcharlesji/yor-patch-policy-checkpoints: direct link, hf CLI and curl.
- Browser
- Download file 10.6 kB
-
https://huggingface.co/szcharlesji/yor-patch-policy-checkpoints/resolve/main/code/datasets/core.py
- Command line
-
hf download hf://szcharlesji/yor-patch-policy-checkpoints/code/datasets/core.py
-
curl -L -o core.py https://huggingface.co/szcharlesji/yor-patch-policy-checkpoints/resolve/main/code/datasets/core.py
10.6 kB
| import abc | |
| import utils | |
| import torch | |
| import numpy as np | |
| from torch import default_generator, randperm | |
| from torch.utils.data import Dataset, Subset | |
| from typing import Callable, Optional, Sequence, List | |
| from torch.nn.utils.rnn import pad_sequence | |
| # Taken from python 3.5 docs | |
| def _accumulate(iterable, fn=lambda x, y: x + y): | |
| "Return running totals" | |
| # _accumulate([1,2,3,4,5]) --> 1 3 6 10 15 | |
| # _accumulate([1,2,3,4,5], operator.mul) --> 1 2 6 24 120 | |
| it = iter(iterable) | |
| try: | |
| total = next(it) | |
| except StopIteration: | |
| return | |
| yield total | |
| for element in it: | |
| total = fn(total, element) | |
| yield total | |
| class TrajectoryDataset(Dataset, abc.ABC): | |
| """ | |
| A dataset containing trajectories. | |
| TrajectoryDataset[i] returns: (observations, actions, mask) | |
| observations: Tensor[T, ...], T frames of observations | |
| actions: Tensor[T, ...], T frames of actions | |
| mask: Tensor[T]: False: invalid; True: valid | |
| """ | |
| def get_seq_length(self, idx): | |
| """ | |
| Returns the length of the idx-th trajectory. | |
| """ | |
| raise NotImplementedError | |
| def get_frames(self, idx, frames): | |
| """ | |
| Returns the frames from the idx-th trajectory at the specified frames. | |
| Used to speed up slicing. | |
| """ | |
| raise NotImplementedError | |
| class TrajectorySubset(TrajectoryDataset, Subset): | |
| """ | |
| Subset of a trajectory dataset at specified indices. | |
| Args: | |
| dataset (TrajectoryDataset): The whole Dataset | |
| indices (sequence): Indices in the whole set selected for subset | |
| """ | |
| def __init__(self, dataset: TrajectoryDataset, indices: Sequence[int]): | |
| Subset.__init__(self, dataset, indices) | |
| def get_seq_length(self, idx): | |
| return self.dataset.get_seq_length(self.indices[idx]) | |
| def get_all_actions(self): | |
| return self.dataset.get_all_actions() | |
| def get_frames(self, idx, frames): | |
| return self.dataset.get_frames(self.indices[idx], frames) | |
| class TrajectorySlicerDataset(Dataset): | |
| """ | |
| Slice a trajectory dataset into (overlapping) windows of `window` observations | |
| paired with an action chunk. | |
| dataset: a trajectory dataset that satisfies: | |
| dataset.get_seq_length(i) returns the length of sequence i | |
| dataset[i] = (observations, actions, *others) | |
| observations: Tensor[T, ...] | |
| actions: Tensor[T, ...] | |
| window: int | |
| number of observation timesteps in each slice | |
| action_window: int | |
| number of action timesteps to predict | |
| vqbet_get_future_action_chunk: bool = True | |
| if True, return only the action chunk following the observation window; | |
| otherwise return actions spanning the whole window plus the chunk | |
| transform: function (values) -> values | |
| pad_seq_length: bool = True | |
| pad actions at the end to ensure a fixed length instead of dropping short slices | |
| Goal conditioning is not handled here: any goal tensor is carried through as part | |
| of `*others` by the underlying dataset. The remaining arguments | |
| (future_conditional, min_future_sep, future_seq_len, only_sample_tail, | |
| use_libero_goal) are recorded on the instance but do not affect slicing; | |
| future_conditional only changes the value reported by get_seq_length. | |
| """ | |
| def __init__( | |
| self, | |
| dataset: TrajectoryDataset, | |
| window: int, | |
| action_window: int, | |
| vqbet_get_future_action_chunk: bool = True, | |
| future_conditional: bool = False, | |
| min_future_sep: int = 0, | |
| future_seq_len: Optional[int] = None, | |
| only_sample_tail: bool = False, | |
| transform: Optional[Callable] = None, | |
| use_libero_goal: bool = False, | |
| pad_seq_length: bool = True, # pad actions at end to ensure fixed length | |
| ): | |
| if future_conditional: | |
| assert future_seq_len is not None, "must specify a future_seq_len" | |
| self.dataset = dataset | |
| self.window = window | |
| self.action_window = action_window | |
| self.vqbet_get_future_action_chunk = vqbet_get_future_action_chunk | |
| self.future_conditional = future_conditional | |
| self.min_future_sep = min_future_sep | |
| self.future_seq_len = future_seq_len | |
| self.only_sample_tail = only_sample_tail | |
| self.transform = transform | |
| self.slices = [] | |
| self.use_libero_goal = use_libero_goal | |
| self.pad_seq_length = pad_seq_length | |
| min_seq_length = np.inf | |
| min_window_required = window + action_window - 1 | |
| for i in range(len(self.dataset)): # type: ignore | |
| T = self.dataset.get_seq_length(i) # avoid reading actual seq (slow) | |
| min_seq_length = min(T, min_seq_length) | |
| self.slices += [ | |
| (i, 0, end + 1) for end in range(window - 1) | |
| ] # slice indices follow convention [start, end) | |
| if self.pad_seq_length: | |
| if T - self.window >= 0: | |
| self.slices += [ | |
| (i, start, start + self.window) | |
| for start in range(T - self.window + 1) | |
| ] | |
| else: | |
| if T - min_window_required < 0: | |
| print( | |
| f"Ignored short sequence #{i}: len={T}, window={min_window_required}" | |
| ) | |
| else: | |
| self.slices += [ | |
| (i, start, start + self.window) | |
| for start in range(T - min_window_required + 1) | |
| ] | |
| if (not self.pad_seq_length) and (min_seq_length < min_window_required): | |
| print( | |
| f"Ignored short sequences. To include all, set window <= {min_seq_length}." | |
| ) | |
| def get_seq_length(self, idx: int) -> int: | |
| if self.future_conditional: | |
| return self.future_seq_len + self.window | |
| else: | |
| return self.window | |
| def __len__(self): | |
| return len(self.slices) | |
| def __getitem__(self, idx): | |
| i, start, end = self.slices[idx] | |
| obs, act, *others = self.dataset[i] | |
| if end - start < self.window: | |
| obs_win = utils.inference.repeat_start_to_length( | |
| obs[start:end], self.window, dim=0 | |
| ) | |
| act = utils.inference.repeat_start_to_length( | |
| # -1 due to overlap for 1 step between obs and act | |
| act[start : end - 1 + self.action_window], | |
| self.window + self.action_window - 1, | |
| dim=0, | |
| ) | |
| repeated_others = [ | |
| utils.inference.repeat_start_to_length( | |
| other[start:end], self.window, dim=0 | |
| ) | |
| for other in others | |
| ] | |
| else: | |
| obs_win = obs[start:end] | |
| # -1 due to overlap for 1 step between obs and act | |
| act = act[start : end - 1 + self.action_window] | |
| repeated_others = [other[start:end] for other in others] | |
| if self.vqbet_get_future_action_chunk: | |
| expected_len = self.action_window | |
| act = act[self.window - 1 :] | |
| else: | |
| expected_len = self.window + self.action_window - 1 | |
| if act.shape[0] < expected_len: | |
| if self.pad_seq_length: | |
| act = utils.inference.repeat_end_to_length(act, expected_len, dim=0) | |
| else: | |
| raise ValueError( | |
| f"Action chunk too short: {act.shape[0]} < {expected_len}, " | |
| f"but pad_seq_length is False" | |
| ) | |
| values = [obs_win, act, *repeated_others] | |
| # optionally apply transform | |
| if self.transform is not None: | |
| values = self.transform(values) | |
| return tuple(values) | |
| class TrajectoryEmbeddingDataset(TrajectoryDataset): | |
| def __init__( | |
| self, | |
| model, | |
| dataset: TrajectoryDataset, | |
| device="cpu", | |
| embed_goal=False, | |
| ): | |
| self.data = utils.inference.embed_trajectory_dataset( | |
| model, | |
| dataset, | |
| obs_only=False, | |
| device=device, | |
| embed_goal=embed_goal, | |
| ) | |
| assert len(self.data) == len(dataset) | |
| self.seq_lengths = [len(x[0]) for x in self.data] | |
| self.on_device_data = [] | |
| n_tensors = len(self.data[0]) | |
| for i in range(n_tensors): | |
| self.on_device_data.append( | |
| pad_sequence([x[i] for x in self.data], batch_first=True).to(device) | |
| ) | |
| self.data = self.on_device_data | |
| def get_seq_length(self, idx): | |
| return self.seq_lengths[idx] | |
| def get_all_actions(self): | |
| return torch.cat([x[1] for x in self.data], dim=0) | |
| def get_frames(self, idx, frames): | |
| return [x[idx, frames] for x in self.data] | |
| def __getitem__(self, idx): | |
| return self.get_frames(idx, range(self.get_seq_length(idx))) | |
| def __len__(self): | |
| return len(self.seq_lengths) | |
| def random_split_traj( | |
| dataset: TrajectoryDataset, | |
| lengths: Sequence[int], | |
| generator: Optional[torch.Generator] = default_generator, | |
| ) -> List[TrajectorySubset]: | |
| """ | |
| (Modified from torch.utils.data.dataset.random_split) | |
| Randomly split a trajectory dataset into non-overlapping new datasets of given lengths. | |
| Optionally fix the generator for reproducible results, e.g.: | |
| >>> random_split_traj(range(10), [3, 7], generator=torch.Generator().manual_seed(42)) | |
| Args: | |
| dataset (TrajectoryDataset): TrajectoryDataset to be split | |
| lengths (sequence): lengths of splits to be produced | |
| generator (Generator): Generator used for the random permutation. | |
| """ | |
| # Cannot verify that dataset is Sized | |
| if sum(lengths) != len(dataset): # type: ignore[arg-type] | |
| raise ValueError( | |
| "Sum of input lengths does not equal the length of the input dataset!" | |
| ) | |
| indices = randperm(sum(lengths), generator=generator).tolist() | |
| return [ | |
| TrajectorySubset(dataset, indices[offset - length : offset]) | |
| for offset, length in zip(_accumulate(lengths), lengths) | |
| ] | |
| def split_traj_datasets(dataset, train_fraction=0.95, random_seed=42): | |
| dataset_length = len(dataset) | |
| lengths = [ | |
| int(train_fraction * dataset_length), | |
| dataset_length - int(train_fraction * dataset_length), | |
| ] | |
| train_set, val_set = random_split_traj( | |
| dataset, lengths, generator=torch.Generator().manual_seed(random_seed) | |
| ) | |
| return train_set, val_set | |