Chunks actions and observations into the given window_size. "observation" keys are given a new axis (at index 1) of size `window_size` containing `window_size - 1` observations from the past and the current observation. "action" is given a new axis (at index 1) of size `window_size
(
traj: Dict,
backward_windows_size: int = 0,
backward_delta: int = 1,
forward_window_size: int = 0,
)
| 10 | import tensorflow as tf |
| 11 | |
| 12 | def chunk_act_obs( |
| 13 | traj: Dict, |
| 14 | backward_windows_size: int = 0, |
| 15 | backward_delta: int = 1, |
| 16 | forward_window_size: int = 0, |
| 17 | ) -> Dict: |
| 18 | """ |
| 19 | Chunks actions and observations into the given window_size. |
| 20 | |
| 21 | "observation" keys are given a new axis (at index 1) of size `window_size` containing `window_size - 1` |
| 22 | observations from the past and the current observation. "action" is given a new axis (at index 1) of size |
| 23 | `window_size + future_action_window_size` containing `window_size - 1` actions from the past, the current |
| 24 | action, and `future_action_window_size` actions from the future. "pad_mask" is added to "observation" and |
| 25 | indicates whether an observation should be considered padding (i.e. if it had come from a timestep |
| 26 | before the start of the trajectory). |
| 27 | """ |
| 28 | traj_len = tf.shape(traj["action"])[0] |
| 29 | action_dim = traj["action"].shape[-1] |
| 30 | chunk_indices = tf.broadcast_to(tf.range(-backward_windows_size, 1) * backward_delta, [traj_len, backward_windows_size + 1]) + tf.broadcast_to( |
| 31 | tf.range(traj_len)[:, None], [traj_len, backward_windows_size + 1] |
| 32 | ) |
| 33 | |
| 34 | action_chunk_indices = tf.broadcast_to( |
| 35 | tf.range(0, 1 + forward_window_size), |
| 36 | [traj_len, 1 + forward_window_size], |
| 37 | ) + tf.broadcast_to( |
| 38 | tf.range(traj_len)[:, None], |
| 39 | [traj_len, 1 + forward_window_size], |
| 40 | ) |
| 41 | |
| 42 | floored_chunk_indices = tf.maximum(chunk_indices, 0) |
| 43 | |
| 44 | if "timestep" in traj["task"]: |
| 45 | goal_timestep = traj["task"]["timestep"] |
| 46 | else: |
| 47 | goal_timestep = tf.fill([traj_len], traj_len - 1) |
| 48 | |
| 49 | floored_action_chunk_indices = tf.minimum(tf.maximum(action_chunk_indices, 0), goal_timestep[:, None]) |
| 50 | |
| 51 | traj["observation"] = tf.nest.map_structure(lambda x: tf.gather(x, floored_chunk_indices), traj["observation"]) |
| 52 | traj["action"] = tf.gather(traj["action"], floored_action_chunk_indices) |
| 53 | |
| 54 | # indicates whether an entire observation is padding |
| 55 | traj["observation"]["pad_mask"] = chunk_indices >= 0 |
| 56 | |
| 57 | # if no absolute_action_mask was provided, assume all actions are relative |
| 58 | if "absolute_action_mask" not in traj and forward_window_size > 0: |
| 59 | logging.warning( |
| 60 | "future_action_window_size > 0 but no absolute_action_mask was provided. " |
| 61 | "Assuming all actions are relative for the purpose of making neutral actions." |
| 62 | ) |
| 63 | absolute_action_mask = traj.get("absolute_action_mask", tf.zeros([traj_len, action_dim], dtype=tf.bool)) |
| 64 | neutral_actions = tf.where( |
| 65 | absolute_action_mask[:, None, :], |
| 66 | traj["action"], # absolute actions are repeated (already done during chunking) |
| 67 | tf.zeros_like(traj["action"]), # relative actions are zeroed |
| 68 | ) |
| 69 |
nothing calls this directly
no outgoing calls
no test coverage detected