Initial commit
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +16 -0
- codecs/imagecodecs_numcodecs.py +1386 -0
- common/checkpoint_util.py +59 -0
- common/compute_all_loss.py +483 -0
- common/cv2_util.py +150 -0
- common/env_util.py +23 -0
- common/json_logger.py +117 -0
- common/nested_dict_util.py +32 -0
- common/normalize_util.py +223 -0
- common/pose_trajectory_interpolator.py +208 -0
- common/precise_sleep.py +25 -0
- common/pref_replay_buffer.py +365 -0
- common/pref_sampler.py +74 -0
- common/prior_utils_confidence.py +723 -0
- common/pymunk_override.py +248 -0
- common/pymunk_util.py +52 -0
- common/pytorch_util.py +82 -0
- common/replay_buffer.py +615 -0
- common/robomimic_config_util.py +47 -0
- common/robomimic_util.py +177 -0
- common/sampler.py +154 -0
- common/timestamp_accumulator.py +222 -0
- config/data_collect/diffusion_policy_cnn/datacollect_diffusion_policy_cnn_lowdim_can_mh_abs.yaml +29 -0
- config/data_collect/diffusion_policy_cnn/datacollect_diffusion_policy_cnn_lowdim_kitchen_abs.yaml +29 -0
- config/data_collect/diffusion_policy_cnn/datacollect_diffusion_policy_cnn_lowdim_lift_mh_abs.yaml +29 -0
- config/data_collect/diffusion_policy_cnn/datacollect_diffusion_policy_cnn_lowdim_pusht.yaml +29 -0
- config/data_collect/diffusion_policy_cnn/datacollect_diffusion_policy_cnn_lowdim_square_mh_abs.yaml +29 -0
- config/data_collect/diffusion_policy_cnn/datacollect_diffusion_policy_cnn_lowdim_transport_mh_abs.yaml +29 -0
- config/data_collect/diffusion_policy_transformer/datacollect_diffusion_policy_transformer_lowdim_can_mh_abs.yaml +29 -0
- config/data_collect/diffusion_policy_transformer/datacollect_diffusion_policy_transformer_lowdim_kitchen_abs.yaml +29 -0
- config/data_collect/diffusion_policy_transformer/datacollect_diffusion_policy_transformer_lowdim_lift_mh_abs.yaml +29 -0
- config/data_collect/diffusion_policy_transformer/datacollect_diffusion_policy_transformer_lowdim_pusht.yaml +29 -0
- config/data_collect/diffusion_policy_transformer/datacollect_diffusion_policy_transformer_lowdim_square_mh_abs.yaml +29 -0
- config/data_collect/diffusion_policy_transformer/datacollect_diffusion_policy_transformer_lowdim_tool_hang_ph_abs.yaml +29 -0
- config/data_collect/diffusion_policy_transformer/datacollect_diffusion_policy_transformer_lowdim_transport_mh_abs.yaml +29 -0
- config/pbrl_dp_transformer/train_pbrl_diffusion_policy_transformer_lowdim_can_mh_abs.yaml +163 -0
- config/pbrl_dp_transformer/train_pbrl_diffusion_policy_transformer_lowdim_lift_mh_abs.yaml +163 -0
- config/pbrl_dp_transformer/train_pbrl_diffusion_policy_transformer_lowdim_square_mh_abs.yaml +163 -0
- config/pbrl_dp_transformer/train_pbrl_diffusion_policy_transformer_lowdim_tool_hang_ph_abs.yaml +163 -0
- config/pbrl_dp_transformer/train_pbrl_diffusion_policy_transformer_lowdim_transport_mh_abs.yaml +163 -0
- config/pbrl_reflow_transformer/train_pbrl_reflow_policy_transformer_lowdim_can_mh_abs.yaml +145 -0
- config/pbrl_reflow_transformer/train_pbrl_reflow_policy_transformer_lowdim_lift_mh_abs.yaml +145 -0
- config/pbrl_reflow_transformer/train_pbrl_reflow_policy_transformer_lowdim_square_mh_abs.yaml +145 -0
- config/pbrl_reflow_transformer/train_pbrl_reflow_policy_transformer_lowdim_tool_hang_ph_abs.yaml +145 -0
- config/pbrl_reflow_transformer/train_pbrl_reflow_policy_transformer_lowdim_transport_mh_abs.yaml +145 -0
- config/task/aloha.yaml +58 -0
- config/task/blockpush_lowdim_seed.yaml +34 -0
- config/task/blockpush_lowdim_seed_abs.yaml +34 -0
- config/task/can_image.yaml +64 -0
- config/task/can_image_abs.yaml +64 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,19 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
env/kitchen/relay_policy_learning/adept_models/kitchen/textures/marble1.png filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
env/kitchen/relay_policy_learning/adept_models/kitchen/textures/metal1.png filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
env/kitchen/relay_policy_learning/adept_models/kitchen/textures/tile1.png filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
env/kitchen/relay_policy_learning/adept_models/kitchen/textures/wood1.png filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
env/kitchen/relay_policy_learning/adept_models/scenes/textures/white_marble_tile.png filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
env/kitchen/relay_policy_learning/adept_models/scenes/textures/white_marble_tile2.png filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
env/kitchen/relay_policy_learning/third_party/franka/franka_panda.png filter=lfs diff=lfs merge=lfs -text
|
| 43 |
+
env/kitchen/relay_policy_learning/third_party/franka/meshes/visual/hand.stl filter=lfs diff=lfs merge=lfs -text
|
| 44 |
+
env/kitchen/relay_policy_learning/third_party/franka/meshes/visual/link0.stl filter=lfs diff=lfs merge=lfs -text
|
| 45 |
+
env/kitchen/relay_policy_learning/third_party/franka/meshes/visual/link1.stl filter=lfs diff=lfs merge=lfs -text
|
| 46 |
+
env/kitchen/relay_policy_learning/third_party/franka/meshes/visual/link2.stl filter=lfs diff=lfs merge=lfs -text
|
| 47 |
+
env/kitchen/relay_policy_learning/third_party/franka/meshes/visual/link3.stl filter=lfs diff=lfs merge=lfs -text
|
| 48 |
+
env/kitchen/relay_policy_learning/third_party/franka/meshes/visual/link4.stl filter=lfs diff=lfs merge=lfs -text
|
| 49 |
+
env/kitchen/relay_policy_learning/third_party/franka/meshes/visual/link5.stl filter=lfs diff=lfs merge=lfs -text
|
| 50 |
+
env/kitchen/relay_policy_learning/third_party/franka/meshes/visual/link6.stl filter=lfs diff=lfs merge=lfs -text
|
| 51 |
+
env/kitchen/relay_policy_learning/third_party/franka/meshes/visual/link7.stl filter=lfs diff=lfs merge=lfs -text
|
codecs/imagecodecs_numcodecs.py
ADDED
|
@@ -0,0 +1,1386 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
# imagecodecs/numcodecs.py
|
| 3 |
+
|
| 4 |
+
# Copyright (c) 2021-2022, Christoph Gohlke
|
| 5 |
+
# All rights reserved.
|
| 6 |
+
#
|
| 7 |
+
# Redistribution and use in source and binary forms, with or without
|
| 8 |
+
# modification, are permitted provided that the following conditions are met:
|
| 9 |
+
#
|
| 10 |
+
# 1. Redistributions of source code must retain the above copyright notice,
|
| 11 |
+
# this list of conditions and the following disclaimer.
|
| 12 |
+
#
|
| 13 |
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
| 14 |
+
# this list of conditions and the following disclaimer in the documentation
|
| 15 |
+
# and/or other materials provided with the distribution.
|
| 16 |
+
#
|
| 17 |
+
# 3. Neither the name of the copyright holder nor the names of its
|
| 18 |
+
# contributors may be used to endorse or promote products derived from
|
| 19 |
+
# this software without specific prior written permission.
|
| 20 |
+
#
|
| 21 |
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
| 22 |
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
| 23 |
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
| 24 |
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
| 25 |
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
| 26 |
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
| 27 |
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
| 28 |
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
| 29 |
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
| 30 |
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
| 31 |
+
# POSSIBILITY OF SUCH DAMAGE.
|
| 32 |
+
|
| 33 |
+
"""Additional numcodecs implemented using imagecodecs."""
|
| 34 |
+
|
| 35 |
+
__version__ = '2022.9.26'
|
| 36 |
+
|
| 37 |
+
__all__ = ('register_codecs',)
|
| 38 |
+
|
| 39 |
+
import numpy
|
| 40 |
+
from numcodecs.abc import Codec
|
| 41 |
+
from numcodecs.registry import register_codec, get_codec
|
| 42 |
+
|
| 43 |
+
import imagecodecs
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def protective_squeeze(x: numpy.ndarray):
|
| 47 |
+
"""
|
| 48 |
+
Squeeze dim only if it's not the last dim.
|
| 49 |
+
Image dim expected to be *, H, W, C
|
| 50 |
+
"""
|
| 51 |
+
img_shape = x.shape[-3:]
|
| 52 |
+
if len(x.shape) > 3:
|
| 53 |
+
n_imgs = numpy.prod(x.shape[:-3])
|
| 54 |
+
if n_imgs > 1:
|
| 55 |
+
img_shape = (-1,) + img_shape
|
| 56 |
+
return x.reshape(img_shape)
|
| 57 |
+
|
| 58 |
+
def get_default_image_compressor(**kwargs):
|
| 59 |
+
if imagecodecs.JPEGXL:
|
| 60 |
+
# has JPEGXL
|
| 61 |
+
this_kwargs = {
|
| 62 |
+
'effort': 3,
|
| 63 |
+
'distance': 0.3,
|
| 64 |
+
# bug in libjxl, invalid codestream for non-lossless
|
| 65 |
+
# when decoding speed > 1
|
| 66 |
+
'decodingspeed': 1
|
| 67 |
+
}
|
| 68 |
+
this_kwargs.update(kwargs)
|
| 69 |
+
return JpegXl(**this_kwargs)
|
| 70 |
+
else:
|
| 71 |
+
this_kwargs = {
|
| 72 |
+
'level': 50
|
| 73 |
+
}
|
| 74 |
+
this_kwargs.update(kwargs)
|
| 75 |
+
return Jpeg2k(**this_kwargs)
|
| 76 |
+
|
| 77 |
+
class Aec(Codec):
|
| 78 |
+
"""AEC codec for numcodecs."""
|
| 79 |
+
|
| 80 |
+
codec_id = 'imagecodecs_aec'
|
| 81 |
+
|
| 82 |
+
def __init__(
|
| 83 |
+
self, bitspersample=None, flags=None, blocksize=None, rsi=None
|
| 84 |
+
):
|
| 85 |
+
self.bitspersample = bitspersample
|
| 86 |
+
self.flags = flags
|
| 87 |
+
self.blocksize = blocksize
|
| 88 |
+
self.rsi = rsi
|
| 89 |
+
|
| 90 |
+
def encode(self, buf):
|
| 91 |
+
return imagecodecs.aec_encode(
|
| 92 |
+
buf,
|
| 93 |
+
bitspersample=self.bitspersample,
|
| 94 |
+
flags=self.flags,
|
| 95 |
+
blocksize=self.blocksize,
|
| 96 |
+
rsi=self.rsi,
|
| 97 |
+
)
|
| 98 |
+
|
| 99 |
+
def decode(self, buf, out=None):
|
| 100 |
+
return imagecodecs.aec_decode(
|
| 101 |
+
buf,
|
| 102 |
+
bitspersample=self.bitspersample,
|
| 103 |
+
flags=self.flags,
|
| 104 |
+
blocksize=self.blocksize,
|
| 105 |
+
rsi=self.rsi,
|
| 106 |
+
out=_flat(out),
|
| 107 |
+
)
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
class Apng(Codec):
|
| 111 |
+
"""APNG codec for numcodecs."""
|
| 112 |
+
|
| 113 |
+
codec_id = 'imagecodecs_apng'
|
| 114 |
+
|
| 115 |
+
def __init__(self, level=None, photometric=None, delay=None):
|
| 116 |
+
self.level = level
|
| 117 |
+
self.photometric = photometric
|
| 118 |
+
self.delay = delay
|
| 119 |
+
|
| 120 |
+
def encode(self, buf):
|
| 121 |
+
buf = protective_squeeze(numpy.asarray(buf))
|
| 122 |
+
return imagecodecs.apng_encode(
|
| 123 |
+
buf,
|
| 124 |
+
level=self.level,
|
| 125 |
+
photometric=self.photometric,
|
| 126 |
+
delay=self.delay,
|
| 127 |
+
)
|
| 128 |
+
|
| 129 |
+
def decode(self, buf, out=None):
|
| 130 |
+
return imagecodecs.apng_decode(buf, out=out)
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
class Avif(Codec):
|
| 134 |
+
"""AVIF codec for numcodecs."""
|
| 135 |
+
|
| 136 |
+
codec_id = 'imagecodecs_avif'
|
| 137 |
+
|
| 138 |
+
def __init__(
|
| 139 |
+
self,
|
| 140 |
+
level=None,
|
| 141 |
+
speed=None,
|
| 142 |
+
tilelog2=None,
|
| 143 |
+
bitspersample=None,
|
| 144 |
+
pixelformat=None,
|
| 145 |
+
numthreads=None,
|
| 146 |
+
index=None,
|
| 147 |
+
):
|
| 148 |
+
self.level = level
|
| 149 |
+
self.speed = speed
|
| 150 |
+
self.tilelog2 = tilelog2
|
| 151 |
+
self.bitspersample = bitspersample
|
| 152 |
+
self.pixelformat = pixelformat
|
| 153 |
+
self.numthreads = numthreads
|
| 154 |
+
self.index = index
|
| 155 |
+
|
| 156 |
+
def encode(self, buf):
|
| 157 |
+
buf = protective_squeeze(numpy.asarray(buf))
|
| 158 |
+
return imagecodecs.avif_encode(
|
| 159 |
+
buf,
|
| 160 |
+
level=self.level,
|
| 161 |
+
speed=self.speed,
|
| 162 |
+
tilelog2=self.tilelog2,
|
| 163 |
+
bitspersample=self.bitspersample,
|
| 164 |
+
pixelformat=self.pixelformat,
|
| 165 |
+
numthreads=self.numthreads,
|
| 166 |
+
)
|
| 167 |
+
|
| 168 |
+
def decode(self, buf, out=None):
|
| 169 |
+
return imagecodecs.avif_decode(
|
| 170 |
+
buf, index=self.index, numthreads=self.numthreads, out=out
|
| 171 |
+
)
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
class Bitorder(Codec):
|
| 175 |
+
"""Bitorder codec for numcodecs."""
|
| 176 |
+
|
| 177 |
+
codec_id = 'imagecodecs_bitorder'
|
| 178 |
+
|
| 179 |
+
def encode(self, buf):
|
| 180 |
+
return imagecodecs.bitorder_encode(buf)
|
| 181 |
+
|
| 182 |
+
def decode(self, buf, out=None):
|
| 183 |
+
return imagecodecs.bitorder_decode(buf, out=_flat(out))
|
| 184 |
+
|
| 185 |
+
|
| 186 |
+
class Bitshuffle(Codec):
|
| 187 |
+
"""Bitshuffle codec for numcodecs."""
|
| 188 |
+
|
| 189 |
+
codec_id = 'imagecodecs_bitshuffle'
|
| 190 |
+
|
| 191 |
+
def __init__(self, itemsize=1, blocksize=0):
|
| 192 |
+
self.itemsize = itemsize
|
| 193 |
+
self.blocksize = blocksize
|
| 194 |
+
|
| 195 |
+
def encode(self, buf):
|
| 196 |
+
return imagecodecs.bitshuffle_encode(
|
| 197 |
+
buf, itemsize=self.itemsize, blocksize=self.blocksize
|
| 198 |
+
).tobytes()
|
| 199 |
+
|
| 200 |
+
def decode(self, buf, out=None):
|
| 201 |
+
return imagecodecs.bitshuffle_decode(
|
| 202 |
+
buf,
|
| 203 |
+
itemsize=self.itemsize,
|
| 204 |
+
blocksize=self.blocksize,
|
| 205 |
+
out=_flat(out),
|
| 206 |
+
)
|
| 207 |
+
|
| 208 |
+
|
| 209 |
+
class Blosc(Codec):
|
| 210 |
+
"""Blosc codec for numcodecs."""
|
| 211 |
+
|
| 212 |
+
codec_id = 'imagecodecs_blosc'
|
| 213 |
+
|
| 214 |
+
def __init__(
|
| 215 |
+
self,
|
| 216 |
+
level=None,
|
| 217 |
+
compressor=None,
|
| 218 |
+
typesize=None,
|
| 219 |
+
blocksize=None,
|
| 220 |
+
shuffle=None,
|
| 221 |
+
numthreads=None,
|
| 222 |
+
):
|
| 223 |
+
self.level = level
|
| 224 |
+
self.compressor = compressor
|
| 225 |
+
self.typesize = typesize
|
| 226 |
+
self.blocksize = blocksize
|
| 227 |
+
self.shuffle = shuffle
|
| 228 |
+
self.numthreads = numthreads
|
| 229 |
+
|
| 230 |
+
def encode(self, buf):
|
| 231 |
+
buf = protective_squeeze(numpy.asarray(buf))
|
| 232 |
+
return imagecodecs.blosc_encode(
|
| 233 |
+
buf,
|
| 234 |
+
level=self.level,
|
| 235 |
+
compressor=self.compressor,
|
| 236 |
+
typesize=self.typesize,
|
| 237 |
+
blocksize=self.blocksize,
|
| 238 |
+
shuffle=self.shuffle,
|
| 239 |
+
numthreads=self.numthreads,
|
| 240 |
+
)
|
| 241 |
+
|
| 242 |
+
def decode(self, buf, out=None):
|
| 243 |
+
return imagecodecs.blosc_decode(
|
| 244 |
+
buf, numthreads=self.numthreads, out=_flat(out)
|
| 245 |
+
)
|
| 246 |
+
|
| 247 |
+
|
| 248 |
+
class Blosc2(Codec):
|
| 249 |
+
"""Blosc2 codec for numcodecs."""
|
| 250 |
+
|
| 251 |
+
codec_id = 'imagecodecs_blosc2'
|
| 252 |
+
|
| 253 |
+
def __init__(
|
| 254 |
+
self,
|
| 255 |
+
level=None,
|
| 256 |
+
compressor=None,
|
| 257 |
+
typesize=None,
|
| 258 |
+
blocksize=None,
|
| 259 |
+
shuffle=None,
|
| 260 |
+
numthreads=None,
|
| 261 |
+
):
|
| 262 |
+
self.level = level
|
| 263 |
+
self.compressor = compressor
|
| 264 |
+
self.typesize = typesize
|
| 265 |
+
self.blocksize = blocksize
|
| 266 |
+
self.shuffle = shuffle
|
| 267 |
+
self.numthreads = numthreads
|
| 268 |
+
|
| 269 |
+
def encode(self, buf):
|
| 270 |
+
buf = protective_squeeze(numpy.asarray(buf))
|
| 271 |
+
return imagecodecs.blosc2_encode(
|
| 272 |
+
buf,
|
| 273 |
+
level=self.level,
|
| 274 |
+
compressor=self.compressor,
|
| 275 |
+
typesize=self.typesize,
|
| 276 |
+
blocksize=self.blocksize,
|
| 277 |
+
shuffle=self.shuffle,
|
| 278 |
+
numthreads=self.numthreads,
|
| 279 |
+
)
|
| 280 |
+
|
| 281 |
+
def decode(self, buf, out=None):
|
| 282 |
+
return imagecodecs.blosc2_decode(
|
| 283 |
+
buf, numthreads=self.numthreads, out=_flat(out)
|
| 284 |
+
)
|
| 285 |
+
|
| 286 |
+
|
| 287 |
+
class Brotli(Codec):
|
| 288 |
+
"""Brotli codec for numcodecs."""
|
| 289 |
+
|
| 290 |
+
codec_id = 'imagecodecs_brotli'
|
| 291 |
+
|
| 292 |
+
def __init__(self, level=None, mode=None, lgwin=None):
|
| 293 |
+
self.level = level
|
| 294 |
+
self.mode = mode
|
| 295 |
+
self.lgwin = lgwin
|
| 296 |
+
|
| 297 |
+
def encode(self, buf):
|
| 298 |
+
return imagecodecs.brotli_encode(
|
| 299 |
+
buf, level=self.level, mode=self.mode, lgwin=self.lgwin
|
| 300 |
+
)
|
| 301 |
+
|
| 302 |
+
def decode(self, buf, out=None):
|
| 303 |
+
return imagecodecs.brotli_decode(buf, out=_flat(out))
|
| 304 |
+
|
| 305 |
+
|
| 306 |
+
class ByteShuffle(Codec):
|
| 307 |
+
"""ByteShuffle codec for numcodecs."""
|
| 308 |
+
|
| 309 |
+
codec_id = 'imagecodecs_byteshuffle'
|
| 310 |
+
|
| 311 |
+
def __init__(
|
| 312 |
+
self, shape, dtype, axis=-1, dist=1, delta=False, reorder=False
|
| 313 |
+
):
|
| 314 |
+
self.shape = tuple(shape)
|
| 315 |
+
self.dtype = numpy.dtype(dtype).str
|
| 316 |
+
self.axis = axis
|
| 317 |
+
self.dist = dist
|
| 318 |
+
self.delta = bool(delta)
|
| 319 |
+
self.reorder = bool(reorder)
|
| 320 |
+
|
| 321 |
+
def encode(self, buf):
|
| 322 |
+
buf = protective_squeeze(numpy.asarray(buf))
|
| 323 |
+
assert buf.shape == self.shape
|
| 324 |
+
assert buf.dtype == self.dtype
|
| 325 |
+
return imagecodecs.byteshuffle_encode(
|
| 326 |
+
buf,
|
| 327 |
+
axis=self.axis,
|
| 328 |
+
dist=self.dist,
|
| 329 |
+
delta=self.delta,
|
| 330 |
+
reorder=self.reorder,
|
| 331 |
+
).tobytes()
|
| 332 |
+
|
| 333 |
+
def decode(self, buf, out=None):
|
| 334 |
+
if not isinstance(buf, numpy.ndarray):
|
| 335 |
+
buf = numpy.frombuffer(buf, dtype=self.dtype).reshape(*self.shape)
|
| 336 |
+
return imagecodecs.byteshuffle_decode(
|
| 337 |
+
buf,
|
| 338 |
+
axis=self.axis,
|
| 339 |
+
dist=self.dist,
|
| 340 |
+
delta=self.delta,
|
| 341 |
+
reorder=self.reorder,
|
| 342 |
+
out=out,
|
| 343 |
+
)
|
| 344 |
+
|
| 345 |
+
|
| 346 |
+
class Bz2(Codec):
|
| 347 |
+
"""Bz2 codec for numcodecs."""
|
| 348 |
+
|
| 349 |
+
codec_id = 'imagecodecs_bz2'
|
| 350 |
+
|
| 351 |
+
def __init__(self, level=None):
|
| 352 |
+
self.level = level
|
| 353 |
+
|
| 354 |
+
def encode(self, buf):
|
| 355 |
+
return imagecodecs.bz2_encode(buf, level=self.level)
|
| 356 |
+
|
| 357 |
+
def decode(self, buf, out=None):
|
| 358 |
+
return imagecodecs.bz2_decode(buf, out=_flat(out))
|
| 359 |
+
|
| 360 |
+
|
| 361 |
+
class Cms(Codec):
|
| 362 |
+
"""CMS codec for numcodecs."""
|
| 363 |
+
|
| 364 |
+
codec_id = 'imagecodecs_cms'
|
| 365 |
+
|
| 366 |
+
def __init__(self, *args, **kwargs):
|
| 367 |
+
pass
|
| 368 |
+
|
| 369 |
+
def encode(self, buf, out=None):
|
| 370 |
+
# return imagecodecs.cms_transform(buf)
|
| 371 |
+
raise NotImplementedError
|
| 372 |
+
|
| 373 |
+
def decode(self, buf, out=None):
|
| 374 |
+
# return imagecodecs.cms_transform(buf)
|
| 375 |
+
raise NotImplementedError
|
| 376 |
+
|
| 377 |
+
|
| 378 |
+
class Deflate(Codec):
|
| 379 |
+
"""Deflate codec for numcodecs."""
|
| 380 |
+
|
| 381 |
+
codec_id = 'imagecodecs_deflate'
|
| 382 |
+
|
| 383 |
+
def __init__(self, level=None, raw=False):
|
| 384 |
+
self.level = level
|
| 385 |
+
self.raw = bool(raw)
|
| 386 |
+
|
| 387 |
+
def encode(self, buf):
|
| 388 |
+
return imagecodecs.deflate_encode(buf, level=self.level, raw=self.raw)
|
| 389 |
+
|
| 390 |
+
def decode(self, buf, out=None):
|
| 391 |
+
return imagecodecs.deflate_decode(buf, out=_flat(out), raw=self.raw)
|
| 392 |
+
|
| 393 |
+
|
| 394 |
+
class Delta(Codec):
|
| 395 |
+
"""Delta codec for numcodecs."""
|
| 396 |
+
|
| 397 |
+
codec_id = 'imagecodecs_delta'
|
| 398 |
+
|
| 399 |
+
def __init__(self, shape=None, dtype=None, axis=-1, dist=1):
|
| 400 |
+
self.shape = None if shape is None else tuple(shape)
|
| 401 |
+
self.dtype = None if dtype is None else numpy.dtype(dtype).str
|
| 402 |
+
self.axis = axis
|
| 403 |
+
self.dist = dist
|
| 404 |
+
|
| 405 |
+
def encode(self, buf):
|
| 406 |
+
if self.shape is not None or self.dtype is not None:
|
| 407 |
+
buf = protective_squeeze(numpy.asarray(buf))
|
| 408 |
+
assert buf.shape == self.shape
|
| 409 |
+
assert buf.dtype == self.dtype
|
| 410 |
+
return imagecodecs.delta_encode(
|
| 411 |
+
buf, axis=self.axis, dist=self.dist
|
| 412 |
+
).tobytes()
|
| 413 |
+
|
| 414 |
+
def decode(self, buf, out=None):
|
| 415 |
+
if self.shape is not None or self.dtype is not None:
|
| 416 |
+
buf = numpy.frombuffer(buf, dtype=self.dtype).reshape(*self.shape)
|
| 417 |
+
return imagecodecs.delta_decode(
|
| 418 |
+
buf, axis=self.axis, dist=self.dist, out=out
|
| 419 |
+
)
|
| 420 |
+
|
| 421 |
+
|
| 422 |
+
class Float24(Codec):
|
| 423 |
+
"""Float24 codec for numcodecs."""
|
| 424 |
+
|
| 425 |
+
codec_id = 'imagecodecs_float24'
|
| 426 |
+
|
| 427 |
+
def __init__(self, byteorder=None, rounding=None):
|
| 428 |
+
self.byteorder = byteorder
|
| 429 |
+
self.rounding = rounding
|
| 430 |
+
|
| 431 |
+
def encode(self, buf):
|
| 432 |
+
buf = protective_squeeze(numpy.asarray(buf))
|
| 433 |
+
return imagecodecs.float24_encode(
|
| 434 |
+
buf, byteorder=self.byteorder, rounding=self.rounding
|
| 435 |
+
)
|
| 436 |
+
|
| 437 |
+
def decode(self, buf, out=None):
|
| 438 |
+
return imagecodecs.float24_decode(
|
| 439 |
+
buf, byteorder=self.byteorder, out=out
|
| 440 |
+
)
|
| 441 |
+
|
| 442 |
+
|
| 443 |
+
class FloatPred(Codec):
|
| 444 |
+
"""Floating Point Predictor codec for numcodecs."""
|
| 445 |
+
|
| 446 |
+
codec_id = 'imagecodecs_floatpred'
|
| 447 |
+
|
| 448 |
+
def __init__(self, shape, dtype, axis=-1, dist=1):
|
| 449 |
+
self.shape = tuple(shape)
|
| 450 |
+
self.dtype = numpy.dtype(dtype).str
|
| 451 |
+
self.axis = axis
|
| 452 |
+
self.dist = dist
|
| 453 |
+
|
| 454 |
+
def encode(self, buf):
|
| 455 |
+
buf = protective_squeeze(numpy.asarray(buf))
|
| 456 |
+
assert buf.shape == self.shape
|
| 457 |
+
assert buf.dtype == self.dtype
|
| 458 |
+
return imagecodecs.floatpred_encode(
|
| 459 |
+
buf, axis=self.axis, dist=self.dist
|
| 460 |
+
).tobytes()
|
| 461 |
+
|
| 462 |
+
def decode(self, buf, out=None):
|
| 463 |
+
if not isinstance(buf, numpy.ndarray):
|
| 464 |
+
buf = numpy.frombuffer(buf, dtype=self.dtype).reshape(*self.shape)
|
| 465 |
+
return imagecodecs.floatpred_decode(
|
| 466 |
+
buf, axis=self.axis, dist=self.dist, out=out
|
| 467 |
+
)
|
| 468 |
+
|
| 469 |
+
|
| 470 |
+
class Gif(Codec):
|
| 471 |
+
"""GIF codec for numcodecs."""
|
| 472 |
+
|
| 473 |
+
codec_id = 'imagecodecs_gif'
|
| 474 |
+
|
| 475 |
+
def encode(self, buf):
|
| 476 |
+
buf = protective_squeeze(numpy.asarray(buf))
|
| 477 |
+
return imagecodecs.gif_encode(buf)
|
| 478 |
+
|
| 479 |
+
def decode(self, buf, out=None):
|
| 480 |
+
return imagecodecs.gif_decode(buf, asrgb=False, out=out)
|
| 481 |
+
|
| 482 |
+
|
| 483 |
+
class Heif(Codec):
|
| 484 |
+
"""HEIF codec for numcodecs."""
|
| 485 |
+
|
| 486 |
+
codec_id = 'imagecodecs_heif'
|
| 487 |
+
|
| 488 |
+
def __init__(
|
| 489 |
+
self,
|
| 490 |
+
level=None,
|
| 491 |
+
bitspersample=None,
|
| 492 |
+
photometric=None,
|
| 493 |
+
compression=None,
|
| 494 |
+
numthreads=None,
|
| 495 |
+
index=None,
|
| 496 |
+
):
|
| 497 |
+
self.level = level
|
| 498 |
+
self.bitspersample = bitspersample
|
| 499 |
+
self.photometric = photometric
|
| 500 |
+
self.compression = compression
|
| 501 |
+
self.numthreads = numthreads
|
| 502 |
+
self.index = index
|
| 503 |
+
|
| 504 |
+
def encode(self, buf):
|
| 505 |
+
buf = protective_squeeze(numpy.asarray(buf))
|
| 506 |
+
return imagecodecs.heif_encode(
|
| 507 |
+
buf,
|
| 508 |
+
level=self.level,
|
| 509 |
+
bitspersample=self.bitspersample,
|
| 510 |
+
photometric=self.photometric,
|
| 511 |
+
compression=self.compression,
|
| 512 |
+
numthreads=self.numthreads,
|
| 513 |
+
)
|
| 514 |
+
|
| 515 |
+
def decode(self, buf, out=None):
|
| 516 |
+
return imagecodecs.heif_decode(
|
| 517 |
+
buf,
|
| 518 |
+
index=self.index,
|
| 519 |
+
photometric=self.photometric,
|
| 520 |
+
numthreads=self.numthreads,
|
| 521 |
+
out=out,
|
| 522 |
+
)
|
| 523 |
+
|
| 524 |
+
|
| 525 |
+
class Jetraw(Codec):
|
| 526 |
+
"""Jetraw codec for numcodecs."""
|
| 527 |
+
|
| 528 |
+
codec_id = 'imagecodecs_jetraw'
|
| 529 |
+
|
| 530 |
+
def __init__(
|
| 531 |
+
self,
|
| 532 |
+
shape,
|
| 533 |
+
identifier,
|
| 534 |
+
parameters=None,
|
| 535 |
+
verbosity=None,
|
| 536 |
+
errorbound=None,
|
| 537 |
+
):
|
| 538 |
+
self.shape = shape
|
| 539 |
+
self.identifier = identifier
|
| 540 |
+
self.errorbound = errorbound
|
| 541 |
+
imagecodecs.jetraw_init(parameters, verbosity)
|
| 542 |
+
|
| 543 |
+
def encode(self, buf):
|
| 544 |
+
return imagecodecs.jetraw_encode(
|
| 545 |
+
buf, identifier=self.identifier, errorbound=self.errorbound
|
| 546 |
+
)
|
| 547 |
+
|
| 548 |
+
def decode(self, buf, out=None):
|
| 549 |
+
if out is None:
|
| 550 |
+
out = numpy.empty(self.shape, numpy.uint16)
|
| 551 |
+
return imagecodecs.jetraw_decode(buf, out=out)
|
| 552 |
+
|
| 553 |
+
|
| 554 |
+
class Jpeg(Codec):
|
| 555 |
+
"""JPEG codec for numcodecs."""
|
| 556 |
+
|
| 557 |
+
codec_id = 'imagecodecs_jpeg'
|
| 558 |
+
|
| 559 |
+
def __init__(
|
| 560 |
+
self,
|
| 561 |
+
bitspersample=None,
|
| 562 |
+
tables=None,
|
| 563 |
+
header=None,
|
| 564 |
+
colorspace_data=None,
|
| 565 |
+
colorspace_jpeg=None,
|
| 566 |
+
level=None,
|
| 567 |
+
subsampling=None,
|
| 568 |
+
optimize=None,
|
| 569 |
+
smoothing=None,
|
| 570 |
+
):
|
| 571 |
+
self.tables = tables
|
| 572 |
+
self.header = header
|
| 573 |
+
self.bitspersample = bitspersample
|
| 574 |
+
self.colorspace_data = colorspace_data
|
| 575 |
+
self.colorspace_jpeg = colorspace_jpeg
|
| 576 |
+
self.level = level
|
| 577 |
+
self.subsampling = subsampling
|
| 578 |
+
self.optimize = optimize
|
| 579 |
+
self.smoothing = smoothing
|
| 580 |
+
|
| 581 |
+
def encode(self, buf):
|
| 582 |
+
buf = protective_squeeze(numpy.asarray(buf))
|
| 583 |
+
return imagecodecs.jpeg_encode(
|
| 584 |
+
buf,
|
| 585 |
+
level=self.level,
|
| 586 |
+
colorspace=self.colorspace_data,
|
| 587 |
+
outcolorspace=self.colorspace_jpeg,
|
| 588 |
+
subsampling=self.subsampling,
|
| 589 |
+
optimize=self.optimize,
|
| 590 |
+
smoothing=self.smoothing,
|
| 591 |
+
)
|
| 592 |
+
|
| 593 |
+
def decode(self, buf, out=None):
|
| 594 |
+
out_shape = None
|
| 595 |
+
if out is not None:
|
| 596 |
+
out_shape = out.shape
|
| 597 |
+
out = protective_squeeze(out)
|
| 598 |
+
img = imagecodecs.jpeg_decode(
|
| 599 |
+
buf,
|
| 600 |
+
bitspersample=self.bitspersample,
|
| 601 |
+
tables=self.tables,
|
| 602 |
+
header=self.header,
|
| 603 |
+
colorspace=self.colorspace_jpeg,
|
| 604 |
+
outcolorspace=self.colorspace_data,
|
| 605 |
+
out=out,
|
| 606 |
+
)
|
| 607 |
+
if out_shape is not None:
|
| 608 |
+
img = img.reshape(out_shape)
|
| 609 |
+
return img
|
| 610 |
+
|
| 611 |
+
def get_config(self):
|
| 612 |
+
"""Return dictionary holding configuration parameters."""
|
| 613 |
+
config = dict(id=self.codec_id)
|
| 614 |
+
for key in self.__dict__:
|
| 615 |
+
if not key.startswith('_'):
|
| 616 |
+
value = getattr(self, key)
|
| 617 |
+
if value is not None and key in ('header', 'tables'):
|
| 618 |
+
import base64
|
| 619 |
+
|
| 620 |
+
value = base64.b64encode(value).decode()
|
| 621 |
+
config[key] = value
|
| 622 |
+
return config
|
| 623 |
+
|
| 624 |
+
@classmethod
|
| 625 |
+
def from_config(cls, config):
|
| 626 |
+
"""Instantiate codec from configuration object."""
|
| 627 |
+
for key in ('header', 'tables'):
|
| 628 |
+
value = config.get(key, None)
|
| 629 |
+
if value is not None and isinstance(value, str):
|
| 630 |
+
import base64
|
| 631 |
+
|
| 632 |
+
config[key] = base64.b64decode(value.encode())
|
| 633 |
+
return cls(**config)
|
| 634 |
+
|
| 635 |
+
|
| 636 |
+
class Jpeg2k(Codec):
|
| 637 |
+
"""JPEG 2000 codec for numcodecs."""
|
| 638 |
+
|
| 639 |
+
codec_id = 'imagecodecs_jpeg2k'
|
| 640 |
+
|
| 641 |
+
def __init__(
|
| 642 |
+
self,
|
| 643 |
+
level=None,
|
| 644 |
+
codecformat=None,
|
| 645 |
+
colorspace=None,
|
| 646 |
+
tile=None,
|
| 647 |
+
reversible=None,
|
| 648 |
+
bitspersample=None,
|
| 649 |
+
resolutions=None,
|
| 650 |
+
numthreads=None,
|
| 651 |
+
verbose=0,
|
| 652 |
+
):
|
| 653 |
+
self.level = level
|
| 654 |
+
self.codecformat = codecformat
|
| 655 |
+
self.colorspace = colorspace
|
| 656 |
+
self.tile = None if tile is None else tuple(tile)
|
| 657 |
+
self.reversible = reversible
|
| 658 |
+
self.bitspersample = bitspersample
|
| 659 |
+
self.resolutions = resolutions
|
| 660 |
+
self.numthreads = numthreads
|
| 661 |
+
self.verbose = verbose
|
| 662 |
+
|
| 663 |
+
def encode(self, buf):
|
| 664 |
+
buf = protective_squeeze(numpy.asarray(buf))
|
| 665 |
+
return imagecodecs.jpeg2k_encode(
|
| 666 |
+
buf,
|
| 667 |
+
level=self.level,
|
| 668 |
+
codecformat=self.codecformat,
|
| 669 |
+
colorspace=self.colorspace,
|
| 670 |
+
tile=self.tile,
|
| 671 |
+
reversible=self.reversible,
|
| 672 |
+
bitspersample=self.bitspersample,
|
| 673 |
+
resolutions=self.resolutions,
|
| 674 |
+
numthreads=self.numthreads,
|
| 675 |
+
verbose=self.verbose,
|
| 676 |
+
)
|
| 677 |
+
|
| 678 |
+
def decode(self, buf, out=None):
|
| 679 |
+
return imagecodecs.jpeg2k_decode(
|
| 680 |
+
buf, verbose=self.verbose, numthreads=self.numthreads, out=out
|
| 681 |
+
)
|
| 682 |
+
|
| 683 |
+
|
| 684 |
+
class JpegLs(Codec):
|
| 685 |
+
"""JPEG LS codec for numcodecs."""
|
| 686 |
+
|
| 687 |
+
codec_id = 'imagecodecs_jpegls'
|
| 688 |
+
|
| 689 |
+
def __init__(self, level=None):
|
| 690 |
+
self.level = level
|
| 691 |
+
|
| 692 |
+
def encode(self, buf):
|
| 693 |
+
buf = protective_squeeze(numpy.asarray(buf))
|
| 694 |
+
return imagecodecs.jpegls_encode(buf, level=self.level)
|
| 695 |
+
|
| 696 |
+
def decode(self, buf, out=None):
|
| 697 |
+
return imagecodecs.jpegls_decode(buf, out=out)
|
| 698 |
+
|
| 699 |
+
|
| 700 |
+
class JpegXl(Codec):
|
| 701 |
+
"""JPEG XL codec for numcodecs."""
|
| 702 |
+
|
| 703 |
+
codec_id = 'imagecodecs_jpegxl'
|
| 704 |
+
|
| 705 |
+
def __init__(
|
| 706 |
+
self,
|
| 707 |
+
# encode
|
| 708 |
+
level=None,
|
| 709 |
+
effort=None,
|
| 710 |
+
distance=None,
|
| 711 |
+
lossless=None,
|
| 712 |
+
decodingspeed=None,
|
| 713 |
+
photometric=None,
|
| 714 |
+
planar=None,
|
| 715 |
+
usecontainer=None,
|
| 716 |
+
# decode
|
| 717 |
+
index=None,
|
| 718 |
+
keeporientation=None,
|
| 719 |
+
# both
|
| 720 |
+
numthreads=None,
|
| 721 |
+
):
|
| 722 |
+
"""
|
| 723 |
+
Return JPEG XL image from numpy array.
|
| 724 |
+
Float must be in nominal range 0..1.
|
| 725 |
+
|
| 726 |
+
Currently L, LA, RGB, RGBA images are supported in contig mode.
|
| 727 |
+
Extra channels are only supported for grayscale images in planar mode.
|
| 728 |
+
|
| 729 |
+
Parameters
|
| 730 |
+
----------
|
| 731 |
+
level : Default to None, i.e. not overwriting lossess and decodingspeed options.
|
| 732 |
+
When < 0: Use lossless compression
|
| 733 |
+
When in [0,1,2,3,4]: Sets the decoding speed tier for the provided options.
|
| 734 |
+
Minimum is 0 (slowest to decode, best quality/density), and maximum
|
| 735 |
+
is 4 (fastest to decode, at the cost of some quality/density).
|
| 736 |
+
effort : Default to 3.
|
| 737 |
+
Sets encoder effort/speed level without affecting decoding speed.
|
| 738 |
+
Valid values are, from faster to slower speed: 1:lightning 2:thunder
|
| 739 |
+
3:falcon 4:cheetah 5:hare 6:wombat 7:squirrel 8:kitten 9:tortoise.
|
| 740 |
+
Speed: lightning, thunder, falcon, cheetah, hare, wombat, squirrel, kitten, tortoise
|
| 741 |
+
control the encoder effort in ascending order.
|
| 742 |
+
This also affects memory usage: using lower effort will typically reduce memory
|
| 743 |
+
consumption during encoding.
|
| 744 |
+
lightning and thunder are fast modes useful for lossless mode (modular).
|
| 745 |
+
falcon disables all of the following tools.
|
| 746 |
+
cheetah enables coefficient reordering, context clustering, and heuristics for selecting DCT sizes and quantization steps.
|
| 747 |
+
hare enables Gaborish filtering, chroma from luma, and an initial estimate of quantization steps.
|
| 748 |
+
wombat enables error diffusion quantization and full DCT size selection heuristics.
|
| 749 |
+
squirrel (default) enables dots, patches, and spline detection, and full context clustering.
|
| 750 |
+
kitten optimizes the adaptive quantization for a psychovisual metric.
|
| 751 |
+
tortoise enables a more thorough adaptive quantization search.
|
| 752 |
+
distance : Default to 1.0
|
| 753 |
+
Sets the distance level for lossy compression: target max butteraugli distance,
|
| 754 |
+
lower = higher quality. Range: 0 .. 15. 0.0 = mathematically lossless
|
| 755 |
+
(however, use JxlEncoderSetFrameLossless instead to use true lossless,
|
| 756 |
+
as setting distance to 0 alone is not the only requirement).
|
| 757 |
+
1.0 = visually lossless. Recommended range: 0.5 .. 3.0.
|
| 758 |
+
lossess : Default to False.
|
| 759 |
+
Use lossess encoding.
|
| 760 |
+
decodingspeed : Default to 0.
|
| 761 |
+
Duplicate to level. [0,4]
|
| 762 |
+
photometric : Return JxlColorSpace value.
|
| 763 |
+
Default logic is quite complicated but works most of the time.
|
| 764 |
+
Accepted value:
|
| 765 |
+
int: [-1,3]
|
| 766 |
+
str: ['RGB',
|
| 767 |
+
'WHITEISZERO', 'MINISWHITE',
|
| 768 |
+
'BLACKISZERO', 'MINISBLACK', 'GRAY',
|
| 769 |
+
'XYB', 'KNOWN']
|
| 770 |
+
planar : Enable multi-channel mode.
|
| 771 |
+
Default to false.
|
| 772 |
+
usecontainer :
|
| 773 |
+
Forces the encoder to use the box-based container format (BMFF)
|
| 774 |
+
even when not necessary.
|
| 775 |
+
When using JxlEncoderUseBoxes, JxlEncoderStoreJPEGMetadata or
|
| 776 |
+
JxlEncoderSetCodestreamLevel with level 10, the encoder will
|
| 777 |
+
automatically also use the container format, it is not necessary
|
| 778 |
+
to use JxlEncoderUseContainer for those use cases.
|
| 779 |
+
By default this setting is disabled.
|
| 780 |
+
index : Selectively decode frames for animation.
|
| 781 |
+
Default to 0, decode all frames.
|
| 782 |
+
When set to > 0, decode that frame index only.
|
| 783 |
+
keeporientation :
|
| 784 |
+
Enables or disables preserving of as-in-bitstream pixeldata orientation.
|
| 785 |
+
Some images are encoded with an Orientation tag indicating that the
|
| 786 |
+
decoder must perform a rotation and/or mirroring to the encoded image data.
|
| 787 |
+
|
| 788 |
+
If skip_reorientation is JXL_FALSE (the default): the decoder will apply
|
| 789 |
+
the transformation from the orientation setting, hence rendering the image
|
| 790 |
+
according to its specified intent. When producing a JxlBasicInfo, the decoder
|
| 791 |
+
will always set the orientation field to JXL_ORIENT_IDENTITY (matching the
|
| 792 |
+
returned pixel data) and also align xsize and ysize so that they correspond
|
| 793 |
+
to the width and the height of the returned pixel data.
|
| 794 |
+
|
| 795 |
+
If skip_reorientation is JXL_TRUE: the decoder will skip applying the
|
| 796 |
+
transformation from the orientation setting, returning the image in
|
| 797 |
+
the as-in-bitstream pixeldata orientation. This may be faster to decode
|
| 798 |
+
since the decoder doesnt have to apply the transformation, but can
|
| 799 |
+
cause wrong display of the image if the orientation tag is not correctly
|
| 800 |
+
taken into account by the user.
|
| 801 |
+
|
| 802 |
+
By default, this option is disabled, and the returned pixel data is
|
| 803 |
+
re-oriented according to the images Orientation setting.
|
| 804 |
+
threads : Default to 1.
|
| 805 |
+
If <= 0, use all cores.
|
| 806 |
+
If > 32, clipped to 32.
|
| 807 |
+
"""
|
| 808 |
+
|
| 809 |
+
self.level = level
|
| 810 |
+
self.effort = effort
|
| 811 |
+
self.distance = distance
|
| 812 |
+
self.lossless = bool(lossless)
|
| 813 |
+
self.decodingspeed = decodingspeed
|
| 814 |
+
self.photometric = photometric
|
| 815 |
+
self.planar = planar
|
| 816 |
+
self.usecontainer = usecontainer
|
| 817 |
+
self.index = index
|
| 818 |
+
self.keeporientation = keeporientation
|
| 819 |
+
self.numthreads = numthreads
|
| 820 |
+
|
| 821 |
+
def encode(self, buf):
|
| 822 |
+
# TODO: only squeeze all but last dim
|
| 823 |
+
buf = protective_squeeze(numpy.asarray(buf))
|
| 824 |
+
return imagecodecs.jpegxl_encode(
|
| 825 |
+
buf,
|
| 826 |
+
level=self.level,
|
| 827 |
+
effort=self.effort,
|
| 828 |
+
distance=self.distance,
|
| 829 |
+
lossless=self.lossless,
|
| 830 |
+
decodingspeed=self.decodingspeed,
|
| 831 |
+
photometric=self.photometric,
|
| 832 |
+
planar=self.planar,
|
| 833 |
+
usecontainer=self.usecontainer,
|
| 834 |
+
numthreads=self.numthreads,
|
| 835 |
+
)
|
| 836 |
+
|
| 837 |
+
def decode(self, buf, out=None):
|
| 838 |
+
return imagecodecs.jpegxl_decode(
|
| 839 |
+
buf,
|
| 840 |
+
index=self.index,
|
| 841 |
+
keeporientation=self.keeporientation,
|
| 842 |
+
numthreads=self.numthreads,
|
| 843 |
+
out=out,
|
| 844 |
+
)
|
| 845 |
+
|
| 846 |
+
|
| 847 |
+
class JpegXr(Codec):
|
| 848 |
+
"""JPEG XR codec for numcodecs."""
|
| 849 |
+
|
| 850 |
+
codec_id = 'imagecodecs_jpegxr'
|
| 851 |
+
|
| 852 |
+
def __init__(
|
| 853 |
+
self,
|
| 854 |
+
level=None,
|
| 855 |
+
photometric=None,
|
| 856 |
+
hasalpha=None,
|
| 857 |
+
resolution=None,
|
| 858 |
+
fp2int=None,
|
| 859 |
+
):
|
| 860 |
+
self.level = level
|
| 861 |
+
self.photometric = photometric
|
| 862 |
+
self.hasalpha = hasalpha
|
| 863 |
+
self.resolution = resolution
|
| 864 |
+
self.fp2int = fp2int
|
| 865 |
+
|
| 866 |
+
def encode(self, buf):
|
| 867 |
+
buf = protective_squeeze(numpy.asarray(buf))
|
| 868 |
+
return imagecodecs.jpegxr_encode(
|
| 869 |
+
buf,
|
| 870 |
+
level=self.level,
|
| 871 |
+
photometric=self.photometric,
|
| 872 |
+
hasalpha=self.hasalpha,
|
| 873 |
+
resolution=self.resolution,
|
| 874 |
+
)
|
| 875 |
+
|
| 876 |
+
def decode(self, buf, out=None):
|
| 877 |
+
return imagecodecs.jpegxr_decode(buf, fp2int=self.fp2int, out=out)
|
| 878 |
+
|
| 879 |
+
|
| 880 |
+
class Lerc(Codec):
|
| 881 |
+
"""LERC codec for numcodecs."""
|
| 882 |
+
|
| 883 |
+
codec_id = 'imagecodecs_lerc'
|
| 884 |
+
|
| 885 |
+
def __init__(self, level=None, version=None, planar=None):
|
| 886 |
+
self.level = level
|
| 887 |
+
self.version = version
|
| 888 |
+
self.planar = bool(planar)
|
| 889 |
+
# TODO: support mask?
|
| 890 |
+
# self.mask = None
|
| 891 |
+
|
| 892 |
+
def encode(self, buf):
|
| 893 |
+
buf = protective_squeeze(numpy.asarray(buf))
|
| 894 |
+
return imagecodecs.lerc_encode(
|
| 895 |
+
buf,
|
| 896 |
+
level=self.level,
|
| 897 |
+
version=self.version,
|
| 898 |
+
planar=self.planar,
|
| 899 |
+
)
|
| 900 |
+
|
| 901 |
+
def decode(self, buf, out=None):
|
| 902 |
+
return imagecodecs.lerc_decode(buf, out=out)
|
| 903 |
+
|
| 904 |
+
|
| 905 |
+
class Ljpeg(Codec):
|
| 906 |
+
"""LJPEG codec for numcodecs."""
|
| 907 |
+
|
| 908 |
+
codec_id = 'imagecodecs_ljpeg'
|
| 909 |
+
|
| 910 |
+
def __init__(self, bitspersample=None):
|
| 911 |
+
self.bitspersample = bitspersample
|
| 912 |
+
|
| 913 |
+
def encode(self, buf):
|
| 914 |
+
buf = protective_squeeze(numpy.asarray(buf))
|
| 915 |
+
return imagecodecs.ljpeg_encode(buf, bitspersample=self.bitspersample)
|
| 916 |
+
|
| 917 |
+
def decode(self, buf, out=None):
|
| 918 |
+
return imagecodecs.ljpeg_decode(buf, out=out)
|
| 919 |
+
|
| 920 |
+
|
| 921 |
+
class Lz4(Codec):
|
| 922 |
+
"""LZ4 codec for numcodecs."""
|
| 923 |
+
|
| 924 |
+
codec_id = 'imagecodecs_lz4'
|
| 925 |
+
|
| 926 |
+
def __init__(self, level=None, hc=False, header=True):
|
| 927 |
+
self.level = level
|
| 928 |
+
self.hc = hc
|
| 929 |
+
self.header = bool(header)
|
| 930 |
+
|
| 931 |
+
def encode(self, buf):
|
| 932 |
+
return imagecodecs.lz4_encode(
|
| 933 |
+
buf, level=self.level, hc=self.hc, header=self.header
|
| 934 |
+
)
|
| 935 |
+
|
| 936 |
+
def decode(self, buf, out=None):
|
| 937 |
+
return imagecodecs.lz4_decode(buf, header=self.header, out=_flat(out))
|
| 938 |
+
|
| 939 |
+
|
| 940 |
+
class Lz4f(Codec):
|
| 941 |
+
"""LZ4F codec for numcodecs."""
|
| 942 |
+
|
| 943 |
+
codec_id = 'imagecodecs_lz4f'
|
| 944 |
+
|
| 945 |
+
def __init__(
|
| 946 |
+
self,
|
| 947 |
+
level=None,
|
| 948 |
+
blocksizeid=False,
|
| 949 |
+
contentchecksum=None,
|
| 950 |
+
blockchecksum=None,
|
| 951 |
+
):
|
| 952 |
+
self.level = level
|
| 953 |
+
self.blocksizeid = blocksizeid
|
| 954 |
+
self.contentchecksum = contentchecksum
|
| 955 |
+
self.blockchecksum = blockchecksum
|
| 956 |
+
|
| 957 |
+
def encode(self, buf):
|
| 958 |
+
return imagecodecs.lz4f_encode(
|
| 959 |
+
buf,
|
| 960 |
+
level=self.level,
|
| 961 |
+
blocksizeid=self.blocksizeid,
|
| 962 |
+
contentchecksum=self.contentchecksum,
|
| 963 |
+
blockchecksum=self.blockchecksum,
|
| 964 |
+
)
|
| 965 |
+
|
| 966 |
+
def decode(self, buf, out=None):
|
| 967 |
+
return imagecodecs.lz4f_decode(buf, out=_flat(out))
|
| 968 |
+
|
| 969 |
+
|
| 970 |
+
class Lzf(Codec):
|
| 971 |
+
"""LZF codec for numcodecs."""
|
| 972 |
+
|
| 973 |
+
codec_id = 'imagecodecs_lzf'
|
| 974 |
+
|
| 975 |
+
def __init__(self, header=True):
|
| 976 |
+
self.header = bool(header)
|
| 977 |
+
|
| 978 |
+
def encode(self, buf):
|
| 979 |
+
return imagecodecs.lzf_encode(buf, header=self.header)
|
| 980 |
+
|
| 981 |
+
def decode(self, buf, out=None):
|
| 982 |
+
return imagecodecs.lzf_decode(buf, header=self.header, out=_flat(out))
|
| 983 |
+
|
| 984 |
+
|
| 985 |
+
class Lzma(Codec):
|
| 986 |
+
"""LZMA codec for numcodecs."""
|
| 987 |
+
|
| 988 |
+
codec_id = 'imagecodecs_lzma'
|
| 989 |
+
|
| 990 |
+
def __init__(self, level=None):
|
| 991 |
+
self.level = level
|
| 992 |
+
|
| 993 |
+
def encode(self, buf):
|
| 994 |
+
return imagecodecs.lzma_encode(buf, level=self.level)
|
| 995 |
+
|
| 996 |
+
def decode(self, buf, out=None):
|
| 997 |
+
return imagecodecs.lzma_decode(buf, out=_flat(out))
|
| 998 |
+
|
| 999 |
+
|
| 1000 |
+
class Lzw(Codec):
|
| 1001 |
+
"""LZW codec for numcodecs."""
|
| 1002 |
+
|
| 1003 |
+
codec_id = 'imagecodecs_lzw'
|
| 1004 |
+
|
| 1005 |
+
def encode(self, buf):
|
| 1006 |
+
return imagecodecs.lzw_encode(buf)
|
| 1007 |
+
|
| 1008 |
+
def decode(self, buf, out=None):
|
| 1009 |
+
return imagecodecs.lzw_decode(buf, out=_flat(out))
|
| 1010 |
+
|
| 1011 |
+
|
| 1012 |
+
class PackBits(Codec):
|
| 1013 |
+
"""PackBits codec for numcodecs."""
|
| 1014 |
+
|
| 1015 |
+
codec_id = 'imagecodecs_packbits'
|
| 1016 |
+
|
| 1017 |
+
def __init__(self, axis=None):
|
| 1018 |
+
self.axis = axis
|
| 1019 |
+
|
| 1020 |
+
def encode(self, buf):
|
| 1021 |
+
if not isinstance(buf, (bytes, bytearray)):
|
| 1022 |
+
buf = protective_squeeze(numpy.asarray(buf))
|
| 1023 |
+
return imagecodecs.packbits_encode(buf, axis=self.axis)
|
| 1024 |
+
|
| 1025 |
+
def decode(self, buf, out=None):
|
| 1026 |
+
return imagecodecs.packbits_decode(buf, out=_flat(out))
|
| 1027 |
+
|
| 1028 |
+
|
| 1029 |
+
class Pglz(Codec):
|
| 1030 |
+
"""PGLZ codec for numcodecs."""
|
| 1031 |
+
|
| 1032 |
+
codec_id = 'imagecodecs_pglz'
|
| 1033 |
+
|
| 1034 |
+
def __init__(self, header=True, strategy=None):
|
| 1035 |
+
self.header = bool(header)
|
| 1036 |
+
self.strategy = strategy
|
| 1037 |
+
|
| 1038 |
+
def encode(self, buf):
|
| 1039 |
+
return imagecodecs.pglz_encode(
|
| 1040 |
+
buf, strategy=self.strategy, header=self.header
|
| 1041 |
+
)
|
| 1042 |
+
|
| 1043 |
+
def decode(self, buf, out=None):
|
| 1044 |
+
return imagecodecs.pglz_decode(buf, header=self.header, out=_flat(out))
|
| 1045 |
+
|
| 1046 |
+
|
| 1047 |
+
class Png(Codec):
|
| 1048 |
+
"""PNG codec for numcodecs."""
|
| 1049 |
+
|
| 1050 |
+
codec_id = 'imagecodecs_png'
|
| 1051 |
+
|
| 1052 |
+
def __init__(self, level=None):
|
| 1053 |
+
self.level = level
|
| 1054 |
+
|
| 1055 |
+
def encode(self, buf):
|
| 1056 |
+
buf = protective_squeeze(numpy.asarray(buf))
|
| 1057 |
+
return imagecodecs.png_encode(buf, level=self.level)
|
| 1058 |
+
|
| 1059 |
+
def decode(self, buf, out=None):
|
| 1060 |
+
return imagecodecs.png_decode(buf, out=out)
|
| 1061 |
+
|
| 1062 |
+
|
| 1063 |
+
class Qoi(Codec):
|
| 1064 |
+
"""QOI codec for numcodecs."""
|
| 1065 |
+
|
| 1066 |
+
codec_id = 'imagecodecs_qoi'
|
| 1067 |
+
|
| 1068 |
+
def __init__(self):
|
| 1069 |
+
pass
|
| 1070 |
+
|
| 1071 |
+
def encode(self, buf):
|
| 1072 |
+
buf = protective_squeeze(numpy.asarray(buf))
|
| 1073 |
+
return imagecodecs.qoi_encode(buf)
|
| 1074 |
+
|
| 1075 |
+
def decode(self, buf, out=None):
|
| 1076 |
+
return imagecodecs.qoi_decode(buf, out=out)
|
| 1077 |
+
|
| 1078 |
+
|
| 1079 |
+
class Rgbe(Codec):
|
| 1080 |
+
"""RGBE codec for numcodecs."""
|
| 1081 |
+
|
| 1082 |
+
codec_id = 'imagecodecs_rgbe'
|
| 1083 |
+
|
| 1084 |
+
def __init__(self, header=False, shape=None, rle=None):
|
| 1085 |
+
if not header and shape is None:
|
| 1086 |
+
raise ValueError('must specify data shape if no header')
|
| 1087 |
+
if shape and shape[-1] != 3:
|
| 1088 |
+
raise ValueError('invalid shape')
|
| 1089 |
+
self.shape = shape
|
| 1090 |
+
self.header = bool(header)
|
| 1091 |
+
self.rle = None if rle is None else bool(rle)
|
| 1092 |
+
|
| 1093 |
+
def encode(self, buf):
|
| 1094 |
+
buf = protective_squeeze(numpy.asarray(buf))
|
| 1095 |
+
return imagecodecs.rgbe_encode(buf, header=self.header, rle=self.rle)
|
| 1096 |
+
|
| 1097 |
+
def decode(self, buf, out=None):
|
| 1098 |
+
if out is None and not self.header:
|
| 1099 |
+
out = numpy.empty(self.shape, numpy.float32)
|
| 1100 |
+
return imagecodecs.rgbe_decode(
|
| 1101 |
+
buf, header=self.header, rle=self.rle, out=out
|
| 1102 |
+
)
|
| 1103 |
+
|
| 1104 |
+
|
| 1105 |
+
class Rcomp(Codec):
|
| 1106 |
+
"""Rcomp codec for numcodecs."""
|
| 1107 |
+
|
| 1108 |
+
codec_id = 'imagecodecs_rcomp'
|
| 1109 |
+
|
| 1110 |
+
def __init__(self, shape, dtype, nblock=None):
|
| 1111 |
+
self.shape = tuple(shape)
|
| 1112 |
+
self.dtype = numpy.dtype(dtype).str
|
| 1113 |
+
self.nblock = nblock
|
| 1114 |
+
|
| 1115 |
+
def encode(self, buf):
|
| 1116 |
+
return imagecodecs.rcomp_encode(buf, nblock=self.nblock)
|
| 1117 |
+
|
| 1118 |
+
def decode(self, buf, out=None):
|
| 1119 |
+
return imagecodecs.rcomp_decode(
|
| 1120 |
+
buf,
|
| 1121 |
+
shape=self.shape,
|
| 1122 |
+
dtype=self.dtype,
|
| 1123 |
+
nblock=self.nblock,
|
| 1124 |
+
out=out,
|
| 1125 |
+
)
|
| 1126 |
+
|
| 1127 |
+
|
| 1128 |
+
class Snappy(Codec):
|
| 1129 |
+
"""Snappy codec for numcodecs."""
|
| 1130 |
+
|
| 1131 |
+
codec_id = 'imagecodecs_snappy'
|
| 1132 |
+
|
| 1133 |
+
def encode(self, buf):
|
| 1134 |
+
return imagecodecs.snappy_encode(buf)
|
| 1135 |
+
|
| 1136 |
+
def decode(self, buf, out=None):
|
| 1137 |
+
return imagecodecs.snappy_decode(buf, out=_flat(out))
|
| 1138 |
+
|
| 1139 |
+
|
| 1140 |
+
class Spng(Codec):
|
| 1141 |
+
"""SPNG codec for numcodecs."""
|
| 1142 |
+
|
| 1143 |
+
codec_id = 'imagecodecs_spng'
|
| 1144 |
+
|
| 1145 |
+
def __init__(self, level=None):
|
| 1146 |
+
self.level = level
|
| 1147 |
+
|
| 1148 |
+
def encode(self, buf):
|
| 1149 |
+
buf = protective_squeeze(numpy.asarray(buf))
|
| 1150 |
+
return imagecodecs.spng_encode(buf, level=self.level)
|
| 1151 |
+
|
| 1152 |
+
def decode(self, buf, out=None):
|
| 1153 |
+
return imagecodecs.spng_decode(buf, out=out)
|
| 1154 |
+
|
| 1155 |
+
|
| 1156 |
+
class Tiff(Codec):
|
| 1157 |
+
"""TIFF codec for numcodecs."""
|
| 1158 |
+
|
| 1159 |
+
codec_id = 'imagecodecs_tiff'
|
| 1160 |
+
|
| 1161 |
+
def __init__(self, index=None, asrgb=None, verbose=0):
|
| 1162 |
+
self.index = index
|
| 1163 |
+
self.asrgb = bool(asrgb)
|
| 1164 |
+
self.verbose = verbose
|
| 1165 |
+
|
| 1166 |
+
def encode(self, buf):
|
| 1167 |
+
# TODO: not implemented
|
| 1168 |
+
buf = protective_squeeze(numpy.asarray(buf))
|
| 1169 |
+
return imagecodecs.tiff_encode(buf)
|
| 1170 |
+
|
| 1171 |
+
def decode(self, buf, out=None):
|
| 1172 |
+
return imagecodecs.tiff_decode(
|
| 1173 |
+
buf,
|
| 1174 |
+
index=self.index,
|
| 1175 |
+
asrgb=self.asrgb,
|
| 1176 |
+
verbose=self.verbose,
|
| 1177 |
+
out=out,
|
| 1178 |
+
)
|
| 1179 |
+
|
| 1180 |
+
|
| 1181 |
+
class Webp(Codec):
|
| 1182 |
+
"""WebP codec for numcodecs."""
|
| 1183 |
+
|
| 1184 |
+
codec_id = 'imagecodecs_webp'
|
| 1185 |
+
|
| 1186 |
+
def __init__(self, level=None, lossless=None, method=None, hasalpha=None):
|
| 1187 |
+
self.level = level
|
| 1188 |
+
self.hasalpha = bool(hasalpha)
|
| 1189 |
+
self.method = method
|
| 1190 |
+
self.lossless = lossless
|
| 1191 |
+
|
| 1192 |
+
def encode(self, buf):
|
| 1193 |
+
buf = protective_squeeze(numpy.asarray(buf))
|
| 1194 |
+
return imagecodecs.webp_encode(
|
| 1195 |
+
buf, level=self.level, lossless=self.lossless, method=self.method
|
| 1196 |
+
)
|
| 1197 |
+
|
| 1198 |
+
def decode(self, buf, out=None):
|
| 1199 |
+
return imagecodecs.webp_decode(buf, hasalpha=self.hasalpha, out=out)
|
| 1200 |
+
|
| 1201 |
+
|
| 1202 |
+
class Xor(Codec):
|
| 1203 |
+
"""XOR codec for numcodecs."""
|
| 1204 |
+
|
| 1205 |
+
codec_id = 'imagecodecs_xor'
|
| 1206 |
+
|
| 1207 |
+
def __init__(self, shape=None, dtype=None, axis=-1):
|
| 1208 |
+
self.shape = None if shape is None else tuple(shape)
|
| 1209 |
+
self.dtype = None if dtype is None else numpy.dtype(dtype).str
|
| 1210 |
+
self.axis = axis
|
| 1211 |
+
|
| 1212 |
+
def encode(self, buf):
|
| 1213 |
+
if self.shape is not None or self.dtype is not None:
|
| 1214 |
+
buf = protective_squeeze(numpy.asarray(buf))
|
| 1215 |
+
assert buf.shape == self.shape
|
| 1216 |
+
assert buf.dtype == self.dtype
|
| 1217 |
+
return imagecodecs.xor_encode(buf, axis=self.axis).tobytes()
|
| 1218 |
+
|
| 1219 |
+
def decode(self, buf, out=None):
|
| 1220 |
+
if self.shape is not None or self.dtype is not None:
|
| 1221 |
+
buf = numpy.frombuffer(buf, dtype=self.dtype).reshape(*self.shape)
|
| 1222 |
+
return imagecodecs.xor_decode(buf, axis=self.axis, out=_flat(out))
|
| 1223 |
+
|
| 1224 |
+
|
| 1225 |
+
class Zfp(Codec):
|
| 1226 |
+
"""ZFP codec for numcodecs."""
|
| 1227 |
+
|
| 1228 |
+
codec_id = 'imagecodecs_zfp'
|
| 1229 |
+
|
| 1230 |
+
def __init__(
|
| 1231 |
+
self,
|
| 1232 |
+
shape=None,
|
| 1233 |
+
dtype=None,
|
| 1234 |
+
strides=None,
|
| 1235 |
+
level=None,
|
| 1236 |
+
mode=None,
|
| 1237 |
+
execution=None,
|
| 1238 |
+
numthreads=None,
|
| 1239 |
+
chunksize=None,
|
| 1240 |
+
header=True,
|
| 1241 |
+
):
|
| 1242 |
+
if header:
|
| 1243 |
+
self.shape = None
|
| 1244 |
+
self.dtype = None
|
| 1245 |
+
self.strides = None
|
| 1246 |
+
elif shape is None or dtype is None:
|
| 1247 |
+
raise ValueError('invalid shape or dtype')
|
| 1248 |
+
else:
|
| 1249 |
+
self.shape = tuple(shape)
|
| 1250 |
+
self.dtype = numpy.dtype(dtype).str
|
| 1251 |
+
self.strides = None if strides is None else tuple(strides)
|
| 1252 |
+
self.level = level
|
| 1253 |
+
self.mode = mode
|
| 1254 |
+
self.execution = execution
|
| 1255 |
+
self.numthreads = numthreads
|
| 1256 |
+
self.chunksize = chunksize
|
| 1257 |
+
self.header = bool(header)
|
| 1258 |
+
|
| 1259 |
+
def encode(self, buf):
|
| 1260 |
+
buf = protective_squeeze(numpy.asarray(buf))
|
| 1261 |
+
if not self.header:
|
| 1262 |
+
assert buf.shape == self.shape
|
| 1263 |
+
assert buf.dtype == self.dtype
|
| 1264 |
+
return imagecodecs.zfp_encode(
|
| 1265 |
+
buf,
|
| 1266 |
+
level=self.level,
|
| 1267 |
+
mode=self.mode,
|
| 1268 |
+
execution=self.execution,
|
| 1269 |
+
header=self.header,
|
| 1270 |
+
numthreads=self.numthreads,
|
| 1271 |
+
chunksize=self.chunksize,
|
| 1272 |
+
)
|
| 1273 |
+
|
| 1274 |
+
def decode(self, buf, out=None):
|
| 1275 |
+
if self.header:
|
| 1276 |
+
return imagecodecs.zfp_decode(buf, out=out)
|
| 1277 |
+
return imagecodecs.zfp_decode(
|
| 1278 |
+
buf,
|
| 1279 |
+
shape=self.shape,
|
| 1280 |
+
dtype=numpy.dtype(self.dtype),
|
| 1281 |
+
strides=self.strides,
|
| 1282 |
+
numthreads=self.numthreads,
|
| 1283 |
+
out=out,
|
| 1284 |
+
)
|
| 1285 |
+
|
| 1286 |
+
|
| 1287 |
+
class Zlib(Codec):
|
| 1288 |
+
"""Zlib codec for numcodecs."""
|
| 1289 |
+
|
| 1290 |
+
codec_id = 'imagecodecs_zlib'
|
| 1291 |
+
|
| 1292 |
+
def __init__(self, level=None):
|
| 1293 |
+
self.level = level
|
| 1294 |
+
|
| 1295 |
+
def encode(self, buf):
|
| 1296 |
+
return imagecodecs.zlib_encode(buf, level=self.level)
|
| 1297 |
+
|
| 1298 |
+
def decode(self, buf, out=None):
|
| 1299 |
+
return imagecodecs.zlib_decode(buf, out=_flat(out))
|
| 1300 |
+
|
| 1301 |
+
|
| 1302 |
+
class Zlibng(Codec):
|
| 1303 |
+
"""Zlibng codec for numcodecs."""
|
| 1304 |
+
|
| 1305 |
+
codec_id = 'imagecodecs_zlibng'
|
| 1306 |
+
|
| 1307 |
+
def __init__(self, level=None):
|
| 1308 |
+
self.level = level
|
| 1309 |
+
|
| 1310 |
+
def encode(self, buf):
|
| 1311 |
+
return imagecodecs.zlibng_encode(buf, level=self.level)
|
| 1312 |
+
|
| 1313 |
+
def decode(self, buf, out=None):
|
| 1314 |
+
return imagecodecs.zlibng_decode(buf, out=_flat(out))
|
| 1315 |
+
|
| 1316 |
+
|
| 1317 |
+
class Zopfli(Codec):
|
| 1318 |
+
"""Zopfli codec for numcodecs."""
|
| 1319 |
+
|
| 1320 |
+
codec_id = 'imagecodecs_zopfli'
|
| 1321 |
+
|
| 1322 |
+
def encode(self, buf):
|
| 1323 |
+
return imagecodecs.zopfli_encode(buf)
|
| 1324 |
+
|
| 1325 |
+
def decode(self, buf, out=None):
|
| 1326 |
+
return imagecodecs.zopfli_decode(buf, out=_flat(out))
|
| 1327 |
+
|
| 1328 |
+
|
| 1329 |
+
class Zstd(Codec):
|
| 1330 |
+
"""ZStandard codec for numcodecs."""
|
| 1331 |
+
|
| 1332 |
+
codec_id = 'imagecodecs_zstd'
|
| 1333 |
+
|
| 1334 |
+
def __init__(self, level=None):
|
| 1335 |
+
self.level = level
|
| 1336 |
+
|
| 1337 |
+
def encode(self, buf):
|
| 1338 |
+
return imagecodecs.zstd_encode(buf, level=self.level)
|
| 1339 |
+
|
| 1340 |
+
def decode(self, buf, out=None):
|
| 1341 |
+
return imagecodecs.zstd_decode(buf, out=_flat(out))
|
| 1342 |
+
|
| 1343 |
+
|
| 1344 |
+
def _flat(out):
|
| 1345 |
+
"""Return numpy array as contiguous view of bytes if possible."""
|
| 1346 |
+
if out is None:
|
| 1347 |
+
return None
|
| 1348 |
+
view = memoryview(out)
|
| 1349 |
+
if view.readonly or not view.contiguous:
|
| 1350 |
+
return None
|
| 1351 |
+
return view.cast('B')
|
| 1352 |
+
|
| 1353 |
+
|
| 1354 |
+
def register_codecs(codecs=None, force=False, verbose=True):
|
| 1355 |
+
"""Register codecs in this module with numcodecs."""
|
| 1356 |
+
for name, cls in globals().items():
|
| 1357 |
+
if not hasattr(cls, 'codec_id') or name == 'Codec':
|
| 1358 |
+
continue
|
| 1359 |
+
if codecs is not None and cls.codec_id not in codecs:
|
| 1360 |
+
continue
|
| 1361 |
+
try:
|
| 1362 |
+
try:
|
| 1363 |
+
get_codec({'id': cls.codec_id})
|
| 1364 |
+
except TypeError:
|
| 1365 |
+
# registered, but failed
|
| 1366 |
+
pass
|
| 1367 |
+
except ValueError:
|
| 1368 |
+
# not registered yet
|
| 1369 |
+
pass
|
| 1370 |
+
else:
|
| 1371 |
+
if not force:
|
| 1372 |
+
if verbose:
|
| 1373 |
+
log_warning(
|
| 1374 |
+
f'numcodec {cls.codec_id!r} already registered'
|
| 1375 |
+
)
|
| 1376 |
+
continue
|
| 1377 |
+
if verbose:
|
| 1378 |
+
log_warning(f'replacing registered numcodec {cls.codec_id!r}')
|
| 1379 |
+
register_codec(cls)
|
| 1380 |
+
|
| 1381 |
+
|
| 1382 |
+
def log_warning(msg, *args, **kwargs):
|
| 1383 |
+
"""Log message with level WARNING."""
|
| 1384 |
+
import logging
|
| 1385 |
+
|
| 1386 |
+
logging.getLogger(__name__).warning(msg, *args, **kwargs)
|
common/checkpoint_util.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Optional, Dict
|
| 2 |
+
import os
|
| 3 |
+
|
| 4 |
+
class TopKCheckpointManager:
|
| 5 |
+
def __init__(self,
|
| 6 |
+
save_dir,
|
| 7 |
+
monitor_key: str,
|
| 8 |
+
mode='min',
|
| 9 |
+
k=1,
|
| 10 |
+
format_str='epoch={epoch:03d}-train_loss={train_loss:.3f}.ckpt'
|
| 11 |
+
):
|
| 12 |
+
assert mode in ['max', 'min']
|
| 13 |
+
assert k >= 0
|
| 14 |
+
|
| 15 |
+
self.save_dir = save_dir
|
| 16 |
+
self.monitor_key = monitor_key
|
| 17 |
+
self.mode = mode
|
| 18 |
+
self.k = k
|
| 19 |
+
self.format_str = format_str
|
| 20 |
+
self.path_value_map = dict()
|
| 21 |
+
|
| 22 |
+
def get_ckpt_path(self, data: Dict[str, float]) -> Optional[str]:
|
| 23 |
+
if self.k == 0:
|
| 24 |
+
return None
|
| 25 |
+
|
| 26 |
+
value = data[self.monitor_key]
|
| 27 |
+
ckpt_path = os.path.join(
|
| 28 |
+
self.save_dir, self.format_str.format(**data))
|
| 29 |
+
|
| 30 |
+
if len(self.path_value_map) < self.k:
|
| 31 |
+
# under-capacity
|
| 32 |
+
self.path_value_map[ckpt_path] = value
|
| 33 |
+
return ckpt_path
|
| 34 |
+
|
| 35 |
+
# at capacity
|
| 36 |
+
sorted_map = sorted(self.path_value_map.items(), key=lambda x: x[1])
|
| 37 |
+
min_path, min_value = sorted_map[0]
|
| 38 |
+
max_path, max_value = sorted_map[-1]
|
| 39 |
+
|
| 40 |
+
delete_path = None
|
| 41 |
+
if self.mode == 'max':
|
| 42 |
+
if value > min_value:
|
| 43 |
+
delete_path = min_path
|
| 44 |
+
else:
|
| 45 |
+
if value < max_value:
|
| 46 |
+
delete_path = max_path
|
| 47 |
+
|
| 48 |
+
if delete_path is None:
|
| 49 |
+
return None
|
| 50 |
+
else:
|
| 51 |
+
del self.path_value_map[delete_path]
|
| 52 |
+
self.path_value_map[ckpt_path] = value
|
| 53 |
+
|
| 54 |
+
if not os.path.exists(self.save_dir):
|
| 55 |
+
os.mkdir(self.save_dir)
|
| 56 |
+
|
| 57 |
+
if os.path.exists(delete_path):
|
| 58 |
+
os.remove(delete_path)
|
| 59 |
+
return ckpt_path
|
common/compute_all_loss.py
ADDED
|
@@ -0,0 +1,483 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import numpy as np
|
| 3 |
+
import torch.nn.functional as F
|
| 4 |
+
import random
|
| 5 |
+
from einops import rearrange, reduce
|
| 6 |
+
import cv2
|
| 7 |
+
import concurrent.futures
|
| 8 |
+
from diffusion_policy.policy.base_image_policy import BaseImagePolicy
|
| 9 |
+
from diffusion_policy.common.pytorch_util import dict_apply, replace_submodules
|
| 10 |
+
from diffusion_policy.model.common.slice import slice_episode
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def unflatten_dataset_dict(flat_dict, delimiter='/'):
|
| 14 |
+
result = {}
|
| 15 |
+
for compound_key, value in flat_dict.items():
|
| 16 |
+
keys = compound_key.split(delimiter)
|
| 17 |
+
current = result
|
| 18 |
+
for key in keys[:-1]:
|
| 19 |
+
if key not in current:
|
| 20 |
+
current[key] = {}
|
| 21 |
+
current = current[key]
|
| 22 |
+
current[keys[-1]] = value
|
| 23 |
+
|
| 24 |
+
return result
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def decode_image(data):
|
| 28 |
+
return cv2.imdecode(data, 1)
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def compute_all_traj_loss(replay_buffer=None, model:BaseImagePolicy=None, ref_model:BaseImagePolicy=None, stride=1):
|
| 32 |
+
if replay_buffer is None:
|
| 33 |
+
return np.zeros([1])
|
| 34 |
+
else:
|
| 35 |
+
data = replay_buffer.data
|
| 36 |
+
meta_data = replay_buffer.meta
|
| 37 |
+
observations_1 = np.array(data['obs'], dtype=np.float32)
|
| 38 |
+
actions_1 = np.array(data['action'], dtype=np.float32)
|
| 39 |
+
observations_2 = np.array(data['obs_2'], dtype=np.float32)
|
| 40 |
+
actions_2 = np.array(data['action_2'], dtype=np.float32)
|
| 41 |
+
|
| 42 |
+
total_size = len(observations_1)
|
| 43 |
+
|
| 44 |
+
# Calculate 25% of the data size
|
| 45 |
+
sample_size = int(total_size * 0.25)
|
| 46 |
+
|
| 47 |
+
# Generate random indices for sampling
|
| 48 |
+
indices = np.random.choice(total_size, size=sample_size, replace=False)
|
| 49 |
+
|
| 50 |
+
# Extract 25% of the data using the indices
|
| 51 |
+
observations_1 = observations_1[indices]
|
| 52 |
+
actions_1 = actions_1[indices]
|
| 53 |
+
observations_2 = observations_2[indices]
|
| 54 |
+
actions_2 = actions_2[indices]
|
| 55 |
+
|
| 56 |
+
for param in ref_model.parameters():
|
| 57 |
+
param.requires_grad = False
|
| 58 |
+
|
| 59 |
+
ref_model = ref_model.to(model.device)
|
| 60 |
+
|
| 61 |
+
# Normalize data
|
| 62 |
+
batch_1 = {
|
| 63 |
+
'obs': observations_1,
|
| 64 |
+
'action': actions_1,
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
batch_2 = {
|
| 68 |
+
'obs': observations_2,
|
| 69 |
+
'action': actions_2,
|
| 70 |
+
}
|
| 71 |
+
nbatch_1 = model.normalizer.normalize(batch_1)
|
| 72 |
+
nbatch_2 = model.normalizer.normalize(batch_2)
|
| 73 |
+
obs_1, obs_2 = nbatch_1['obs'], nbatch_2['obs']
|
| 74 |
+
actions_1, actions_2 = nbatch_1['action'], nbatch_2['action']
|
| 75 |
+
|
| 76 |
+
# Slice trajectories
|
| 77 |
+
obs_1 = slice_episode(obs_1, horizon=model.horizon, stride=stride)
|
| 78 |
+
action_1 = slice_episode(actions_1, horizon=model.horizon, stride=stride)
|
| 79 |
+
obs_2 = slice_episode(obs_2, horizon=model.horizon, stride=stride)
|
| 80 |
+
action_2 = slice_episode(actions_2, horizon=model.horizon, stride=stride)
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
bsz = obs_1[0].shape[0]
|
| 84 |
+
timesteps = torch.randint(0, model.noise_scheduler.config.num_train_timesteps, (bsz,), device=model.device).long()
|
| 85 |
+
|
| 86 |
+
# Pre-allocate loss
|
| 87 |
+
traj_loss_1, traj_loss_2 = 0, 0
|
| 88 |
+
|
| 89 |
+
# Helper function to compute loss for a single trajectory
|
| 90 |
+
def compute_traj_loss(obs_slices, action_slices, timestep, model, ref_policy):
|
| 91 |
+
total_loss = 0
|
| 92 |
+
|
| 93 |
+
for idx, (obs_slide, action_slide) in enumerate(zip(obs_slices, action_slices)):
|
| 94 |
+
gamma_factors = model.gamma ** (idx * model.horizon + torch.arange(model.horizon, device=model.device))
|
| 95 |
+
if model.obs_as_cond:
|
| 96 |
+
cond = obs_slide[:, :model.n_obs_steps, :]
|
| 97 |
+
cond = cond.detach().to(model.device)
|
| 98 |
+
# cond.detach().to(model.device)
|
| 99 |
+
trajectory = action_slide[:, -model.n_action_steps:] if model.pred_action_steps_only else action_slide
|
| 100 |
+
else:
|
| 101 |
+
cond = None
|
| 102 |
+
trajectory = np.concatenate([action_slide, obs_slide], axis=-1)
|
| 103 |
+
|
| 104 |
+
condition_mask = model.mask_generator(trajectory.shape).to(model.device)
|
| 105 |
+
loss_mask = (~condition_mask).float()
|
| 106 |
+
|
| 107 |
+
trajectory = torch.tensor(trajectory, device=model.device, dtype=torch.float32)
|
| 108 |
+
noise = torch.randn(trajectory.shape, device=model.device)
|
| 109 |
+
|
| 110 |
+
# Disable gradient computation
|
| 111 |
+
with torch.no_grad():
|
| 112 |
+
noisy_trajectory = model.noise_scheduler.add_noise(trajectory, noise, timestep)
|
| 113 |
+
noisy_trajectory[condition_mask] = trajectory[condition_mask]
|
| 114 |
+
|
| 115 |
+
pred_ref = ref_policy(noisy_trajectory, timestep, cond)
|
| 116 |
+
pred = model.model(noisy_trajectory, timestep, cond)
|
| 117 |
+
|
| 118 |
+
pred_type = model.noise_scheduler.config.prediction_type
|
| 119 |
+
if pred_type == 'epsilon':
|
| 120 |
+
target = noise
|
| 121 |
+
elif pred_type == 'sample':
|
| 122 |
+
target = trajectory
|
| 123 |
+
else:
|
| 124 |
+
raise ValueError(f"Unsupported prediction type {pred_type}")
|
| 125 |
+
|
| 126 |
+
loss = F.mse_loss(pred, target, reduction='none')
|
| 127 |
+
loss_ref = F.mse_loss(pred_ref, target, reduction='none')
|
| 128 |
+
loss = loss * loss_mask.type(loss.dtype)
|
| 129 |
+
loss_ref = loss_ref * loss_mask.type(loss.dtype)
|
| 130 |
+
loss = reduce(loss, 'b t ... -> b t (...)', 'mean')
|
| 131 |
+
loss_ref = reduce(loss_ref, 'b t ... -> b t (...)', 'mean')
|
| 132 |
+
|
| 133 |
+
slice_loss = torch.sum((loss - loss_ref), dim=-1)
|
| 134 |
+
total_loss += torch.sum(slice_loss * gamma_factors)
|
| 135 |
+
# Explicitly delete unused variables to release GPU memory
|
| 136 |
+
del trajectory, noise, noisy_trajectory, pred_ref, pred, loss_mask, condition_mask
|
| 137 |
+
torch.cuda.empty_cache()
|
| 138 |
+
|
| 139 |
+
return total_loss.detach()
|
| 140 |
+
|
| 141 |
+
# Compute loss for trajectory 1
|
| 142 |
+
traj_loss_1 = compute_traj_loss(obs_1, action_1, timesteps, model, ref_model)
|
| 143 |
+
# Compute loss for trajectory 2
|
| 144 |
+
traj_loss_2 = compute_traj_loss(obs_2, action_2, timesteps, model, ref_model)
|
| 145 |
+
|
| 146 |
+
# Average the losses
|
| 147 |
+
loss = (traj_loss_1 + traj_loss_2) / 2
|
| 148 |
+
|
| 149 |
+
return torch.mean(loss)
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
def compute_all_traj_loss_realrobot(replay_buffer=None, model=None, ref_model=None, stride=1, sample_size = 20, batch_size=10, gc_every_n_batches=12):
|
| 153 |
+
import gc
|
| 154 |
+
gc.collect()
|
| 155 |
+
torch.cuda.empty_cache()
|
| 156 |
+
|
| 157 |
+
if replay_buffer is None:
|
| 158 |
+
return np.zeros([1])
|
| 159 |
+
else:
|
| 160 |
+
assert sample_size >= batch_size, "data_size should be greater than or equal to batch_size"
|
| 161 |
+
if hasattr(torch.cuda, 'set_per_process_memory_fraction'):
|
| 162 |
+
torch.cuda.set_per_process_memory_fraction(0.8)
|
| 163 |
+
|
| 164 |
+
data = replay_buffer.data
|
| 165 |
+
data = unflatten_dataset_dict(flat_dict=data)
|
| 166 |
+
data_size = len(data['action'])
|
| 167 |
+
indicis = np.random.choice(data_size, size=sample_size, replace=False)
|
| 168 |
+
|
| 169 |
+
observations_1 = data['obs']
|
| 170 |
+
actions_1 = np.array(data['action'][indicis], dtype=np.float32)
|
| 171 |
+
observations_2 = data['obs_2']
|
| 172 |
+
actions_2 = np.array(data['action_2'][indicis], dtype=np.float32)
|
| 173 |
+
compress_len_1 = data['compress_len'][indicis]
|
| 174 |
+
compress_len_2 = data['compress_len_2'][indicis]
|
| 175 |
+
camera_keys = observations_1['images'].keys()
|
| 176 |
+
qpos_keys = [key for key in observations_1.keys() if key != 'images']
|
| 177 |
+
del data
|
| 178 |
+
|
| 179 |
+
for key in camera_keys:
|
| 180 |
+
img_data_1 = observations_1['images'][key][indicis]
|
| 181 |
+
img_data_2 = observations_2['images'][key][indicis]
|
| 182 |
+
total_images = img_data_1.shape[0]
|
| 183 |
+
|
| 184 |
+
img_batch_size = min(batch_size, total_images)
|
| 185 |
+
decompressed_images_1 = []
|
| 186 |
+
|
| 187 |
+
for batch_idx in range(0, total_images, img_batch_size):
|
| 188 |
+
end_idx = min(batch_idx + img_batch_size, total_images)
|
| 189 |
+
batch_decompressed = []
|
| 190 |
+
|
| 191 |
+
for k in range(batch_idx, end_idx):
|
| 192 |
+
image = img_data_1[k, :, :int(compress_len_1[k, 0])].copy()
|
| 193 |
+
with concurrent.futures.ThreadPoolExecutor() as executor:
|
| 194 |
+
results = executor.map(decode_image, image)
|
| 195 |
+
decompressed_images = list(results)
|
| 196 |
+
batch_decompressed.append(decompressed_images)
|
| 197 |
+
|
| 198 |
+
batch_decompressed = np.array(batch_decompressed)
|
| 199 |
+
batch_decompressed = np.einsum('b k h w c -> b k c h w', batch_decompressed)
|
| 200 |
+
decompressed_images_1.append(torch.from_numpy(batch_decompressed / 255.0).float())
|
| 201 |
+
|
| 202 |
+
del batch_decompressed
|
| 203 |
+
torch.cuda.empty_cache()
|
| 204 |
+
|
| 205 |
+
if batch_idx % (img_batch_size * gc_every_n_batches) == 0:
|
| 206 |
+
gc.collect()
|
| 207 |
+
|
| 208 |
+
observations_1[key] = torch.cat(decompressed_images_1, dim=0)
|
| 209 |
+
del observations_1['images']
|
| 210 |
+
del decompressed_images_1
|
| 211 |
+
|
| 212 |
+
decompressed_images_2 = []
|
| 213 |
+
|
| 214 |
+
for batch_idx in range(0, total_images, img_batch_size):
|
| 215 |
+
end_idx = min(batch_idx + img_batch_size, total_images)
|
| 216 |
+
batch_decompressed = []
|
| 217 |
+
|
| 218 |
+
for k in range(batch_idx, end_idx):
|
| 219 |
+
image = img_data_2[k, :, :int(compress_len_2[k, 0])].copy()
|
| 220 |
+
with concurrent.futures.ThreadPoolExecutor() as executor:
|
| 221 |
+
results = executor.map(decode_image, image)
|
| 222 |
+
decompressed_images = list(results)
|
| 223 |
+
batch_decompressed.append(decompressed_images)
|
| 224 |
+
|
| 225 |
+
batch_decompressed = np.array(batch_decompressed)
|
| 226 |
+
batch_decompressed = np.einsum('b k h w c -> b k c h w', batch_decompressed)
|
| 227 |
+
decompressed_images_2.append(torch.from_numpy(batch_decompressed / 255.0).float())
|
| 228 |
+
|
| 229 |
+
del batch_decompressed
|
| 230 |
+
torch.cuda.empty_cache()
|
| 231 |
+
|
| 232 |
+
if batch_idx % (img_batch_size * gc_every_n_batches) == 0:
|
| 233 |
+
gc.collect()
|
| 234 |
+
|
| 235 |
+
observations_2[key] = torch.cat(decompressed_images_2, dim=0)
|
| 236 |
+
del observations_2['images']
|
| 237 |
+
del decompressed_images_2
|
| 238 |
+
|
| 239 |
+
del img_data_1, img_data_2
|
| 240 |
+
gc.collect()
|
| 241 |
+
torch.cuda.empty_cache()
|
| 242 |
+
|
| 243 |
+
for key in qpos_keys:
|
| 244 |
+
observations_1[key] = torch.from_numpy(observations_1[key]).float()
|
| 245 |
+
observations_2[key] = torch.from_numpy(observations_2[key]).float()
|
| 246 |
+
|
| 247 |
+
observations_1[key] = observations_1[key].cpu()
|
| 248 |
+
observations_2[key] = observations_2[key].cpu()
|
| 249 |
+
|
| 250 |
+
gc.collect()
|
| 251 |
+
torch.cuda.empty_cache()
|
| 252 |
+
|
| 253 |
+
for param in ref_model.parameters():
|
| 254 |
+
param.requires_grad = False
|
| 255 |
+
|
| 256 |
+
device = model.device
|
| 257 |
+
ref_model = ref_model.to(device)
|
| 258 |
+
|
| 259 |
+
with torch.no_grad():
|
| 260 |
+
obs_1 = model.normalizer.normalize(observations_1)
|
| 261 |
+
action_1 = model.normalizer['action'].normalize(actions_1)
|
| 262 |
+
obs_2 = model.normalizer.normalize(observations_2)
|
| 263 |
+
action_2 = model.normalizer['action'].normalize(actions_2)
|
| 264 |
+
|
| 265 |
+
start_1 = random.randint(0, model.n_obs_steps)
|
| 266 |
+
start_2 = random.randint(0, model.n_obs_steps)
|
| 267 |
+
|
| 268 |
+
with torch.no_grad():
|
| 269 |
+
obs_1 = {key: slice_episode(obs_1[key], horizon=model.horizon, stride=stride, start=start_1) for key in obs_1.keys()}
|
| 270 |
+
action_1 = slice_episode(action_1, horizon=model.horizon, stride=stride, start=start_1)
|
| 271 |
+
obs_2 = {key: slice_episode(obs_2[key], horizon=model.horizon, stride=stride, start=start_2) for key in obs_2.keys()}
|
| 272 |
+
action_2 = slice_episode(action_2, horizon=model.horizon, stride=stride, start=start_2)
|
| 273 |
+
|
| 274 |
+
del observations_1, observations_2, actions_1, actions_2
|
| 275 |
+
gc.collect()
|
| 276 |
+
torch.cuda.empty_cache()
|
| 277 |
+
|
| 278 |
+
def compute_traj_image_loss_batched(obs_slices, action_slices, model, ref_model, batch_size):
|
| 279 |
+
with torch.no_grad():
|
| 280 |
+
To = model.n_obs_steps
|
| 281 |
+
horizon = model.horizon
|
| 282 |
+
total_samples = action_slices.shape[0]
|
| 283 |
+
total_loss = torch.zeros(total_samples, device='cpu')
|
| 284 |
+
|
| 285 |
+
num_batches = (total_samples + batch_size - 1) // batch_size
|
| 286 |
+
device = model.device
|
| 287 |
+
|
| 288 |
+
for batch_idx in range(num_batches):
|
| 289 |
+
|
| 290 |
+
if batch_idx > 0 and batch_idx % gc_every_n_batches == 0:
|
| 291 |
+
gc.collect()
|
| 292 |
+
torch.cuda.empty_cache()
|
| 293 |
+
|
| 294 |
+
start_idx = batch_idx * batch_size
|
| 295 |
+
end_idx = min(start_idx + batch_size, total_samples)
|
| 296 |
+
current_batch_size = end_idx - start_idx
|
| 297 |
+
|
| 298 |
+
batch_timesteps = torch.randint(0, model.noise_scheduler.config.num_train_timesteps,
|
| 299 |
+
(current_batch_size,), device=device).long()
|
| 300 |
+
|
| 301 |
+
batch_action_slices = action_slices[start_idx:end_idx].to(device)
|
| 302 |
+
batch_obs_slices = {key: obs_slices[key][start_idx:end_idx].to(device) for key in obs_slices.keys()}
|
| 303 |
+
|
| 304 |
+
batch_total_loss = torch.zeros(current_batch_size, device=device)
|
| 305 |
+
|
| 306 |
+
for idx in range(current_batch_size):
|
| 307 |
+
action_slide = batch_action_slices[idx:idx+1]
|
| 308 |
+
obs_slide = {key: batch_obs_slices[key][idx:idx+1] for key in batch_obs_slices.keys()}
|
| 309 |
+
sample_timesteps = batch_timesteps[idx:idx+1]
|
| 310 |
+
|
| 311 |
+
local_cond = None
|
| 312 |
+
global_cond = None
|
| 313 |
+
global_cond_ref = None
|
| 314 |
+
|
| 315 |
+
if model.obs_as_global_cond:
|
| 316 |
+
this_nobs = dict_apply(obs_slide,
|
| 317 |
+
lambda x: x[:,:To,...].reshape(-1, *x.shape[2:]))
|
| 318 |
+
nobs_features = model.obs_encoder(this_nobs)
|
| 319 |
+
nobs_features_ref = ref_model.obs_encoder(this_nobs)
|
| 320 |
+
|
| 321 |
+
global_cond = nobs_features.reshape(1, -1)
|
| 322 |
+
global_cond_ref = nobs_features_ref.reshape(1, -1)
|
| 323 |
+
trajectory = action_slide
|
| 324 |
+
else:
|
| 325 |
+
this_nobs = dict_apply(obs_slide,
|
| 326 |
+
lambda x: x.reshape(-1, *x.shape[2:]))
|
| 327 |
+
nobs_features = model.obs_encoder(this_nobs)
|
| 328 |
+
nobs_features_ref = ref_model.obs_encoder(this_nobs)
|
| 329 |
+
|
| 330 |
+
nobs_features = nobs_features.reshape(1, horizon, -1)
|
| 331 |
+
nobs_features_ref = nobs_features_ref.reshape(1, horizon, -1)
|
| 332 |
+
|
| 333 |
+
trajectory = torch.cat([action_slide, nobs_features], dim=-1)
|
| 334 |
+
trajectory_ref = torch.cat([action_slide, nobs_features_ref], dim=-1)
|
| 335 |
+
|
| 336 |
+
condition_mask = model.mask_generator(trajectory.shape).to(device)
|
| 337 |
+
loss_mask = (~condition_mask).float()
|
| 338 |
+
|
| 339 |
+
noise = torch.randn(trajectory.shape, device=device)
|
| 340 |
+
|
| 341 |
+
noisy_trajectory = model.noise_scheduler.add_noise(trajectory, noise, sample_timesteps)
|
| 342 |
+
noisy_trajectory[condition_mask] = trajectory[condition_mask]
|
| 343 |
+
|
| 344 |
+
if not model.obs_as_global_cond:
|
| 345 |
+
noisy_trajectory_ref = model.noise_scheduler.add_noise(trajectory_ref, noise, sample_timesteps)
|
| 346 |
+
noisy_trajectory_ref[condition_mask] = trajectory_ref[condition_mask]
|
| 347 |
+
else:
|
| 348 |
+
noisy_trajectory_ref = noisy_trajectory.clone()
|
| 349 |
+
|
| 350 |
+
pred = model.model(noisy_trajectory, sample_timesteps,
|
| 351 |
+
local_cond=local_cond, global_cond=global_cond)
|
| 352 |
+
pred_ref = ref_model.model(noisy_trajectory_ref if not model.obs_as_global_cond else noisy_trajectory,
|
| 353 |
+
sample_timesteps, local_cond=local_cond, global_cond=global_cond_ref)
|
| 354 |
+
|
| 355 |
+
pred_type = model.noise_scheduler.config.prediction_type
|
| 356 |
+
if pred_type == 'epsilon':
|
| 357 |
+
target = noise
|
| 358 |
+
elif pred_type == 'sample':
|
| 359 |
+
target = trajectory
|
| 360 |
+
else:
|
| 361 |
+
raise ValueError(f"Unsupported prediction type {pred_type}")
|
| 362 |
+
|
| 363 |
+
loss = F.mse_loss(pred, target, reduction='none')
|
| 364 |
+
loss_ref = F.mse_loss(pred_ref, target, reduction='none')
|
| 365 |
+
|
| 366 |
+
loss = loss * loss_mask
|
| 367 |
+
loss_ref = loss_ref * loss_mask
|
| 368 |
+
loss = reduce(loss, 'b t ... -> b t (...)', 'mean')
|
| 369 |
+
loss_ref = reduce(loss_ref, 'b t ... -> b t (...)', 'mean')
|
| 370 |
+
|
| 371 |
+
slice_loss = torch.sum(loss_ref - loss, dim=1)
|
| 372 |
+
batch_total_loss[idx] = slice_loss.squeeze()
|
| 373 |
+
|
| 374 |
+
del trajectory, noise, noisy_trajectory, pred, pred_ref
|
| 375 |
+
if not model.obs_as_global_cond:
|
| 376 |
+
del trajectory_ref, noisy_trajectory_ref
|
| 377 |
+
del nobs_features, nobs_features_ref, this_nobs
|
| 378 |
+
|
| 379 |
+
total_loss[start_idx:end_idx] = batch_total_loss.cpu()
|
| 380 |
+
|
| 381 |
+
del batch_action_slices, batch_obs_slices, batch_timesteps, batch_total_loss
|
| 382 |
+
torch.cuda.empty_cache()
|
| 383 |
+
|
| 384 |
+
return total_loss
|
| 385 |
+
|
| 386 |
+
with torch.no_grad():
|
| 387 |
+
total_samples = action_1.shape[0]
|
| 388 |
+
traj_loss_1 = compute_traj_image_loss_batched(obs_1, action_1, model, ref_model, batch_size)
|
| 389 |
+
|
| 390 |
+
if traj_loss_1.device.type != 'cpu':
|
| 391 |
+
traj_loss_1 = traj_loss_1.cpu()
|
| 392 |
+
|
| 393 |
+
del obs_1, action_1
|
| 394 |
+
gc.collect()
|
| 395 |
+
torch.cuda.empty_cache()
|
| 396 |
+
|
| 397 |
+
total_samples = action_2.shape[0]
|
| 398 |
+
traj_loss_2 = compute_traj_image_loss_batched(obs_2, action_2, model, ref_model, batch_size)
|
| 399 |
+
|
| 400 |
+
if traj_loss_2.device.type != 'cpu':
|
| 401 |
+
traj_loss_2 = traj_loss_2.cpu()
|
| 402 |
+
|
| 403 |
+
loss = (traj_loss_1 + traj_loss_2) / 2
|
| 404 |
+
final_loss = torch.mean(loss)
|
| 405 |
+
|
| 406 |
+
del obs_2, action_2, traj_loss_1, traj_loss_2, loss
|
| 407 |
+
gc.collect()
|
| 408 |
+
torch.cuda.empty_cache()
|
| 409 |
+
|
| 410 |
+
return final_loss
|
| 411 |
+
|
| 412 |
+
|
| 413 |
+
def compute_all_bet_traj_loss(replay_buffer=None, model=None, stride=1):
|
| 414 |
+
if replay_buffer is None:
|
| 415 |
+
return np.zeros([1])
|
| 416 |
+
else:
|
| 417 |
+
data = replay_buffer.data
|
| 418 |
+
meta_data = replay_buffer.meta
|
| 419 |
+
observations_1 = np.array(data['obs'], dtype=np.float32)
|
| 420 |
+
actions_1 = np.array(data['action'], dtype=np.float32)
|
| 421 |
+
observations_2 = np.array(data['obs_2'], dtype=np.float32)
|
| 422 |
+
actions_2 = np.array(data['action_2'], dtype=np.float32)
|
| 423 |
+
length_1 = torch.tensor(meta_data['length'], device=model.device)
|
| 424 |
+
length_2 = torch.tensor(meta_data['length_2'], device=model.device)
|
| 425 |
+
|
| 426 |
+
# Normalize data
|
| 427 |
+
batch_1 = {
|
| 428 |
+
'obs': observations_1,
|
| 429 |
+
'action': actions_1,
|
| 430 |
+
}
|
| 431 |
+
|
| 432 |
+
batch_2 = {
|
| 433 |
+
'obs': observations_2,
|
| 434 |
+
'action': actions_2,
|
| 435 |
+
}
|
| 436 |
+
nbatch_1 = model.normalizer.normalize(batch_1)
|
| 437 |
+
nbatch_2 = model.normalizer.normalize(batch_2)
|
| 438 |
+
obs_1, obs_2 = nbatch_1['obs'], nbatch_2['obs']
|
| 439 |
+
actions_1, actions_2 = nbatch_1['action'], nbatch_2['action']
|
| 440 |
+
|
| 441 |
+
# Slice trajectories
|
| 442 |
+
obs_1 = slice_episode(obs_1, horizon=model.horizon, stride=stride)
|
| 443 |
+
action_1 = slice_episode(actions_1, horizon=model.horizon, stride=stride)
|
| 444 |
+
obs_2 = slice_episode(obs_2, horizon=model.horizon, stride=stride)
|
| 445 |
+
action_2 = slice_episode(actions_2, horizon=model.horizon, stride=stride)
|
| 446 |
+
|
| 447 |
+
# Pre-allocate loss
|
| 448 |
+
traj_loss_1, traj_loss_2 = 0, 0
|
| 449 |
+
|
| 450 |
+
# Helper function to compute loss for a single trajectory
|
| 451 |
+
def compute_traj_loss(obs_slices, action_slices, model, length, stride):
|
| 452 |
+
total_loss = 0
|
| 453 |
+
|
| 454 |
+
for idx, (obs_slide, action_slide) in enumerate(zip(obs_slices, action_slices)):
|
| 455 |
+
gamma_factors = model.gamma ** (idx * model.horizon)
|
| 456 |
+
obs_slide[:, model.n_obs_steps:, :] = -2
|
| 457 |
+
|
| 458 |
+
enc_obs = model.obs_encoding_net(obs_slide)
|
| 459 |
+
latent = model.action_ae.encode_into_latent(action_slide, enc_obs)
|
| 460 |
+
|
| 461 |
+
loss = model.get_pred_loss(
|
| 462 |
+
obs_rep=enc_obs.clone(),
|
| 463 |
+
target_latents=latent,
|
| 464 |
+
)
|
| 465 |
+
|
| 466 |
+
mask = (model.horizon + (idx - 1)*stride) <= length
|
| 467 |
+
mask = mask.int()
|
| 468 |
+
|
| 469 |
+
total_loss += (loss * mask) * gamma_factors
|
| 470 |
+
|
| 471 |
+
total_loss = torch.sum(total_loss, dim=-1)
|
| 472 |
+
|
| 473 |
+
return total_loss.detach()
|
| 474 |
+
|
| 475 |
+
# Compute loss for trajectory 1
|
| 476 |
+
traj_loss_1 = compute_traj_loss(obs_1, action_1, model, length_1, stride)
|
| 477 |
+
# Compute loss for trajectory 2
|
| 478 |
+
traj_loss_2 = compute_traj_loss(obs_2, action_2, model, length_2, stride)
|
| 479 |
+
|
| 480 |
+
# Average the losses
|
| 481 |
+
loss = (traj_loss_1 + traj_loss_2) / 2
|
| 482 |
+
|
| 483 |
+
return torch.mean(loss)
|
common/cv2_util.py
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Tuple
|
| 2 |
+
import math
|
| 3 |
+
import cv2
|
| 4 |
+
import numpy as np
|
| 5 |
+
|
| 6 |
+
def draw_reticle(img, u, v, label_color):
|
| 7 |
+
"""
|
| 8 |
+
Draws a reticle (cross-hair) on the image at the given position on top of
|
| 9 |
+
the original image.
|
| 10 |
+
@param img (In/Out) uint8 3 channel image
|
| 11 |
+
@param u X coordinate (width)
|
| 12 |
+
@param v Y coordinate (height)
|
| 13 |
+
@param label_color tuple of 3 ints for RGB color used for drawing.
|
| 14 |
+
"""
|
| 15 |
+
# Cast to int.
|
| 16 |
+
u = int(u)
|
| 17 |
+
v = int(v)
|
| 18 |
+
|
| 19 |
+
white = (255, 255, 255)
|
| 20 |
+
cv2.circle(img, (u, v), 10, label_color, 1)
|
| 21 |
+
cv2.circle(img, (u, v), 11, white, 1)
|
| 22 |
+
cv2.circle(img, (u, v), 12, label_color, 1)
|
| 23 |
+
cv2.line(img, (u, v + 1), (u, v + 3), white, 1)
|
| 24 |
+
cv2.line(img, (u + 1, v), (u + 3, v), white, 1)
|
| 25 |
+
cv2.line(img, (u, v - 1), (u, v - 3), white, 1)
|
| 26 |
+
cv2.line(img, (u - 1, v), (u - 3, v), white, 1)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def draw_text(
|
| 30 |
+
img,
|
| 31 |
+
*,
|
| 32 |
+
text,
|
| 33 |
+
uv_top_left,
|
| 34 |
+
color=(255, 255, 255),
|
| 35 |
+
fontScale=0.5,
|
| 36 |
+
thickness=1,
|
| 37 |
+
fontFace=cv2.FONT_HERSHEY_SIMPLEX,
|
| 38 |
+
outline_color=(0, 0, 0),
|
| 39 |
+
line_spacing=1.5,
|
| 40 |
+
):
|
| 41 |
+
"""
|
| 42 |
+
Draws multiline with an outline.
|
| 43 |
+
"""
|
| 44 |
+
assert isinstance(text, str)
|
| 45 |
+
|
| 46 |
+
uv_top_left = np.array(uv_top_left, dtype=float)
|
| 47 |
+
assert uv_top_left.shape == (2,)
|
| 48 |
+
|
| 49 |
+
for line in text.splitlines():
|
| 50 |
+
(w, h), _ = cv2.getTextSize(
|
| 51 |
+
text=line,
|
| 52 |
+
fontFace=fontFace,
|
| 53 |
+
fontScale=fontScale,
|
| 54 |
+
thickness=thickness,
|
| 55 |
+
)
|
| 56 |
+
uv_bottom_left_i = uv_top_left + [0, h]
|
| 57 |
+
org = tuple(uv_bottom_left_i.astype(int))
|
| 58 |
+
|
| 59 |
+
if outline_color is not None:
|
| 60 |
+
cv2.putText(
|
| 61 |
+
img,
|
| 62 |
+
text=line,
|
| 63 |
+
org=org,
|
| 64 |
+
fontFace=fontFace,
|
| 65 |
+
fontScale=fontScale,
|
| 66 |
+
color=outline_color,
|
| 67 |
+
thickness=thickness * 3,
|
| 68 |
+
lineType=cv2.LINE_AA,
|
| 69 |
+
)
|
| 70 |
+
cv2.putText(
|
| 71 |
+
img,
|
| 72 |
+
text=line,
|
| 73 |
+
org=org,
|
| 74 |
+
fontFace=fontFace,
|
| 75 |
+
fontScale=fontScale,
|
| 76 |
+
color=color,
|
| 77 |
+
thickness=thickness,
|
| 78 |
+
lineType=cv2.LINE_AA,
|
| 79 |
+
)
|
| 80 |
+
|
| 81 |
+
uv_top_left += [0, h * line_spacing]
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def get_image_transform(
|
| 85 |
+
input_res: Tuple[int,int]=(1280,720),
|
| 86 |
+
output_res: Tuple[int,int]=(640,480),
|
| 87 |
+
bgr_to_rgb: bool=False):
|
| 88 |
+
|
| 89 |
+
iw, ih = input_res
|
| 90 |
+
ow, oh = output_res
|
| 91 |
+
rw, rh = None, None
|
| 92 |
+
interp_method = cv2.INTER_AREA
|
| 93 |
+
|
| 94 |
+
if (iw/ih) >= (ow/oh):
|
| 95 |
+
# input is wider
|
| 96 |
+
rh = oh
|
| 97 |
+
rw = math.ceil(rh / ih * iw)
|
| 98 |
+
if oh > ih:
|
| 99 |
+
interp_method = cv2.INTER_LINEAR
|
| 100 |
+
else:
|
| 101 |
+
rw = ow
|
| 102 |
+
rh = math.ceil(rw / iw * ih)
|
| 103 |
+
if ow > iw:
|
| 104 |
+
interp_method = cv2.INTER_LINEAR
|
| 105 |
+
|
| 106 |
+
w_slice_start = (rw - ow) // 2
|
| 107 |
+
w_slice = slice(w_slice_start, w_slice_start + ow)
|
| 108 |
+
h_slice_start = (rh - oh) // 2
|
| 109 |
+
h_slice = slice(h_slice_start, h_slice_start + oh)
|
| 110 |
+
c_slice = slice(None)
|
| 111 |
+
if bgr_to_rgb:
|
| 112 |
+
c_slice = slice(None, None, -1)
|
| 113 |
+
|
| 114 |
+
def transform(img: np.ndarray):
|
| 115 |
+
assert img.shape == ((ih,iw,3))
|
| 116 |
+
# resize
|
| 117 |
+
img = cv2.resize(img, (rw, rh), interpolation=interp_method)
|
| 118 |
+
# crop
|
| 119 |
+
img = img[h_slice, w_slice, c_slice]
|
| 120 |
+
return img
|
| 121 |
+
return transform
|
| 122 |
+
|
| 123 |
+
def optimal_row_cols(
|
| 124 |
+
n_cameras,
|
| 125 |
+
in_wh_ratio,
|
| 126 |
+
max_resolution=(1920, 1080)
|
| 127 |
+
):
|
| 128 |
+
out_w, out_h = max_resolution
|
| 129 |
+
out_wh_ratio = out_w / out_h
|
| 130 |
+
|
| 131 |
+
n_rows = np.arange(n_cameras,dtype=np.int64) + 1
|
| 132 |
+
n_cols = np.ceil(n_cameras / n_rows).astype(np.int64)
|
| 133 |
+
cat_wh_ratio = in_wh_ratio * (n_cols / n_rows)
|
| 134 |
+
ratio_diff = np.abs(out_wh_ratio - cat_wh_ratio)
|
| 135 |
+
best_idx = np.argmin(ratio_diff)
|
| 136 |
+
best_n_row = n_rows[best_idx]
|
| 137 |
+
best_n_col = n_cols[best_idx]
|
| 138 |
+
best_cat_wh_ratio = cat_wh_ratio[best_idx]
|
| 139 |
+
|
| 140 |
+
rw, rh = None, None
|
| 141 |
+
if best_cat_wh_ratio >= out_wh_ratio:
|
| 142 |
+
# cat is wider
|
| 143 |
+
rw = math.floor(out_w / best_n_col)
|
| 144 |
+
rh = math.floor(rw / in_wh_ratio)
|
| 145 |
+
else:
|
| 146 |
+
rh = math.floor(out_h / best_n_row)
|
| 147 |
+
rw = math.floor(rh * in_wh_ratio)
|
| 148 |
+
|
| 149 |
+
# crop_resolution = (rw, rh)
|
| 150 |
+
return rw, rh, best_n_col, best_n_row
|
common/env_util.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import cv2
|
| 2 |
+
import numpy as np
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
def render_env_video(env, states, actions=None):
|
| 6 |
+
observations = states
|
| 7 |
+
imgs = list()
|
| 8 |
+
for i in range(len(observations)):
|
| 9 |
+
state = observations[i]
|
| 10 |
+
env.set_state(state)
|
| 11 |
+
if i == 0:
|
| 12 |
+
env.set_state(state)
|
| 13 |
+
img = env.render()
|
| 14 |
+
# draw action
|
| 15 |
+
if actions is not None:
|
| 16 |
+
action = actions[i]
|
| 17 |
+
coord = (action / 512 * 96).astype(np.int32)
|
| 18 |
+
cv2.drawMarker(img, coord,
|
| 19 |
+
color=(255,0,0), markerType=cv2.MARKER_CROSS,
|
| 20 |
+
markerSize=8, thickness=1)
|
| 21 |
+
imgs.append(img)
|
| 22 |
+
imgs = np.array(imgs)
|
| 23 |
+
return imgs
|
common/json_logger.py
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Optional, Callable, Any, Sequence
|
| 2 |
+
import os
|
| 3 |
+
import copy
|
| 4 |
+
import json
|
| 5 |
+
import numbers
|
| 6 |
+
import pandas as pd
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def read_json_log(path: str,
|
| 10 |
+
required_keys: Sequence[str]=tuple(),
|
| 11 |
+
**kwargs) -> pd.DataFrame:
|
| 12 |
+
"""
|
| 13 |
+
Read json-per-line file, with potentially incomplete lines.
|
| 14 |
+
kwargs passed to pd.read_json
|
| 15 |
+
"""
|
| 16 |
+
lines = list()
|
| 17 |
+
with open(path, 'r') as f:
|
| 18 |
+
while True:
|
| 19 |
+
# one json per line
|
| 20 |
+
line = f.readline()
|
| 21 |
+
if len(line) == 0:
|
| 22 |
+
# EOF
|
| 23 |
+
break
|
| 24 |
+
elif not line.endswith('\n'):
|
| 25 |
+
# incomplete line
|
| 26 |
+
break
|
| 27 |
+
is_relevant = False
|
| 28 |
+
for k in required_keys:
|
| 29 |
+
if k in line:
|
| 30 |
+
is_relevant = True
|
| 31 |
+
break
|
| 32 |
+
if is_relevant:
|
| 33 |
+
lines.append(line)
|
| 34 |
+
if len(lines) < 1:
|
| 35 |
+
return pd.DataFrame()
|
| 36 |
+
json_buf = f'[{",".join([line for line in (line.strip() for line in lines) if line])}]'
|
| 37 |
+
df = pd.read_json(json_buf, **kwargs)
|
| 38 |
+
return df
|
| 39 |
+
|
| 40 |
+
class JsonLogger:
|
| 41 |
+
def __init__(self, path: str,
|
| 42 |
+
filter_fn: Optional[Callable[[str,Any],bool]]=None):
|
| 43 |
+
if filter_fn is None:
|
| 44 |
+
filter_fn = lambda k,v: isinstance(v, numbers.Number)
|
| 45 |
+
|
| 46 |
+
# default to append mode
|
| 47 |
+
self.path = path
|
| 48 |
+
self.filter_fn = filter_fn
|
| 49 |
+
self.file = None
|
| 50 |
+
self.last_log = None
|
| 51 |
+
|
| 52 |
+
def start(self):
|
| 53 |
+
# use line buffering
|
| 54 |
+
try:
|
| 55 |
+
self.file = file = open(self.path, 'r+', buffering=1)
|
| 56 |
+
except FileNotFoundError:
|
| 57 |
+
self.file = file = open(self.path, 'w+', buffering=1)
|
| 58 |
+
|
| 59 |
+
# Move the pointer (similar to a cursor in a text editor) to the end of the file
|
| 60 |
+
pos = file.seek(0, os.SEEK_END)
|
| 61 |
+
|
| 62 |
+
# Read each character in the file one at a time from the last
|
| 63 |
+
# character going backwards, searching for a newline character
|
| 64 |
+
# If we find a new line, exit the search
|
| 65 |
+
while pos > 0 and file.read(1) != "\n":
|
| 66 |
+
pos -= 1
|
| 67 |
+
file.seek(pos, os.SEEK_SET)
|
| 68 |
+
# now the file pointer is at one past the last '\n'
|
| 69 |
+
# and pos is at the last '\n'.
|
| 70 |
+
last_line_end = file.tell()
|
| 71 |
+
|
| 72 |
+
# find the start of second last line
|
| 73 |
+
pos = max(0, pos-1)
|
| 74 |
+
file.seek(pos, os.SEEK_SET)
|
| 75 |
+
while pos > 0 and file.read(1) != "\n":
|
| 76 |
+
pos -= 1
|
| 77 |
+
file.seek(pos, os.SEEK_SET)
|
| 78 |
+
# now the file pointer is at one past the second last '\n'
|
| 79 |
+
last_line_start = file.tell()
|
| 80 |
+
|
| 81 |
+
if last_line_start < last_line_end:
|
| 82 |
+
# has last line of json
|
| 83 |
+
last_line = file.readline()
|
| 84 |
+
self.last_log = json.loads(last_line)
|
| 85 |
+
|
| 86 |
+
# remove the last incomplete line
|
| 87 |
+
file.seek(last_line_end)
|
| 88 |
+
file.truncate()
|
| 89 |
+
|
| 90 |
+
def stop(self):
|
| 91 |
+
self.file.close()
|
| 92 |
+
self.file = None
|
| 93 |
+
|
| 94 |
+
def __enter__(self):
|
| 95 |
+
self.start()
|
| 96 |
+
return self
|
| 97 |
+
|
| 98 |
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
| 99 |
+
self.stop()
|
| 100 |
+
|
| 101 |
+
def log(self, data: dict):
|
| 102 |
+
filtered_data = dict(
|
| 103 |
+
filter(lambda x: self.filter_fn(*x), data.items()))
|
| 104 |
+
# save current as last log
|
| 105 |
+
self.last_log = filtered_data
|
| 106 |
+
for k, v in filtered_data.items():
|
| 107 |
+
if isinstance(v, numbers.Integral):
|
| 108 |
+
filtered_data[k] = int(v)
|
| 109 |
+
elif isinstance(v, numbers.Number):
|
| 110 |
+
filtered_data[k] = float(v)
|
| 111 |
+
buf = json.dumps(filtered_data)
|
| 112 |
+
# ensure one line per json
|
| 113 |
+
buf = buf.replace('\n','') + '\n'
|
| 114 |
+
self.file.write(buf)
|
| 115 |
+
|
| 116 |
+
def get_last_log(self):
|
| 117 |
+
return copy.deepcopy(self.last_log)
|
common/nested_dict_util.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import functools
|
| 2 |
+
|
| 3 |
+
def nested_dict_map(f, x):
|
| 4 |
+
"""
|
| 5 |
+
Map f over all leaf of nested dict x
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
if not isinstance(x, dict):
|
| 9 |
+
return f(x)
|
| 10 |
+
y = dict()
|
| 11 |
+
for key, value in x.items():
|
| 12 |
+
y[key] = nested_dict_map(f, value)
|
| 13 |
+
return y
|
| 14 |
+
|
| 15 |
+
def nested_dict_reduce(f, x):
|
| 16 |
+
"""
|
| 17 |
+
Map f over all values of nested dict x, and reduce to a single value
|
| 18 |
+
"""
|
| 19 |
+
if not isinstance(x, dict):
|
| 20 |
+
return x
|
| 21 |
+
|
| 22 |
+
reduced_values = list()
|
| 23 |
+
for value in x.values():
|
| 24 |
+
reduced_values.append(nested_dict_reduce(f, value))
|
| 25 |
+
y = functools.reduce(f, reduced_values)
|
| 26 |
+
return y
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def nested_dict_check(f, x):
|
| 30 |
+
bool_dict = nested_dict_map(f, x)
|
| 31 |
+
result = nested_dict_reduce(lambda x, y: x and y, bool_dict)
|
| 32 |
+
return result
|
common/normalize_util.py
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from diffusion_policy.model.common.normalizer import SingleFieldLinearNormalizer
|
| 2 |
+
from diffusion_policy.common.pytorch_util import dict_apply, dict_apply_reduce, dict_apply_split
|
| 3 |
+
import numpy as np
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def get_range_normalizer_from_stat(stat, output_max=1, output_min=-1, range_eps=1e-7):
|
| 7 |
+
# -1, 1 normalization
|
| 8 |
+
input_max = stat['max']
|
| 9 |
+
input_min = stat['min']
|
| 10 |
+
input_range = input_max - input_min
|
| 11 |
+
ignore_dim = input_range < range_eps
|
| 12 |
+
input_range[ignore_dim] = output_max - output_min
|
| 13 |
+
scale = (output_max - output_min) / input_range
|
| 14 |
+
offset = output_min - scale * input_min
|
| 15 |
+
offset[ignore_dim] = (output_max + output_min) / 2 - input_min[ignore_dim]
|
| 16 |
+
|
| 17 |
+
return SingleFieldLinearNormalizer.create_manual(
|
| 18 |
+
scale=scale,
|
| 19 |
+
offset=offset,
|
| 20 |
+
input_stats_dict=stat
|
| 21 |
+
)
|
| 22 |
+
|
| 23 |
+
def get_image_range_normalizer():
|
| 24 |
+
scale = np.array([2], dtype=np.float32)
|
| 25 |
+
offset = np.array([-1], dtype=np.float32)
|
| 26 |
+
stat = {
|
| 27 |
+
'min': np.array([0], dtype=np.float32),
|
| 28 |
+
'max': np.array([1], dtype=np.float32),
|
| 29 |
+
'mean': np.array([0.5], dtype=np.float32),
|
| 30 |
+
'std': np.array([np.sqrt(1/12)], dtype=np.float32)
|
| 31 |
+
}
|
| 32 |
+
return SingleFieldLinearNormalizer.create_manual(
|
| 33 |
+
scale=scale,
|
| 34 |
+
offset=offset,
|
| 35 |
+
input_stats_dict=stat
|
| 36 |
+
)
|
| 37 |
+
|
| 38 |
+
def get_identity_normalizer_from_stat(stat):
|
| 39 |
+
scale = np.ones_like(stat['min'])
|
| 40 |
+
offset = np.zeros_like(stat['min'])
|
| 41 |
+
return SingleFieldLinearNormalizer.create_manual(
|
| 42 |
+
scale=scale,
|
| 43 |
+
offset=offset,
|
| 44 |
+
input_stats_dict=stat
|
| 45 |
+
)
|
| 46 |
+
|
| 47 |
+
def robomimic_abs_action_normalizer_from_stat(stat, rotation_transformer):
|
| 48 |
+
result = dict_apply_split(
|
| 49 |
+
stat, lambda x: {
|
| 50 |
+
'pos': x[...,:3],
|
| 51 |
+
'rot': x[...,3:6],
|
| 52 |
+
'gripper': x[...,6:]
|
| 53 |
+
})
|
| 54 |
+
|
| 55 |
+
def get_pos_param_info(stat, output_max=1, output_min=-1, range_eps=1e-7):
|
| 56 |
+
# -1, 1 normalization
|
| 57 |
+
input_max = stat['max']
|
| 58 |
+
input_min = stat['min']
|
| 59 |
+
input_range = input_max - input_min
|
| 60 |
+
ignore_dim = input_range < range_eps
|
| 61 |
+
input_range[ignore_dim] = output_max - output_min
|
| 62 |
+
scale = (output_max - output_min) / input_range
|
| 63 |
+
offset = output_min - scale * input_min
|
| 64 |
+
offset[ignore_dim] = (output_max + output_min) / 2 - input_min[ignore_dim]
|
| 65 |
+
|
| 66 |
+
return {'scale': scale, 'offset': offset}, stat
|
| 67 |
+
|
| 68 |
+
def get_rot_param_info(stat):
|
| 69 |
+
example = rotation_transformer.forward(stat['mean'])
|
| 70 |
+
scale = np.ones_like(example)
|
| 71 |
+
offset = np.zeros_like(example)
|
| 72 |
+
info = {
|
| 73 |
+
'max': np.ones_like(example),
|
| 74 |
+
'min': np.full_like(example, -1),
|
| 75 |
+
'mean': np.zeros_like(example),
|
| 76 |
+
'std': np.ones_like(example)
|
| 77 |
+
}
|
| 78 |
+
return {'scale': scale, 'offset': offset}, info
|
| 79 |
+
|
| 80 |
+
def get_gripper_param_info(stat):
|
| 81 |
+
example = stat['max']
|
| 82 |
+
scale = np.ones_like(example)
|
| 83 |
+
offset = np.zeros_like(example)
|
| 84 |
+
info = {
|
| 85 |
+
'max': np.ones_like(example),
|
| 86 |
+
'min': np.full_like(example, -1),
|
| 87 |
+
'mean': np.zeros_like(example),
|
| 88 |
+
'std': np.ones_like(example)
|
| 89 |
+
}
|
| 90 |
+
return {'scale': scale, 'offset': offset}, info
|
| 91 |
+
|
| 92 |
+
pos_param, pos_info = get_pos_param_info(result['pos'])
|
| 93 |
+
rot_param, rot_info = get_rot_param_info(result['rot'])
|
| 94 |
+
gripper_param, gripper_info = get_gripper_param_info(result['gripper'])
|
| 95 |
+
|
| 96 |
+
param = dict_apply_reduce(
|
| 97 |
+
[pos_param, rot_param, gripper_param],
|
| 98 |
+
lambda x: np.concatenate(x,axis=-1))
|
| 99 |
+
info = dict_apply_reduce(
|
| 100 |
+
[pos_info, rot_info, gripper_info],
|
| 101 |
+
lambda x: np.concatenate(x,axis=-1))
|
| 102 |
+
|
| 103 |
+
return SingleFieldLinearNormalizer.create_manual(
|
| 104 |
+
scale=param['scale'],
|
| 105 |
+
offset=param['offset'],
|
| 106 |
+
input_stats_dict=info
|
| 107 |
+
)
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
def robomimic_abs_action_only_normalizer_from_stat(stat):
|
| 111 |
+
result = dict_apply_split(
|
| 112 |
+
stat, lambda x: {
|
| 113 |
+
'pos': x[...,:3],
|
| 114 |
+
'other': x[...,3:]
|
| 115 |
+
})
|
| 116 |
+
|
| 117 |
+
def get_pos_param_info(stat, output_max=1, output_min=-1, range_eps=1e-7):
|
| 118 |
+
# -1, 1 normalization
|
| 119 |
+
input_max = stat['max']
|
| 120 |
+
input_min = stat['min']
|
| 121 |
+
input_range = input_max - input_min
|
| 122 |
+
ignore_dim = input_range < range_eps
|
| 123 |
+
input_range[ignore_dim] = output_max - output_min
|
| 124 |
+
scale = (output_max - output_min) / input_range
|
| 125 |
+
offset = output_min - scale * input_min
|
| 126 |
+
offset[ignore_dim] = (output_max + output_min) / 2 - input_min[ignore_dim]
|
| 127 |
+
|
| 128 |
+
return {'scale': scale, 'offset': offset}, stat
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
def get_other_param_info(stat):
|
| 132 |
+
example = stat['max']
|
| 133 |
+
scale = np.ones_like(example)
|
| 134 |
+
offset = np.zeros_like(example)
|
| 135 |
+
info = {
|
| 136 |
+
'max': np.ones_like(example),
|
| 137 |
+
'min': np.full_like(example, -1),
|
| 138 |
+
'mean': np.zeros_like(example),
|
| 139 |
+
'std': np.ones_like(example)
|
| 140 |
+
}
|
| 141 |
+
return {'scale': scale, 'offset': offset}, info
|
| 142 |
+
|
| 143 |
+
pos_param, pos_info = get_pos_param_info(result['pos'])
|
| 144 |
+
other_param, other_info = get_other_param_info(result['other'])
|
| 145 |
+
|
| 146 |
+
param = dict_apply_reduce(
|
| 147 |
+
[pos_param, other_param],
|
| 148 |
+
lambda x: np.concatenate(x,axis=-1))
|
| 149 |
+
info = dict_apply_reduce(
|
| 150 |
+
[pos_info, other_info],
|
| 151 |
+
lambda x: np.concatenate(x,axis=-1))
|
| 152 |
+
|
| 153 |
+
return SingleFieldLinearNormalizer.create_manual(
|
| 154 |
+
scale=param['scale'],
|
| 155 |
+
offset=param['offset'],
|
| 156 |
+
input_stats_dict=info
|
| 157 |
+
)
|
| 158 |
+
|
| 159 |
+
|
| 160 |
+
def robomimic_abs_action_only_dual_arm_normalizer_from_stat(stat):
|
| 161 |
+
Da = stat['max'].shape[-1]
|
| 162 |
+
Dah = Da // 2
|
| 163 |
+
result = dict_apply_split(
|
| 164 |
+
stat, lambda x: {
|
| 165 |
+
'pos0': x[...,:3],
|
| 166 |
+
'other0': x[...,3:Dah],
|
| 167 |
+
'pos1': x[...,Dah:Dah+3],
|
| 168 |
+
'other1': x[...,Dah+3:]
|
| 169 |
+
})
|
| 170 |
+
|
| 171 |
+
def get_pos_param_info(stat, output_max=1, output_min=-1, range_eps=1e-7):
|
| 172 |
+
# -1, 1 normalization
|
| 173 |
+
input_max = stat['max']
|
| 174 |
+
input_min = stat['min']
|
| 175 |
+
input_range = input_max - input_min
|
| 176 |
+
ignore_dim = input_range < range_eps
|
| 177 |
+
input_range[ignore_dim] = output_max - output_min
|
| 178 |
+
scale = (output_max - output_min) / input_range
|
| 179 |
+
offset = output_min - scale * input_min
|
| 180 |
+
offset[ignore_dim] = (output_max + output_min) / 2 - input_min[ignore_dim]
|
| 181 |
+
|
| 182 |
+
return {'scale': scale, 'offset': offset}, stat
|
| 183 |
+
|
| 184 |
+
|
| 185 |
+
def get_other_param_info(stat):
|
| 186 |
+
example = stat['max']
|
| 187 |
+
scale = np.ones_like(example)
|
| 188 |
+
offset = np.zeros_like(example)
|
| 189 |
+
info = {
|
| 190 |
+
'max': np.ones_like(example),
|
| 191 |
+
'min': np.full_like(example, -1),
|
| 192 |
+
'mean': np.zeros_like(example),
|
| 193 |
+
'std': np.ones_like(example)
|
| 194 |
+
}
|
| 195 |
+
return {'scale': scale, 'offset': offset}, info
|
| 196 |
+
|
| 197 |
+
pos0_param, pos0_info = get_pos_param_info(result['pos0'])
|
| 198 |
+
pos1_param, pos1_info = get_pos_param_info(result['pos1'])
|
| 199 |
+
other0_param, other0_info = get_other_param_info(result['other0'])
|
| 200 |
+
other1_param, other1_info = get_other_param_info(result['other1'])
|
| 201 |
+
|
| 202 |
+
param = dict_apply_reduce(
|
| 203 |
+
[pos0_param, other0_param, pos1_param, other1_param],
|
| 204 |
+
lambda x: np.concatenate(x,axis=-1))
|
| 205 |
+
info = dict_apply_reduce(
|
| 206 |
+
[pos0_info, other0_info, pos1_info, other1_info],
|
| 207 |
+
lambda x: np.concatenate(x,axis=-1))
|
| 208 |
+
|
| 209 |
+
return SingleFieldLinearNormalizer.create_manual(
|
| 210 |
+
scale=param['scale'],
|
| 211 |
+
offset=param['offset'],
|
| 212 |
+
input_stats_dict=info
|
| 213 |
+
)
|
| 214 |
+
|
| 215 |
+
|
| 216 |
+
def array_to_stats(arr: np.ndarray):
|
| 217 |
+
stat = {
|
| 218 |
+
'min': np.min(arr, axis=0),
|
| 219 |
+
'max': np.max(arr, axis=0),
|
| 220 |
+
'mean': np.mean(arr, axis=0),
|
| 221 |
+
'std': np.std(arr, axis=0)
|
| 222 |
+
}
|
| 223 |
+
return stat
|
common/pose_trajectory_interpolator.py
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Union
|
| 2 |
+
import numbers
|
| 3 |
+
import numpy as np
|
| 4 |
+
import scipy.interpolate as si
|
| 5 |
+
import scipy.spatial.transform as st
|
| 6 |
+
|
| 7 |
+
def rotation_distance(a: st.Rotation, b: st.Rotation) -> float:
|
| 8 |
+
return (b * a.inv()).magnitude()
|
| 9 |
+
|
| 10 |
+
def pose_distance(start_pose, end_pose):
|
| 11 |
+
start_pose = np.array(start_pose)
|
| 12 |
+
end_pose = np.array(end_pose)
|
| 13 |
+
start_pos = start_pose[:3]
|
| 14 |
+
end_pos = end_pose[:3]
|
| 15 |
+
start_rot = st.Rotation.from_rotvec(start_pose[3:])
|
| 16 |
+
end_rot = st.Rotation.from_rotvec(end_pose[3:])
|
| 17 |
+
pos_dist = np.linalg.norm(end_pos - start_pos)
|
| 18 |
+
rot_dist = rotation_distance(start_rot, end_rot)
|
| 19 |
+
return pos_dist, rot_dist
|
| 20 |
+
|
| 21 |
+
class PoseTrajectoryInterpolator:
|
| 22 |
+
def __init__(self, times: np.ndarray, poses: np.ndarray):
|
| 23 |
+
assert len(times) >= 1
|
| 24 |
+
assert len(poses) == len(times)
|
| 25 |
+
if not isinstance(times, np.ndarray):
|
| 26 |
+
times = np.array(times)
|
| 27 |
+
if not isinstance(poses, np.ndarray):
|
| 28 |
+
poses = np.array(poses)
|
| 29 |
+
|
| 30 |
+
if len(times) == 1:
|
| 31 |
+
# special treatment for single step interpolation
|
| 32 |
+
self.single_step = True
|
| 33 |
+
self._times = times
|
| 34 |
+
self._poses = poses
|
| 35 |
+
else:
|
| 36 |
+
self.single_step = False
|
| 37 |
+
assert np.all(times[1:] >= times[:-1])
|
| 38 |
+
|
| 39 |
+
pos = poses[:,:3]
|
| 40 |
+
rot = st.Rotation.from_rotvec(poses[:,3:])
|
| 41 |
+
|
| 42 |
+
self.pos_interp = si.interp1d(times, pos,
|
| 43 |
+
axis=0, assume_sorted=True)
|
| 44 |
+
self.rot_interp = st.Slerp(times, rot)
|
| 45 |
+
|
| 46 |
+
@property
|
| 47 |
+
def times(self) -> np.ndarray:
|
| 48 |
+
if self.single_step:
|
| 49 |
+
return self._times
|
| 50 |
+
else:
|
| 51 |
+
return self.pos_interp.x
|
| 52 |
+
|
| 53 |
+
@property
|
| 54 |
+
def poses(self) -> np.ndarray:
|
| 55 |
+
if self.single_step:
|
| 56 |
+
return self._poses
|
| 57 |
+
else:
|
| 58 |
+
n = len(self.times)
|
| 59 |
+
poses = np.zeros((n, 6))
|
| 60 |
+
poses[:,:3] = self.pos_interp.y
|
| 61 |
+
poses[:,3:] = self.rot_interp(self.times).as_rotvec()
|
| 62 |
+
return poses
|
| 63 |
+
|
| 64 |
+
def trim(self,
|
| 65 |
+
start_t: float, end_t: float
|
| 66 |
+
) -> "PoseTrajectoryInterpolator":
|
| 67 |
+
assert start_t <= end_t
|
| 68 |
+
times = self.times
|
| 69 |
+
should_keep = (start_t < times) & (times < end_t)
|
| 70 |
+
keep_times = times[should_keep]
|
| 71 |
+
all_times = np.concatenate([[start_t], keep_times, [end_t]])
|
| 72 |
+
# remove duplicates, Slerp requires strictly increasing x
|
| 73 |
+
all_times = np.unique(all_times)
|
| 74 |
+
# interpolate
|
| 75 |
+
all_poses = self(all_times)
|
| 76 |
+
return PoseTrajectoryInterpolator(times=all_times, poses=all_poses)
|
| 77 |
+
|
| 78 |
+
def drive_to_waypoint(self,
|
| 79 |
+
pose, time, curr_time,
|
| 80 |
+
max_pos_speed=np.inf,
|
| 81 |
+
max_rot_speed=np.inf
|
| 82 |
+
) -> "PoseTrajectoryInterpolator":
|
| 83 |
+
assert(max_pos_speed > 0)
|
| 84 |
+
assert(max_rot_speed > 0)
|
| 85 |
+
time = max(time, curr_time)
|
| 86 |
+
|
| 87 |
+
curr_pose = self(curr_time)
|
| 88 |
+
pos_dist, rot_dist = pose_distance(curr_pose, pose)
|
| 89 |
+
pos_min_duration = pos_dist / max_pos_speed
|
| 90 |
+
rot_min_duration = rot_dist / max_rot_speed
|
| 91 |
+
duration = time - curr_time
|
| 92 |
+
duration = max(duration, max(pos_min_duration, rot_min_duration))
|
| 93 |
+
assert duration >= 0
|
| 94 |
+
last_waypoint_time = curr_time + duration
|
| 95 |
+
|
| 96 |
+
# insert new pose
|
| 97 |
+
trimmed_interp = self.trim(curr_time, curr_time)
|
| 98 |
+
times = np.append(trimmed_interp.times, [last_waypoint_time], axis=0)
|
| 99 |
+
poses = np.append(trimmed_interp.poses, [pose], axis=0)
|
| 100 |
+
|
| 101 |
+
# create new interpolator
|
| 102 |
+
final_interp = PoseTrajectoryInterpolator(times, poses)
|
| 103 |
+
return final_interp
|
| 104 |
+
|
| 105 |
+
def schedule_waypoint(self,
|
| 106 |
+
pose, time,
|
| 107 |
+
max_pos_speed=np.inf,
|
| 108 |
+
max_rot_speed=np.inf,
|
| 109 |
+
curr_time=None,
|
| 110 |
+
last_waypoint_time=None
|
| 111 |
+
) -> "PoseTrajectoryInterpolator":
|
| 112 |
+
assert(max_pos_speed > 0)
|
| 113 |
+
assert(max_rot_speed > 0)
|
| 114 |
+
if last_waypoint_time is not None:
|
| 115 |
+
assert curr_time is not None
|
| 116 |
+
|
| 117 |
+
# trim current interpolator to between curr_time and last_waypoint_time
|
| 118 |
+
start_time = self.times[0]
|
| 119 |
+
end_time = self.times[-1]
|
| 120 |
+
assert start_time <= end_time
|
| 121 |
+
|
| 122 |
+
if curr_time is not None:
|
| 123 |
+
if time <= curr_time:
|
| 124 |
+
# if insert time is earlier than current time
|
| 125 |
+
# no effect should be done to the interpolator
|
| 126 |
+
return self
|
| 127 |
+
# now, curr_time < time
|
| 128 |
+
start_time = max(curr_time, start_time)
|
| 129 |
+
|
| 130 |
+
if last_waypoint_time is not None:
|
| 131 |
+
# if last_waypoint_time is earlier than start_time
|
| 132 |
+
# use start_time
|
| 133 |
+
if time <= last_waypoint_time:
|
| 134 |
+
end_time = curr_time
|
| 135 |
+
else:
|
| 136 |
+
end_time = max(last_waypoint_time, curr_time)
|
| 137 |
+
else:
|
| 138 |
+
end_time = curr_time
|
| 139 |
+
|
| 140 |
+
end_time = min(end_time, time)
|
| 141 |
+
start_time = min(start_time, end_time)
|
| 142 |
+
# end time should be the latest of all times except time
|
| 143 |
+
# after this we can assume order (proven by zhenjia, due to the 2 min operations)
|
| 144 |
+
|
| 145 |
+
# Constraints:
|
| 146 |
+
# start_time <= end_time <= time (proven by zhenjia)
|
| 147 |
+
# curr_time <= start_time (proven by zhenjia)
|
| 148 |
+
# curr_time <= time (proven by zhenjia)
|
| 149 |
+
|
| 150 |
+
# time can't change
|
| 151 |
+
# last_waypoint_time can't change
|
| 152 |
+
# curr_time can't change
|
| 153 |
+
assert start_time <= end_time
|
| 154 |
+
assert end_time <= time
|
| 155 |
+
if last_waypoint_time is not None:
|
| 156 |
+
if time <= last_waypoint_time:
|
| 157 |
+
assert end_time == curr_time
|
| 158 |
+
else:
|
| 159 |
+
assert end_time == max(last_waypoint_time, curr_time)
|
| 160 |
+
|
| 161 |
+
if curr_time is not None:
|
| 162 |
+
assert curr_time <= start_time
|
| 163 |
+
assert curr_time <= time
|
| 164 |
+
|
| 165 |
+
trimmed_interp = self.trim(start_time, end_time)
|
| 166 |
+
# after this, all waypoints in trimmed_interp is within start_time and end_time
|
| 167 |
+
# and is earlier than time
|
| 168 |
+
|
| 169 |
+
# determine speed
|
| 170 |
+
duration = time - end_time
|
| 171 |
+
end_pose = trimmed_interp(end_time)
|
| 172 |
+
pos_dist, rot_dist = pose_distance(pose, end_pose)
|
| 173 |
+
pos_min_duration = pos_dist / max_pos_speed
|
| 174 |
+
rot_min_duration = rot_dist / max_rot_speed
|
| 175 |
+
duration = max(duration, max(pos_min_duration, rot_min_duration))
|
| 176 |
+
assert duration >= 0
|
| 177 |
+
last_waypoint_time = end_time + duration
|
| 178 |
+
|
| 179 |
+
# insert new pose
|
| 180 |
+
times = np.append(trimmed_interp.times, [last_waypoint_time], axis=0)
|
| 181 |
+
poses = np.append(trimmed_interp.poses, [pose], axis=0)
|
| 182 |
+
|
| 183 |
+
# create new interpolator
|
| 184 |
+
final_interp = PoseTrajectoryInterpolator(times, poses)
|
| 185 |
+
return final_interp
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
def __call__(self, t: Union[numbers.Number, np.ndarray]) -> np.ndarray:
|
| 189 |
+
is_single = False
|
| 190 |
+
if isinstance(t, numbers.Number):
|
| 191 |
+
is_single = True
|
| 192 |
+
t = np.array([t])
|
| 193 |
+
|
| 194 |
+
pose = np.zeros((len(t), 6))
|
| 195 |
+
if self.single_step:
|
| 196 |
+
pose[:] = self._poses[0]
|
| 197 |
+
else:
|
| 198 |
+
start_time = self.times[0]
|
| 199 |
+
end_time = self.times[-1]
|
| 200 |
+
t = np.clip(t, start_time, end_time)
|
| 201 |
+
|
| 202 |
+
pose = np.zeros((len(t), 6))
|
| 203 |
+
pose[:,:3] = self.pos_interp(t)
|
| 204 |
+
pose[:,3:] = self.rot_interp(t).as_rotvec()
|
| 205 |
+
|
| 206 |
+
if is_single:
|
| 207 |
+
pose = pose[0]
|
| 208 |
+
return pose
|
common/precise_sleep.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import time
|
| 2 |
+
|
| 3 |
+
def precise_sleep(dt: float, slack_time: float=0.001, time_func=time.monotonic):
|
| 4 |
+
"""
|
| 5 |
+
Use hybrid of time.sleep and spinning to minimize jitter.
|
| 6 |
+
Sleep dt - slack_time seconds first, then spin for the rest.
|
| 7 |
+
"""
|
| 8 |
+
t_start = time_func()
|
| 9 |
+
if dt > slack_time:
|
| 10 |
+
time.sleep(dt - slack_time)
|
| 11 |
+
t_end = t_start + dt
|
| 12 |
+
while time_func() < t_end:
|
| 13 |
+
pass
|
| 14 |
+
return
|
| 15 |
+
|
| 16 |
+
def precise_wait(t_end: float, slack_time: float=0.001, time_func=time.monotonic):
|
| 17 |
+
t_start = time_func()
|
| 18 |
+
t_wait = t_end - t_start
|
| 19 |
+
if t_wait > 0:
|
| 20 |
+
t_sleep = t_wait - slack_time
|
| 21 |
+
if t_sleep > 0:
|
| 22 |
+
time.sleep(t_sleep)
|
| 23 |
+
while time_func() < t_end:
|
| 24 |
+
pass
|
| 25 |
+
return
|
common/pref_replay_buffer.py
ADDED
|
@@ -0,0 +1,365 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Union, Dict, Optional
|
| 2 |
+
import os
|
| 3 |
+
import math
|
| 4 |
+
import numbers
|
| 5 |
+
import zarr
|
| 6 |
+
import numcodecs
|
| 7 |
+
import numpy as np
|
| 8 |
+
from functools import cached_property
|
| 9 |
+
|
| 10 |
+
def check_chunks_compatible(chunks: tuple, shape: tuple):
|
| 11 |
+
assert len(shape) == len(chunks)
|
| 12 |
+
for c in chunks:
|
| 13 |
+
assert isinstance(c, numbers.Integral)
|
| 14 |
+
assert c > 0
|
| 15 |
+
|
| 16 |
+
def rechunk_recompress_array(group, name,
|
| 17 |
+
chunks=None, chunk_length=None,
|
| 18 |
+
compressor=None, tmp_key='_temp'):
|
| 19 |
+
old_arr = group[name]
|
| 20 |
+
if chunks is None:
|
| 21 |
+
if chunk_length is not None:
|
| 22 |
+
chunks = (chunk_length,) + old_arr.chunks[1:]
|
| 23 |
+
else:
|
| 24 |
+
chunks = old_arr.chunks
|
| 25 |
+
check_chunks_compatible(chunks, old_arr.shape)
|
| 26 |
+
|
| 27 |
+
if compressor is None:
|
| 28 |
+
compressor = old_arr.compressor
|
| 29 |
+
|
| 30 |
+
if (chunks == old_arr.chunks) and (compressor == old_arr.compressor):
|
| 31 |
+
# no change
|
| 32 |
+
return old_arr
|
| 33 |
+
|
| 34 |
+
# rechunk recompress
|
| 35 |
+
group.move(name, tmp_key)
|
| 36 |
+
old_arr = group[tmp_key]
|
| 37 |
+
n_copied, n_skipped, n_bytes_copied = zarr.copy(
|
| 38 |
+
source=old_arr,
|
| 39 |
+
dest=group,
|
| 40 |
+
name=name,
|
| 41 |
+
chunks=chunks,
|
| 42 |
+
compressor=compressor,
|
| 43 |
+
)
|
| 44 |
+
del group[tmp_key]
|
| 45 |
+
arr = group[name]
|
| 46 |
+
return arr
|
| 47 |
+
|
| 48 |
+
def get_optimal_chunks(shape, dtype,
|
| 49 |
+
target_chunk_bytes=2e6,
|
| 50 |
+
max_chunk_length=None):
|
| 51 |
+
"""
|
| 52 |
+
Common shapes
|
| 53 |
+
T,D
|
| 54 |
+
T,N,D
|
| 55 |
+
T,H,W,C
|
| 56 |
+
T,N,H,W,C
|
| 57 |
+
"""
|
| 58 |
+
itemsize = np.dtype(dtype).itemsize
|
| 59 |
+
# reversed
|
| 60 |
+
rshape = list(shape[::-1])
|
| 61 |
+
if max_chunk_length is not None:
|
| 62 |
+
rshape[-1] = int(max_chunk_length)
|
| 63 |
+
split_idx = len(shape)-1
|
| 64 |
+
for i in range(len(shape)-1):
|
| 65 |
+
this_chunk_bytes = itemsize * np.prod(rshape[:i])
|
| 66 |
+
next_chunk_bytes = itemsize * np.prod(rshape[:i+1])
|
| 67 |
+
if this_chunk_bytes <= target_chunk_bytes \
|
| 68 |
+
and next_chunk_bytes > target_chunk_bytes:
|
| 69 |
+
split_idx = i
|
| 70 |
+
|
| 71 |
+
rchunks = rshape[:split_idx]
|
| 72 |
+
item_chunk_bytes = itemsize * np.prod(rshape[:split_idx])
|
| 73 |
+
this_max_chunk_length = rshape[split_idx]
|
| 74 |
+
next_chunk_length = min(this_max_chunk_length, math.ceil(
|
| 75 |
+
target_chunk_bytes / item_chunk_bytes))
|
| 76 |
+
rchunks.append(next_chunk_length)
|
| 77 |
+
len_diff = len(shape) - len(rchunks)
|
| 78 |
+
rchunks.extend([1] * len_diff)
|
| 79 |
+
chunks = tuple(rchunks[::-1])
|
| 80 |
+
# print(np.prod(chunks) * itemsize / target_chunk_bytes)
|
| 81 |
+
return chunks
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
class PrefReplayBuffer:
|
| 85 |
+
"""
|
| 86 |
+
Zarr-based temporal data structure specifically for preference dataset.
|
| 87 |
+
Stores pairs of trajectories (observations, actions) along with votes.
|
| 88 |
+
"""
|
| 89 |
+
|
| 90 |
+
def __init__(self, root: Union[zarr.Group, Dict[str, dict]]):
|
| 91 |
+
"""
|
| 92 |
+
Initialize the preference replay buffer. Use class methods to create or load buffers.
|
| 93 |
+
"""
|
| 94 |
+
assert 'data' in root
|
| 95 |
+
assert 'meta' in root
|
| 96 |
+
for key, value in root['data'].items():
|
| 97 |
+
assert value.shape[0] == root['meta']['votes'].shape[0]
|
| 98 |
+
self.root = root
|
| 99 |
+
|
| 100 |
+
# ============= create constructors ===============
|
| 101 |
+
@classmethod
|
| 102 |
+
def create_empty_zarr(cls, storage=None, root=None):
|
| 103 |
+
if root is None:
|
| 104 |
+
if storage is None:
|
| 105 |
+
storage = zarr.MemoryStore()
|
| 106 |
+
root = zarr.group(store=storage)
|
| 107 |
+
data = root.require_group('data', overwrite=False)
|
| 108 |
+
meta = root.require_group('meta', overwrite=False)
|
| 109 |
+
|
| 110 |
+
if 'votes' not in meta:
|
| 111 |
+
votes = meta.zeros('votes', shape=(0,), dtype=np.float32, compressor=None, overwrite=False)
|
| 112 |
+
if 'votes_2' not in meta:
|
| 113 |
+
votes_2 = meta.zeros('votes_2', shape=(0,), dtype=np.float32, compressor=None, overwrite=False)
|
| 114 |
+
|
| 115 |
+
return cls(root=root)
|
| 116 |
+
|
| 117 |
+
@classmethod
|
| 118 |
+
def create_empty_numpy(cls):
|
| 119 |
+
root = {
|
| 120 |
+
'data': dict(),
|
| 121 |
+
'meta': {
|
| 122 |
+
'episode_ends': np.zeros((0,), dtype=np.int64),
|
| 123 |
+
'votes': np.zeros((0,), dtype=np.float32),
|
| 124 |
+
'votes_2': np.zeros((0,), dtype=np.float32),
|
| 125 |
+
'length': np.zeros((0,), dtype=np.int64),
|
| 126 |
+
'length_2': np.zeros((0,), dtype=np.int64),
|
| 127 |
+
'beta_priori': np.zeros((0,), dtype=np.float32),
|
| 128 |
+
'beta_priori_2': np.zeros((0,), dtype=np.float32),
|
| 129 |
+
}
|
| 130 |
+
}
|
| 131 |
+
return cls(root=root)
|
| 132 |
+
|
| 133 |
+
@classmethod
|
| 134 |
+
def create_from_group(cls, group, **kwargs):
|
| 135 |
+
if 'data' not in group:
|
| 136 |
+
# create from scratch
|
| 137 |
+
buffer = cls.create_empty_zarr(root=group, **kwargs)
|
| 138 |
+
else:
|
| 139 |
+
# already exists
|
| 140 |
+
buffer = cls(root=group, **kwargs)
|
| 141 |
+
return buffer
|
| 142 |
+
|
| 143 |
+
@classmethod
|
| 144 |
+
def create_from_path(cls, zarr_path, mode='r', **kwargs):
|
| 145 |
+
"""
|
| 146 |
+
Open a Zarr file from disk for large datasets that cannot fit in memory.
|
| 147 |
+
"""
|
| 148 |
+
group = zarr.open(zarr_path, mode=mode)
|
| 149 |
+
return cls.create_from_group(group=group, **kwargs)
|
| 150 |
+
|
| 151 |
+
# ============= Add episodes ===============
|
| 152 |
+
def add_pref_episode(self, data: Dict[str, np.ndarray],
|
| 153 |
+
meta_data: Optional[Dict[str, Union[np.ndarray, int]]] = None,
|
| 154 |
+
chunks: Optional[Dict[str, tuple]] = dict(),
|
| 155 |
+
compressors: Union[str, numcodecs.abc.Codec, dict] = dict()):
|
| 156 |
+
"""
|
| 157 |
+
Add a pair of episodes (obs/action for each trajectory) along with metadata (votes).
|
| 158 |
+
"""
|
| 159 |
+
assert 'obs' in data and 'obs_2' in data, "obs and obs_2 keys are required"
|
| 160 |
+
assert 'action' in data and 'action_2' in data, "action and action_2 keys are required"
|
| 161 |
+
|
| 162 |
+
is_zarr = isinstance(self.root, zarr.Group)
|
| 163 |
+
curr_len = len(self.root['meta']['votes'])
|
| 164 |
+
episode_length = len(data['obs'])
|
| 165 |
+
new_len = curr_len + 1
|
| 166 |
+
|
| 167 |
+
# Add trajectory 1
|
| 168 |
+
for key in ['obs', 'action']:
|
| 169 |
+
# Create the new shape to accommodate all time steps
|
| 170 |
+
value = data[key]
|
| 171 |
+
new_shape = (new_len,) + (episode_length,) + data[key].shape[1:] # This will set (new_len, T, dim)
|
| 172 |
+
|
| 173 |
+
if key not in self.root['data']:
|
| 174 |
+
# Create a new array if it doesn't exist
|
| 175 |
+
if is_zarr:
|
| 176 |
+
cks = self._resolve_array_chunks(chunks, key, data[key])
|
| 177 |
+
cpr = self._resolve_array_compressor(compressors, key, data[key])
|
| 178 |
+
arr = self.root['data'].zeros(name=key, shape=new_shape, chunks=cks, dtype=data[key].dtype, compressor=cpr)
|
| 179 |
+
else:
|
| 180 |
+
arr = np.zeros(new_shape, dtype=data[key].dtype)
|
| 181 |
+
self.root['data'][key] = arr
|
| 182 |
+
else:
|
| 183 |
+
arr = self.root['data'][key]
|
| 184 |
+
if is_zarr:
|
| 185 |
+
arr.resize(new_shape)
|
| 186 |
+
else:
|
| 187 |
+
arr.resize(new_shape, refcheck=False)
|
| 188 |
+
|
| 189 |
+
# Store the full sequence, adjusting the shape to match the time steps in data[key]
|
| 190 |
+
arr[new_len-1, -value.shape[0]:, :] = value # Now this assumes data[key] has shape (T, dim)
|
| 191 |
+
|
| 192 |
+
|
| 193 |
+
# Add trajectory 2 (obs_2, action_2)
|
| 194 |
+
for key in ['obs_2', 'action_2']:
|
| 195 |
+
value = data[key]
|
| 196 |
+
# Create the new shape to accommodate all time steps
|
| 197 |
+
new_shape = (new_len,) + (episode_length,) + data[key].shape[1:] # This will set (new_len, T, dim)
|
| 198 |
+
|
| 199 |
+
if key not in self.root['data']:
|
| 200 |
+
# Create a new array if it doesn't exist
|
| 201 |
+
if is_zarr:
|
| 202 |
+
cks = self._resolve_array_chunks(chunks, key, data[key])
|
| 203 |
+
cpr = self._resolve_array_compressor(compressors, key, data[key])
|
| 204 |
+
arr = self.root['data'].zeros(name = key, shape=new_shape, chunks=cks, dtype=data[key].dtype, compressor=cpr)
|
| 205 |
+
else:
|
| 206 |
+
arr = np.zeros(new_shape, dtype=data[key].dtype)
|
| 207 |
+
self.root['data'][key] = arr
|
| 208 |
+
else:
|
| 209 |
+
arr = self.root['data'][key]
|
| 210 |
+
if is_zarr:
|
| 211 |
+
arr.resize(new_shape)
|
| 212 |
+
else:
|
| 213 |
+
arr.resize(new_shape, refcheck=False)
|
| 214 |
+
|
| 215 |
+
# Store the full sequence, adjusting the shape to match the time steps in data[key]
|
| 216 |
+
arr[new_len-1, -value.shape[0]:, :] = value # Now this assumes data[key] has shape (T, dim)
|
| 217 |
+
|
| 218 |
+
# Add votes to meta
|
| 219 |
+
if meta_data:
|
| 220 |
+
for key in ['votes', 'votes_2']:
|
| 221 |
+
new_shape = (new_len,) + (1,)
|
| 222 |
+
if key not in self.root['meta']:
|
| 223 |
+
if is_zarr:
|
| 224 |
+
self.root['meta'].zeros(name=key, shape=new_shape, chunks=new_shape, dtype=np.float32)
|
| 225 |
+
else:
|
| 226 |
+
self.root['meta'][key] = np.zeros(new_shape, dtype=np.float32)
|
| 227 |
+
arr = self.root['meta'][key]
|
| 228 |
+
if is_zarr:
|
| 229 |
+
arr.resize(new_shape)
|
| 230 |
+
else:
|
| 231 |
+
arr.resize(new_shape, refcheck=False)
|
| 232 |
+
arr[new_len-1] = meta_data[key]
|
| 233 |
+
|
| 234 |
+
for key in ['length', 'length_2']:
|
| 235 |
+
new_shape = (new_len,) + (1,)
|
| 236 |
+
if key not in self.root['meta']:
|
| 237 |
+
if is_zarr:
|
| 238 |
+
self.root['meta'].zeros(name=key, shape=new_shape, chunks=new_shape, dtype=np.float32)
|
| 239 |
+
else:
|
| 240 |
+
self.root['meta'][key] = np.zeros(new_shape, dtype=np.float32)
|
| 241 |
+
arr = self.root['meta'][key]
|
| 242 |
+
if is_zarr:
|
| 243 |
+
arr.resize(new_shape)
|
| 244 |
+
else:
|
| 245 |
+
arr.resize(new_shape, refcheck=False)
|
| 246 |
+
arr[new_len-1] = meta_data[key]
|
| 247 |
+
|
| 248 |
+
for key in ['beta_priori', 'beta_priori_2']:
|
| 249 |
+
new_shape = (new_len,) + (2,)
|
| 250 |
+
if key not in self.root['meta']:
|
| 251 |
+
if is_zarr:
|
| 252 |
+
self.root['meta'].zeros(name=key, shape=new_shape, chunks=new_shape, dtype=np.float32)
|
| 253 |
+
else:
|
| 254 |
+
self.root['meta'][key] = np.zeros(new_shape, dtype=np.float32)
|
| 255 |
+
arr = self.root['meta'][key]
|
| 256 |
+
if is_zarr:
|
| 257 |
+
arr.resize(new_shape)
|
| 258 |
+
else:
|
| 259 |
+
arr.resize(new_shape, refcheck=False)
|
| 260 |
+
arr[new_len-1] = meta_data[key]
|
| 261 |
+
|
| 262 |
+
# ============= Get episodes ===============
|
| 263 |
+
def get_pref_episode(self, idx: int, copy: bool = False):
|
| 264 |
+
"""
|
| 265 |
+
Get a pair of episodes by index, including observation and action sequences for both trajectories.
|
| 266 |
+
"""
|
| 267 |
+
if copy:
|
| 268 |
+
return {
|
| 269 |
+
'obs': self.root['data']['obs'][idx].copy(),
|
| 270 |
+
'action': self.root['data']['action'][idx].copy(),
|
| 271 |
+
'obs_2': self.root['data']['obs_2'][idx].copy(),
|
| 272 |
+
'action_2': self.root['data']['action_2'][idx].copy(),
|
| 273 |
+
'votes': self.root['meta']['votes'][idx].copy(),
|
| 274 |
+
'votes_2': self.root['meta']['votes_2'][idx].copy(),
|
| 275 |
+
'length': self.root['meta']['length'][idx].copy(),
|
| 276 |
+
'length_2': self.root['meta']['length_2'][idx].copy(),
|
| 277 |
+
'beta_priori': self.root['meta']['beta_priori'][idx].copy(),
|
| 278 |
+
'beta_priori_2': self.root['meta']['beta_priori_2'][idx].copy(),
|
| 279 |
+
}
|
| 280 |
+
else:
|
| 281 |
+
return {
|
| 282 |
+
'obs': self.root['data']['obs'][idx],
|
| 283 |
+
'action': self.root['data']['action'][idx],
|
| 284 |
+
'obs_2': self.root['data']['obs_2'][idx],
|
| 285 |
+
'action_2': self.root['data']['action_2'][idx],
|
| 286 |
+
'votes': self.root['meta']['votes'][idx],
|
| 287 |
+
'votes_2': self.root['meta']['votes_2'][idx],
|
| 288 |
+
'length': self.root['meta']['length'][idx],
|
| 289 |
+
'length_2': self.root['meta']['length_2'][idx],
|
| 290 |
+
'beta_priori': self.root['meta']['beta_priori'][idx],
|
| 291 |
+
'beta_priori_2': self.root['meta']['beta_priori_2'][idx],
|
| 292 |
+
}
|
| 293 |
+
|
| 294 |
+
def get_episode_slice(self, idx):
|
| 295 |
+
"""
|
| 296 |
+
Get the slice range for an episode based on the index for slicing observation and action arrays.
|
| 297 |
+
"""
|
| 298 |
+
return slice(idx, idx + 1)
|
| 299 |
+
|
| 300 |
+
# ============= Save methods ===============
|
| 301 |
+
def save_to_store(self, store, chunks: Optional[Dict[str, tuple]] = dict(),
|
| 302 |
+
compressors: Union[str, numcodecs.abc.Codec, dict] = dict(),
|
| 303 |
+
if_exists='replace', **kwargs):
|
| 304 |
+
"""
|
| 305 |
+
Save the replay buffer to the given store with optional chunking and compression.
|
| 306 |
+
"""
|
| 307 |
+
root = zarr.group(store=store)
|
| 308 |
+
# Save data and meta with chunking and compression if provided
|
| 309 |
+
for key, value in self.root['data'].items():
|
| 310 |
+
cks = self._resolve_array_chunks(chunks, key, value)
|
| 311 |
+
cpr = self._resolve_array_compressor(compressors, key, value)
|
| 312 |
+
arr = self.root['data'][key]
|
| 313 |
+
zarr.copy(arr, root['data'], name=key, chunks=cks, compressor=cpr)
|
| 314 |
+
|
| 315 |
+
# Save meta
|
| 316 |
+
for key, value in self.root['meta'].items():
|
| 317 |
+
zarr.copy(value, root['meta'], name=key)
|
| 318 |
+
|
| 319 |
+
def save_to_path(self, zarr_path, chunks: Optional[Dict[str, tuple]] = dict(),
|
| 320 |
+
compressors: Union[str, numcodecs.abc.Codec, dict] = dict(),
|
| 321 |
+
if_exists='replace', **kwargs):
|
| 322 |
+
"""
|
| 323 |
+
Save the replay buffer to a path.
|
| 324 |
+
"""
|
| 325 |
+
store = zarr.DirectoryStore(zarr_path)
|
| 326 |
+
return self.save_to_store(store, chunks=chunks, compressors=compressors, if_exists=if_exists, **kwargs)
|
| 327 |
+
|
| 328 |
+
# ============= Helper methods ===============
|
| 329 |
+
@classmethod
|
| 330 |
+
def _resolve_array_chunks(cls,
|
| 331 |
+
chunks: Union[dict, tuple], key, array):
|
| 332 |
+
cks = None
|
| 333 |
+
if isinstance(chunks, dict):
|
| 334 |
+
if key in chunks:
|
| 335 |
+
cks = chunks[key]
|
| 336 |
+
elif isinstance(array, zarr.Array):
|
| 337 |
+
cks = array.chunks
|
| 338 |
+
elif isinstance(chunks, tuple):
|
| 339 |
+
cks = chunks
|
| 340 |
+
else:
|
| 341 |
+
raise TypeError(f"Unsupported chunks type {type(chunks)}")
|
| 342 |
+
# backup default
|
| 343 |
+
if cks is None:
|
| 344 |
+
cks = get_optimal_chunks(shape=array.shape, dtype=array.dtype)
|
| 345 |
+
# check
|
| 346 |
+
check_chunks_compatible(chunks=cks, shape=array.shape)
|
| 347 |
+
return cks
|
| 348 |
+
|
| 349 |
+
@classmethod
|
| 350 |
+
def _resolve_array_compressor(cls, compressors: Union[dict, str, numcodecs.abc.Codec], key, array):
|
| 351 |
+
# Resolves the compressor for the array
|
| 352 |
+
return compressors.get(key, array.compressor if isinstance(array, zarr.Array) else numcodecs.Blosc())
|
| 353 |
+
|
| 354 |
+
@property
|
| 355 |
+
def n_steps(self):
|
| 356 |
+
return len(self.root['meta']['votes'])
|
| 357 |
+
|
| 358 |
+
@property
|
| 359 |
+
def data(self):
|
| 360 |
+
return self.root['data']
|
| 361 |
+
|
| 362 |
+
@property
|
| 363 |
+
def meta(self):
|
| 364 |
+
return self.root['meta']
|
| 365 |
+
|
common/pref_sampler.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Optional, Dict
|
| 2 |
+
import numpy as np
|
| 3 |
+
from diffusion_policy.common.pref_replay_buffer import PrefReplayBuffer
|
| 4 |
+
import torch
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
def get_val_mask(n_episodes, val_ratio, seed=0):
|
| 8 |
+
val_mask = np.zeros(n_episodes, dtype=bool)
|
| 9 |
+
if val_ratio <= 0:
|
| 10 |
+
return val_mask
|
| 11 |
+
|
| 12 |
+
# have at least 1 episode for validation, and at least 1 episode for train
|
| 13 |
+
n_val = min(max(1, round(n_episodes * val_ratio)), n_episodes-1)
|
| 14 |
+
rng = np.random.default_rng(seed=seed)
|
| 15 |
+
val_idxs = rng.choice(n_episodes, size=n_val, replace=False)
|
| 16 |
+
val_mask[val_idxs] = True
|
| 17 |
+
return val_mask
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
class PrefSequenceSampler:
|
| 21 |
+
def __init__(self,
|
| 22 |
+
replay_buffer: PrefReplayBuffer,
|
| 23 |
+
sequence_length: int,
|
| 24 |
+
episode_mask: Optional[np.ndarray]=None,
|
| 25 |
+
keys: Optional[Dict[str, int]] = None,
|
| 26 |
+
):
|
| 27 |
+
"""
|
| 28 |
+
Initializes a sampler for the preference replay buffer.
|
| 29 |
+
|
| 30 |
+
Parameters:
|
| 31 |
+
- replay_buffer: PrefReplayBuffer instance from which to sample data.
|
| 32 |
+
- sequence_length: The length of sequences to sample.
|
| 33 |
+
- pad_before, pad_after: Padding before and after sequences (optional).
|
| 34 |
+
- keys: Optional dictionary to specify specific keys and limits on how much data to load.
|
| 35 |
+
- episode_mask: Mask indicating valid episodes for sampling.
|
| 36 |
+
"""
|
| 37 |
+
super().__init__()
|
| 38 |
+
assert sequence_length >= 1
|
| 39 |
+
|
| 40 |
+
if keys is None:
|
| 41 |
+
keys = list(replay_buffer.data.keys())
|
| 42 |
+
|
| 43 |
+
# Store generated indices
|
| 44 |
+
self.keys = keys
|
| 45 |
+
self.sequence_length = sequence_length
|
| 46 |
+
self.replay_buffer = replay_buffer
|
| 47 |
+
self.episode_mask = episode_mask
|
| 48 |
+
|
| 49 |
+
def __len__(self):
|
| 50 |
+
|
| 51 |
+
return np.sum(self.episode_mask)
|
| 52 |
+
|
| 53 |
+
def sample_sequence(self, idx: int) -> Dict[str, np.ndarray]:
|
| 54 |
+
"""
|
| 55 |
+
Samples the sequence of data based on the provided index (idx).
|
| 56 |
+
|
| 57 |
+
Parameters:
|
| 58 |
+
- idx: The index from which to sample an episode sequence.
|
| 59 |
+
|
| 60 |
+
Returns:
|
| 61 |
+
- A dictionary containing the sampled data for the specified keys and votes.
|
| 62 |
+
"""
|
| 63 |
+
indices = np.where(self.episode_mask)[0]
|
| 64 |
+
result = self.replay_buffer.get_pref_episode(indices[idx])
|
| 65 |
+
|
| 66 |
+
for key in result:
|
| 67 |
+
value = result[key]
|
| 68 |
+
if isinstance(value, np.ndarray):
|
| 69 |
+
result[key] = torch.from_numpy(value)
|
| 70 |
+
elif isinstance(value, (np.float32, np.float64, float, int)):
|
| 71 |
+
result[key] = torch.tensor(value, dtype=torch.float32)
|
| 72 |
+
else:
|
| 73 |
+
raise TypeError(f"Unsupported type {type(value)} for key '{key}'")
|
| 74 |
+
return result
|
common/prior_utils_confidence.py
ADDED
|
@@ -0,0 +1,723 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import collections
|
| 2 |
+
import numpy as np
|
| 3 |
+
# import gym
|
| 4 |
+
import torch.utils
|
| 5 |
+
from tqdm import trange
|
| 6 |
+
import torch
|
| 7 |
+
import torch.nn as nn
|
| 8 |
+
import math
|
| 9 |
+
# import d4rl
|
| 10 |
+
from tqdm import tqdm
|
| 11 |
+
import torch.nn.functional as F
|
| 12 |
+
from pathlib import Path
|
| 13 |
+
import copy
|
| 14 |
+
from torch.optim.lr_scheduler import SequentialLR, LinearLR, CosineAnnealingLR
|
| 15 |
+
from diffusion_policy.policy.diffusion_transformer_hybrid_image_policy import DiffusionTransformerHybridImagePolicy
|
| 16 |
+
from diffusion_policy.common.pytorch_util import dict_apply
|
| 17 |
+
|
| 18 |
+
import time
|
| 19 |
+
import logging
|
| 20 |
+
from datetime import datetime
|
| 21 |
+
|
| 22 |
+
Batch = collections.namedtuple(
|
| 23 |
+
'Batch',
|
| 24 |
+
['observations', 'actions', 'rewards', 'masks', 'next_observations'])
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def gen_net(in_size=1, out_size=1, H=128, n_layers=3, activation='tanh'):
|
| 28 |
+
net = []
|
| 29 |
+
for i in range(n_layers):
|
| 30 |
+
net.append(nn.Linear(in_size, H))
|
| 31 |
+
net.append(nn.LeakyReLU())
|
| 32 |
+
in_size = H
|
| 33 |
+
net.append(nn.Linear(in_size, out_size))
|
| 34 |
+
if activation == 'tanh':
|
| 35 |
+
net.append(nn.Tanh())
|
| 36 |
+
elif activation == 'sig':
|
| 37 |
+
net.append(nn.Sigmoid())
|
| 38 |
+
else:
|
| 39 |
+
pass
|
| 40 |
+
|
| 41 |
+
return net
|
| 42 |
+
|
| 43 |
+
def get_discrete_traj(trajectory, dim=2):
|
| 44 |
+
discrete_traj = np.round(trajectory[:, :, :dim].astype(float)).astype(int)
|
| 45 |
+
|
| 46 |
+
unique_states_per_trajectory = []
|
| 47 |
+
for trajectory in discrete_traj:
|
| 48 |
+
# Find unique states in the current trajectory
|
| 49 |
+
unique_states = np.unique(trajectory, axis=0)
|
| 50 |
+
# Convert the trajectory to a hashable type
|
| 51 |
+
unique_states_per_trajectory.append(tuple(map(tuple, unique_states)))
|
| 52 |
+
return unique_states_per_trajectory
|
| 53 |
+
|
| 54 |
+
def init_trajectory_dict(discrete_trajectory):
|
| 55 |
+
|
| 56 |
+
trajectory_dict = {}
|
| 57 |
+
for trajectory in discrete_trajectory:
|
| 58 |
+
if trajectory in trajectory_dict:
|
| 59 |
+
continue
|
| 60 |
+
else:
|
| 61 |
+
trajectory_dict[trajectory] = np.array([1,1])
|
| 62 |
+
return trajectory_dict
|
| 63 |
+
|
| 64 |
+
def get_trajectory_dict_from_pair(traj_alpha_beta_dict, discrete_obs_1, discrete_obs_2, single_labels):
|
| 65 |
+
for i in range(len(single_labels)):
|
| 66 |
+
if single_labels[i] == 1:
|
| 67 |
+
traj_alpha_beta_dict[discrete_obs_1[i]][0] += 1
|
| 68 |
+
traj_alpha_beta_dict[discrete_obs_2[i]][1] += 1
|
| 69 |
+
elif single_labels[i] == -1:
|
| 70 |
+
traj_alpha_beta_dict[discrete_obs_1[i]][1] += 1
|
| 71 |
+
traj_alpha_beta_dict[discrete_obs_2[i]][0] += 1
|
| 72 |
+
# TODO how to treat not compaied ones?
|
| 73 |
+
elif single_labels[i] == 0:
|
| 74 |
+
traj_alpha_beta_dict[discrete_obs_1[i]][0] += 1
|
| 75 |
+
traj_alpha_beta_dict[discrete_obs_1[i]][1] += 1
|
| 76 |
+
traj_alpha_beta_dict[discrete_obs_2[i]][0] += 1
|
| 77 |
+
traj_alpha_beta_dict[discrete_obs_2[i]][1] += 1
|
| 78 |
+
return traj_alpha_beta_dict
|
| 79 |
+
|
| 80 |
+
def to_torch(x, dtype=torch.float32):
|
| 81 |
+
if isinstance(x, np.ndarray):
|
| 82 |
+
return torch.from_numpy(x).to(dtype)
|
| 83 |
+
|
| 84 |
+
def index_batch(batch, indices):
|
| 85 |
+
indexed = {}
|
| 86 |
+
for key in batch.keys():
|
| 87 |
+
indexed[key] = batch[key][indices, ...]
|
| 88 |
+
return indexed
|
| 89 |
+
|
| 90 |
+
class Dataset(object):
|
| 91 |
+
def __init__(self, observations: np.ndarray, actions: np.ndarray,
|
| 92 |
+
rewards: np.ndarray, masks: np.ndarray,
|
| 93 |
+
dones_float: np.ndarray, next_observations: np.ndarray,
|
| 94 |
+
size: int):
|
| 95 |
+
self.observations = observations
|
| 96 |
+
self.actions = actions
|
| 97 |
+
self.rewards = rewards
|
| 98 |
+
self.masks = masks
|
| 99 |
+
self.dones_float = dones_float
|
| 100 |
+
self.next_observations = next_observations
|
| 101 |
+
self.size = size
|
| 102 |
+
|
| 103 |
+
def sample(self, batch_size: int) -> Batch:
|
| 104 |
+
indx = np.random.randint(self.size, size=batch_size)
|
| 105 |
+
return Batch(observations=self.observations[indx],
|
| 106 |
+
actions=self.actions[indx],
|
| 107 |
+
rewards=self.rewards[indx],
|
| 108 |
+
masks=self.masks[indx],
|
| 109 |
+
next_observations=self.next_observations[indx])
|
| 110 |
+
|
| 111 |
+
class NormalComparisonModel(nn.Module):
|
| 112 |
+
def __init__(self, input_dim, dense_units, dropout_rate, device):
|
| 113 |
+
super(NormalComparisonModel, self).__init__()
|
| 114 |
+
|
| 115 |
+
# Fully connected layers for classification
|
| 116 |
+
self.fc1 = nn.Linear(4 * input_dim, dense_units)
|
| 117 |
+
self.dropout = nn.Dropout(dropout_rate)
|
| 118 |
+
self.fc2 = nn.Linear(dense_units, dense_units // 2)
|
| 119 |
+
self.output = nn.Linear(dense_units // 2, 1)
|
| 120 |
+
|
| 121 |
+
# Device setup
|
| 122 |
+
self.device = device
|
| 123 |
+
self.to(device)
|
| 124 |
+
|
| 125 |
+
def forward(self, f1, f2):
|
| 126 |
+
f1, f2 = f1.to(self.device), f2.to(self.device)
|
| 127 |
+
|
| 128 |
+
# Shape of f1: (N1, L1, D), f2: (N2, L2, D)
|
| 129 |
+
N1, L1, D = f1.shape
|
| 130 |
+
N2, L2, _ = f2.shape
|
| 131 |
+
|
| 132 |
+
# Expand dimensions for pairwise comparison
|
| 133 |
+
f1_expanded = f1.unsqueeze(1).expand(N1, N2, L1, D) # Shape: (N1, N2, L1, D)
|
| 134 |
+
f2_expanded = f2.unsqueeze(0).expand(N1, N2, L2, D) # Shape: (N1, N2, L2, D)
|
| 135 |
+
|
| 136 |
+
# Flatten for processing through Transformer
|
| 137 |
+
f1_flat = f1_expanded.reshape(-1, L1, D) # Shape: (N1*N2, L1, D)
|
| 138 |
+
f2_flat = f2_expanded.reshape(-1, L2, D) # Shape: (N1*N2, L2, D)
|
| 139 |
+
|
| 140 |
+
# Sequence pooling: Reduce sequence dimension
|
| 141 |
+
f1_pooled = f1_flat.mean(dim=1)
|
| 142 |
+
f2_pooled = f2_flat.mean(dim=1)
|
| 143 |
+
|
| 144 |
+
# Pairwise comparison (concatenate, subtract)
|
| 145 |
+
combined_features = torch.cat([
|
| 146 |
+
f1_pooled, # Reduced f1
|
| 147 |
+
f2_pooled, # Reduced f2
|
| 148 |
+
f1_pooled - f2_pooled, # Difference
|
| 149 |
+
], dim=-1) # Shape: (N1*N2, 3*D)
|
| 150 |
+
|
| 151 |
+
# Fully connected layers
|
| 152 |
+
x = F.gelu(self.fc1(combined_features)) # Shape: (N1*N2, dense_units)
|
| 153 |
+
x = self.dropout(x)
|
| 154 |
+
x = F.gelu(self.fc2(x)) # Shape: (N1*N2, dense_units // 2)
|
| 155 |
+
x = self.output(x) # Shape: (N1*N2, 1)
|
| 156 |
+
|
| 157 |
+
# Sigmoid activation for [0, 1] output
|
| 158 |
+
output = torch.sigmoid(x).squeeze(-1) # Shape: (N1*N2)
|
| 159 |
+
output = output.view(N1, N2) # Reshape to (N1, N2)
|
| 160 |
+
return output
|
| 161 |
+
|
| 162 |
+
class AttentionComparisonModel(nn.Module):
|
| 163 |
+
def __init__(self, input_dim, dropout_rate, nhead, device):
|
| 164 |
+
super(AttentionComparisonModel, self).__init__()
|
| 165 |
+
|
| 166 |
+
self.attention = nn.MultiheadAttention(embed_dim=input_dim, num_heads=nhead, batch_first=True)
|
| 167 |
+
self.query_attn = nn.Parameter(torch.randn(1, input_dim)) # Learnable query for attention
|
| 168 |
+
|
| 169 |
+
self.fc1 = nn.Linear(input_dim, input_dim // 2)
|
| 170 |
+
self.dropout = nn.Dropout(dropout_rate)
|
| 171 |
+
self.output = nn.Linear(input_dim // 2, 1)
|
| 172 |
+
self.device = device
|
| 173 |
+
self.to(device)
|
| 174 |
+
|
| 175 |
+
def attention_pool(self, x, query):
|
| 176 |
+
# x: (batch_size, seq_len, input_dim)
|
| 177 |
+
# query: (1, input_dim)
|
| 178 |
+
attn_weights = torch.softmax(torch.matmul(x, query.T), dim=1) # Compute attention weights
|
| 179 |
+
weighted_sum = torch.sum(attn_weights * x, dim=1) # Weighted sum along sequence dimension
|
| 180 |
+
return weighted_sum
|
| 181 |
+
|
| 182 |
+
def forward(self, f1, f2):
|
| 183 |
+
f1, f2 = f1.to(self.device), f2.to(self.device)
|
| 184 |
+
|
| 185 |
+
N1, L1, D = f1.shape
|
| 186 |
+
N2, L2, _ = f2.shape
|
| 187 |
+
|
| 188 |
+
f1_flat = f1.unsqueeze(1).expand(N1, N2, L1, D).reshape(-1, L1, D)
|
| 189 |
+
f2_flat = f2.unsqueeze(0).expand(N1, N2, L2, D).reshape(-1, L2, D)
|
| 190 |
+
|
| 191 |
+
attn_out, _ = self.attention(f1_flat, f2_flat, f2_flat)
|
| 192 |
+
|
| 193 |
+
attn_reduced = self.attention_pool(attn_out, self.query_attn)
|
| 194 |
+
|
| 195 |
+
x = F.gelu(self.fc1(attn_reduced))
|
| 196 |
+
x = self.dropout(x)
|
| 197 |
+
x = self.output(x)
|
| 198 |
+
output = torch.sigmoid(x)
|
| 199 |
+
output = output.view(N1, N2)
|
| 200 |
+
return output
|
| 201 |
+
|
| 202 |
+
def one_to_one_forward(self, f1, f2):
|
| 203 |
+
f1, f2 = f1.to(self.device), f2.to(self.device)
|
| 204 |
+
|
| 205 |
+
attn_out, _ = self.attention(f1, f2, f2)
|
| 206 |
+
|
| 207 |
+
# Apply self-attention weighted compression
|
| 208 |
+
attn_reduced = self.attention_pool(attn_out, self.query_attn)
|
| 209 |
+
|
| 210 |
+
x = F.gelu(self.fc1(attn_reduced))
|
| 211 |
+
x = self.dropout(x)
|
| 212 |
+
x = self.output(x)
|
| 213 |
+
output = torch.sigmoid(x)
|
| 214 |
+
return output
|
| 215 |
+
|
| 216 |
+
|
| 217 |
+
class SinusoidalPosEmb(nn.Module):
|
| 218 |
+
def __init__(self, dim):
|
| 219 |
+
super().__init__()
|
| 220 |
+
self.dim = dim
|
| 221 |
+
|
| 222 |
+
def forward(self, x):
|
| 223 |
+
device = x.device
|
| 224 |
+
half_dim = self.dim // 2
|
| 225 |
+
emb = math.log(10000) / (half_dim - 1)
|
| 226 |
+
emb = torch.exp(torch.arange(half_dim, device=device) * -emb)
|
| 227 |
+
emb = x[:, None] * emb[None, :]
|
| 228 |
+
emb = torch.cat((emb.sin(), emb.cos()), dim=-1)
|
| 229 |
+
return emb
|
| 230 |
+
|
| 231 |
+
class TransformerEncModel(nn.Module):
|
| 232 |
+
def __init__(self, data_dim, embedding_dim, nhead, num_encoder_layers, device):
|
| 233 |
+
super(TransformerEncModel, self).__init__()
|
| 234 |
+
self.device = device
|
| 235 |
+
self.embedding = nn.Linear(data_dim, embedding_dim)
|
| 236 |
+
# self.pos_encoder = self.create_positional_encoding(seq_length, embedding_dim)
|
| 237 |
+
self.pos_emb = SinusoidalPosEmb(embedding_dim)
|
| 238 |
+
encoder_layers = nn.TransformerEncoderLayer(d_model=embedding_dim, nhead=nhead, batch_first=True)
|
| 239 |
+
self.transformer_encoder = nn.TransformerEncoder(encoder_layers, num_layers=num_encoder_layers)
|
| 240 |
+
|
| 241 |
+
def forward(self, x):
|
| 242 |
+
traj_len = x.shape[1]
|
| 243 |
+
x = self.embedding(x) # Map input to embedding dimension
|
| 244 |
+
pos = self.pos_emb(
|
| 245 |
+
torch.arange(traj_len, device=self.device))[None,]
|
| 246 |
+
x += pos # Add positional encoding
|
| 247 |
+
output = self.transformer_encoder(x)
|
| 248 |
+
return output
|
| 249 |
+
|
| 250 |
+
class CausalTransformerBetaModel(nn.Module):
|
| 251 |
+
def __init__(self, data_dim, embedding_dim, nhead, num_encoder_layers, output_dim, device):
|
| 252 |
+
super(CausalTransformerBetaModel, self).__init__()
|
| 253 |
+
self.device = device
|
| 254 |
+
self.embedding = nn.Linear(data_dim, embedding_dim)
|
| 255 |
+
# self.pos_encoder = self.create_positional_encoding(seq_length, embedding_dim)
|
| 256 |
+
self.pos_emb = SinusoidalPosEmb(embedding_dim)
|
| 257 |
+
encoder_layers = nn.TransformerEncoderLayer(d_model=embedding_dim, nhead=nhead, batch_first=True)
|
| 258 |
+
self.transformer_encoder = nn.TransformerEncoder(encoder_layers, num_layers=num_encoder_layers)
|
| 259 |
+
self.output_layer = nn.Linear(embedding_dim, output_dim)
|
| 260 |
+
self.softplus = nn.Softplus()
|
| 261 |
+
|
| 262 |
+
def generate_square_subsequent_mask(self, sz):
|
| 263 |
+
mask = torch.triu(torch.ones(sz, sz), diagonal=1)
|
| 264 |
+
return mask.masked_fill(mask==1, float('-inf'))
|
| 265 |
+
|
| 266 |
+
def forward(self, x):
|
| 267 |
+
traj_len = x.shape[1]
|
| 268 |
+
x = self.embedding(x) # Map input to embedding dimension
|
| 269 |
+
pos = self.pos_emb(
|
| 270 |
+
torch.arange(traj_len, device=self.device))[None,]
|
| 271 |
+
x += pos # Add positional encoding
|
| 272 |
+
mask = self.generate_square_subsequent_mask(traj_len).to(x.device)
|
| 273 |
+
output = self.transformer_encoder(x, mask=mask)
|
| 274 |
+
output = self.output_layer(output)
|
| 275 |
+
# TODO mean or last one
|
| 276 |
+
# output = output.mean(dim=1) # Aggregate across the sequence dimension
|
| 277 |
+
output = output[:, -1, :] # Take the output from the last timestep
|
| 278 |
+
output = self.softplus(output)
|
| 279 |
+
return output
|
| 280 |
+
|
| 281 |
+
class BetaNetwork(nn.Module):
|
| 282 |
+
def __init__(self, data, device=torch.device('cuda'), data_size = 500):
|
| 283 |
+
super(BetaNetwork, self).__init__()
|
| 284 |
+
|
| 285 |
+
act_data = np.concatenate((data['action'], data['action_2']), axis=0)
|
| 286 |
+
obs_data = np.concatenate((data['obs'], data['obs_2']), axis=0)
|
| 287 |
+
votes_data = np.concatenate((data['votes'], data['votes_2']), axis=0)
|
| 288 |
+
|
| 289 |
+
if data_size <= obs_data.shape[0]:
|
| 290 |
+
indices = np.random.randint(0, obs_data.shape[0], size=data_size)
|
| 291 |
+
obs_data = obs_data[indices, ...]
|
| 292 |
+
act_data = act_data[indices, ...]
|
| 293 |
+
votes_data = votes_data[indices, ...]
|
| 294 |
+
|
| 295 |
+
act_data = torch.from_numpy(act_data).float().to(device)
|
| 296 |
+
obs_data = torch.from_numpy(obs_data).float().to(device)
|
| 297 |
+
self.votes_data = torch.from_numpy(votes_data).to(device)
|
| 298 |
+
self.lr = None
|
| 299 |
+
self.device = device
|
| 300 |
+
self.data = torch.concat((obs_data, act_data), dim=-1)
|
| 301 |
+
|
| 302 |
+
class BetaModel(nn.Module):
|
| 303 |
+
def __init__(self, obs_data, act_data, device=torch.device('cuda')):
|
| 304 |
+
super(BetaModel, self).__init__()
|
| 305 |
+
|
| 306 |
+
self.enc_model = TransformerEncModel(
|
| 307 |
+
data_dim = act_data.shape[-1] + obs_data.shape[-1],
|
| 308 |
+
embedding_dim = 256,
|
| 309 |
+
nhead = 4,
|
| 310 |
+
num_encoder_layers = 2,
|
| 311 |
+
device = device
|
| 312 |
+
).to(device)
|
| 313 |
+
|
| 314 |
+
self.comp_model = AttentionComparisonModel(
|
| 315 |
+
input_dim = 256,
|
| 316 |
+
dropout_rate = 0.3,
|
| 317 |
+
nhead = 16,
|
| 318 |
+
device = device
|
| 319 |
+
).to(device)
|
| 320 |
+
|
| 321 |
+
self.data = torch.concat((obs_data, act_data), dim=-1)
|
| 322 |
+
|
| 323 |
+
def forward(self, x):
|
| 324 |
+
batch_f = self.enc_model(x)
|
| 325 |
+
all_data_f = self.enc_model(self.data)
|
| 326 |
+
bias = all_data_f.mean()
|
| 327 |
+
std = all_data_f.std()
|
| 328 |
+
all_data_f = (all_data_f - bias) / std
|
| 329 |
+
batch_f = (batch_f - bias) / std
|
| 330 |
+
output = self.comp_model(batch_f, all_data_f)
|
| 331 |
+
return output
|
| 332 |
+
|
| 333 |
+
def one_to_one_forward(self, x, y):
|
| 334 |
+
x_f = self.enc_model(x)
|
| 335 |
+
y_f = self.enc_model(y)
|
| 336 |
+
all_data_f = self.enc_model(self.data)
|
| 337 |
+
bias = all_data_f.mean()
|
| 338 |
+
std = all_data_f.std()
|
| 339 |
+
x_f = (x_f - bias) / std
|
| 340 |
+
y_f = (y_f - bias) / std
|
| 341 |
+
output = self.comp_model.one_to_one_forward(x_f, y_f)
|
| 342 |
+
return output
|
| 343 |
+
|
| 344 |
+
|
| 345 |
+
|
| 346 |
+
self.model = BetaModel(obs_data, act_data, device)
|
| 347 |
+
self.opt = None #, weight_decay=1.0e-4
|
| 348 |
+
self.scheduler = None #torch.optim.lr_scheduler.ReduceLROnPlateau(self.opt, mode='min', patience=100, verbose=True)
|
| 349 |
+
self.ref_model: BetaModel
|
| 350 |
+
|
| 351 |
+
def get_alpha_beta(self, x):
|
| 352 |
+
batch_comp = self.model(x).detach()
|
| 353 |
+
alpha = torch.sum(batch_comp, dim=-1)
|
| 354 |
+
beta = torch.sum(1 - batch_comp, dim=-1)
|
| 355 |
+
# alpha = torch.sum(torch.where(batch_comp > 0.5, torch.tensor(1.0, device=self.device), torch.tensor(0.0, device=self.device)), dim=-1)
|
| 356 |
+
# beta = torch.sum(torch.where(batch_comp < 0.5, torch.tensor(1.0, device=self.device), torch.tensor(0.0, device=self.device)), dim=-1)
|
| 357 |
+
|
| 358 |
+
return alpha.detach(), beta.detach()
|
| 359 |
+
|
| 360 |
+
def fit_data(self, save_dir=None, load_dir=None, num_epochs=1, warm_up_epochs=0, batch_size=1, lr=1.0e-5):
|
| 361 |
+
if load_dir is None:
|
| 362 |
+
interval = math.ceil(self.data.shape[0] / batch_size)
|
| 363 |
+
total_steps = num_epochs * interval
|
| 364 |
+
warm_up_steps = warm_up_epochs * interval
|
| 365 |
+
main_steps = total_steps - warm_up_steps
|
| 366 |
+
|
| 367 |
+
# Learning rate schedulers
|
| 368 |
+
self.lr = lr
|
| 369 |
+
self.opt = torch.optim.Adam(self.model.parameters(), lr=self.lr, weight_decay=1e-5)
|
| 370 |
+
warm_up_scheduler = LinearLR(self.opt, start_factor=1e-8, end_factor=1.0, total_iters=warm_up_steps)
|
| 371 |
+
cosine_scheduler = CosineAnnealingLR(self.opt, T_max=main_steps)
|
| 372 |
+
self.scheduler = SequentialLR(self.opt, schedulers=[warm_up_scheduler, cosine_scheduler], milestones=[warm_up_steps])
|
| 373 |
+
|
| 374 |
+
logger = logging.getLogger()
|
| 375 |
+
time.sleep(0.5)
|
| 376 |
+
stage_time = datetime.now()
|
| 377 |
+
logger.info(f'Beta Model Training Start: {stage_time} seconds')
|
| 378 |
+
|
| 379 |
+
|
| 380 |
+
for epoch in range(num_epochs):
|
| 381 |
+
beta_loss_all = []
|
| 382 |
+
batch_shuffled_idx = np.random.permutation(self.data.shape[0])
|
| 383 |
+
|
| 384 |
+
time.sleep(0.5)
|
| 385 |
+
stage_time_last = datetime.now()
|
| 386 |
+
logger.info(f'Epoch {epoch + 1} Start: {stage_time_last} seconds')
|
| 387 |
+
|
| 388 |
+
|
| 389 |
+
for i in tqdm(range(interval)):
|
| 390 |
+
|
| 391 |
+
start_pt = i * batch_size
|
| 392 |
+
end_pt = min((i + 1) * batch_size, self.data.shape[0])
|
| 393 |
+
local_idx = batch_shuffled_idx[start_pt:end_pt]
|
| 394 |
+
batch = self.data[local_idx, ...]
|
| 395 |
+
batch_votes = self.votes_data[local_idx, ...]
|
| 396 |
+
|
| 397 |
+
comp = torch.sigmoid(batch_votes - self.votes_data.T)
|
| 398 |
+
pred_comp = self.model(batch)
|
| 399 |
+
|
| 400 |
+
beta_loss = torch.mean((comp - pred_comp) ** 2)
|
| 401 |
+
|
| 402 |
+
beta_loss_all.append(beta_loss)
|
| 403 |
+
|
| 404 |
+
self.opt.zero_grad()
|
| 405 |
+
beta_loss.backward()
|
| 406 |
+
self.opt.step()
|
| 407 |
+
self.scheduler.step() # Update LR after each optimizer step
|
| 408 |
+
|
| 409 |
+
beta_loss_all = torch.stack(beta_loss_all, dim=0)
|
| 410 |
+
print("iteration:", epoch + 1)
|
| 411 |
+
print("mean_beta_loss_all:", torch.mean(beta_loss_all).item())
|
| 412 |
+
|
| 413 |
+
time.sleep(0.5)
|
| 414 |
+
stage_time_last = datetime.now()
|
| 415 |
+
logger.info(f'Epoch {epoch + 1} End: {stage_time_last} seconds')
|
| 416 |
+
|
| 417 |
+
if save_dir is not None and (((epoch + 1) % 50 == 0) or ((epoch + 1) == num_epochs)):
|
| 418 |
+
tmp_save_dir = Path(save_dir) / f'itr_{epoch + 1}'
|
| 419 |
+
tmp_save_dir.mkdir(parents=True, exist_ok=True)
|
| 420 |
+
model_file = tmp_save_dir / 'beta_model.pth'
|
| 421 |
+
self.save_model(model_file)
|
| 422 |
+
self.ref_model = copy.deepcopy(self.model)
|
| 423 |
+
else:
|
| 424 |
+
self.load_model(load_dir)
|
| 425 |
+
self.ref_model = copy.deepcopy(self.model)
|
| 426 |
+
|
| 427 |
+
def online_update(self, dataset, num_epochs=1, warm_up_epochs=0, batch_size=1, lr = 1.0e-6):
|
| 428 |
+
interval = math.ceil(dataset["obs"].shape[0] / batch_size)
|
| 429 |
+
total_steps = num_epochs * interval
|
| 430 |
+
warm_up_steps = warm_up_epochs * interval
|
| 431 |
+
main_steps = total_steps - warm_up_steps
|
| 432 |
+
|
| 433 |
+
# Learning rate schedulers
|
| 434 |
+
self.lr = lr
|
| 435 |
+
self.opt = torch.optim.Adam(self.model.parameters(), lr=self.lr, weight_decay=1e-5)
|
| 436 |
+
warm_up_scheduler = LinearLR(self.opt, start_factor=1e-8, end_factor=1.0, total_iters=warm_up_steps)
|
| 437 |
+
cosine_scheduler = CosineAnnealingLR(self.opt, T_max=main_steps)
|
| 438 |
+
self.scheduler = SequentialLR(self.opt, schedulers=[warm_up_scheduler, cosine_scheduler], milestones=[warm_up_steps])
|
| 439 |
+
|
| 440 |
+
logger = logging.getLogger()
|
| 441 |
+
time.sleep(0.5)
|
| 442 |
+
stage_time = datetime.now()
|
| 443 |
+
logger.info(f'Beta Model Training Start: {stage_time} seconds')
|
| 444 |
+
|
| 445 |
+
for epoch in range(num_epochs):
|
| 446 |
+
beta_loss_all = []
|
| 447 |
+
batch_shuffled_idx = np.random.permutation(dataset["obs"].shape[0])
|
| 448 |
+
|
| 449 |
+
time.sleep(0.5)
|
| 450 |
+
stage_time_last = datetime.now()
|
| 451 |
+
logger.info(f'Epoch {epoch + 1} Start: {stage_time_last} seconds')
|
| 452 |
+
|
| 453 |
+
for i in tqdm(range(interval)):
|
| 454 |
+
|
| 455 |
+
start_pt = i * batch_size
|
| 456 |
+
end_pt = min((i + 1) * batch_size, dataset["obs"].shape[0])
|
| 457 |
+
batch = index_batch(dataset, batch_shuffled_idx[start_pt:end_pt])
|
| 458 |
+
|
| 459 |
+
obs_1 = batch['obs'] # batch_size * traj_len * obs_dim
|
| 460 |
+
act_1 = batch['action'] # batch_size * traj_len * action_dim
|
| 461 |
+
obs_2 = batch['obs_2']
|
| 462 |
+
act_2 = batch['action_2']
|
| 463 |
+
s_a_1 = np.concatenate([obs_1, act_1], axis=-1)
|
| 464 |
+
s_a_2 = np.concatenate([obs_2, act_2], axis=-1)
|
| 465 |
+
|
| 466 |
+
votes_1 = torch.from_numpy(batch['votes']).to(self.device)
|
| 467 |
+
votes_2 = torch.from_numpy(batch['votes_2']).to(self.device)
|
| 468 |
+
|
| 469 |
+
# threshold = 1e-3
|
| 470 |
+
# diff = torch.abs(votes_1 - votes_2)
|
| 471 |
+
# condition_1 = (votes_1 > votes_2) & (diff >= threshold) # votes_1 > votes_2 and diff >= threshold
|
| 472 |
+
# condition_2 = (votes_1 < votes_2) & (diff >= threshold) # votes_1 < votes_2 and diff >= threshold
|
| 473 |
+
|
| 474 |
+
# comp_1 = torch.where(condition_1, torch.tensor(1.0, device=self.device), torch.tensor(0.0, device=self.device))
|
| 475 |
+
# comp_1 = torch.squeeze(comp_1, dim=-1)
|
| 476 |
+
# comp_2 = torch.where(condition_2, torch.tensor(1.0, device=self.device), torch.tensor(0.0, device=self.device))
|
| 477 |
+
# comp_2 = torch.squeeze(comp_2, dim=-1)
|
| 478 |
+
|
| 479 |
+
comp_1 = torch.sigmoid(votes_1 - votes_2)
|
| 480 |
+
comp_2 = torch.sigmoid(votes_2 - votes_1)
|
| 481 |
+
|
| 482 |
+
pred_comp_1 = self.model.one_to_one_forward(torch.from_numpy(s_a_1).float().to(self.device), torch.from_numpy(s_a_2).float().to(self.device))
|
| 483 |
+
pred_comp_2 = self.model.one_to_one_forward(torch.from_numpy(s_a_2).float().to(self.device), torch.from_numpy(s_a_1).float().to(self.device))
|
| 484 |
+
# ref_comp_1 = self.ref_model.one_to_one_forward(torch.from_numpy(s_a_1).float().to(self.device), torch.from_numpy(s_a_2).float().to(self.device)).detach()
|
| 485 |
+
# ref_comp_2 = self.ref_model.one_to_one_forward(torch.from_numpy(s_a_2).float().to(self.device), torch.from_numpy(s_a_1).float().to(self.device)).detach()
|
| 486 |
+
|
| 487 |
+
beta_loss = (torch.mean((comp_1 - pred_comp_1) ** 2) + torch.mean((comp_2 - pred_comp_2) ** 2)) / 2
|
| 488 |
+
|
| 489 |
+
beta_loss_all.append(beta_loss)
|
| 490 |
+
|
| 491 |
+
self.opt.zero_grad()
|
| 492 |
+
beta_loss.backward()
|
| 493 |
+
self.opt.step()
|
| 494 |
+
self.scheduler.step() # Update LR after each optimizer step
|
| 495 |
+
|
| 496 |
+
|
| 497 |
+
time.sleep(0.5)
|
| 498 |
+
stage_time_now = datetime.now()
|
| 499 |
+
logger.info(f'Epoch {epoch + 1} Spending time:{stage_time_now - stage_time_last} End: {stage_time_now} seconds')
|
| 500 |
+
|
| 501 |
+
beta_loss_all = torch.stack(beta_loss_all, dim=0)
|
| 502 |
+
print("iteration:", epoch + 1)
|
| 503 |
+
print("mean_beta_loss_all:", torch.mean(beta_loss_all).item())
|
| 504 |
+
|
| 505 |
+
def save_model(self, filepath):
|
| 506 |
+
torch.save(self.state_dict(), filepath)
|
| 507 |
+
|
| 508 |
+
def load_model(self, filepath):
|
| 509 |
+
self.load_state_dict(torch.load(filepath, map_location=self.device))
|
| 510 |
+
|
| 511 |
+
|
| 512 |
+
class BetaImageNetwork(nn.Module):
|
| 513 |
+
def __init__(self, data, policy: DiffusionTransformerHybridImagePolicy, device=torch.device('cuda'), data_size = 500, ):
|
| 514 |
+
super(BetaNetwork, self).__init__()
|
| 515 |
+
|
| 516 |
+
act_data = np.concatenate((data['action'], data['action_2']), axis=0)
|
| 517 |
+
votes_data = np.concatenate((data['votes'], data['votes_2']), axis=0)
|
| 518 |
+
obs_data = {key:np.concatenate((data['obs'][key], data['obs_2'][key]), axis=0) for key in data['obs']}
|
| 519 |
+
if data_size <= act_data.shape[0]:
|
| 520 |
+
indices = np.random.randint(0, act_data.shape[0], size=data_size)
|
| 521 |
+
obs_data = {key:obs_data[key][indices, ...]for key in obs_data.keys()}
|
| 522 |
+
act_data = act_data[indices, ...]
|
| 523 |
+
votes_data = votes_data[indices, ...]
|
| 524 |
+
|
| 525 |
+
act_data = torch.from_numpy(act_data).float().to(device)
|
| 526 |
+
obs_data = {key: torch.from_numpy(obs_data[key]).float().to(device) for key in obs_data.keys()}
|
| 527 |
+
|
| 528 |
+
obs_encoder = policy.nets['policy'].nets['encoder'].nets['obs']
|
| 529 |
+
|
| 530 |
+
obs_data = policy.normalizer.normalize(obs_data)
|
| 531 |
+
act_data = policy.normalizer.normalize(act_data)
|
| 532 |
+
|
| 533 |
+
this_nobs = dict_apply(obs_data,
|
| 534 |
+
lambda x: x.reshape(-1,*x.shape[2:]))
|
| 535 |
+
nobs_features = obs_encoder(this_nobs)
|
| 536 |
+
nobs_features = nobs_features.reshape(data_size, act_data.shape[1], -1)
|
| 537 |
+
nobs_features.detach()
|
| 538 |
+
|
| 539 |
+
|
| 540 |
+
self.votes_data = torch.from_numpy(votes_data).to(device)
|
| 541 |
+
self.lr = None
|
| 542 |
+
self.device = device
|
| 543 |
+
self.data = torch.concat((act_data, nobs_features), dim=-1)
|
| 544 |
+
|
| 545 |
+
class BetaModel(nn.Module):
|
| 546 |
+
def __init__(self, obs_data, act_data, device=torch.device('cuda')):
|
| 547 |
+
super(BetaModel, self).__init__()
|
| 548 |
+
|
| 549 |
+
self.enc_model = TransformerEncModel(
|
| 550 |
+
data_dim = act_data.shape[-1] + obs_data.shape[-1],
|
| 551 |
+
embedding_dim = 256,
|
| 552 |
+
nhead = 4,
|
| 553 |
+
num_encoder_layers = 2,
|
| 554 |
+
device = device
|
| 555 |
+
).to(device)
|
| 556 |
+
|
| 557 |
+
self.comp_model = AttentionComparisonModel(
|
| 558 |
+
input_dim = 256,
|
| 559 |
+
dropout_rate = 0.3,
|
| 560 |
+
nhead = 16,
|
| 561 |
+
device = device
|
| 562 |
+
).to(device)
|
| 563 |
+
|
| 564 |
+
self.data = torch.concat((obs_data, act_data), dim=-1)
|
| 565 |
+
|
| 566 |
+
def forward(self, x):
|
| 567 |
+
batch_f = self.enc_model(x)
|
| 568 |
+
all_data_f = self.enc_model(self.data)
|
| 569 |
+
bias = all_data_f.mean()
|
| 570 |
+
std = all_data_f.std()
|
| 571 |
+
all_data_f = (all_data_f - bias) / std
|
| 572 |
+
batch_f = (batch_f - bias) / std
|
| 573 |
+
output = self.comp_model(batch_f, all_data_f)
|
| 574 |
+
return output
|
| 575 |
+
|
| 576 |
+
def one_to_one_forward(self, x, y):
|
| 577 |
+
x_f = self.enc_model(x)
|
| 578 |
+
y_f = self.enc_model(y)
|
| 579 |
+
all_data_f = self.enc_model(self.data)
|
| 580 |
+
bias = all_data_f.mean()
|
| 581 |
+
std = all_data_f.std()
|
| 582 |
+
x_f = (x_f - bias) / std
|
| 583 |
+
y_f = (y_f - bias) / std
|
| 584 |
+
output = self.comp_model.one_to_one_forward(x_f, y_f)
|
| 585 |
+
return output
|
| 586 |
+
|
| 587 |
+
|
| 588 |
+
|
| 589 |
+
self.model = BetaModel(nobs_features, act_data, device)
|
| 590 |
+
self.opt = None #, weight_decay=1.0e-4
|
| 591 |
+
self.scheduler = None #torch.optim.lr_scheduler.ReduceLROnPlateau(self.opt, mode='min', patience=100, verbose=True)
|
| 592 |
+
self.ref_model: BetaModel
|
| 593 |
+
|
| 594 |
+
def get_alpha_beta(self, x):
|
| 595 |
+
batch_comp = self.model(x).detach()
|
| 596 |
+
alpha = torch.sum(batch_comp, dim=-1)
|
| 597 |
+
beta = torch.sum(1 - batch_comp, dim=-1)
|
| 598 |
+
# alpha = torch.sum(torch.where(batch_comp > 0.5, torch.tensor(1.0, device=self.device), torch.tensor(0.0, device=self.device)), dim=-1)
|
| 599 |
+
# beta = torch.sum(torch.where(batch_comp < 0.5, torch.tensor(1.0, device=self.device), torch.tensor(0.0, device=self.device)), dim=-1)
|
| 600 |
+
|
| 601 |
+
return alpha.detach(), beta.detach()
|
| 602 |
+
|
| 603 |
+
# def fit_data(self, save_dir=None, load_dir=None, num_epochs=1, warm_up_epochs=0, batch_size=1, lr=1.0e-5):
|
| 604 |
+
# if load_dir is None:
|
| 605 |
+
# interval = math.ceil(self.data.shape[0] / batch_size)
|
| 606 |
+
# total_steps = num_epochs * interval
|
| 607 |
+
# warm_up_steps = warm_up_epochs * interval
|
| 608 |
+
# main_steps = total_steps - warm_up_steps
|
| 609 |
+
|
| 610 |
+
# # Learning rate schedulers
|
| 611 |
+
# self.lr = lr
|
| 612 |
+
# self.opt = torch.optim.Adam(self.model.parameters(), lr=self.lr, weight_decay=1e-5)
|
| 613 |
+
# warm_up_scheduler = LinearLR(self.opt, start_factor=1e-8, end_factor=1.0, total_iters=warm_up_steps)
|
| 614 |
+
# cosine_scheduler = CosineAnnealingLR(self.opt, T_max=main_steps)
|
| 615 |
+
# self.scheduler = SequentialLR(self.opt, schedulers=[warm_up_scheduler, cosine_scheduler], milestones=[warm_up_steps])
|
| 616 |
+
|
| 617 |
+
# for epoch in range(num_epochs):
|
| 618 |
+
# beta_loss_all = []
|
| 619 |
+
|
| 620 |
+
# batch_shuffled_idx = np.random.permutation(self.data.shape[0])
|
| 621 |
+
# for i in tqdm(range(interval)):
|
| 622 |
+
|
| 623 |
+
# start_pt = i * batch_size
|
| 624 |
+
# end_pt = min((i + 1) * batch_size, self.data.shape[0])
|
| 625 |
+
# local_idx = batch_shuffled_idx[start_pt:end_pt]
|
| 626 |
+
# batch = self.data[local_idx, ...]
|
| 627 |
+
# batch_votes = self.votes_data[local_idx, ...]
|
| 628 |
+
|
| 629 |
+
# comp = torch.sigmoid(batch_votes - self.votes_data.T)
|
| 630 |
+
# pred_comp = self.model(batch)
|
| 631 |
+
|
| 632 |
+
# beta_loss = torch.mean((comp - pred_comp) ** 2)
|
| 633 |
+
|
| 634 |
+
# beta_loss_all.append(beta_loss)
|
| 635 |
+
|
| 636 |
+
# self.opt.zero_grad()
|
| 637 |
+
# beta_loss.backward()
|
| 638 |
+
# self.opt.step()
|
| 639 |
+
# self.scheduler.step() # Update LR after each optimizer step
|
| 640 |
+
|
| 641 |
+
# beta_loss_all = torch.stack(beta_loss_all, dim=0)
|
| 642 |
+
# print("iteration:", epoch + 1)
|
| 643 |
+
# print("mean_beta_loss_all:", torch.mean(beta_loss_all).item())
|
| 644 |
+
|
| 645 |
+
# if save_dir is not None and (((epoch + 1) % 50 == 0) or ((epoch + 1) == num_epochs)):
|
| 646 |
+
# tmp_save_dir = Path(save_dir) / f'itr_{epoch + 1}'
|
| 647 |
+
# tmp_save_dir.mkdir(parents=True, exist_ok=True)
|
| 648 |
+
# model_file = tmp_save_dir / 'beta_model.pth'
|
| 649 |
+
# self.save_model(model_file)
|
| 650 |
+
# self.ref_model = copy.deepcopy(self.model)
|
| 651 |
+
# else:
|
| 652 |
+
# self.load_model(load_dir)
|
| 653 |
+
# self.ref_model = copy.deepcopy(self.model)
|
| 654 |
+
|
| 655 |
+
def online_update(self, dataset, num_epochs=1, warm_up_epochs=0, batch_size=1, lr = 1.0e-6):
|
| 656 |
+
interval = math.ceil(dataset["obs"].shape[0] / batch_size)
|
| 657 |
+
total_steps = num_epochs * interval
|
| 658 |
+
warm_up_steps = warm_up_epochs * interval
|
| 659 |
+
main_steps = total_steps - warm_up_steps
|
| 660 |
+
|
| 661 |
+
# Learning rate schedulers
|
| 662 |
+
self.lr = lr
|
| 663 |
+
self.opt = torch.optim.Adam(self.model.parameters(), lr=self.lr, weight_decay=1e-5)
|
| 664 |
+
warm_up_scheduler = LinearLR(self.opt, start_factor=1e-8, end_factor=1.0, total_iters=warm_up_steps)
|
| 665 |
+
cosine_scheduler = CosineAnnealingLR(self.opt, T_max=main_steps)
|
| 666 |
+
self.scheduler = SequentialLR(self.opt, schedulers=[warm_up_scheduler, cosine_scheduler], milestones=[warm_up_steps])
|
| 667 |
+
|
| 668 |
+
for epoch in range(num_epochs):
|
| 669 |
+
beta_loss_all = []
|
| 670 |
+
|
| 671 |
+
batch_shuffled_idx = np.random.permutation(dataset["obs"].shape[0])
|
| 672 |
+
for i in tqdm(range(interval)):
|
| 673 |
+
|
| 674 |
+
start_pt = i * batch_size
|
| 675 |
+
end_pt = min((i + 1) * batch_size, dataset["obs"].shape[0])
|
| 676 |
+
batch = index_batch(dataset, batch_shuffled_idx[start_pt:end_pt])
|
| 677 |
+
|
| 678 |
+
obs_1 = batch['obs'] # batch_size * traj_len * obs_dim
|
| 679 |
+
act_1 = batch['action'] # batch_size * traj_len * action_dim
|
| 680 |
+
obs_2 = batch['obs_2']
|
| 681 |
+
act_2 = batch['action_2']
|
| 682 |
+
s_a_1 = np.concatenate([obs_1, act_1], axis=-1)
|
| 683 |
+
s_a_2 = np.concatenate([obs_2, act_2], axis=-1)
|
| 684 |
+
|
| 685 |
+
votes_1 = torch.from_numpy(batch['votes']).to(self.device)
|
| 686 |
+
votes_2 = torch.from_numpy(batch['votes_2']).to(self.device)
|
| 687 |
+
|
| 688 |
+
# threshold = 1e-3
|
| 689 |
+
# diff = torch.abs(votes_1 - votes_2)
|
| 690 |
+
# condition_1 = (votes_1 > votes_2) & (diff >= threshold) # votes_1 > votes_2 and diff >= threshold
|
| 691 |
+
# condition_2 = (votes_1 < votes_2) & (diff >= threshold) # votes_1 < votes_2 and diff >= threshold
|
| 692 |
+
|
| 693 |
+
# comp_1 = torch.where(condition_1, torch.tensor(1.0, device=self.device), torch.tensor(0.0, device=self.device))
|
| 694 |
+
# comp_1 = torch.squeeze(comp_1, dim=-1)
|
| 695 |
+
# comp_2 = torch.where(condition_2, torch.tensor(1.0, device=self.device), torch.tensor(0.0, device=self.device))
|
| 696 |
+
# comp_2 = torch.squeeze(comp_2, dim=-1)
|
| 697 |
+
|
| 698 |
+
comp_1 = torch.sigmoid(votes_1 - votes_2)
|
| 699 |
+
comp_2 = torch.sigmoid(votes_2 - votes_1)
|
| 700 |
+
|
| 701 |
+
pred_comp_1 = self.model.one_to_one_forward(torch.from_numpy(s_a_1).float().to(self.device), torch.from_numpy(s_a_2).float().to(self.device))
|
| 702 |
+
pred_comp_2 = self.model.one_to_one_forward(torch.from_numpy(s_a_2).float().to(self.device), torch.from_numpy(s_a_1).float().to(self.device))
|
| 703 |
+
# ref_comp_1 = self.ref_model.one_to_one_forward(torch.from_numpy(s_a_1).float().to(self.device), torch.from_numpy(s_a_2).float().to(self.device)).detach()
|
| 704 |
+
# ref_comp_2 = self.ref_model.one_to_one_forward(torch.from_numpy(s_a_2).float().to(self.device), torch.from_numpy(s_a_1).float().to(self.device)).detach()
|
| 705 |
+
|
| 706 |
+
beta_loss = (torch.mean((comp_1 - pred_comp_1) ** 2) + torch.mean((comp_2 - pred_comp_2) ** 2)) / 2
|
| 707 |
+
|
| 708 |
+
beta_loss_all.append(beta_loss)
|
| 709 |
+
|
| 710 |
+
self.opt.zero_grad()
|
| 711 |
+
beta_loss.backward()
|
| 712 |
+
self.opt.step()
|
| 713 |
+
self.scheduler.step() # Update LR after each optimizer step
|
| 714 |
+
|
| 715 |
+
beta_loss_all = torch.stack(beta_loss_all, dim=0)
|
| 716 |
+
print("iteration:", epoch + 1)
|
| 717 |
+
print("mean_beta_loss_all:", torch.mean(beta_loss_all).item())
|
| 718 |
+
|
| 719 |
+
def save_model(self, filepath):
|
| 720 |
+
torch.save(self.state_dict(), filepath)
|
| 721 |
+
|
| 722 |
+
def load_model(self, filepath):
|
| 723 |
+
self.load_state_dict(torch.load(filepath, map_location=self.device))
|
common/pymunk_override.py
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ----------------------------------------------------------------------------
|
| 2 |
+
# pymunk
|
| 3 |
+
# Copyright (c) 2007-2016 Victor Blomqvist
|
| 4 |
+
#
|
| 5 |
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
# of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
# in the Software without restriction, including without limitation the rights
|
| 8 |
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
# copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
# furnished to do so, subject to the following conditions:
|
| 11 |
+
#
|
| 12 |
+
# The above copyright notice and this permission notice shall be included in
|
| 13 |
+
# all copies or substantial portions of the Software.
|
| 14 |
+
#
|
| 15 |
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
# SOFTWARE.
|
| 22 |
+
# ----------------------------------------------------------------------------
|
| 23 |
+
|
| 24 |
+
"""This submodule contains helper functions to help with quick prototyping
|
| 25 |
+
using pymunk together with pygame.
|
| 26 |
+
|
| 27 |
+
Intended to help with debugging and prototyping, not for actual production use
|
| 28 |
+
in a full application. The methods contained in this module is opinionated
|
| 29 |
+
about your coordinate system and not in any way optimized.
|
| 30 |
+
"""
|
| 31 |
+
|
| 32 |
+
__docformat__ = "reStructuredText"
|
| 33 |
+
|
| 34 |
+
__all__ = [
|
| 35 |
+
"DrawOptions",
|
| 36 |
+
"get_mouse_pos",
|
| 37 |
+
"to_pygame",
|
| 38 |
+
"from_pygame",
|
| 39 |
+
"lighten",
|
| 40 |
+
"positive_y_is_up",
|
| 41 |
+
]
|
| 42 |
+
|
| 43 |
+
from typing import List, Sequence, Tuple
|
| 44 |
+
|
| 45 |
+
import pygame
|
| 46 |
+
|
| 47 |
+
import numpy as np
|
| 48 |
+
|
| 49 |
+
import pymunk
|
| 50 |
+
from pymunk.space_debug_draw_options import SpaceDebugColor
|
| 51 |
+
from pymunk.vec2d import Vec2d
|
| 52 |
+
|
| 53 |
+
positive_y_is_up: bool = False
|
| 54 |
+
"""Make increasing values of y point upwards.
|
| 55 |
+
|
| 56 |
+
When True::
|
| 57 |
+
|
| 58 |
+
y
|
| 59 |
+
^
|
| 60 |
+
| . (3, 3)
|
| 61 |
+
|
|
| 62 |
+
| . (2, 2)
|
| 63 |
+
|
|
| 64 |
+
+------ > x
|
| 65 |
+
|
| 66 |
+
When False::
|
| 67 |
+
|
| 68 |
+
+------ > x
|
| 69 |
+
|
|
| 70 |
+
| . (2, 2)
|
| 71 |
+
|
|
| 72 |
+
| . (3, 3)
|
| 73 |
+
v
|
| 74 |
+
y
|
| 75 |
+
|
| 76 |
+
"""
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
class DrawOptions(pymunk.SpaceDebugDrawOptions):
|
| 80 |
+
def __init__(self, surface: pygame.Surface) -> None:
|
| 81 |
+
"""Draw a pymunk.Space on a pygame.Surface object.
|
| 82 |
+
|
| 83 |
+
Typical usage::
|
| 84 |
+
|
| 85 |
+
>>> import pymunk
|
| 86 |
+
>>> surface = pygame.Surface((10,10))
|
| 87 |
+
>>> space = pymunk.Space()
|
| 88 |
+
>>> options = pymunk.pygame_util.DrawOptions(surface)
|
| 89 |
+
>>> space.debug_draw(options)
|
| 90 |
+
|
| 91 |
+
You can control the color of a shape by setting shape.color to the color
|
| 92 |
+
you want it drawn in::
|
| 93 |
+
|
| 94 |
+
>>> c = pymunk.Circle(None, 10)
|
| 95 |
+
>>> c.color = pygame.Color("pink")
|
| 96 |
+
|
| 97 |
+
See pygame_util.demo.py for a full example
|
| 98 |
+
|
| 99 |
+
Since pygame uses a coordinate system where y points down (in contrast
|
| 100 |
+
to many other cases), you either have to make the physics simulation
|
| 101 |
+
with Pymunk also behave in that way, or flip everything when you draw.
|
| 102 |
+
|
| 103 |
+
The easiest is probably to just make the simulation behave the same
|
| 104 |
+
way as Pygame does. In that way all coordinates used are in the same
|
| 105 |
+
orientation and easy to reason about::
|
| 106 |
+
|
| 107 |
+
>>> space = pymunk.Space()
|
| 108 |
+
>>> space.gravity = (0, -1000)
|
| 109 |
+
>>> body = pymunk.Body()
|
| 110 |
+
>>> body.position = (0, 0) # will be positioned in the top left corner
|
| 111 |
+
>>> space.debug_draw(options)
|
| 112 |
+
|
| 113 |
+
To flip the drawing its possible to set the module property
|
| 114 |
+
:py:data:`positive_y_is_up` to True. Then the pygame drawing will flip
|
| 115 |
+
the simulation upside down before drawing::
|
| 116 |
+
|
| 117 |
+
>>> positive_y_is_up = True
|
| 118 |
+
>>> body = pymunk.Body()
|
| 119 |
+
>>> body.position = (0, 0)
|
| 120 |
+
>>> # Body will be position in bottom left corner
|
| 121 |
+
|
| 122 |
+
:Parameters:
|
| 123 |
+
surface : pygame.Surface
|
| 124 |
+
Surface that the objects will be drawn on
|
| 125 |
+
"""
|
| 126 |
+
self.surface = surface
|
| 127 |
+
super(DrawOptions, self).__init__()
|
| 128 |
+
|
| 129 |
+
def draw_circle(
|
| 130 |
+
self,
|
| 131 |
+
pos: Vec2d,
|
| 132 |
+
angle: float,
|
| 133 |
+
radius: float,
|
| 134 |
+
outline_color: SpaceDebugColor,
|
| 135 |
+
fill_color: SpaceDebugColor,
|
| 136 |
+
) -> None:
|
| 137 |
+
p = to_pygame(pos, self.surface)
|
| 138 |
+
|
| 139 |
+
pygame.draw.circle(self.surface, fill_color.as_int(), p, round(radius), 0)
|
| 140 |
+
pygame.draw.circle(self.surface, light_color(fill_color).as_int(), p, round(radius-4), 0)
|
| 141 |
+
|
| 142 |
+
circle_edge = pos + Vec2d(radius, 0).rotated(angle)
|
| 143 |
+
p2 = to_pygame(circle_edge, self.surface)
|
| 144 |
+
line_r = 2 if radius > 20 else 1
|
| 145 |
+
# pygame.draw.lines(self.surface, outline_color.as_int(), False, [p, p2], line_r)
|
| 146 |
+
|
| 147 |
+
def draw_segment(self, a: Vec2d, b: Vec2d, color: SpaceDebugColor) -> None:
|
| 148 |
+
p1 = to_pygame(a, self.surface)
|
| 149 |
+
p2 = to_pygame(b, self.surface)
|
| 150 |
+
|
| 151 |
+
pygame.draw.aalines(self.surface, color.as_int(), False, [p1, p2])
|
| 152 |
+
|
| 153 |
+
def draw_fat_segment(
|
| 154 |
+
self,
|
| 155 |
+
a: Tuple[float, float],
|
| 156 |
+
b: Tuple[float, float],
|
| 157 |
+
radius: float,
|
| 158 |
+
outline_color: SpaceDebugColor,
|
| 159 |
+
fill_color: SpaceDebugColor,
|
| 160 |
+
) -> None:
|
| 161 |
+
p1 = to_pygame(a, self.surface)
|
| 162 |
+
p2 = to_pygame(b, self.surface)
|
| 163 |
+
|
| 164 |
+
r = round(max(1, radius * 2))
|
| 165 |
+
pygame.draw.lines(self.surface, fill_color.as_int(), False, [p1, p2], r)
|
| 166 |
+
if r > 2:
|
| 167 |
+
orthog = [abs(p2[1] - p1[1]), abs(p2[0] - p1[0])]
|
| 168 |
+
if orthog[0] == 0 and orthog[1] == 0:
|
| 169 |
+
return
|
| 170 |
+
scale = radius / (orthog[0] * orthog[0] + orthog[1] * orthog[1]) ** 0.5
|
| 171 |
+
orthog[0] = round(orthog[0] * scale)
|
| 172 |
+
orthog[1] = round(orthog[1] * scale)
|
| 173 |
+
points = [
|
| 174 |
+
(p1[0] - orthog[0], p1[1] - orthog[1]),
|
| 175 |
+
(p1[0] + orthog[0], p1[1] + orthog[1]),
|
| 176 |
+
(p2[0] + orthog[0], p2[1] + orthog[1]),
|
| 177 |
+
(p2[0] - orthog[0], p2[1] - orthog[1]),
|
| 178 |
+
]
|
| 179 |
+
pygame.draw.polygon(self.surface, fill_color.as_int(), points)
|
| 180 |
+
pygame.draw.circle(
|
| 181 |
+
self.surface,
|
| 182 |
+
fill_color.as_int(),
|
| 183 |
+
(round(p1[0]), round(p1[1])),
|
| 184 |
+
round(radius),
|
| 185 |
+
)
|
| 186 |
+
pygame.draw.circle(
|
| 187 |
+
self.surface,
|
| 188 |
+
fill_color.as_int(),
|
| 189 |
+
(round(p2[0]), round(p2[1])),
|
| 190 |
+
round(radius),
|
| 191 |
+
)
|
| 192 |
+
|
| 193 |
+
def draw_polygon(
|
| 194 |
+
self,
|
| 195 |
+
verts: Sequence[Tuple[float, float]],
|
| 196 |
+
radius: float,
|
| 197 |
+
outline_color: SpaceDebugColor,
|
| 198 |
+
fill_color: SpaceDebugColor,
|
| 199 |
+
) -> None:
|
| 200 |
+
ps = [to_pygame(v, self.surface) for v in verts]
|
| 201 |
+
ps += [ps[0]]
|
| 202 |
+
|
| 203 |
+
radius = 2
|
| 204 |
+
pygame.draw.polygon(self.surface, light_color(fill_color).as_int(), ps)
|
| 205 |
+
|
| 206 |
+
if radius > 0:
|
| 207 |
+
for i in range(len(verts)):
|
| 208 |
+
a = verts[i]
|
| 209 |
+
b = verts[(i + 1) % len(verts)]
|
| 210 |
+
self.draw_fat_segment(a, b, radius, fill_color, fill_color)
|
| 211 |
+
|
| 212 |
+
def draw_dot(
|
| 213 |
+
self, size: float, pos: Tuple[float, float], color: SpaceDebugColor
|
| 214 |
+
) -> None:
|
| 215 |
+
p = to_pygame(pos, self.surface)
|
| 216 |
+
pygame.draw.circle(self.surface, color.as_int(), p, round(size), 0)
|
| 217 |
+
|
| 218 |
+
|
| 219 |
+
def get_mouse_pos(surface: pygame.Surface) -> Tuple[int, int]:
|
| 220 |
+
"""Get position of the mouse pointer in pymunk coordinates."""
|
| 221 |
+
p = pygame.mouse.get_pos()
|
| 222 |
+
return from_pygame(p, surface)
|
| 223 |
+
|
| 224 |
+
|
| 225 |
+
def to_pygame(p: Tuple[float, float], surface: pygame.Surface) -> Tuple[int, int]:
|
| 226 |
+
"""Convenience method to convert pymunk coordinates to pygame surface
|
| 227 |
+
local coordinates.
|
| 228 |
+
|
| 229 |
+
Note that in case positive_y_is_up is False, this function won't actually do
|
| 230 |
+
anything except converting the point to integers.
|
| 231 |
+
"""
|
| 232 |
+
if positive_y_is_up:
|
| 233 |
+
return round(p[0]), surface.get_height() - round(p[1])
|
| 234 |
+
else:
|
| 235 |
+
return round(p[0]), round(p[1])
|
| 236 |
+
|
| 237 |
+
|
| 238 |
+
def from_pygame(p: Tuple[float, float], surface: pygame.Surface) -> Tuple[int, int]:
|
| 239 |
+
"""Convenience method to convert pygame surface local coordinates to
|
| 240 |
+
pymunk coordinates
|
| 241 |
+
"""
|
| 242 |
+
return to_pygame(p, surface)
|
| 243 |
+
|
| 244 |
+
|
| 245 |
+
def light_color(color: SpaceDebugColor):
|
| 246 |
+
color = np.minimum(1.2 * np.float32([color.r, color.g, color.b, color.a]), np.float32([255]))
|
| 247 |
+
color = SpaceDebugColor(r=color[0], g=color[1], b=color[2], a=color[3])
|
| 248 |
+
return color
|
common/pymunk_util.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pygame
|
| 2 |
+
import pymunk
|
| 3 |
+
import pymunk.pygame_util
|
| 4 |
+
import numpy as np
|
| 5 |
+
|
| 6 |
+
COLLTYPE_DEFAULT = 0
|
| 7 |
+
COLLTYPE_MOUSE = 1
|
| 8 |
+
COLLTYPE_BALL = 2
|
| 9 |
+
|
| 10 |
+
def get_body_type(static=False):
|
| 11 |
+
body_type = pymunk.Body.DYNAMIC
|
| 12 |
+
if static:
|
| 13 |
+
body_type = pymunk.Body.STATIC
|
| 14 |
+
return body_type
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def create_rectangle(space,
|
| 18 |
+
pos_x,pos_y,width,height,
|
| 19 |
+
density=3,static=False):
|
| 20 |
+
body = pymunk.Body(body_type=get_body_type(static))
|
| 21 |
+
body.position = (pos_x,pos_y)
|
| 22 |
+
shape = pymunk.Poly.create_box(body,(width,height))
|
| 23 |
+
shape.density = density
|
| 24 |
+
space.add(body,shape)
|
| 25 |
+
return body, shape
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def create_rectangle_bb(space,
|
| 29 |
+
left, bottom, right, top,
|
| 30 |
+
**kwargs):
|
| 31 |
+
pos_x = (left + right) / 2
|
| 32 |
+
pos_y = (top + bottom) / 2
|
| 33 |
+
height = top - bottom
|
| 34 |
+
width = right - left
|
| 35 |
+
return create_rectangle(space, pos_x, pos_y, width, height, **kwargs)
|
| 36 |
+
|
| 37 |
+
def create_circle(space, pos_x, pos_y, radius, density=3, static=False):
|
| 38 |
+
body = pymunk.Body(body_type=get_body_type(static))
|
| 39 |
+
body.position = (pos_x, pos_y)
|
| 40 |
+
shape = pymunk.Circle(body, radius=radius)
|
| 41 |
+
shape.density = density
|
| 42 |
+
shape.collision_type = COLLTYPE_BALL
|
| 43 |
+
space.add(body, shape)
|
| 44 |
+
return body, shape
|
| 45 |
+
|
| 46 |
+
def get_body_state(body):
|
| 47 |
+
state = np.zeros(6, dtype=np.float32)
|
| 48 |
+
state[:2] = body.position
|
| 49 |
+
state[2] = body.angle
|
| 50 |
+
state[3:5] = body.velocity
|
| 51 |
+
state[5] = body.angular_velocity
|
| 52 |
+
return state
|
common/pytorch_util.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Dict, Callable, List
|
| 2 |
+
import collections
|
| 3 |
+
import torch
|
| 4 |
+
import torch.nn as nn
|
| 5 |
+
|
| 6 |
+
def dict_apply(
|
| 7 |
+
x: Dict[str, torch.Tensor],
|
| 8 |
+
func: Callable[[torch.Tensor], torch.Tensor]
|
| 9 |
+
) -> Dict[str, torch.Tensor]:
|
| 10 |
+
result = dict()
|
| 11 |
+
for key, value in x.items():
|
| 12 |
+
if isinstance(value, dict):
|
| 13 |
+
result[key] = dict_apply(value, func)
|
| 14 |
+
else:
|
| 15 |
+
result[key] = func(value)
|
| 16 |
+
return result
|
| 17 |
+
|
| 18 |
+
def pad_remaining_dims(x, target):
|
| 19 |
+
assert x.shape == target.shape[:len(x.shape)]
|
| 20 |
+
return x.reshape(x.shape + (1,)*(len(target.shape) - len(x.shape)))
|
| 21 |
+
|
| 22 |
+
def dict_apply_split(
|
| 23 |
+
x: Dict[str, torch.Tensor],
|
| 24 |
+
split_func: Callable[[torch.Tensor], Dict[str, torch.Tensor]]
|
| 25 |
+
) -> Dict[str, torch.Tensor]:
|
| 26 |
+
results = collections.defaultdict(dict)
|
| 27 |
+
for key, value in x.items():
|
| 28 |
+
result = split_func(value)
|
| 29 |
+
for k, v in result.items():
|
| 30 |
+
results[k][key] = v
|
| 31 |
+
return results
|
| 32 |
+
|
| 33 |
+
def dict_apply_reduce(
|
| 34 |
+
x: List[Dict[str, torch.Tensor]],
|
| 35 |
+
reduce_func: Callable[[List[torch.Tensor]], torch.Tensor]
|
| 36 |
+
) -> Dict[str, torch.Tensor]:
|
| 37 |
+
result = dict()
|
| 38 |
+
for key in x[0].keys():
|
| 39 |
+
result[key] = reduce_func([x_[key] for x_ in x])
|
| 40 |
+
return result
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def replace_submodules(
|
| 44 |
+
root_module: nn.Module,
|
| 45 |
+
predicate: Callable[[nn.Module], bool],
|
| 46 |
+
func: Callable[[nn.Module], nn.Module]) -> nn.Module:
|
| 47 |
+
"""
|
| 48 |
+
predicate: Return true if the module is to be replaced.
|
| 49 |
+
func: Return new module to use.
|
| 50 |
+
"""
|
| 51 |
+
if predicate(root_module):
|
| 52 |
+
return func(root_module)
|
| 53 |
+
|
| 54 |
+
bn_list = [k.split('.') for k, m
|
| 55 |
+
in root_module.named_modules(remove_duplicate=True)
|
| 56 |
+
if predicate(m)]
|
| 57 |
+
for *parent, k in bn_list:
|
| 58 |
+
parent_module = root_module
|
| 59 |
+
if len(parent) > 0:
|
| 60 |
+
parent_module = root_module.get_submodule('.'.join(parent))
|
| 61 |
+
if isinstance(parent_module, nn.Sequential):
|
| 62 |
+
src_module = parent_module[int(k)]
|
| 63 |
+
else:
|
| 64 |
+
src_module = getattr(parent_module, k)
|
| 65 |
+
tgt_module = func(src_module)
|
| 66 |
+
if isinstance(parent_module, nn.Sequential):
|
| 67 |
+
parent_module[int(k)] = tgt_module
|
| 68 |
+
else:
|
| 69 |
+
setattr(parent_module, k, tgt_module)
|
| 70 |
+
# verify that all BN are replaced
|
| 71 |
+
bn_list = [k.split('.') for k, m
|
| 72 |
+
in root_module.named_modules(remove_duplicate=True)
|
| 73 |
+
if predicate(m)]
|
| 74 |
+
assert len(bn_list) == 0
|
| 75 |
+
return root_module
|
| 76 |
+
|
| 77 |
+
def optimizer_to(optimizer, device):
|
| 78 |
+
for state in optimizer.state.values():
|
| 79 |
+
for k, v in state.items():
|
| 80 |
+
if isinstance(v, torch.Tensor):
|
| 81 |
+
state[k] = v.to(device=device)
|
| 82 |
+
return optimizer
|
common/replay_buffer.py
ADDED
|
@@ -0,0 +1,615 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Union, Dict, Optional
|
| 2 |
+
import os
|
| 3 |
+
import math
|
| 4 |
+
import numbers
|
| 5 |
+
import zarr
|
| 6 |
+
import numcodecs
|
| 7 |
+
import numpy as np
|
| 8 |
+
from functools import cached_property
|
| 9 |
+
|
| 10 |
+
def check_chunks_compatible(chunks: tuple, shape: tuple):
|
| 11 |
+
assert len(shape) == len(chunks)
|
| 12 |
+
for c in chunks:
|
| 13 |
+
assert isinstance(c, numbers.Integral)
|
| 14 |
+
assert c > 0
|
| 15 |
+
|
| 16 |
+
def rechunk_recompress_array(group, name,
|
| 17 |
+
chunks=None, chunk_length=None,
|
| 18 |
+
compressor=None, tmp_key='_temp'):
|
| 19 |
+
old_arr = group[name]
|
| 20 |
+
if chunks is None:
|
| 21 |
+
if chunk_length is not None:
|
| 22 |
+
chunks = (chunk_length,) + old_arr.chunks[1:]
|
| 23 |
+
else:
|
| 24 |
+
chunks = old_arr.chunks
|
| 25 |
+
check_chunks_compatible(chunks, old_arr.shape)
|
| 26 |
+
|
| 27 |
+
if compressor is None:
|
| 28 |
+
compressor = old_arr.compressor
|
| 29 |
+
|
| 30 |
+
if (chunks == old_arr.chunks) and (compressor == old_arr.compressor):
|
| 31 |
+
# no change
|
| 32 |
+
return old_arr
|
| 33 |
+
|
| 34 |
+
# rechunk recompress
|
| 35 |
+
group.move(name, tmp_key)
|
| 36 |
+
old_arr = group[tmp_key]
|
| 37 |
+
n_copied, n_skipped, n_bytes_copied = zarr.copy(
|
| 38 |
+
source=old_arr,
|
| 39 |
+
dest=group,
|
| 40 |
+
name=name,
|
| 41 |
+
chunks=chunks,
|
| 42 |
+
compressor=compressor,
|
| 43 |
+
)
|
| 44 |
+
del group[tmp_key]
|
| 45 |
+
arr = group[name]
|
| 46 |
+
return arr
|
| 47 |
+
|
| 48 |
+
def get_optimal_chunks(shape, dtype,
|
| 49 |
+
target_chunk_bytes=2e6,
|
| 50 |
+
max_chunk_length=None):
|
| 51 |
+
"""
|
| 52 |
+
Common shapes
|
| 53 |
+
T,D
|
| 54 |
+
T,N,D
|
| 55 |
+
T,H,W,C
|
| 56 |
+
T,N,H,W,C
|
| 57 |
+
"""
|
| 58 |
+
itemsize = np.dtype(dtype).itemsize
|
| 59 |
+
# reversed
|
| 60 |
+
rshape = list(shape[::-1])
|
| 61 |
+
if max_chunk_length is not None:
|
| 62 |
+
rshape[-1] = int(max_chunk_length)
|
| 63 |
+
split_idx = len(shape)-1
|
| 64 |
+
for i in range(len(shape)-1):
|
| 65 |
+
this_chunk_bytes = itemsize * np.prod(rshape[:i])
|
| 66 |
+
next_chunk_bytes = itemsize * np.prod(rshape[:i+1])
|
| 67 |
+
if this_chunk_bytes <= target_chunk_bytes \
|
| 68 |
+
and next_chunk_bytes > target_chunk_bytes:
|
| 69 |
+
split_idx = i
|
| 70 |
+
|
| 71 |
+
rchunks = rshape[:split_idx]
|
| 72 |
+
item_chunk_bytes = itemsize * np.prod(rshape[:split_idx])
|
| 73 |
+
this_max_chunk_length = rshape[split_idx]
|
| 74 |
+
next_chunk_length = min(this_max_chunk_length, math.ceil(
|
| 75 |
+
target_chunk_bytes / item_chunk_bytes))
|
| 76 |
+
rchunks.append(next_chunk_length)
|
| 77 |
+
len_diff = len(shape) - len(rchunks)
|
| 78 |
+
rchunks.extend([1] * len_diff)
|
| 79 |
+
chunks = tuple(rchunks[::-1])
|
| 80 |
+
# print(np.prod(chunks) * itemsize / target_chunk_bytes)
|
| 81 |
+
return chunks
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
class ReplayBuffer:
|
| 85 |
+
"""
|
| 86 |
+
Zarr-based temporal datastructure.
|
| 87 |
+
Assumes first dimension to be time. Only chunk in time dimension.
|
| 88 |
+
"""
|
| 89 |
+
def __init__(self,
|
| 90 |
+
root: Union[zarr.Group,
|
| 91 |
+
Dict[str,dict]]):
|
| 92 |
+
"""
|
| 93 |
+
Dummy constructor. Use copy_from* and create_from* class methods instead.
|
| 94 |
+
"""
|
| 95 |
+
assert('data' in root)
|
| 96 |
+
assert('meta' in root)
|
| 97 |
+
assert('episode_ends' in root['meta'])
|
| 98 |
+
for key, value in root['data'].items():
|
| 99 |
+
assert(value.shape[0] == root['meta']['episode_ends'][-1])
|
| 100 |
+
self.root = root
|
| 101 |
+
|
| 102 |
+
# ============= create constructors ===============
|
| 103 |
+
@classmethod
|
| 104 |
+
def create_empty_zarr(cls, storage=None, root=None):
|
| 105 |
+
if root is None:
|
| 106 |
+
if storage is None:
|
| 107 |
+
storage = zarr.MemoryStore()
|
| 108 |
+
root = zarr.group(store=storage)
|
| 109 |
+
data = root.require_group('data', overwrite=False)
|
| 110 |
+
meta = root.require_group('meta', overwrite=False)
|
| 111 |
+
if 'episode_ends' not in meta:
|
| 112 |
+
episode_ends = meta.zeros('episode_ends', shape=(0,), dtype=np.int64,
|
| 113 |
+
compressor=None, overwrite=False)
|
| 114 |
+
return cls(root=root)
|
| 115 |
+
|
| 116 |
+
@classmethod
|
| 117 |
+
def create_empty_numpy(cls):
|
| 118 |
+
root = {
|
| 119 |
+
'data': dict(),
|
| 120 |
+
'meta': {
|
| 121 |
+
'episode_ends': np.zeros((0,), dtype=np.int64)
|
| 122 |
+
}
|
| 123 |
+
}
|
| 124 |
+
return cls(root=root)
|
| 125 |
+
|
| 126 |
+
@classmethod
|
| 127 |
+
def create_from_group(cls, group, **kwargs):
|
| 128 |
+
if 'data' not in group:
|
| 129 |
+
# create from stratch
|
| 130 |
+
buffer = cls.create_empty_zarr(root=group, **kwargs)
|
| 131 |
+
else:
|
| 132 |
+
# already exist
|
| 133 |
+
buffer = cls(root=group, **kwargs)
|
| 134 |
+
return buffer
|
| 135 |
+
|
| 136 |
+
@classmethod
|
| 137 |
+
def create_from_path(cls, zarr_path, mode='r', **kwargs):
|
| 138 |
+
"""
|
| 139 |
+
Open a on-disk zarr directly (for dataset larger than memory).
|
| 140 |
+
Slower.
|
| 141 |
+
"""
|
| 142 |
+
group = zarr.open(os.path.expanduser(zarr_path), mode)
|
| 143 |
+
return cls.create_from_group(group, **kwargs)
|
| 144 |
+
|
| 145 |
+
# ============= copy constructors ===============
|
| 146 |
+
@classmethod
|
| 147 |
+
def copy_from_store(cls, src_store, store=None, keys=None,
|
| 148 |
+
chunks: Dict[str,tuple]=dict(),
|
| 149 |
+
compressors: Union[dict, str, numcodecs.abc.Codec]=dict(),
|
| 150 |
+
if_exists='replace',
|
| 151 |
+
**kwargs):
|
| 152 |
+
"""
|
| 153 |
+
Load to memory.
|
| 154 |
+
"""
|
| 155 |
+
src_root = zarr.group(src_store)
|
| 156 |
+
root = None
|
| 157 |
+
if store is None:
|
| 158 |
+
# numpy backend
|
| 159 |
+
meta = dict()
|
| 160 |
+
for key, value in src_root['meta'].items():
|
| 161 |
+
if len(value.shape) == 0:
|
| 162 |
+
meta[key] = np.array(value)
|
| 163 |
+
else:
|
| 164 |
+
meta[key] = value[:]
|
| 165 |
+
|
| 166 |
+
if keys is None:
|
| 167 |
+
keys = src_root['data'].keys()
|
| 168 |
+
data = dict()
|
| 169 |
+
for key in keys:
|
| 170 |
+
arr = src_root['data'][key]
|
| 171 |
+
data[key] = arr[:]
|
| 172 |
+
|
| 173 |
+
root = {
|
| 174 |
+
'meta': meta,
|
| 175 |
+
'data': data
|
| 176 |
+
}
|
| 177 |
+
else:
|
| 178 |
+
root = zarr.group(store=store)
|
| 179 |
+
# copy without recompression
|
| 180 |
+
n_copied, n_skipped, n_bytes_copied = zarr.copy_store(source=src_store, dest=store,
|
| 181 |
+
source_path='/meta', dest_path='/meta', if_exists=if_exists)
|
| 182 |
+
data_group = root.create_group('data', overwrite=True)
|
| 183 |
+
if keys is None:
|
| 184 |
+
keys = src_root['data'].keys()
|
| 185 |
+
for key in keys:
|
| 186 |
+
value = src_root['data'][key]
|
| 187 |
+
cks = cls._resolve_array_chunks(
|
| 188 |
+
chunks=chunks, key=key, array=value)
|
| 189 |
+
cpr = cls._resolve_array_compressor(
|
| 190 |
+
compressors=compressors, key=key, array=value)
|
| 191 |
+
if cks == value.chunks and cpr == value.compressor:
|
| 192 |
+
# copy without recompression
|
| 193 |
+
this_path = '/data/' + key
|
| 194 |
+
n_copied, n_skipped, n_bytes_copied = zarr.copy_store(
|
| 195 |
+
source=src_store, dest=store,
|
| 196 |
+
source_path=this_path, dest_path=this_path,
|
| 197 |
+
if_exists=if_exists
|
| 198 |
+
)
|
| 199 |
+
else:
|
| 200 |
+
# copy with recompression
|
| 201 |
+
n_copied, n_skipped, n_bytes_copied = zarr.copy(
|
| 202 |
+
source=value, dest=data_group, name=key,
|
| 203 |
+
chunks=cks, compressor=cpr, if_exists=if_exists
|
| 204 |
+
)
|
| 205 |
+
buffer = cls(root=root)
|
| 206 |
+
return buffer
|
| 207 |
+
|
| 208 |
+
@classmethod
|
| 209 |
+
def copy_from_path(cls, zarr_path, backend=None, store=None, keys=None,
|
| 210 |
+
chunks: Dict[str,tuple]=dict(),
|
| 211 |
+
compressors: Union[dict, str, numcodecs.abc.Codec]=dict(),
|
| 212 |
+
if_exists='replace',
|
| 213 |
+
**kwargs):
|
| 214 |
+
"""
|
| 215 |
+
Copy a on-disk zarr to in-memory compressed.
|
| 216 |
+
Recommended
|
| 217 |
+
"""
|
| 218 |
+
if backend == 'numpy':
|
| 219 |
+
print('backend argument is deprecated!')
|
| 220 |
+
store = None
|
| 221 |
+
group = zarr.open(os.path.expanduser(zarr_path), 'r')
|
| 222 |
+
return cls.copy_from_store(src_store=group.store, store=store,
|
| 223 |
+
keys=keys, chunks=chunks, compressors=compressors,
|
| 224 |
+
if_exists=if_exists, **kwargs)
|
| 225 |
+
|
| 226 |
+
# ============= save methods ===============
|
| 227 |
+
def save_to_store(self, store,
|
| 228 |
+
chunks: Optional[Dict[str,tuple]]=dict(),
|
| 229 |
+
compressors: Union[str, numcodecs.abc.Codec, dict]=dict(),
|
| 230 |
+
if_exists='replace',
|
| 231 |
+
**kwargs):
|
| 232 |
+
|
| 233 |
+
root = zarr.group(store)
|
| 234 |
+
if self.backend == 'zarr':
|
| 235 |
+
# recompression free copy
|
| 236 |
+
n_copied, n_skipped, n_bytes_copied = zarr.copy_store(
|
| 237 |
+
source=self.root.store, dest=store,
|
| 238 |
+
source_path='/meta', dest_path='/meta', if_exists=if_exists)
|
| 239 |
+
else:
|
| 240 |
+
meta_group = root.create_group('meta', overwrite=True)
|
| 241 |
+
# save meta, no chunking
|
| 242 |
+
for key, value in self.root['meta'].items():
|
| 243 |
+
_ = meta_group.array(
|
| 244 |
+
name=key,
|
| 245 |
+
data=value,
|
| 246 |
+
shape=value.shape,
|
| 247 |
+
chunks=value.shape)
|
| 248 |
+
|
| 249 |
+
# save data, chunk
|
| 250 |
+
data_group = root.create_group('data', overwrite=True)
|
| 251 |
+
for key, value in self.root['data'].items():
|
| 252 |
+
cks = self._resolve_array_chunks(
|
| 253 |
+
chunks=chunks, key=key, array=value)
|
| 254 |
+
cpr = self._resolve_array_compressor(
|
| 255 |
+
compressors=compressors, key=key, array=value)
|
| 256 |
+
if isinstance(value, zarr.Array):
|
| 257 |
+
if cks == value.chunks and cpr == value.compressor:
|
| 258 |
+
# copy without recompression
|
| 259 |
+
this_path = '/data/' + key
|
| 260 |
+
n_copied, n_skipped, n_bytes_copied = zarr.copy_store(
|
| 261 |
+
source=self.root.store, dest=store,
|
| 262 |
+
source_path=this_path, dest_path=this_path, if_exists=if_exists)
|
| 263 |
+
else:
|
| 264 |
+
# copy with recompression
|
| 265 |
+
n_copied, n_skipped, n_bytes_copied = zarr.copy(
|
| 266 |
+
source=value, dest=data_group, name=key,
|
| 267 |
+
chunks=cks, compressor=cpr, if_exists=if_exists
|
| 268 |
+
)
|
| 269 |
+
else:
|
| 270 |
+
# numpy
|
| 271 |
+
_ = data_group.array(
|
| 272 |
+
name=key,
|
| 273 |
+
data=value,
|
| 274 |
+
chunks=cks,
|
| 275 |
+
compressor=cpr
|
| 276 |
+
)
|
| 277 |
+
return store
|
| 278 |
+
|
| 279 |
+
def save_to_path(self, zarr_path,
|
| 280 |
+
chunks: Optional[Dict[str,tuple]]=dict(),
|
| 281 |
+
compressors: Union[str, numcodecs.abc.Codec, dict]=dict(),
|
| 282 |
+
if_exists='replace',
|
| 283 |
+
**kwargs):
|
| 284 |
+
store = zarr.DirectoryStore(os.path.expanduser(zarr_path))
|
| 285 |
+
return self.save_to_store(store, chunks=chunks,
|
| 286 |
+
compressors=compressors, if_exists=if_exists, **kwargs)
|
| 287 |
+
|
| 288 |
+
@staticmethod
|
| 289 |
+
def resolve_compressor(compressor='default'):
|
| 290 |
+
if compressor == 'default':
|
| 291 |
+
compressor = numcodecs.Blosc(cname='lz4', clevel=5,
|
| 292 |
+
shuffle=numcodecs.Blosc.NOSHUFFLE)
|
| 293 |
+
elif compressor == 'disk':
|
| 294 |
+
compressor = numcodecs.Blosc('zstd', clevel=5,
|
| 295 |
+
shuffle=numcodecs.Blosc.BITSHUFFLE)
|
| 296 |
+
return compressor
|
| 297 |
+
|
| 298 |
+
@classmethod
|
| 299 |
+
def _resolve_array_compressor(cls,
|
| 300 |
+
compressors: Union[dict, str, numcodecs.abc.Codec], key, array):
|
| 301 |
+
# allows compressor to be explicitly set to None
|
| 302 |
+
cpr = 'nil'
|
| 303 |
+
if isinstance(compressors, dict):
|
| 304 |
+
if key in compressors:
|
| 305 |
+
cpr = cls.resolve_compressor(compressors[key])
|
| 306 |
+
elif isinstance(array, zarr.Array):
|
| 307 |
+
cpr = array.compressor
|
| 308 |
+
else:
|
| 309 |
+
cpr = cls.resolve_compressor(compressors)
|
| 310 |
+
# backup default
|
| 311 |
+
if cpr == 'nil':
|
| 312 |
+
cpr = cls.resolve_compressor('default')
|
| 313 |
+
return cpr
|
| 314 |
+
|
| 315 |
+
@classmethod
|
| 316 |
+
def _resolve_array_chunks(cls,
|
| 317 |
+
chunks: Union[dict, tuple], key, array):
|
| 318 |
+
cks = None
|
| 319 |
+
if isinstance(chunks, dict):
|
| 320 |
+
if key in chunks:
|
| 321 |
+
cks = chunks[key]
|
| 322 |
+
elif isinstance(array, zarr.Array):
|
| 323 |
+
cks = array.chunks
|
| 324 |
+
elif isinstance(chunks, tuple):
|
| 325 |
+
cks = chunks
|
| 326 |
+
else:
|
| 327 |
+
raise TypeError(f"Unsupported chunks type {type(chunks)}")
|
| 328 |
+
# backup default
|
| 329 |
+
if cks is None:
|
| 330 |
+
cks = get_optimal_chunks(shape=array.shape, dtype=array.dtype)
|
| 331 |
+
# check
|
| 332 |
+
check_chunks_compatible(chunks=cks, shape=array.shape)
|
| 333 |
+
return cks
|
| 334 |
+
|
| 335 |
+
# ============= properties =================
|
| 336 |
+
@cached_property
|
| 337 |
+
def data(self):
|
| 338 |
+
return self.root['data']
|
| 339 |
+
|
| 340 |
+
@cached_property
|
| 341 |
+
def meta(self):
|
| 342 |
+
return self.root['meta']
|
| 343 |
+
|
| 344 |
+
def update_meta(self, data):
|
| 345 |
+
# sanitize data
|
| 346 |
+
np_data = dict()
|
| 347 |
+
for key, value in data.items():
|
| 348 |
+
if isinstance(value, np.ndarray):
|
| 349 |
+
np_data[key] = value
|
| 350 |
+
else:
|
| 351 |
+
arr = np.array(value)
|
| 352 |
+
if arr.dtype == object:
|
| 353 |
+
raise TypeError(f"Invalid value type {type(value)}")
|
| 354 |
+
np_data[key] = arr
|
| 355 |
+
|
| 356 |
+
meta_group = self.meta
|
| 357 |
+
if self.backend == 'zarr':
|
| 358 |
+
for key, value in np_data.items():
|
| 359 |
+
_ = meta_group.array(
|
| 360 |
+
name=key,
|
| 361 |
+
data=value,
|
| 362 |
+
shape=value.shape,
|
| 363 |
+
chunks=value.shape,
|
| 364 |
+
overwrite=True)
|
| 365 |
+
else:
|
| 366 |
+
meta_group.update(np_data)
|
| 367 |
+
|
| 368 |
+
return meta_group
|
| 369 |
+
|
| 370 |
+
@property
|
| 371 |
+
def episode_ends(self):
|
| 372 |
+
return self.meta['episode_ends']
|
| 373 |
+
|
| 374 |
+
def get_episode_idxs(self):
|
| 375 |
+
import numba
|
| 376 |
+
numba.jit(nopython=True)
|
| 377 |
+
def _get_episode_idxs(episode_ends):
|
| 378 |
+
result = np.zeros((episode_ends[-1],), dtype=np.int64)
|
| 379 |
+
for i in range(len(episode_ends)):
|
| 380 |
+
start = 0
|
| 381 |
+
if i > 0:
|
| 382 |
+
start = episode_ends[i-1]
|
| 383 |
+
end = episode_ends[i]
|
| 384 |
+
for idx in range(start, end):
|
| 385 |
+
result[idx] = i
|
| 386 |
+
return result
|
| 387 |
+
return _get_episode_idxs(self.episode_ends)
|
| 388 |
+
|
| 389 |
+
|
| 390 |
+
@property
|
| 391 |
+
def backend(self):
|
| 392 |
+
backend = 'numpy'
|
| 393 |
+
if isinstance(self.root, zarr.Group):
|
| 394 |
+
backend = 'zarr'
|
| 395 |
+
return backend
|
| 396 |
+
|
| 397 |
+
# =========== dict-like API ==============
|
| 398 |
+
def __repr__(self) -> str:
|
| 399 |
+
if self.backend == 'zarr':
|
| 400 |
+
return str(self.root.tree())
|
| 401 |
+
else:
|
| 402 |
+
return super().__repr__()
|
| 403 |
+
|
| 404 |
+
def keys(self):
|
| 405 |
+
return self.data.keys()
|
| 406 |
+
|
| 407 |
+
def values(self):
|
| 408 |
+
return self.data.values()
|
| 409 |
+
|
| 410 |
+
def items(self):
|
| 411 |
+
return self.data.items()
|
| 412 |
+
|
| 413 |
+
def __getitem__(self, key):
|
| 414 |
+
return self.data[key]
|
| 415 |
+
|
| 416 |
+
def __contains__(self, key):
|
| 417 |
+
return key in self.data
|
| 418 |
+
|
| 419 |
+
# =========== our API ==============
|
| 420 |
+
@property
|
| 421 |
+
def n_steps(self):
|
| 422 |
+
if len(self.episode_ends) == 0:
|
| 423 |
+
return 0
|
| 424 |
+
return self.episode_ends[-1]
|
| 425 |
+
|
| 426 |
+
@property
|
| 427 |
+
def n_episodes(self):
|
| 428 |
+
return len(self.episode_ends)
|
| 429 |
+
|
| 430 |
+
@property
|
| 431 |
+
def chunk_size(self):
|
| 432 |
+
if self.backend == 'zarr':
|
| 433 |
+
return next(iter(self.data.arrays()))[-1].chunks[0]
|
| 434 |
+
return None
|
| 435 |
+
|
| 436 |
+
@property
|
| 437 |
+
def episode_lengths(self):
|
| 438 |
+
ends = self.episode_ends[:]
|
| 439 |
+
ends = np.insert(ends, 0, 0)
|
| 440 |
+
lengths = np.diff(ends)
|
| 441 |
+
return lengths
|
| 442 |
+
|
| 443 |
+
def add_episode(self,
|
| 444 |
+
data: Dict[str, np.ndarray],
|
| 445 |
+
meta_data: Optional[Dict[str, Union[np.ndarray, int]]] = None,
|
| 446 |
+
chunks: Optional[Dict[str, tuple]] = dict(),
|
| 447 |
+
compressors: Union[str, numcodecs.abc.Codec, dict] = dict()):
|
| 448 |
+
assert(len(data) > 0)
|
| 449 |
+
is_zarr = (self.backend == 'zarr')
|
| 450 |
+
|
| 451 |
+
curr_len = self.n_steps
|
| 452 |
+
episode_length = None
|
| 453 |
+
for key, value in data.items():
|
| 454 |
+
assert(len(value.shape) >= 1)
|
| 455 |
+
if episode_length is None:
|
| 456 |
+
episode_length = len(value)
|
| 457 |
+
else:
|
| 458 |
+
assert(episode_length == len(value))
|
| 459 |
+
new_len = curr_len + episode_length
|
| 460 |
+
|
| 461 |
+
for key, value in data.items():
|
| 462 |
+
new_shape = (new_len,) + value.shape[1:]
|
| 463 |
+
# create array
|
| 464 |
+
if key not in self.data:
|
| 465 |
+
if is_zarr:
|
| 466 |
+
cks = self._resolve_array_chunks(
|
| 467 |
+
chunks=chunks, key=key, array=value)
|
| 468 |
+
cpr = self._resolve_array_compressor(
|
| 469 |
+
compressors=compressors, key=key, array=value)
|
| 470 |
+
arr = self.data.zeros(name=key,
|
| 471 |
+
shape=new_shape,
|
| 472 |
+
chunks=cks,
|
| 473 |
+
dtype=value.dtype,
|
| 474 |
+
compressor=cpr)
|
| 475 |
+
else:
|
| 476 |
+
# copy data to prevent modify
|
| 477 |
+
arr = np.zeros(shape=new_shape, dtype=value.dtype)
|
| 478 |
+
self.data[key] = arr
|
| 479 |
+
else:
|
| 480 |
+
arr = self.data[key]
|
| 481 |
+
assert(value.shape[1:] == arr.shape[1:])
|
| 482 |
+
# same method for both zarr and numpy
|
| 483 |
+
if is_zarr:
|
| 484 |
+
arr.resize(new_shape)
|
| 485 |
+
else:
|
| 486 |
+
arr.resize(new_shape, refcheck=False)
|
| 487 |
+
# copy data
|
| 488 |
+
arr[-value.shape[0]:] = value
|
| 489 |
+
|
| 490 |
+
# append to episode ends
|
| 491 |
+
episode_ends = self.episode_ends
|
| 492 |
+
if is_zarr:
|
| 493 |
+
episode_ends.resize(episode_ends.shape[0] + 1)
|
| 494 |
+
else:
|
| 495 |
+
episode_ends.resize(episode_ends.shape[0] + 1, refcheck=False)
|
| 496 |
+
episode_ends[-1] = new_len
|
| 497 |
+
|
| 498 |
+
# add meta data, e.g., votes
|
| 499 |
+
if meta_data is not None:
|
| 500 |
+
for key, value in meta_data.items():
|
| 501 |
+
if key in self.meta:
|
| 502 |
+
meta_arr = self.meta[key]
|
| 503 |
+
if is_zarr:
|
| 504 |
+
meta_arr.resize(meta_arr.shape[0] + 1)
|
| 505 |
+
else:
|
| 506 |
+
meta_arr.resize(meta_arr.shape[0] + 1, refcheck=False)
|
| 507 |
+
meta_arr[-1] = value
|
| 508 |
+
|
| 509 |
+
# rechunk
|
| 510 |
+
if is_zarr:
|
| 511 |
+
if episode_ends.chunks[0] < episode_ends.shape[0]:
|
| 512 |
+
rechunk_recompress_array(self.meta, 'episode_ends',
|
| 513 |
+
chunk_length=int(episode_ends.shape[0] * 1.5))
|
| 514 |
+
|
| 515 |
+
def drop_episode(self):
|
| 516 |
+
is_zarr = (self.backend == 'zarr')
|
| 517 |
+
episode_ends = self.episode_ends[:].copy()
|
| 518 |
+
assert(len(episode_ends) > 0)
|
| 519 |
+
start_idx = 0
|
| 520 |
+
if len(episode_ends) > 1:
|
| 521 |
+
start_idx = episode_ends[-2]
|
| 522 |
+
for key, value in self.data.items():
|
| 523 |
+
new_shape = (start_idx,) + value.shape[1:]
|
| 524 |
+
if is_zarr:
|
| 525 |
+
value.resize(new_shape)
|
| 526 |
+
else:
|
| 527 |
+
value.resize(new_shape, refcheck=False)
|
| 528 |
+
if is_zarr:
|
| 529 |
+
self.episode_ends.resize(len(episode_ends)-1)
|
| 530 |
+
else:
|
| 531 |
+
self.episode_ends.resize(len(episode_ends)-1, refcheck=False)
|
| 532 |
+
|
| 533 |
+
def pop_episode(self):
|
| 534 |
+
assert(self.n_episodes > 0)
|
| 535 |
+
episode = self.get_episode(self.n_episodes-1, copy=True)
|
| 536 |
+
self.drop_episode()
|
| 537 |
+
return episode
|
| 538 |
+
|
| 539 |
+
def extend(self, data):
|
| 540 |
+
self.add_episode(data)
|
| 541 |
+
|
| 542 |
+
def get_episode(self, idx, keys=None, copy=False):
|
| 543 |
+
idx = list(range(len(self.episode_ends)))[idx]
|
| 544 |
+
start_idx = 0
|
| 545 |
+
if idx > 0:
|
| 546 |
+
start_idx = self.episode_ends[idx-1]
|
| 547 |
+
end_idx = self.episode_ends[idx]
|
| 548 |
+
# result = self.get_steps_slice(start_idx, end_idx, copy=copy)
|
| 549 |
+
|
| 550 |
+
if keys is None:
|
| 551 |
+
keys = self.keys()
|
| 552 |
+
|
| 553 |
+
result = dict()
|
| 554 |
+
for key in keys:
|
| 555 |
+
value = self.data[key]
|
| 556 |
+
x = value[start_idx:end_idx]
|
| 557 |
+
if copy and isinstance(value, np.ndarray):
|
| 558 |
+
x = x.copy()
|
| 559 |
+
result[key] = x
|
| 560 |
+
return result
|
| 561 |
+
|
| 562 |
+
def get_episode_slice(self, idx):
|
| 563 |
+
start_idx = 0
|
| 564 |
+
if idx > 0:
|
| 565 |
+
start_idx = self.episode_ends[idx-1]
|
| 566 |
+
end_idx = self.episode_ends[idx]
|
| 567 |
+
return slice(start_idx, end_idx)
|
| 568 |
+
|
| 569 |
+
def get_steps_slice(self, start, stop, step=None, copy=False):
|
| 570 |
+
_slice = slice(start, stop, step)
|
| 571 |
+
|
| 572 |
+
result = dict()
|
| 573 |
+
for key, value in self.data.items():
|
| 574 |
+
x = value[_slice]
|
| 575 |
+
if copy and isinstance(value, np.ndarray):
|
| 576 |
+
x = x.copy()
|
| 577 |
+
result[key] = x
|
| 578 |
+
return result
|
| 579 |
+
|
| 580 |
+
# =========== chunking =============
|
| 581 |
+
def get_chunks(self) -> dict:
|
| 582 |
+
assert self.backend == 'zarr'
|
| 583 |
+
chunks = dict()
|
| 584 |
+
for key, value in self.data.items():
|
| 585 |
+
chunks[key] = value.chunks
|
| 586 |
+
return chunks
|
| 587 |
+
|
| 588 |
+
def set_chunks(self, chunks: dict):
|
| 589 |
+
assert self.backend == 'zarr'
|
| 590 |
+
for key, value in chunks.items():
|
| 591 |
+
if key in self.data:
|
| 592 |
+
arr = self.data[key]
|
| 593 |
+
if value != arr.chunks:
|
| 594 |
+
check_chunks_compatible(chunks=value, shape=arr.shape)
|
| 595 |
+
rechunk_recompress_array(self.data, key, chunks=value)
|
| 596 |
+
|
| 597 |
+
def get_compressors(self) -> dict:
|
| 598 |
+
assert self.backend == 'zarr'
|
| 599 |
+
compressors = dict()
|
| 600 |
+
for key, value in self.data.items():
|
| 601 |
+
compressors[key] = value.compressor
|
| 602 |
+
return compressors
|
| 603 |
+
|
| 604 |
+
def set_compressors(self, compressors: dict):
|
| 605 |
+
assert self.backend == 'zarr'
|
| 606 |
+
for key, value in compressors.items():
|
| 607 |
+
if key in self.data:
|
| 608 |
+
arr = self.data[key]
|
| 609 |
+
compressor = self.resolve_compressor(value)
|
| 610 |
+
if compressor != arr.compressor:
|
| 611 |
+
rechunk_recompress_array(self.data, key, compressor=compressor)
|
| 612 |
+
|
| 613 |
+
def get_votes(self) -> np.ndarray:
|
| 614 |
+
return self.meta['votes'][:]
|
| 615 |
+
|
common/robomimic_config_util.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from omegaconf import OmegaConf
|
| 2 |
+
from robomimic.config import config_factory
|
| 3 |
+
import robomimic.scripts.generate_paper_configs as gpc
|
| 4 |
+
from robomimic.scripts.generate_paper_configs import (
|
| 5 |
+
modify_config_for_default_image_exp,
|
| 6 |
+
modify_config_for_default_low_dim_exp,
|
| 7 |
+
modify_config_for_dataset,
|
| 8 |
+
)
|
| 9 |
+
|
| 10 |
+
def get_robomimic_config(
|
| 11 |
+
algo_name='bc_rnn',
|
| 12 |
+
hdf5_type='low_dim',
|
| 13 |
+
task_name='square',
|
| 14 |
+
dataset_type='ph'
|
| 15 |
+
):
|
| 16 |
+
base_dataset_dir = '/tmp/null'
|
| 17 |
+
filter_key = None
|
| 18 |
+
|
| 19 |
+
# decide whether to use low-dim or image training defaults
|
| 20 |
+
modifier_for_obs = modify_config_for_default_image_exp
|
| 21 |
+
if hdf5_type in ["low_dim", "low_dim_sparse", "low_dim_dense"]:
|
| 22 |
+
modifier_for_obs = modify_config_for_default_low_dim_exp
|
| 23 |
+
|
| 24 |
+
algo_config_name = "bc" if algo_name == "bc_rnn" else algo_name
|
| 25 |
+
config = config_factory(algo_name=algo_config_name)
|
| 26 |
+
# turn into default config for observation modalities (e.g.: low-dim or rgb)
|
| 27 |
+
config = modifier_for_obs(config)
|
| 28 |
+
# add in config based on the dataset
|
| 29 |
+
config = modify_config_for_dataset(
|
| 30 |
+
config=config,
|
| 31 |
+
task_name=task_name,
|
| 32 |
+
dataset_type=dataset_type,
|
| 33 |
+
hdf5_type=hdf5_type,
|
| 34 |
+
base_dataset_dir=base_dataset_dir,
|
| 35 |
+
filter_key=filter_key,
|
| 36 |
+
)
|
| 37 |
+
# add in algo hypers based on dataset
|
| 38 |
+
algo_config_modifier = getattr(gpc, f'modify_{algo_name}_config_for_dataset')
|
| 39 |
+
config = algo_config_modifier(
|
| 40 |
+
config=config,
|
| 41 |
+
task_name=task_name,
|
| 42 |
+
dataset_type=dataset_type,
|
| 43 |
+
hdf5_type=hdf5_type,
|
| 44 |
+
)
|
| 45 |
+
return config
|
| 46 |
+
|
| 47 |
+
|
common/robomimic_util.py
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import copy
|
| 3 |
+
|
| 4 |
+
import h5py
|
| 5 |
+
import robomimic.utils.obs_utils as ObsUtils
|
| 6 |
+
import robomimic.utils.file_utils as FileUtils
|
| 7 |
+
import robomimic.utils.env_utils as EnvUtils
|
| 8 |
+
from scipy.spatial.transform import Rotation
|
| 9 |
+
|
| 10 |
+
from robomimic.config import config_factory
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
class RobomimicAbsoluteActionConverter:
|
| 14 |
+
def __init__(self, dataset_path, algo_name='bc'):
|
| 15 |
+
# default BC config
|
| 16 |
+
config = config_factory(algo_name=algo_name)
|
| 17 |
+
|
| 18 |
+
# read config to set up metadata for observation modalities (e.g. detecting rgb observations)
|
| 19 |
+
# must ran before create dataset
|
| 20 |
+
ObsUtils.initialize_obs_utils_with_config(config)
|
| 21 |
+
|
| 22 |
+
env_meta = FileUtils.get_env_metadata_from_dataset(dataset_path)
|
| 23 |
+
abs_env_meta = copy.deepcopy(env_meta)
|
| 24 |
+
abs_env_meta['env_kwargs']['controller_configs']['control_delta'] = False
|
| 25 |
+
|
| 26 |
+
env = EnvUtils.create_env_from_metadata(
|
| 27 |
+
env_meta=env_meta,
|
| 28 |
+
render=False,
|
| 29 |
+
render_offscreen=False,
|
| 30 |
+
use_image_obs=False,
|
| 31 |
+
)
|
| 32 |
+
assert len(env.env.robots) in (1, 2)
|
| 33 |
+
|
| 34 |
+
abs_env = EnvUtils.create_env_from_metadata(
|
| 35 |
+
env_meta=abs_env_meta,
|
| 36 |
+
render=False,
|
| 37 |
+
render_offscreen=False,
|
| 38 |
+
use_image_obs=False,
|
| 39 |
+
)
|
| 40 |
+
assert not abs_env.env.robots[0].controller.use_delta
|
| 41 |
+
|
| 42 |
+
self.env = env
|
| 43 |
+
self.abs_env = abs_env
|
| 44 |
+
self.file = h5py.File(dataset_path, 'r')
|
| 45 |
+
|
| 46 |
+
def __len__(self):
|
| 47 |
+
return len(self.file['data'])
|
| 48 |
+
|
| 49 |
+
def convert_actions(self,
|
| 50 |
+
states: np.ndarray,
|
| 51 |
+
actions: np.ndarray) -> np.ndarray:
|
| 52 |
+
"""
|
| 53 |
+
Given state and delta action sequence
|
| 54 |
+
generate equivalent goal position and orientation for each step
|
| 55 |
+
keep the original gripper action intact.
|
| 56 |
+
"""
|
| 57 |
+
# in case of multi robot
|
| 58 |
+
# reshape (N,14) to (N,2,7)
|
| 59 |
+
# or (N,7) to (N,1,7)
|
| 60 |
+
stacked_actions = actions.reshape(*actions.shape[:-1],-1,7)
|
| 61 |
+
|
| 62 |
+
env = self.env
|
| 63 |
+
# generate abs actions
|
| 64 |
+
action_goal_pos = np.zeros(
|
| 65 |
+
stacked_actions.shape[:-1]+(3,),
|
| 66 |
+
dtype=stacked_actions.dtype)
|
| 67 |
+
action_goal_ori = np.zeros(
|
| 68 |
+
stacked_actions.shape[:-1]+(3,),
|
| 69 |
+
dtype=stacked_actions.dtype)
|
| 70 |
+
action_gripper = stacked_actions[...,[-1]]
|
| 71 |
+
for i in range(len(states)):
|
| 72 |
+
_ = env.reset_to({'states': states[i]})
|
| 73 |
+
|
| 74 |
+
# taken from robot_env.py L#454
|
| 75 |
+
for idx, robot in enumerate(env.env.robots):
|
| 76 |
+
# run controller goal generator
|
| 77 |
+
robot.control(stacked_actions[i,idx], policy_step=True)
|
| 78 |
+
|
| 79 |
+
# read pos and ori from robots
|
| 80 |
+
controller = robot.controller
|
| 81 |
+
action_goal_pos[i,idx] = controller.goal_pos
|
| 82 |
+
action_goal_ori[i,idx] = Rotation.from_matrix(
|
| 83 |
+
controller.goal_ori).as_rotvec()
|
| 84 |
+
|
| 85 |
+
stacked_abs_actions = np.concatenate([
|
| 86 |
+
action_goal_pos,
|
| 87 |
+
action_goal_ori,
|
| 88 |
+
action_gripper
|
| 89 |
+
], axis=-1)
|
| 90 |
+
abs_actions = stacked_abs_actions.reshape(actions.shape)
|
| 91 |
+
return abs_actions
|
| 92 |
+
|
| 93 |
+
def convert_idx(self, idx):
|
| 94 |
+
file = self.file
|
| 95 |
+
demo = file[f'data/demo_{idx}']
|
| 96 |
+
# input
|
| 97 |
+
states = demo['states'][:]
|
| 98 |
+
actions = demo['actions'][:]
|
| 99 |
+
|
| 100 |
+
# generate abs actions
|
| 101 |
+
abs_actions = self.convert_actions(states, actions)
|
| 102 |
+
return abs_actions
|
| 103 |
+
|
| 104 |
+
def convert_and_eval_idx(self, idx):
|
| 105 |
+
env = self.env
|
| 106 |
+
abs_env = self.abs_env
|
| 107 |
+
file = self.file
|
| 108 |
+
# first step have high error for some reason, not representative
|
| 109 |
+
eval_skip_steps = 1
|
| 110 |
+
|
| 111 |
+
demo = file[f'data/demo_{idx}']
|
| 112 |
+
# input
|
| 113 |
+
states = demo['states'][:]
|
| 114 |
+
actions = demo['actions'][:]
|
| 115 |
+
|
| 116 |
+
# generate abs actions
|
| 117 |
+
abs_actions = self.convert_actions(states, actions)
|
| 118 |
+
|
| 119 |
+
# verify
|
| 120 |
+
robot0_eef_pos = demo['obs']['robot0_eef_pos'][:]
|
| 121 |
+
robot0_eef_quat = demo['obs']['robot0_eef_quat'][:]
|
| 122 |
+
|
| 123 |
+
delta_error_info = self.evaluate_rollout_error(
|
| 124 |
+
env, states, actions, robot0_eef_pos, robot0_eef_quat,
|
| 125 |
+
metric_skip_steps=eval_skip_steps)
|
| 126 |
+
abs_error_info = self.evaluate_rollout_error(
|
| 127 |
+
abs_env, states, abs_actions, robot0_eef_pos, robot0_eef_quat,
|
| 128 |
+
metric_skip_steps=eval_skip_steps)
|
| 129 |
+
|
| 130 |
+
info = {
|
| 131 |
+
'delta_max_error': delta_error_info,
|
| 132 |
+
'abs_max_error': abs_error_info
|
| 133 |
+
}
|
| 134 |
+
return abs_actions, info
|
| 135 |
+
|
| 136 |
+
@staticmethod
|
| 137 |
+
def evaluate_rollout_error(env,
|
| 138 |
+
states, actions,
|
| 139 |
+
robot0_eef_pos,
|
| 140 |
+
robot0_eef_quat,
|
| 141 |
+
metric_skip_steps=1):
|
| 142 |
+
# first step have high error for some reason, not representative
|
| 143 |
+
|
| 144 |
+
# evaluate abs actions
|
| 145 |
+
rollout_next_states = list()
|
| 146 |
+
rollout_next_eef_pos = list()
|
| 147 |
+
rollout_next_eef_quat = list()
|
| 148 |
+
obs = env.reset_to({'states': states[0]})
|
| 149 |
+
for i in range(len(states)):
|
| 150 |
+
obs = env.reset_to({'states': states[i]})
|
| 151 |
+
obs, reward, done, info = env.step(actions[i])
|
| 152 |
+
obs = env.get_observation()
|
| 153 |
+
rollout_next_states.append(env.get_state()['states'])
|
| 154 |
+
rollout_next_eef_pos.append(obs['robot0_eef_pos'])
|
| 155 |
+
rollout_next_eef_quat.append(obs['robot0_eef_quat'])
|
| 156 |
+
rollout_next_states = np.array(rollout_next_states)
|
| 157 |
+
rollout_next_eef_pos = np.array(rollout_next_eef_pos)
|
| 158 |
+
rollout_next_eef_quat = np.array(rollout_next_eef_quat)
|
| 159 |
+
|
| 160 |
+
next_state_diff = states[1:] - rollout_next_states[:-1]
|
| 161 |
+
max_next_state_diff = np.max(np.abs(next_state_diff[metric_skip_steps:]))
|
| 162 |
+
|
| 163 |
+
next_eef_pos_diff = robot0_eef_pos[1:] - rollout_next_eef_pos[:-1]
|
| 164 |
+
next_eef_pos_dist = np.linalg.norm(next_eef_pos_diff, axis=-1)
|
| 165 |
+
max_next_eef_pos_dist = next_eef_pos_dist[metric_skip_steps:].max()
|
| 166 |
+
|
| 167 |
+
next_eef_rot_diff = Rotation.from_quat(robot0_eef_quat[1:]) \
|
| 168 |
+
* Rotation.from_quat(rollout_next_eef_quat[:-1]).inv()
|
| 169 |
+
next_eef_rot_dist = next_eef_rot_diff.magnitude()
|
| 170 |
+
max_next_eef_rot_dist = next_eef_rot_dist[metric_skip_steps:].max()
|
| 171 |
+
|
| 172 |
+
info = {
|
| 173 |
+
'state': max_next_state_diff,
|
| 174 |
+
'pos': max_next_eef_pos_dist,
|
| 175 |
+
'rot': max_next_eef_rot_dist
|
| 176 |
+
}
|
| 177 |
+
return info
|
common/sampler.py
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Optional
|
| 2 |
+
import numpy as np
|
| 3 |
+
import numba
|
| 4 |
+
from diffusion_policy.common.replay_buffer import ReplayBuffer
|
| 5 |
+
|
| 6 |
+
import pdb
|
| 7 |
+
|
| 8 |
+
@numba.jit(nopython=True)
|
| 9 |
+
def create_indices(
|
| 10 |
+
episode_ends:np.ndarray, sequence_length:int,
|
| 11 |
+
episode_mask: np.ndarray,
|
| 12 |
+
pad_before: int=0, pad_after: int=0,
|
| 13 |
+
debug:bool=True) -> np.ndarray:
|
| 14 |
+
episode_mask.shape == episode_ends.shape
|
| 15 |
+
pad_before = min(max(pad_before, 0), sequence_length-1)
|
| 16 |
+
pad_after = min(max(pad_after, 0), sequence_length-1)
|
| 17 |
+
|
| 18 |
+
indices = list()
|
| 19 |
+
for i in range(len(episode_ends)):
|
| 20 |
+
if not episode_mask[i]:
|
| 21 |
+
# skip episode
|
| 22 |
+
continue
|
| 23 |
+
start_idx = 0
|
| 24 |
+
if i > 0:
|
| 25 |
+
start_idx = episode_ends[i-1]
|
| 26 |
+
end_idx = episode_ends[i]
|
| 27 |
+
episode_length = end_idx - start_idx
|
| 28 |
+
|
| 29 |
+
min_start = -pad_before
|
| 30 |
+
max_start = episode_length - sequence_length + pad_after
|
| 31 |
+
|
| 32 |
+
# range stops one idx before end
|
| 33 |
+
for idx in range(min_start, max_start+1):
|
| 34 |
+
buffer_start_idx = max(idx, 0) + start_idx
|
| 35 |
+
buffer_end_idx = min(idx+sequence_length, episode_length) + start_idx
|
| 36 |
+
start_offset = buffer_start_idx - (idx+start_idx)
|
| 37 |
+
end_offset = (idx+sequence_length+start_idx) - buffer_end_idx
|
| 38 |
+
sample_start_idx = 0 + start_offset
|
| 39 |
+
sample_end_idx = sequence_length - end_offset
|
| 40 |
+
if debug:
|
| 41 |
+
assert(start_offset >= 0)
|
| 42 |
+
assert(end_offset >= 0)
|
| 43 |
+
assert (sample_end_idx - sample_start_idx) == (buffer_end_idx - buffer_start_idx)
|
| 44 |
+
indices.append([
|
| 45 |
+
buffer_start_idx, buffer_end_idx,
|
| 46 |
+
sample_start_idx, sample_end_idx])
|
| 47 |
+
indices = np.array(indices)
|
| 48 |
+
return indices
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def get_val_mask(n_episodes, val_ratio, seed=0):
|
| 52 |
+
val_mask = np.zeros(n_episodes, dtype=bool)
|
| 53 |
+
if val_ratio <= 0:
|
| 54 |
+
return val_mask
|
| 55 |
+
|
| 56 |
+
# have at least 1 episode for validation, and at least 1 episode for train
|
| 57 |
+
n_val = min(max(1, round(n_episodes * val_ratio)), n_episodes-1)
|
| 58 |
+
rng = np.random.default_rng(seed=seed)
|
| 59 |
+
val_idxs = rng.choice(n_episodes, size=n_val, replace=False)
|
| 60 |
+
val_mask[val_idxs] = True
|
| 61 |
+
return val_mask
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
def downsample_mask(mask, max_n, seed=0):
|
| 65 |
+
# subsample training data
|
| 66 |
+
train_mask = mask
|
| 67 |
+
if (max_n is not None) and (np.sum(train_mask) > max_n):
|
| 68 |
+
n_train = int(max_n)
|
| 69 |
+
curr_train_idxs = np.nonzero(train_mask)[0]
|
| 70 |
+
rng = np.random.default_rng(seed=seed)
|
| 71 |
+
train_idxs_idx = rng.choice(len(curr_train_idxs), size=n_train, replace=False)
|
| 72 |
+
train_idxs = curr_train_idxs[train_idxs_idx]
|
| 73 |
+
train_mask = np.zeros_like(train_mask)
|
| 74 |
+
train_mask[train_idxs] = True
|
| 75 |
+
assert np.sum(train_mask) == n_train
|
| 76 |
+
return train_mask
|
| 77 |
+
|
| 78 |
+
class SequenceSampler:
|
| 79 |
+
def __init__(self,
|
| 80 |
+
replay_buffer: ReplayBuffer,
|
| 81 |
+
sequence_length:int,
|
| 82 |
+
pad_before:int=0,
|
| 83 |
+
pad_after:int=0,
|
| 84 |
+
keys=None,
|
| 85 |
+
key_first_k=dict(),
|
| 86 |
+
episode_mask: Optional[np.ndarray]=None,
|
| 87 |
+
):
|
| 88 |
+
"""
|
| 89 |
+
key_first_k: dict str: int
|
| 90 |
+
Only take first k data from these keys (to improve perf)
|
| 91 |
+
"""
|
| 92 |
+
|
| 93 |
+
super().__init__()
|
| 94 |
+
assert(sequence_length >= 1)
|
| 95 |
+
if keys is None:
|
| 96 |
+
keys = list(replay_buffer.keys())
|
| 97 |
+
|
| 98 |
+
episode_ends = replay_buffer.episode_ends[:]
|
| 99 |
+
if episode_mask is None:
|
| 100 |
+
episode_mask = np.ones(episode_ends.shape, dtype=bool)
|
| 101 |
+
|
| 102 |
+
if np.any(episode_mask):
|
| 103 |
+
indices = create_indices(episode_ends,
|
| 104 |
+
sequence_length=sequence_length,
|
| 105 |
+
pad_before=pad_before,
|
| 106 |
+
pad_after=pad_after,
|
| 107 |
+
episode_mask=episode_mask
|
| 108 |
+
)
|
| 109 |
+
else:
|
| 110 |
+
indices = np.zeros((0,4), dtype=np.int64)
|
| 111 |
+
|
| 112 |
+
# (buffer_start_idx, buffer_end_idx, sample_start_idx, sample_end_idx)
|
| 113 |
+
self.indices = indices
|
| 114 |
+
self.keys = list(keys) # prevent OmegaConf list performance problem
|
| 115 |
+
self.sequence_length = sequence_length
|
| 116 |
+
self.replay_buffer = replay_buffer
|
| 117 |
+
self.key_first_k = key_first_k
|
| 118 |
+
|
| 119 |
+
def __len__(self):
|
| 120 |
+
return len(self.indices)
|
| 121 |
+
|
| 122 |
+
def sample_sequence(self, idx):
|
| 123 |
+
buffer_start_idx, buffer_end_idx, sample_start_idx, sample_end_idx \
|
| 124 |
+
= self.indices[idx]
|
| 125 |
+
result = dict()
|
| 126 |
+
for key in self.keys:
|
| 127 |
+
input_arr = self.replay_buffer[key]
|
| 128 |
+
# performance optimization, avoid small allocation if possible
|
| 129 |
+
if key not in self.key_first_k:
|
| 130 |
+
sample = input_arr[buffer_start_idx:buffer_end_idx]
|
| 131 |
+
else:
|
| 132 |
+
# performance optimization, only load used obs steps
|
| 133 |
+
n_data = buffer_end_idx - buffer_start_idx
|
| 134 |
+
k_data = min(self.key_first_k[key], n_data)
|
| 135 |
+
# fill value with Nan to catch bugs
|
| 136 |
+
# the non-loaded region should never be used
|
| 137 |
+
sample = np.full((n_data,) + input_arr.shape[1:],
|
| 138 |
+
fill_value=np.nan, dtype=input_arr.dtype)
|
| 139 |
+
try:
|
| 140 |
+
sample[:k_data] = input_arr[buffer_start_idx:buffer_start_idx+k_data]
|
| 141 |
+
except Exception as e:
|
| 142 |
+
import pdb; pdb.set_trace()
|
| 143 |
+
data = sample
|
| 144 |
+
if (sample_start_idx > 0) or (sample_end_idx < self.sequence_length):
|
| 145 |
+
data = np.zeros(
|
| 146 |
+
shape=(self.sequence_length,) + input_arr.shape[1:],
|
| 147 |
+
dtype=input_arr.dtype)
|
| 148 |
+
if sample_start_idx > 0:
|
| 149 |
+
data[:sample_start_idx] = sample[0]
|
| 150 |
+
if sample_end_idx < self.sequence_length:
|
| 151 |
+
data[sample_end_idx:] = sample[-1]
|
| 152 |
+
data[sample_start_idx:sample_end_idx] = sample
|
| 153 |
+
result[key] = data
|
| 154 |
+
return result
|
common/timestamp_accumulator.py
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import List, Tuple, Optional, Dict
|
| 2 |
+
import math
|
| 3 |
+
import numpy as np
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def get_accumulate_timestamp_idxs(
|
| 7 |
+
timestamps: List[float],
|
| 8 |
+
start_time: float,
|
| 9 |
+
dt: float,
|
| 10 |
+
eps:float=1e-5,
|
| 11 |
+
next_global_idx: Optional[int]=0,
|
| 12 |
+
allow_negative=False
|
| 13 |
+
) -> Tuple[List[int], List[int], int]:
|
| 14 |
+
"""
|
| 15 |
+
For each dt window, choose the first timestamp in the window.
|
| 16 |
+
Assumes timestamps sorted. One timestamp might be chosen multiple times due to dropped frames.
|
| 17 |
+
next_global_idx should start at 0 normally, and then use the returned next_global_idx.
|
| 18 |
+
However, when overwiting previous values are desired, set last_global_idx to None.
|
| 19 |
+
|
| 20 |
+
Returns:
|
| 21 |
+
local_idxs: which index in the given timestamps array to chose from
|
| 22 |
+
global_idxs: the global index of each chosen timestamp
|
| 23 |
+
next_global_idx: used for next call.
|
| 24 |
+
"""
|
| 25 |
+
local_idxs = list()
|
| 26 |
+
global_idxs = list()
|
| 27 |
+
for local_idx, ts in enumerate(timestamps):
|
| 28 |
+
# add eps * dt to timestamps so that when ts == start_time + k * dt
|
| 29 |
+
# is always recorded as kth element (avoiding floating point errors)
|
| 30 |
+
global_idx = math.floor((ts - start_time) / dt + eps)
|
| 31 |
+
if (not allow_negative) and (global_idx < 0):
|
| 32 |
+
continue
|
| 33 |
+
if next_global_idx is None:
|
| 34 |
+
next_global_idx = global_idx
|
| 35 |
+
|
| 36 |
+
n_repeats = max(0, global_idx - next_global_idx + 1)
|
| 37 |
+
for i in range(n_repeats):
|
| 38 |
+
local_idxs.append(local_idx)
|
| 39 |
+
global_idxs.append(next_global_idx + i)
|
| 40 |
+
next_global_idx += n_repeats
|
| 41 |
+
return local_idxs, global_idxs, next_global_idx
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def align_timestamps(
|
| 45 |
+
timestamps: List[float],
|
| 46 |
+
target_global_idxs: List[int],
|
| 47 |
+
start_time: float,
|
| 48 |
+
dt: float,
|
| 49 |
+
eps:float=1e-5):
|
| 50 |
+
if isinstance(target_global_idxs, np.ndarray):
|
| 51 |
+
target_global_idxs = target_global_idxs.tolist()
|
| 52 |
+
assert len(target_global_idxs) > 0
|
| 53 |
+
|
| 54 |
+
local_idxs, global_idxs, _ = get_accumulate_timestamp_idxs(
|
| 55 |
+
timestamps=timestamps,
|
| 56 |
+
start_time=start_time,
|
| 57 |
+
dt=dt,
|
| 58 |
+
eps=eps,
|
| 59 |
+
next_global_idx=target_global_idxs[0],
|
| 60 |
+
allow_negative=True
|
| 61 |
+
)
|
| 62 |
+
if len(global_idxs) > len(target_global_idxs):
|
| 63 |
+
# if more steps available, truncate
|
| 64 |
+
global_idxs = global_idxs[:len(target_global_idxs)]
|
| 65 |
+
local_idxs = local_idxs[:len(target_global_idxs)]
|
| 66 |
+
|
| 67 |
+
if len(global_idxs) == 0:
|
| 68 |
+
import pdb; pdb.set_trace()
|
| 69 |
+
|
| 70 |
+
for i in range(len(target_global_idxs) - len(global_idxs)):
|
| 71 |
+
# if missing, repeat
|
| 72 |
+
local_idxs.append(len(timestamps)-1)
|
| 73 |
+
global_idxs.append(global_idxs[-1] + 1)
|
| 74 |
+
assert global_idxs == target_global_idxs
|
| 75 |
+
assert len(local_idxs) == len(global_idxs)
|
| 76 |
+
return local_idxs
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
class TimestampObsAccumulator:
|
| 80 |
+
def __init__(self,
|
| 81 |
+
start_time: float,
|
| 82 |
+
dt: float,
|
| 83 |
+
eps: float=1e-5):
|
| 84 |
+
self.start_time = start_time
|
| 85 |
+
self.dt = dt
|
| 86 |
+
self.eps = eps
|
| 87 |
+
self.obs_buffer = dict()
|
| 88 |
+
self.timestamp_buffer = None
|
| 89 |
+
self.next_global_idx = 0
|
| 90 |
+
|
| 91 |
+
def __len__(self):
|
| 92 |
+
return self.next_global_idx
|
| 93 |
+
|
| 94 |
+
@property
|
| 95 |
+
def data(self):
|
| 96 |
+
if self.timestamp_buffer is None:
|
| 97 |
+
return dict()
|
| 98 |
+
result = dict()
|
| 99 |
+
for key, value in self.obs_buffer.items():
|
| 100 |
+
result[key] = value[:len(self)]
|
| 101 |
+
return result
|
| 102 |
+
|
| 103 |
+
@property
|
| 104 |
+
def actual_timestamps(self):
|
| 105 |
+
if self.timestamp_buffer is None:
|
| 106 |
+
return np.array([])
|
| 107 |
+
return self.timestamp_buffer[:len(self)]
|
| 108 |
+
|
| 109 |
+
@property
|
| 110 |
+
def timestamps(self):
|
| 111 |
+
if self.timestamp_buffer is None:
|
| 112 |
+
return np.array([])
|
| 113 |
+
return self.start_time + np.arange(len(self)) * self.dt
|
| 114 |
+
|
| 115 |
+
def put(self, data: Dict[str, np.ndarray], timestamps: np.ndarray):
|
| 116 |
+
"""
|
| 117 |
+
data:
|
| 118 |
+
key: T,*
|
| 119 |
+
"""
|
| 120 |
+
|
| 121 |
+
local_idxs, global_idxs, self.next_global_idx = get_accumulate_timestamp_idxs(
|
| 122 |
+
timestamps=timestamps,
|
| 123 |
+
start_time=self.start_time,
|
| 124 |
+
dt=self.dt,
|
| 125 |
+
eps=self.eps,
|
| 126 |
+
next_global_idx=self.next_global_idx
|
| 127 |
+
)
|
| 128 |
+
|
| 129 |
+
if len(global_idxs) > 0:
|
| 130 |
+
if self.timestamp_buffer is None:
|
| 131 |
+
# first allocation
|
| 132 |
+
self.obs_buffer = dict()
|
| 133 |
+
for key, value in data.items():
|
| 134 |
+
self.obs_buffer[key] = np.zeros_like(value)
|
| 135 |
+
self.timestamp_buffer = np.zeros(
|
| 136 |
+
(len(timestamps),), dtype=np.float64)
|
| 137 |
+
|
| 138 |
+
this_max_size = global_idxs[-1] + 1
|
| 139 |
+
if this_max_size > len(self.timestamp_buffer):
|
| 140 |
+
# reallocate
|
| 141 |
+
new_size = max(this_max_size, len(self.timestamp_buffer) * 2)
|
| 142 |
+
for key in list(self.obs_buffer.keys()):
|
| 143 |
+
new_shape = (new_size,) + self.obs_buffer[key].shape[1:]
|
| 144 |
+
self.obs_buffer[key] = np.resize(self.obs_buffer[key], new_shape)
|
| 145 |
+
self.timestamp_buffer = np.resize(self.timestamp_buffer, (new_size))
|
| 146 |
+
|
| 147 |
+
# write data
|
| 148 |
+
for key, value in self.obs_buffer.items():
|
| 149 |
+
value[global_idxs] = data[key][local_idxs]
|
| 150 |
+
self.timestamp_buffer[global_idxs] = timestamps[local_idxs]
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
class TimestampActionAccumulator:
|
| 154 |
+
def __init__(self,
|
| 155 |
+
start_time: float,
|
| 156 |
+
dt: float,
|
| 157 |
+
eps: float=1e-5):
|
| 158 |
+
"""
|
| 159 |
+
Different from Obs accumulator, the action accumulator
|
| 160 |
+
allows overwriting previous values.
|
| 161 |
+
"""
|
| 162 |
+
self.start_time = start_time
|
| 163 |
+
self.dt = dt
|
| 164 |
+
self.eps = eps
|
| 165 |
+
self.action_buffer = None
|
| 166 |
+
self.timestamp_buffer = None
|
| 167 |
+
self.size = 0
|
| 168 |
+
|
| 169 |
+
def __len__(self):
|
| 170 |
+
return self.size
|
| 171 |
+
|
| 172 |
+
@property
|
| 173 |
+
def actions(self):
|
| 174 |
+
if self.action_buffer is None:
|
| 175 |
+
return np.array([])
|
| 176 |
+
return self.action_buffer[:len(self)]
|
| 177 |
+
|
| 178 |
+
@property
|
| 179 |
+
def actual_timestamps(self):
|
| 180 |
+
if self.timestamp_buffer is None:
|
| 181 |
+
return np.array([])
|
| 182 |
+
return self.timestamp_buffer[:len(self)]
|
| 183 |
+
|
| 184 |
+
@property
|
| 185 |
+
def timestamps(self):
|
| 186 |
+
if self.timestamp_buffer is None:
|
| 187 |
+
return np.array([])
|
| 188 |
+
return self.start_time + np.arange(len(self)) * self.dt
|
| 189 |
+
|
| 190 |
+
def put(self, actions: np.ndarray, timestamps: np.ndarray):
|
| 191 |
+
"""
|
| 192 |
+
Note: timestamps is the time when the action will be issued,
|
| 193 |
+
not when the action will be completed (target_timestamp)
|
| 194 |
+
"""
|
| 195 |
+
|
| 196 |
+
local_idxs, global_idxs, _ = get_accumulate_timestamp_idxs(
|
| 197 |
+
timestamps=timestamps,
|
| 198 |
+
start_time=self.start_time,
|
| 199 |
+
dt=self.dt,
|
| 200 |
+
eps=self.eps,
|
| 201 |
+
# allows overwriting previous actions
|
| 202 |
+
next_global_idx=None
|
| 203 |
+
)
|
| 204 |
+
|
| 205 |
+
if len(global_idxs) > 0:
|
| 206 |
+
if self.timestamp_buffer is None:
|
| 207 |
+
# first allocation
|
| 208 |
+
self.action_buffer = np.zeros_like(actions)
|
| 209 |
+
self.timestamp_buffer = np.zeros((len(actions),), dtype=np.float64)
|
| 210 |
+
|
| 211 |
+
this_max_size = global_idxs[-1] + 1
|
| 212 |
+
if this_max_size > len(self.timestamp_buffer):
|
| 213 |
+
# reallocate
|
| 214 |
+
new_size = max(this_max_size, len(self.timestamp_buffer) * 2)
|
| 215 |
+
new_shape = (new_size,) + self.action_buffer.shape[1:]
|
| 216 |
+
self.action_buffer = np.resize(self.action_buffer, new_shape)
|
| 217 |
+
self.timestamp_buffer = np.resize(self.timestamp_buffer, (new_size,))
|
| 218 |
+
|
| 219 |
+
# potentially rewrite old data (as expected)
|
| 220 |
+
self.action_buffer[global_idxs] = actions[local_idxs]
|
| 221 |
+
self.timestamp_buffer[global_idxs] = timestamps[local_idxs]
|
| 222 |
+
self.size = max(self.size, this_max_size)
|
config/data_collect/diffusion_policy_cnn/datacollect_diffusion_policy_cnn_lowdim_can_mh_abs.yaml
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
defaults:
|
| 2 |
+
- _self_
|
| 3 |
+
- task: can_lowdim_abs
|
| 4 |
+
|
| 5 |
+
name: datacollect_diffusion_unet_lowdim
|
| 6 |
+
_target_: diffusion_policy.workspace.datacollect_diffusion_lowdim_workspace.DatacollectDiffusionLowdimWorkspace
|
| 7 |
+
checkpoint_dir: null
|
| 8 |
+
|
| 9 |
+
task_name: ${task.name}
|
| 10 |
+
|
| 11 |
+
horizon: 16
|
| 12 |
+
n_obs_steps: 2
|
| 13 |
+
n_action_steps: 8
|
| 14 |
+
n_latency_steps: 0
|
| 15 |
+
past_action_visible: False
|
| 16 |
+
keypoint_visible_rate: 1.0
|
| 17 |
+
|
| 18 |
+
collecting:
|
| 19 |
+
device: "cuda:0"
|
| 20 |
+
seed: 42
|
| 21 |
+
num_episodes: 500 # number of episodes to collect
|
| 22 |
+
render_image: True
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
hydra:
|
| 26 |
+
job:
|
| 27 |
+
override_dirname: ${name}
|
| 28 |
+
run:
|
| 29 |
+
dir: logs/collect_data/${task_name}/${name}_${task_name}
|
config/data_collect/diffusion_policy_cnn/datacollect_diffusion_policy_cnn_lowdim_kitchen_abs.yaml
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
defaults:
|
| 2 |
+
- _self_
|
| 3 |
+
- task: kitchen_lowdim_abs
|
| 4 |
+
|
| 5 |
+
name: datacollect_diffusion_unet_lowdim
|
| 6 |
+
_target_: diffusion_policy.workspace.datacollect_diffusion_lowdim_workspace.DatacollectDiffusionLowdimWorkspace
|
| 7 |
+
checkpoint_dir: null
|
| 8 |
+
|
| 9 |
+
task_name: ${task.name}
|
| 10 |
+
|
| 11 |
+
horizon: 16
|
| 12 |
+
n_obs_steps: 2
|
| 13 |
+
n_action_steps: 8
|
| 14 |
+
n_latency_steps: 0
|
| 15 |
+
past_action_visible: False
|
| 16 |
+
keypoint_visible_rate: 1.0
|
| 17 |
+
|
| 18 |
+
collecting:
|
| 19 |
+
device: "cuda:0"
|
| 20 |
+
seed: 42
|
| 21 |
+
num_episodes: 500 # number of episodes to collect
|
| 22 |
+
render_image: True
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
hydra:
|
| 26 |
+
job:
|
| 27 |
+
override_dirname: ${name}
|
| 28 |
+
run:
|
| 29 |
+
dir: logs/collect_data/${task_name}/${name}_${task_name}
|
config/data_collect/diffusion_policy_cnn/datacollect_diffusion_policy_cnn_lowdim_lift_mh_abs.yaml
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
defaults:
|
| 2 |
+
- _self_
|
| 3 |
+
- task: lift_lowdim_abs
|
| 4 |
+
|
| 5 |
+
name: datacollect_diffusion_unet_lowdim
|
| 6 |
+
_target_: diffusion_policy.workspace.datacollect_diffusion_lowdim_workspace.DatacollectDiffusionLowdimWorkspace
|
| 7 |
+
checkpoint_dir: null
|
| 8 |
+
|
| 9 |
+
task_name: ${task.name}
|
| 10 |
+
|
| 11 |
+
horizon: 16
|
| 12 |
+
n_obs_steps: 2
|
| 13 |
+
n_action_steps: 8
|
| 14 |
+
n_latency_steps: 0
|
| 15 |
+
past_action_visible: False
|
| 16 |
+
keypoint_visible_rate: 1.0
|
| 17 |
+
|
| 18 |
+
collecting:
|
| 19 |
+
device: "cuda:0"
|
| 20 |
+
seed: 42
|
| 21 |
+
num_episodes: 500 # number of episodes to collect
|
| 22 |
+
render_image: True
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
hydra:
|
| 26 |
+
job:
|
| 27 |
+
override_dirname: ${name}
|
| 28 |
+
run:
|
| 29 |
+
dir: logs/collect_data/${task_name}/${name}_${task_name}
|
config/data_collect/diffusion_policy_cnn/datacollect_diffusion_policy_cnn_lowdim_pusht.yaml
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
defaults:
|
| 2 |
+
- _self_
|
| 3 |
+
- task: pusht_lowdim
|
| 4 |
+
|
| 5 |
+
name: datacollect_diffusion_unet_lowdim
|
| 6 |
+
_target_: diffusion_policy.workspace.datacollect_diffusion_lowdim_workspace.DatacollectDiffusionLowdimWorkspace
|
| 7 |
+
checkpoint_dir: null
|
| 8 |
+
|
| 9 |
+
task_name: ${task.name}
|
| 10 |
+
|
| 11 |
+
horizon: 16
|
| 12 |
+
n_obs_steps: 2
|
| 13 |
+
n_action_steps: 8
|
| 14 |
+
n_latency_steps: 0
|
| 15 |
+
past_action_visible: False
|
| 16 |
+
keypoint_visible_rate: 1.0
|
| 17 |
+
|
| 18 |
+
collecting:
|
| 19 |
+
device: "cuda:0"
|
| 20 |
+
seed: 42
|
| 21 |
+
num_episodes: 500 # number of episodes to collect
|
| 22 |
+
render_image: True
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
hydra:
|
| 26 |
+
job:
|
| 27 |
+
override_dirname: ${name}
|
| 28 |
+
run:
|
| 29 |
+
dir: logs/collect_data/${task_name}/${name}_${task_name}
|
config/data_collect/diffusion_policy_cnn/datacollect_diffusion_policy_cnn_lowdim_square_mh_abs.yaml
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
defaults:
|
| 2 |
+
- _self_
|
| 3 |
+
- task: square_lowdim_abs
|
| 4 |
+
|
| 5 |
+
name: datacollect_diffusion_unet_lowdim
|
| 6 |
+
_target_: diffusion_policy.workspace.datacollect_diffusion_lowdim_workspace.DatacollectDiffusionLowdimWorkspace
|
| 7 |
+
checkpoint_dir: null
|
| 8 |
+
|
| 9 |
+
task_name: ${task.name}
|
| 10 |
+
|
| 11 |
+
horizon: 16
|
| 12 |
+
n_obs_steps: 2
|
| 13 |
+
n_action_steps: 8
|
| 14 |
+
n_latency_steps: 0
|
| 15 |
+
past_action_visible: False
|
| 16 |
+
keypoint_visible_rate: 1.0
|
| 17 |
+
|
| 18 |
+
collecting:
|
| 19 |
+
device: "cuda:0"
|
| 20 |
+
seed: 42
|
| 21 |
+
num_episodes: 500 # number of episodes to collect
|
| 22 |
+
render_image: True
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
hydra:
|
| 26 |
+
job:
|
| 27 |
+
override_dirname: ${name}
|
| 28 |
+
run:
|
| 29 |
+
dir: logs/collect_data/${task_name}/${name}_${task_name}
|
config/data_collect/diffusion_policy_cnn/datacollect_diffusion_policy_cnn_lowdim_transport_mh_abs.yaml
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
defaults:
|
| 2 |
+
- _self_
|
| 3 |
+
- task: transport_lowdim_abs
|
| 4 |
+
|
| 5 |
+
name: datacollect_diffusion_unet_lowdim
|
| 6 |
+
_target_: diffusion_policy.workspace.datacollect_diffusion_lowdim_workspace.DatacollectDiffusionLowdimWorkspace
|
| 7 |
+
checkpoint_dir: null
|
| 8 |
+
|
| 9 |
+
task_name: ${task.name}
|
| 10 |
+
|
| 11 |
+
horizon: 16
|
| 12 |
+
n_obs_steps: 2
|
| 13 |
+
n_action_steps: 8
|
| 14 |
+
n_latency_steps: 0
|
| 15 |
+
past_action_visible: False
|
| 16 |
+
keypoint_visible_rate: 1.0
|
| 17 |
+
|
| 18 |
+
collecting:
|
| 19 |
+
device: "cuda:0"
|
| 20 |
+
seed: 42
|
| 21 |
+
num_episodes: 500 # number of episodes to collect
|
| 22 |
+
render_image: True
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
hydra:
|
| 26 |
+
job:
|
| 27 |
+
override_dirname: ${name}
|
| 28 |
+
run:
|
| 29 |
+
dir: logs/collect_data/${task_name}/${name}_${task_name}
|
config/data_collect/diffusion_policy_transformer/datacollect_diffusion_policy_transformer_lowdim_can_mh_abs.yaml
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
defaults:
|
| 2 |
+
- _self_
|
| 3 |
+
- task: can_lowdim_abs
|
| 4 |
+
|
| 5 |
+
name: datacollect_diffusion_transformer_lowdim
|
| 6 |
+
_target_: diffusion_policy.workspace.datacollect_diffusion_lowdim_workspace.DatacollectDiffusionLowdimWorkspace
|
| 7 |
+
checkpoint_dir: null
|
| 8 |
+
|
| 9 |
+
task_name: ${task.name}
|
| 10 |
+
|
| 11 |
+
horizon: 10
|
| 12 |
+
n_obs_steps: 2
|
| 13 |
+
n_action_steps: 8
|
| 14 |
+
n_latency_steps: 0
|
| 15 |
+
past_action_visible: False
|
| 16 |
+
keypoint_visible_rate: 1.0
|
| 17 |
+
|
| 18 |
+
collecting:
|
| 19 |
+
device: "cuda:0"
|
| 20 |
+
seed: 42
|
| 21 |
+
num_episodes: 500 # number of episodes to collect
|
| 22 |
+
render_image: True
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
hydra:
|
| 26 |
+
job:
|
| 27 |
+
override_dirname: ${name}
|
| 28 |
+
run:
|
| 29 |
+
dir: logs/collect_data/${task_name}/${name}_${task_name}
|
config/data_collect/diffusion_policy_transformer/datacollect_diffusion_policy_transformer_lowdim_kitchen_abs.yaml
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
defaults:
|
| 2 |
+
- _self_
|
| 3 |
+
- task: kitchen_lowdim_abs
|
| 4 |
+
|
| 5 |
+
name: datacollect_diffusion_transformer_lowdim
|
| 6 |
+
_target_: diffusion_policy.workspace.datacollect_diffusion_lowdim_workspace.DatacollectDiffusionLowdimWorkspace
|
| 7 |
+
checkpoint_dir: null
|
| 8 |
+
|
| 9 |
+
task_name: ${task.name}
|
| 10 |
+
|
| 11 |
+
horizon: 16
|
| 12 |
+
n_obs_steps: 4
|
| 13 |
+
n_action_steps: 8
|
| 14 |
+
n_latency_steps: 0
|
| 15 |
+
past_action_visible: False
|
| 16 |
+
keypoint_visible_rate: 1.0
|
| 17 |
+
|
| 18 |
+
collecting:
|
| 19 |
+
device: "cuda:0"
|
| 20 |
+
seed: 42
|
| 21 |
+
num_episodes: 500 # number of episodes to collect
|
| 22 |
+
render_image: True
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
hydra:
|
| 26 |
+
job:
|
| 27 |
+
override_dirname: ${name}
|
| 28 |
+
run:
|
| 29 |
+
dir: logs/collect_data/${task_name}/${name}_${task_name}
|
config/data_collect/diffusion_policy_transformer/datacollect_diffusion_policy_transformer_lowdim_lift_mh_abs.yaml
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
defaults:
|
| 2 |
+
- _self_
|
| 3 |
+
- task: lift_lowdim_abs
|
| 4 |
+
|
| 5 |
+
name: datacollect_diffusion_transformer_lowdim
|
| 6 |
+
_target_: diffusion_policy.workspace.datacollect_diffusion_lowdim_workspace.DatacollectDiffusionLowdimWorkspace
|
| 7 |
+
checkpoint_dir: null
|
| 8 |
+
|
| 9 |
+
task_name: ${task.name}
|
| 10 |
+
|
| 11 |
+
horizon: 10
|
| 12 |
+
n_obs_steps: 2
|
| 13 |
+
n_action_steps: 8
|
| 14 |
+
n_latency_steps: 0
|
| 15 |
+
past_action_visible: False
|
| 16 |
+
keypoint_visible_rate: 1.0
|
| 17 |
+
|
| 18 |
+
collecting:
|
| 19 |
+
device: "cuda:0"
|
| 20 |
+
seed: 42
|
| 21 |
+
num_episodes: 500 # number of episodes to collect
|
| 22 |
+
render_image: True
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
hydra:
|
| 26 |
+
job:
|
| 27 |
+
override_dirname: ${name}
|
| 28 |
+
run:
|
| 29 |
+
dir: logs/collect_data/${task_name}/${name}_${task_name}
|
config/data_collect/diffusion_policy_transformer/datacollect_diffusion_policy_transformer_lowdim_pusht.yaml
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
defaults:
|
| 2 |
+
- _self_
|
| 3 |
+
- task: pusht_lowdim
|
| 4 |
+
|
| 5 |
+
name: datacollect_diffusion_transformer_lowdim
|
| 6 |
+
_target_: diffusion_policy.workspace.datacollect_diffusion_lowdim_workspace.DatacollectDiffusionLowdimWorkspace
|
| 7 |
+
checkpoint_dir: null
|
| 8 |
+
|
| 9 |
+
task_name: ${task.name}
|
| 10 |
+
|
| 11 |
+
horizon: 16
|
| 12 |
+
n_obs_steps: 2
|
| 13 |
+
n_action_steps: 8
|
| 14 |
+
n_latency_steps: 0
|
| 15 |
+
past_action_visible: False
|
| 16 |
+
keypoint_visible_rate: 1.0
|
| 17 |
+
|
| 18 |
+
collecting:
|
| 19 |
+
device: "cuda:0"
|
| 20 |
+
seed: 42
|
| 21 |
+
num_episodes: 500 # number of episodes to collect
|
| 22 |
+
render_image: True
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
hydra:
|
| 26 |
+
job:
|
| 27 |
+
override_dirname: ${name}
|
| 28 |
+
run:
|
| 29 |
+
dir: logs/collect_data/${task_name}/${name}_${task_name}
|
config/data_collect/diffusion_policy_transformer/datacollect_diffusion_policy_transformer_lowdim_square_mh_abs.yaml
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
defaults:
|
| 2 |
+
- _self_
|
| 3 |
+
- task: square_lowdim_abs
|
| 4 |
+
|
| 5 |
+
name: datacollect_diffusion_transformer_lowdim
|
| 6 |
+
_target_: diffusion_policy.workspace.datacollect_diffusion_lowdim_workspace.DatacollectDiffusionLowdimWorkspace
|
| 7 |
+
checkpoint_dir: null
|
| 8 |
+
|
| 9 |
+
task_name: ${task.name}
|
| 10 |
+
|
| 11 |
+
horizon: 10
|
| 12 |
+
n_obs_steps: 2
|
| 13 |
+
n_action_steps: 8
|
| 14 |
+
n_latency_steps: 0
|
| 15 |
+
past_action_visible: False
|
| 16 |
+
keypoint_visible_rate: 1.0
|
| 17 |
+
|
| 18 |
+
collecting:
|
| 19 |
+
device: "cuda:0"
|
| 20 |
+
seed: 42
|
| 21 |
+
num_episodes: 500 # number of episodes to collect
|
| 22 |
+
render_image: True
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
hydra:
|
| 26 |
+
job:
|
| 27 |
+
override_dirname: ${name}
|
| 28 |
+
run:
|
| 29 |
+
dir: logs/collect_data/${task_name}/${name}_${task_name}
|
config/data_collect/diffusion_policy_transformer/datacollect_diffusion_policy_transformer_lowdim_tool_hang_ph_abs.yaml
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
defaults:
|
| 2 |
+
- _self_
|
| 3 |
+
- task: tool_hang_lowdim_abs
|
| 4 |
+
|
| 5 |
+
name: datacollect_diffusion_transformer_lowdim
|
| 6 |
+
_target_: diffusion_policy.workspace.datacollect_diffusion_lowdim_workspace.DatacollectDiffusionLowdimWorkspace
|
| 7 |
+
checkpoint_dir: null
|
| 8 |
+
|
| 9 |
+
task_name: ${task.name}
|
| 10 |
+
|
| 11 |
+
horizon: 10
|
| 12 |
+
n_obs_steps: 2
|
| 13 |
+
n_action_steps: 8
|
| 14 |
+
n_latency_steps: 0
|
| 15 |
+
past_action_visible: False
|
| 16 |
+
keypoint_visible_rate: 1.0
|
| 17 |
+
|
| 18 |
+
collecting:
|
| 19 |
+
device: "cuda:0"
|
| 20 |
+
seed: 42
|
| 21 |
+
num_episodes: 500 # number of episodes to collect
|
| 22 |
+
render_image: True
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
hydra:
|
| 26 |
+
job:
|
| 27 |
+
override_dirname: ${name}
|
| 28 |
+
run:
|
| 29 |
+
dir: logs/collect_data/${task_name}/${name}_${task_name}
|
config/data_collect/diffusion_policy_transformer/datacollect_diffusion_policy_transformer_lowdim_transport_mh_abs.yaml
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
defaults:
|
| 2 |
+
- _self_
|
| 3 |
+
- task: transport_lowdim_abs
|
| 4 |
+
|
| 5 |
+
name: datacollect_diffusion_transformer_lowdim
|
| 6 |
+
_target_: diffusion_policy.workspace.datacollect_diffusion_lowdim_workspace.DatacollectDiffusionLowdimWorkspace
|
| 7 |
+
checkpoint_dir: null
|
| 8 |
+
|
| 9 |
+
task_name: ${task.name}
|
| 10 |
+
|
| 11 |
+
horizon: 10
|
| 12 |
+
n_obs_steps: 2
|
| 13 |
+
n_action_steps: 8
|
| 14 |
+
n_latency_steps: 0
|
| 15 |
+
past_action_visible: False
|
| 16 |
+
keypoint_visible_rate: 1.0
|
| 17 |
+
|
| 18 |
+
collecting:
|
| 19 |
+
device: "cuda:0"
|
| 20 |
+
seed: 42
|
| 21 |
+
num_episodes: 500 # number of episodes to collect
|
| 22 |
+
render_image: True
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
hydra:
|
| 26 |
+
job:
|
| 27 |
+
override_dirname: ${name}
|
| 28 |
+
run:
|
| 29 |
+
dir: logs/collect_data/${task_name}/${name}_${task_name}
|
config/pbrl_dp_transformer/train_pbrl_diffusion_policy_transformer_lowdim_can_mh_abs.yaml
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
defaults:
|
| 2 |
+
- _self_
|
| 3 |
+
- task: can_lowdim_abs_pbrl
|
| 4 |
+
|
| 5 |
+
name: train_pbrl_diffusion_transformer
|
| 6 |
+
|
| 7 |
+
_target_: diffusion_policy.workspace.train_pbrl_diffusion_transformer_lowdim_offline_workspace.PbrlDiffusionTransformerLowdimWorkspace
|
| 8 |
+
checkpoint_dir: logs/pretrain/can_lowdim/train_diffusion_transformer/checkpoints/epoch=0040-test_mean_score=0.380.ckpt
|
| 9 |
+
|
| 10 |
+
obs_dim: ${task.obs_dim}
|
| 11 |
+
action_dim: ${task.action_dim}
|
| 12 |
+
keypoint_dim: ${task.keypoint_dim}
|
| 13 |
+
task_name: ${task.name}
|
| 14 |
+
|
| 15 |
+
gamma: 0.999 # discount factor in MDP
|
| 16 |
+
horizon: 10
|
| 17 |
+
n_obs_steps: 2
|
| 18 |
+
n_action_steps: 8
|
| 19 |
+
n_latency_steps: 0
|
| 20 |
+
past_action_visible: False
|
| 21 |
+
keypoint_visible_rate: 1.0
|
| 22 |
+
obs_as_cond: True
|
| 23 |
+
pred_action_steps_only: False
|
| 24 |
+
|
| 25 |
+
policy:
|
| 26 |
+
_target_: diffusion_policy.policy.cpl_diffusion_transformer_lowdim_policy.CplDiffusionTransformerLowdimPolicy
|
| 27 |
+
|
| 28 |
+
model:
|
| 29 |
+
_target_: diffusion_policy.model.diffusion.transformer_for_diffusion.TransformerForDiffusion
|
| 30 |
+
input_dim: ${eval:'${action_dim} if ${obs_as_cond} else ${obs_dim} + ${action_dim}'}
|
| 31 |
+
output_dim: ${policy.model.input_dim}
|
| 32 |
+
horizon: ${horizon}
|
| 33 |
+
n_obs_steps: ${n_obs_steps}
|
| 34 |
+
cond_dim: ${eval:'${obs_dim} if ${obs_as_cond} else 0'}
|
| 35 |
+
|
| 36 |
+
n_layer: 8
|
| 37 |
+
n_head: 4
|
| 38 |
+
n_emb: 256
|
| 39 |
+
p_drop_emb: 0.0
|
| 40 |
+
p_drop_attn: 0.3
|
| 41 |
+
|
| 42 |
+
causal_attn: True
|
| 43 |
+
time_as_cond: True # if false, use BERT like encoder only arch, time as input
|
| 44 |
+
obs_as_cond: ${obs_as_cond}
|
| 45 |
+
n_cond_layers: 0 # >0: use transformer encoder for cond, otherwise use MLP
|
| 46 |
+
|
| 47 |
+
noise_scheduler:
|
| 48 |
+
_target_: diffusers.schedulers.scheduling_ddpm.DDPMScheduler
|
| 49 |
+
num_train_timesteps: 100
|
| 50 |
+
beta_start: 0.0001
|
| 51 |
+
beta_end: 0.02
|
| 52 |
+
beta_schedule: squaredcos_cap_v2
|
| 53 |
+
variance_type: fixed_small # Yilun's paper uses fixed_small_log instead, but easy to cause Nan
|
| 54 |
+
clip_sample: True # required when predict_epsilon=False
|
| 55 |
+
prediction_type: epsilon # or sample
|
| 56 |
+
|
| 57 |
+
horizon: ${horizon}
|
| 58 |
+
obs_dim: ${obs_dim}
|
| 59 |
+
action_dim: ${action_dim}
|
| 60 |
+
n_action_steps: ${n_action_steps}
|
| 61 |
+
n_obs_steps: ${n_obs_steps}
|
| 62 |
+
num_inference_steps: 100
|
| 63 |
+
obs_as_cond: ${obs_as_cond}
|
| 64 |
+
pred_action_steps_only: ${pred_action_steps_only}
|
| 65 |
+
|
| 66 |
+
beta: 0.1
|
| 67 |
+
bias_reg: 0.25
|
| 68 |
+
ignore_equal_pref: False
|
| 69 |
+
clip_margin: 1.0
|
| 70 |
+
smooth_label: 0.0
|
| 71 |
+
confidence_weight: False
|
| 72 |
+
cw_temperature: 0.03
|
| 73 |
+
unclip_win: False
|
| 74 |
+
|
| 75 |
+
ema:
|
| 76 |
+
_target_: diffusion_policy.model.diffusion.ema_model.EMAModel
|
| 77 |
+
update_after_step: 0
|
| 78 |
+
inv_gamma: 1.0 # TODO: UAPA paper use : 2.0
|
| 79 |
+
power: 0.75 # TODO: UAPA paper use : 0.9
|
| 80 |
+
min_value: 0.0
|
| 81 |
+
max_value: 0.9999
|
| 82 |
+
|
| 83 |
+
dataloader:
|
| 84 |
+
batch_size: 128
|
| 85 |
+
num_workers: 1
|
| 86 |
+
shuffle: True
|
| 87 |
+
pin_memory: True
|
| 88 |
+
persistent_workers: False
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
optimizer:
|
| 92 |
+
learning_rate: 1.0e-4 # 1.0e-4
|
| 93 |
+
weight_decay: 1.0e-3
|
| 94 |
+
betas: [0.9, 0.95]
|
| 95 |
+
|
| 96 |
+
training:
|
| 97 |
+
device: "cuda:0"
|
| 98 |
+
seed: 42
|
| 99 |
+
debug: False
|
| 100 |
+
resume: True
|
| 101 |
+
# optimization
|
| 102 |
+
lr_scheduler: cosine
|
| 103 |
+
# Transformer needs LR warmup
|
| 104 |
+
lr_warmup_steps: 0
|
| 105 |
+
num_epochs: 500
|
| 106 |
+
gradient_accumulate_every: 1
|
| 107 |
+
use_ema: True
|
| 108 |
+
# training loop control
|
| 109 |
+
# in epochs
|
| 110 |
+
rollout_every: 25
|
| 111 |
+
checkpoint_every: 25
|
| 112 |
+
val_every: 1
|
| 113 |
+
sample_every: 5
|
| 114 |
+
# steps per epoch
|
| 115 |
+
max_train_steps: null
|
| 116 |
+
max_val_steps: null
|
| 117 |
+
# misc
|
| 118 |
+
tqdm_interval_sec: 1.0
|
| 119 |
+
|
| 120 |
+
cpl_loss_type: 'cplkl' # 'cpl'
|
| 121 |
+
use_expert_data_1: False
|
| 122 |
+
use_expert_data_2: False
|
| 123 |
+
n_epoch_sft: 0
|
| 124 |
+
sft_type: "pos"
|
| 125 |
+
stride_ratio: 0.5
|
| 126 |
+
pseudo_preference: False
|
| 127 |
+
|
| 128 |
+
dataset_1_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5.h5'
|
| 129 |
+
dataset_2_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5_2.h5'
|
| 130 |
+
preference_learning:
|
| 131 |
+
num_rounds: 1
|
| 132 |
+
equal_threshold: 0.05
|
| 133 |
+
|
| 134 |
+
logging:
|
| 135 |
+
project: IL-DP-2026
|
| 136 |
+
resume: False
|
| 137 |
+
mode: online
|
| 138 |
+
name: ${name}_${training.seed}
|
| 139 |
+
tags: ["${task_name}"]
|
| 140 |
+
id: null
|
| 141 |
+
group: ${name}
|
| 142 |
+
|
| 143 |
+
checkpoint:
|
| 144 |
+
topk:
|
| 145 |
+
monitor_key: test_mean_score
|
| 146 |
+
mode: max
|
| 147 |
+
k: 3
|
| 148 |
+
format_str: 'epoch={epoch:04d}-test_mean_score={test_mean_score:.3f}.ckpt'
|
| 149 |
+
save_last_ckpt: True
|
| 150 |
+
save_last_snapshot: False
|
| 151 |
+
|
| 152 |
+
multi_run:
|
| 153 |
+
run_dir: data/outputs/${now:%Y.%m.%d}/${now:%H.%M.%S}_${name}_${task_name}
|
| 154 |
+
wandb_name_base: ${name}_${task_name}
|
| 155 |
+
|
| 156 |
+
hydra:
|
| 157 |
+
job:
|
| 158 |
+
override_dirname: ${name}
|
| 159 |
+
run:
|
| 160 |
+
dir: data/outputs/${now:%Y.%m.%d}/${now:%H.%M.%S}_${name}_${task_name}
|
| 161 |
+
sweep:
|
| 162 |
+
dir: data/outputs/${now:%Y.%m.%d}/${now:%H.%M.%S}_${name}_${task_name}
|
| 163 |
+
subdir: ${hydra.job.num}
|
config/pbrl_dp_transformer/train_pbrl_diffusion_policy_transformer_lowdim_lift_mh_abs.yaml
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
defaults:
|
| 2 |
+
- _self_
|
| 3 |
+
- task: lift_lowdim_abs_pbrl
|
| 4 |
+
|
| 5 |
+
name: train_pbrl_diffusion_transformer
|
| 6 |
+
|
| 7 |
+
_target_: diffusion_policy.workspace.train_pbrl_diffusion_transformer_lowdim_offline_workspace.PbrlDiffusionTransformerLowdimWorkspace
|
| 8 |
+
checkpoint_dir: logs/pretrain/lift_lowdim/train_diffusion_transformer/checkpoints/epoch=0015-test_mean_score=0.400.ckpt
|
| 9 |
+
|
| 10 |
+
obs_dim: ${task.obs_dim}
|
| 11 |
+
action_dim: ${task.action_dim}
|
| 12 |
+
keypoint_dim: ${task.keypoint_dim}
|
| 13 |
+
task_name: ${task.name}
|
| 14 |
+
|
| 15 |
+
gamma: 0.999 # discount factor in MDP
|
| 16 |
+
horizon: 10
|
| 17 |
+
n_obs_steps: 2
|
| 18 |
+
n_action_steps: 8
|
| 19 |
+
n_latency_steps: 0
|
| 20 |
+
past_action_visible: False
|
| 21 |
+
keypoint_visible_rate: 1.0
|
| 22 |
+
obs_as_cond: True
|
| 23 |
+
pred_action_steps_only: False
|
| 24 |
+
|
| 25 |
+
policy:
|
| 26 |
+
_target_: diffusion_policy.policy.cpl_diffusion_transformer_lowdim_policy.CplDiffusionTransformerLowdimPolicy
|
| 27 |
+
|
| 28 |
+
model:
|
| 29 |
+
_target_: diffusion_policy.model.diffusion.transformer_for_diffusion.TransformerForDiffusion
|
| 30 |
+
input_dim: ${eval:'${action_dim} if ${obs_as_cond} else ${obs_dim} + ${action_dim}'}
|
| 31 |
+
output_dim: ${policy.model.input_dim}
|
| 32 |
+
horizon: ${horizon}
|
| 33 |
+
n_obs_steps: ${n_obs_steps}
|
| 34 |
+
cond_dim: ${eval:'${obs_dim} if ${obs_as_cond} else 0'}
|
| 35 |
+
|
| 36 |
+
n_layer: 8
|
| 37 |
+
n_head: 4
|
| 38 |
+
n_emb: 256
|
| 39 |
+
p_drop_emb: 0.0
|
| 40 |
+
p_drop_attn: 0.3
|
| 41 |
+
|
| 42 |
+
causal_attn: True
|
| 43 |
+
time_as_cond: True # if false, use BERT like encoder only arch, time as input
|
| 44 |
+
obs_as_cond: ${obs_as_cond}
|
| 45 |
+
n_cond_layers: 0 # >0: use transformer encoder for cond, otherwise use MLP
|
| 46 |
+
|
| 47 |
+
noise_scheduler:
|
| 48 |
+
_target_: diffusers.schedulers.scheduling_ddpm.DDPMScheduler
|
| 49 |
+
num_train_timesteps: 100
|
| 50 |
+
beta_start: 0.0001
|
| 51 |
+
beta_end: 0.02
|
| 52 |
+
beta_schedule: squaredcos_cap_v2
|
| 53 |
+
variance_type: fixed_small # Yilun's paper uses fixed_small_log instead, but easy to cause Nan
|
| 54 |
+
clip_sample: True # required when predict_epsilon=False
|
| 55 |
+
prediction_type: epsilon # or sample
|
| 56 |
+
|
| 57 |
+
horizon: ${horizon}
|
| 58 |
+
obs_dim: ${obs_dim}
|
| 59 |
+
action_dim: ${action_dim}
|
| 60 |
+
n_action_steps: ${n_action_steps}
|
| 61 |
+
n_obs_steps: ${n_obs_steps}
|
| 62 |
+
num_inference_steps: 100
|
| 63 |
+
obs_as_cond: ${obs_as_cond}
|
| 64 |
+
pred_action_steps_only: ${pred_action_steps_only}
|
| 65 |
+
|
| 66 |
+
beta: 0.1
|
| 67 |
+
bias_reg: 0.25
|
| 68 |
+
ignore_equal_pref: False
|
| 69 |
+
clip_margin: 1.0
|
| 70 |
+
smooth_label: 0.0
|
| 71 |
+
confidence_weight: False
|
| 72 |
+
cw_temperature: 0.03
|
| 73 |
+
unclip_win: False
|
| 74 |
+
|
| 75 |
+
ema:
|
| 76 |
+
_target_: diffusion_policy.model.diffusion.ema_model.EMAModel
|
| 77 |
+
update_after_step: 0
|
| 78 |
+
inv_gamma: 1.0 # TODO: UAPA paper use : 2.0
|
| 79 |
+
power: 0.75 # TODO: UAPA paper use : 0.9
|
| 80 |
+
min_value: 0.0
|
| 81 |
+
max_value: 0.9999
|
| 82 |
+
|
| 83 |
+
dataloader:
|
| 84 |
+
batch_size: 128
|
| 85 |
+
num_workers: 1
|
| 86 |
+
shuffle: True
|
| 87 |
+
pin_memory: True
|
| 88 |
+
persistent_workers: False
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
optimizer:
|
| 92 |
+
learning_rate: 1.0e-4 # 1.0e-4
|
| 93 |
+
weight_decay: 1.0e-3
|
| 94 |
+
betas: [0.9, 0.95]
|
| 95 |
+
|
| 96 |
+
training:
|
| 97 |
+
device: "cuda:0"
|
| 98 |
+
seed: 42
|
| 99 |
+
debug: False
|
| 100 |
+
resume: True
|
| 101 |
+
# optimization
|
| 102 |
+
lr_scheduler: cosine
|
| 103 |
+
# Transformer needs LR warmup
|
| 104 |
+
lr_warmup_steps: 0
|
| 105 |
+
num_epochs: 500
|
| 106 |
+
gradient_accumulate_every: 1
|
| 107 |
+
use_ema: True
|
| 108 |
+
# training loop control
|
| 109 |
+
# in epochs
|
| 110 |
+
rollout_every: 25
|
| 111 |
+
checkpoint_every: 25
|
| 112 |
+
val_every: 1
|
| 113 |
+
sample_every: 5
|
| 114 |
+
# steps per epoch
|
| 115 |
+
max_train_steps: null
|
| 116 |
+
max_val_steps: null
|
| 117 |
+
# misc
|
| 118 |
+
tqdm_interval_sec: 1.0
|
| 119 |
+
|
| 120 |
+
cpl_loss_type: 'cplkl' # 'cpl'
|
| 121 |
+
use_expert_data_1: False
|
| 122 |
+
use_expert_data_2: False
|
| 123 |
+
n_epoch_sft: 0
|
| 124 |
+
sft_type: "pos"
|
| 125 |
+
stride_ratio: 0.5
|
| 126 |
+
pseudo_preference: False
|
| 127 |
+
|
| 128 |
+
dataset_1_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5.h5'
|
| 129 |
+
dataset_2_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5_2.h5'
|
| 130 |
+
preference_learning:
|
| 131 |
+
num_rounds: 1
|
| 132 |
+
equal_threshold: 0.05
|
| 133 |
+
|
| 134 |
+
logging:
|
| 135 |
+
project: IL-DP-2026
|
| 136 |
+
resume: False
|
| 137 |
+
mode: online
|
| 138 |
+
name: ${name}_${training.seed}
|
| 139 |
+
tags: ["${task_name}"]
|
| 140 |
+
id: null
|
| 141 |
+
group: ${name}
|
| 142 |
+
|
| 143 |
+
checkpoint:
|
| 144 |
+
topk:
|
| 145 |
+
monitor_key: test_mean_score
|
| 146 |
+
mode: max
|
| 147 |
+
k: 3
|
| 148 |
+
format_str: 'epoch={epoch:04d}-test_mean_score={test_mean_score:.3f}.ckpt'
|
| 149 |
+
save_last_ckpt: True
|
| 150 |
+
save_last_snapshot: False
|
| 151 |
+
|
| 152 |
+
multi_run:
|
| 153 |
+
run_dir: data/outputs/${now:%Y.%m.%d}/${now:%H.%M.%S}_${name}_${task_name}
|
| 154 |
+
wandb_name_base: ${name}_${task_name}
|
| 155 |
+
|
| 156 |
+
hydra:
|
| 157 |
+
job:
|
| 158 |
+
override_dirname: ${name}
|
| 159 |
+
run:
|
| 160 |
+
dir: data/outputs/${now:%Y.%m.%d}/${now:%H.%M.%S}_${name}_${task_name}
|
| 161 |
+
sweep:
|
| 162 |
+
dir: data/outputs/${now:%Y.%m.%d}/${now:%H.%M.%S}_${name}_${task_name}
|
| 163 |
+
subdir: ${hydra.job.num}
|
config/pbrl_dp_transformer/train_pbrl_diffusion_policy_transformer_lowdim_square_mh_abs.yaml
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
defaults:
|
| 2 |
+
- _self_
|
| 3 |
+
- task: square_lowdim_abs_pbrl
|
| 4 |
+
|
| 5 |
+
name: train_pbrl_diffusion_transformer
|
| 6 |
+
|
| 7 |
+
_target_: diffusion_policy.workspace.train_pbrl_diffusion_transformer_lowdim_offline_workspace.PbrlDiffusionTransformerLowdimWorkspace
|
| 8 |
+
checkpoint_dir: logs/pretrain/square_lowdim/train_diffusion_transformer/checkpoints/epoch=0450-test_mean_score=0.520.ckpt
|
| 9 |
+
|
| 10 |
+
obs_dim: ${task.obs_dim}
|
| 11 |
+
action_dim: ${task.action_dim}
|
| 12 |
+
keypoint_dim: ${task.keypoint_dim}
|
| 13 |
+
task_name: ${task.name}
|
| 14 |
+
|
| 15 |
+
gamma: 0.999 # discount factor in MDP
|
| 16 |
+
horizon: 10
|
| 17 |
+
n_obs_steps: 2
|
| 18 |
+
n_action_steps: 8
|
| 19 |
+
n_latency_steps: 0
|
| 20 |
+
past_action_visible: False
|
| 21 |
+
keypoint_visible_rate: 1.0
|
| 22 |
+
obs_as_cond: True
|
| 23 |
+
pred_action_steps_only: False
|
| 24 |
+
|
| 25 |
+
policy:
|
| 26 |
+
_target_: diffusion_policy.policy.cpl_diffusion_transformer_lowdim_policy.CplDiffusionTransformerLowdimPolicy
|
| 27 |
+
|
| 28 |
+
model:
|
| 29 |
+
_target_: diffusion_policy.model.diffusion.transformer_for_diffusion.TransformerForDiffusion
|
| 30 |
+
input_dim: ${eval:'${action_dim} if ${obs_as_cond} else ${obs_dim} + ${action_dim}'}
|
| 31 |
+
output_dim: ${policy.model.input_dim}
|
| 32 |
+
horizon: ${horizon}
|
| 33 |
+
n_obs_steps: ${n_obs_steps}
|
| 34 |
+
cond_dim: ${eval:'${obs_dim} if ${obs_as_cond} else 0'}
|
| 35 |
+
|
| 36 |
+
n_layer: 8
|
| 37 |
+
n_head: 4
|
| 38 |
+
n_emb: 256
|
| 39 |
+
p_drop_emb: 0.0
|
| 40 |
+
p_drop_attn: 0.3
|
| 41 |
+
|
| 42 |
+
causal_attn: True
|
| 43 |
+
time_as_cond: True # if false, use BERT like encoder only arch, time as input
|
| 44 |
+
obs_as_cond: ${obs_as_cond}
|
| 45 |
+
n_cond_layers: 0 # >0: use transformer encoder for cond, otherwise use MLP
|
| 46 |
+
|
| 47 |
+
noise_scheduler:
|
| 48 |
+
_target_: diffusers.schedulers.scheduling_ddpm.DDPMScheduler
|
| 49 |
+
num_train_timesteps: 100
|
| 50 |
+
beta_start: 0.0001
|
| 51 |
+
beta_end: 0.02
|
| 52 |
+
beta_schedule: squaredcos_cap_v2
|
| 53 |
+
variance_type: fixed_small # Yilun's paper uses fixed_small_log instead, but easy to cause Nan
|
| 54 |
+
clip_sample: True # required when predict_epsilon=False
|
| 55 |
+
prediction_type: epsilon # or sample
|
| 56 |
+
|
| 57 |
+
horizon: ${horizon}
|
| 58 |
+
obs_dim: ${obs_dim}
|
| 59 |
+
action_dim: ${action_dim}
|
| 60 |
+
n_action_steps: ${n_action_steps}
|
| 61 |
+
n_obs_steps: ${n_obs_steps}
|
| 62 |
+
num_inference_steps: 100
|
| 63 |
+
obs_as_cond: ${obs_as_cond}
|
| 64 |
+
pred_action_steps_only: ${pred_action_steps_only}
|
| 65 |
+
|
| 66 |
+
beta: 0.1
|
| 67 |
+
bias_reg: 0.25
|
| 68 |
+
ignore_equal_pref: False
|
| 69 |
+
clip_margin: 1.0
|
| 70 |
+
smooth_label: 0.0
|
| 71 |
+
confidence_weight: False
|
| 72 |
+
cw_temperature: 0.03
|
| 73 |
+
unclip_win: False
|
| 74 |
+
|
| 75 |
+
ema:
|
| 76 |
+
_target_: diffusion_policy.model.diffusion.ema_model.EMAModel
|
| 77 |
+
update_after_step: 0
|
| 78 |
+
inv_gamma: 1.0 # TODO: UAPA paper use : 2.0
|
| 79 |
+
power: 0.75 # TODO: UAPA paper use : 0.9
|
| 80 |
+
min_value: 0.0
|
| 81 |
+
max_value: 0.9999
|
| 82 |
+
|
| 83 |
+
dataloader:
|
| 84 |
+
batch_size: 128
|
| 85 |
+
num_workers: 1
|
| 86 |
+
shuffle: True
|
| 87 |
+
pin_memory: True
|
| 88 |
+
persistent_workers: False
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
optimizer:
|
| 92 |
+
learning_rate: 1.0e-4 # 1.0e-4
|
| 93 |
+
weight_decay: 1.0e-3
|
| 94 |
+
betas: [0.9, 0.95]
|
| 95 |
+
|
| 96 |
+
training:
|
| 97 |
+
device: "cuda:0"
|
| 98 |
+
seed: 42
|
| 99 |
+
debug: False
|
| 100 |
+
resume: True
|
| 101 |
+
# optimization
|
| 102 |
+
lr_scheduler: cosine
|
| 103 |
+
# Transformer needs LR warmup
|
| 104 |
+
lr_warmup_steps: 0
|
| 105 |
+
num_epochs: 500
|
| 106 |
+
gradient_accumulate_every: 1
|
| 107 |
+
use_ema: True
|
| 108 |
+
# training loop control
|
| 109 |
+
# in epochs
|
| 110 |
+
rollout_every: 25
|
| 111 |
+
checkpoint_every: 25
|
| 112 |
+
val_every: 1
|
| 113 |
+
sample_every: 5
|
| 114 |
+
# steps per epoch
|
| 115 |
+
max_train_steps: null
|
| 116 |
+
max_val_steps: null
|
| 117 |
+
# misc
|
| 118 |
+
tqdm_interval_sec: 1.0
|
| 119 |
+
|
| 120 |
+
cpl_loss_type: 'cplkl' # 'cpl'
|
| 121 |
+
use_expert_data_1: False
|
| 122 |
+
use_expert_data_2: False
|
| 123 |
+
n_epoch_sft: 0
|
| 124 |
+
sft_type: "pos"
|
| 125 |
+
stride_ratio: 0.5
|
| 126 |
+
pseudo_preference: False
|
| 127 |
+
|
| 128 |
+
dataset_1_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5.h5'
|
| 129 |
+
dataset_2_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5_2.h5'
|
| 130 |
+
preference_learning:
|
| 131 |
+
num_rounds: 1
|
| 132 |
+
equal_threshold: 0.05
|
| 133 |
+
|
| 134 |
+
logging:
|
| 135 |
+
project: IL-DP-2026
|
| 136 |
+
resume: False
|
| 137 |
+
mode: online
|
| 138 |
+
name: ${name}_${training.seed}
|
| 139 |
+
tags: ["${task_name}"]
|
| 140 |
+
id: null
|
| 141 |
+
group: ${name}
|
| 142 |
+
|
| 143 |
+
checkpoint:
|
| 144 |
+
topk:
|
| 145 |
+
monitor_key: test_mean_score
|
| 146 |
+
mode: max
|
| 147 |
+
k: 3
|
| 148 |
+
format_str: 'epoch={epoch:04d}-test_mean_score={test_mean_score:.3f}.ckpt'
|
| 149 |
+
save_last_ckpt: True
|
| 150 |
+
save_last_snapshot: False
|
| 151 |
+
|
| 152 |
+
multi_run:
|
| 153 |
+
run_dir: data/outputs/${now:%Y.%m.%d}/${now:%H.%M.%S}_${name}_${task_name}
|
| 154 |
+
wandb_name_base: ${name}_${task_name}
|
| 155 |
+
|
| 156 |
+
hydra:
|
| 157 |
+
job:
|
| 158 |
+
override_dirname: ${name}
|
| 159 |
+
run:
|
| 160 |
+
dir: data/outputs/${now:%Y.%m.%d}/${now:%H.%M.%S}_${name}_${task_name}
|
| 161 |
+
sweep:
|
| 162 |
+
dir: data/outputs/${now:%Y.%m.%d}/${now:%H.%M.%S}_${name}_${task_name}
|
| 163 |
+
subdir: ${hydra.job.num}
|
config/pbrl_dp_transformer/train_pbrl_diffusion_policy_transformer_lowdim_tool_hang_ph_abs.yaml
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
defaults:
|
| 2 |
+
- _self_
|
| 3 |
+
- task: tool_hang_lowdim_abs_pbrl
|
| 4 |
+
|
| 5 |
+
name: train_pbrl_diffusion_transformer
|
| 6 |
+
|
| 7 |
+
_target_: diffusion_policy.workspace.train_pbrl_diffusion_transformer_lowdim_offline_workspace.PbrlDiffusionTransformerLowdimWorkspace
|
| 8 |
+
checkpoint_dir: logs/pretrain/tool_hang_lowdim/train_diffusion_transformer/checkpoints/epoch=0500-test_mean_score=0.440.ckpt
|
| 9 |
+
|
| 10 |
+
obs_dim: ${task.obs_dim}
|
| 11 |
+
action_dim: ${task.action_dim}
|
| 12 |
+
keypoint_dim: ${task.keypoint_dim}
|
| 13 |
+
task_name: ${task.name}
|
| 14 |
+
|
| 15 |
+
gamma: 0.999 # discount factor in MDP
|
| 16 |
+
horizon: 10
|
| 17 |
+
n_obs_steps: 2
|
| 18 |
+
n_action_steps: 8
|
| 19 |
+
n_latency_steps: 0
|
| 20 |
+
past_action_visible: False
|
| 21 |
+
keypoint_visible_rate: 1.0
|
| 22 |
+
obs_as_cond: True
|
| 23 |
+
pred_action_steps_only: False
|
| 24 |
+
|
| 25 |
+
policy:
|
| 26 |
+
_target_: diffusion_policy.policy.cpl_diffusion_transformer_lowdim_policy.CplDiffusionTransformerLowdimPolicy
|
| 27 |
+
|
| 28 |
+
model:
|
| 29 |
+
_target_: diffusion_policy.model.diffusion.transformer_for_diffusion.TransformerForDiffusion
|
| 30 |
+
input_dim: ${eval:'${action_dim} if ${obs_as_cond} else ${obs_dim} + ${action_dim}'}
|
| 31 |
+
output_dim: ${policy.model.input_dim}
|
| 32 |
+
horizon: ${horizon}
|
| 33 |
+
n_obs_steps: ${n_obs_steps}
|
| 34 |
+
cond_dim: ${eval:'${obs_dim} if ${obs_as_cond} else 0'}
|
| 35 |
+
|
| 36 |
+
n_layer: 8
|
| 37 |
+
n_head: 4
|
| 38 |
+
n_emb: 256
|
| 39 |
+
p_drop_emb: 0.0
|
| 40 |
+
p_drop_attn: 0.3
|
| 41 |
+
|
| 42 |
+
causal_attn: True
|
| 43 |
+
time_as_cond: True # if false, use BERT like encoder only arch, time as input
|
| 44 |
+
obs_as_cond: ${obs_as_cond}
|
| 45 |
+
n_cond_layers: 0 # >0: use transformer encoder for cond, otherwise use MLP
|
| 46 |
+
|
| 47 |
+
noise_scheduler:
|
| 48 |
+
_target_: diffusers.schedulers.scheduling_ddpm.DDPMScheduler
|
| 49 |
+
num_train_timesteps: 100
|
| 50 |
+
beta_start: 0.0001
|
| 51 |
+
beta_end: 0.02
|
| 52 |
+
beta_schedule: squaredcos_cap_v2
|
| 53 |
+
variance_type: fixed_small # Yilun's paper uses fixed_small_log instead, but easy to cause Nan
|
| 54 |
+
clip_sample: True # required when predict_epsilon=False
|
| 55 |
+
prediction_type: epsilon # or sample
|
| 56 |
+
|
| 57 |
+
horizon: ${horizon}
|
| 58 |
+
obs_dim: ${obs_dim}
|
| 59 |
+
action_dim: ${action_dim}
|
| 60 |
+
n_action_steps: ${n_action_steps}
|
| 61 |
+
n_obs_steps: ${n_obs_steps}
|
| 62 |
+
num_inference_steps: 100
|
| 63 |
+
obs_as_cond: ${obs_as_cond}
|
| 64 |
+
pred_action_steps_only: ${pred_action_steps_only}
|
| 65 |
+
|
| 66 |
+
beta: 0.1
|
| 67 |
+
bias_reg: 0.25
|
| 68 |
+
ignore_equal_pref: False
|
| 69 |
+
clip_margin: 1.0
|
| 70 |
+
smooth_label: 0.0
|
| 71 |
+
confidence_weight: False
|
| 72 |
+
cw_temperature: 0.03
|
| 73 |
+
unclip_win: False
|
| 74 |
+
|
| 75 |
+
ema:
|
| 76 |
+
_target_: diffusion_policy.model.diffusion.ema_model.EMAModel
|
| 77 |
+
update_after_step: 0
|
| 78 |
+
inv_gamma: 1.0 # TODO: UAPA paper use : 2.0
|
| 79 |
+
power: 0.75 # TODO: UAPA paper use : 0.9
|
| 80 |
+
min_value: 0.0
|
| 81 |
+
max_value: 0.9999
|
| 82 |
+
|
| 83 |
+
dataloader:
|
| 84 |
+
batch_size: 128
|
| 85 |
+
num_workers: 1
|
| 86 |
+
shuffle: True
|
| 87 |
+
pin_memory: True
|
| 88 |
+
persistent_workers: False
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
optimizer:
|
| 92 |
+
learning_rate: 1.0e-4 # 1.0e-4
|
| 93 |
+
weight_decay: 1.0e-3
|
| 94 |
+
betas: [0.9, 0.95]
|
| 95 |
+
|
| 96 |
+
training:
|
| 97 |
+
device: "cuda:0"
|
| 98 |
+
seed: 42
|
| 99 |
+
debug: False
|
| 100 |
+
resume: True
|
| 101 |
+
# optimization
|
| 102 |
+
lr_scheduler: cosine
|
| 103 |
+
# Transformer needs LR warmup
|
| 104 |
+
lr_warmup_steps: 0
|
| 105 |
+
num_epochs: 500
|
| 106 |
+
gradient_accumulate_every: 1
|
| 107 |
+
use_ema: True
|
| 108 |
+
# training loop control
|
| 109 |
+
# in epochs
|
| 110 |
+
rollout_every: 25
|
| 111 |
+
checkpoint_every: 25
|
| 112 |
+
val_every: 1
|
| 113 |
+
sample_every: 5
|
| 114 |
+
# steps per epoch
|
| 115 |
+
max_train_steps: null
|
| 116 |
+
max_val_steps: null
|
| 117 |
+
# misc
|
| 118 |
+
tqdm_interval_sec: 1.0
|
| 119 |
+
|
| 120 |
+
cpl_loss_type: 'cplkl' # 'cpl'
|
| 121 |
+
use_expert_data_1: False
|
| 122 |
+
use_expert_data_2: False
|
| 123 |
+
n_epoch_sft: 0
|
| 124 |
+
sft_type: "pos"
|
| 125 |
+
stride_ratio: 0.5
|
| 126 |
+
pseudo_preference: False
|
| 127 |
+
|
| 128 |
+
dataset_1_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5.h5'
|
| 129 |
+
dataset_2_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5_2.h5'
|
| 130 |
+
preference_learning:
|
| 131 |
+
num_rounds: 1
|
| 132 |
+
equal_threshold: 0.05
|
| 133 |
+
|
| 134 |
+
logging:
|
| 135 |
+
project: IL-DP-2026
|
| 136 |
+
resume: False
|
| 137 |
+
mode: online
|
| 138 |
+
name: ${name}_${training.seed}
|
| 139 |
+
tags: ["${task_name}"]
|
| 140 |
+
id: null
|
| 141 |
+
group: ${name}
|
| 142 |
+
|
| 143 |
+
checkpoint:
|
| 144 |
+
topk:
|
| 145 |
+
monitor_key: test_mean_score
|
| 146 |
+
mode: max
|
| 147 |
+
k: 3
|
| 148 |
+
format_str: 'epoch={epoch:04d}-test_mean_score={test_mean_score:.3f}.ckpt'
|
| 149 |
+
save_last_ckpt: True
|
| 150 |
+
save_last_snapshot: False
|
| 151 |
+
|
| 152 |
+
multi_run:
|
| 153 |
+
run_dir: data/outputs/${now:%Y.%m.%d}/${now:%H.%M.%S}_${name}_${task_name}
|
| 154 |
+
wandb_name_base: ${name}_${task_name}
|
| 155 |
+
|
| 156 |
+
hydra:
|
| 157 |
+
job:
|
| 158 |
+
override_dirname: ${name}
|
| 159 |
+
run:
|
| 160 |
+
dir: data/outputs/${now:%Y.%m.%d}/${now:%H.%M.%S}_${name}_${task_name}
|
| 161 |
+
sweep:
|
| 162 |
+
dir: data/outputs/${now:%Y.%m.%d}/${now:%H.%M.%S}_${name}_${task_name}
|
| 163 |
+
subdir: ${hydra.job.num}
|
config/pbrl_dp_transformer/train_pbrl_diffusion_policy_transformer_lowdim_transport_mh_abs.yaml
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
defaults:
|
| 2 |
+
- _self_
|
| 3 |
+
- task: transport_lowdim_abs_pbrl
|
| 4 |
+
|
| 5 |
+
name: train_pbrl_diffusion_transformer
|
| 6 |
+
|
| 7 |
+
_target_: diffusion_policy.workspace.train_pbrl_diffusion_transformer_lowdim_offline_workspace.PbrlDiffusionTransformerLowdimWorkspace
|
| 8 |
+
checkpoint_dir: logs/pretrain/transport_lowdim/train_diffusion_transformer/checkpoints/epoch=0450-test_mean_score=0.240.ckpt
|
| 9 |
+
|
| 10 |
+
obs_dim: ${task.obs_dim}
|
| 11 |
+
action_dim: ${task.action_dim}
|
| 12 |
+
keypoint_dim: ${task.keypoint_dim}
|
| 13 |
+
task_name: ${task.name}
|
| 14 |
+
|
| 15 |
+
gamma: 0.999 # discount factor in MDP
|
| 16 |
+
horizon: 10
|
| 17 |
+
n_obs_steps: 2
|
| 18 |
+
n_action_steps: 8
|
| 19 |
+
n_latency_steps: 0
|
| 20 |
+
past_action_visible: False
|
| 21 |
+
keypoint_visible_rate: 1.0
|
| 22 |
+
obs_as_cond: True
|
| 23 |
+
pred_action_steps_only: False
|
| 24 |
+
|
| 25 |
+
policy:
|
| 26 |
+
_target_: diffusion_policy.policy.cpl_diffusion_transformer_lowdim_policy.CplDiffusionTransformerLowdimPolicy
|
| 27 |
+
|
| 28 |
+
model:
|
| 29 |
+
_target_: diffusion_policy.model.diffusion.transformer_for_diffusion.TransformerForDiffusion
|
| 30 |
+
input_dim: ${eval:'${action_dim} if ${obs_as_cond} else ${obs_dim} + ${action_dim}'}
|
| 31 |
+
output_dim: ${policy.model.input_dim}
|
| 32 |
+
horizon: ${horizon}
|
| 33 |
+
n_obs_steps: ${n_obs_steps}
|
| 34 |
+
cond_dim: ${eval:'${obs_dim} if ${obs_as_cond} else 0'}
|
| 35 |
+
|
| 36 |
+
n_layer: 8
|
| 37 |
+
n_head: 4
|
| 38 |
+
n_emb: 256
|
| 39 |
+
p_drop_emb: 0.0
|
| 40 |
+
p_drop_attn: 0.3
|
| 41 |
+
|
| 42 |
+
causal_attn: True
|
| 43 |
+
time_as_cond: True # if false, use BERT like encoder only arch, time as input
|
| 44 |
+
obs_as_cond: ${obs_as_cond}
|
| 45 |
+
n_cond_layers: 0 # >0: use transformer encoder for cond, otherwise use MLP
|
| 46 |
+
|
| 47 |
+
noise_scheduler:
|
| 48 |
+
_target_: diffusers.schedulers.scheduling_ddpm.DDPMScheduler
|
| 49 |
+
num_train_timesteps: 100
|
| 50 |
+
beta_start: 0.0001
|
| 51 |
+
beta_end: 0.02
|
| 52 |
+
beta_schedule: squaredcos_cap_v2
|
| 53 |
+
variance_type: fixed_small # Yilun's paper uses fixed_small_log instead, but easy to cause Nan
|
| 54 |
+
clip_sample: True # required when predict_epsilon=False
|
| 55 |
+
prediction_type: epsilon # or sample
|
| 56 |
+
|
| 57 |
+
horizon: ${horizon}
|
| 58 |
+
obs_dim: ${obs_dim}
|
| 59 |
+
action_dim: ${action_dim}
|
| 60 |
+
n_action_steps: ${n_action_steps}
|
| 61 |
+
n_obs_steps: ${n_obs_steps}
|
| 62 |
+
num_inference_steps: 100
|
| 63 |
+
obs_as_cond: ${obs_as_cond}
|
| 64 |
+
pred_action_steps_only: ${pred_action_steps_only}
|
| 65 |
+
|
| 66 |
+
beta: 0.1
|
| 67 |
+
bias_reg: 0.25
|
| 68 |
+
ignore_equal_pref: False
|
| 69 |
+
clip_margin: 1.0
|
| 70 |
+
smooth_label: 0.0
|
| 71 |
+
confidence_weight: False
|
| 72 |
+
cw_temperature: 0.03
|
| 73 |
+
unclip_win: False
|
| 74 |
+
|
| 75 |
+
ema:
|
| 76 |
+
_target_: diffusion_policy.model.diffusion.ema_model.EMAModel
|
| 77 |
+
update_after_step: 0
|
| 78 |
+
inv_gamma: 1.0 # TODO: UAPA paper use : 2.0
|
| 79 |
+
power: 0.75 # TODO: UAPA paper use : 0.9
|
| 80 |
+
min_value: 0.0
|
| 81 |
+
max_value: 0.9999
|
| 82 |
+
|
| 83 |
+
dataloader:
|
| 84 |
+
batch_size: 128
|
| 85 |
+
num_workers: 1
|
| 86 |
+
shuffle: True
|
| 87 |
+
pin_memory: True
|
| 88 |
+
persistent_workers: False
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
optimizer:
|
| 92 |
+
learning_rate: 1.0e-4 # 1.0e-4
|
| 93 |
+
weight_decay: 1.0e-3
|
| 94 |
+
betas: [0.9, 0.95]
|
| 95 |
+
|
| 96 |
+
training:
|
| 97 |
+
device: "cuda:0"
|
| 98 |
+
seed: 42
|
| 99 |
+
debug: False
|
| 100 |
+
resume: True
|
| 101 |
+
# optimization
|
| 102 |
+
lr_scheduler: cosine
|
| 103 |
+
# Transformer needs LR warmup
|
| 104 |
+
lr_warmup_steps: 0
|
| 105 |
+
num_epochs: 500
|
| 106 |
+
gradient_accumulate_every: 1
|
| 107 |
+
use_ema: True
|
| 108 |
+
# training loop control
|
| 109 |
+
# in epochs
|
| 110 |
+
rollout_every: 25
|
| 111 |
+
checkpoint_every: 25
|
| 112 |
+
val_every: 1
|
| 113 |
+
sample_every: 5
|
| 114 |
+
# steps per epoch
|
| 115 |
+
max_train_steps: null
|
| 116 |
+
max_val_steps: null
|
| 117 |
+
# misc
|
| 118 |
+
tqdm_interval_sec: 1.0
|
| 119 |
+
|
| 120 |
+
cpl_loss_type: 'cplkl' # 'cpl'
|
| 121 |
+
use_expert_data_1: False
|
| 122 |
+
use_expert_data_2: False
|
| 123 |
+
n_epoch_sft: 0
|
| 124 |
+
sft_type: "pos"
|
| 125 |
+
stride_ratio: 0.5
|
| 126 |
+
pseudo_preference: False
|
| 127 |
+
|
| 128 |
+
dataset_1_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5.h5'
|
| 129 |
+
dataset_2_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5_2.h5'
|
| 130 |
+
preference_learning:
|
| 131 |
+
num_rounds: 1
|
| 132 |
+
equal_threshold: 0.05
|
| 133 |
+
|
| 134 |
+
logging:
|
| 135 |
+
project: IL-DP-2026
|
| 136 |
+
resume: False
|
| 137 |
+
mode: online
|
| 138 |
+
name: ${name}_${training.seed}
|
| 139 |
+
tags: ["${task_name}"]
|
| 140 |
+
id: null
|
| 141 |
+
group: ${name}
|
| 142 |
+
|
| 143 |
+
checkpoint:
|
| 144 |
+
topk:
|
| 145 |
+
monitor_key: test_mean_score
|
| 146 |
+
mode: max
|
| 147 |
+
k: 3
|
| 148 |
+
format_str: 'epoch={epoch:04d}-test_mean_score={test_mean_score:.3f}.ckpt'
|
| 149 |
+
save_last_ckpt: True
|
| 150 |
+
save_last_snapshot: False
|
| 151 |
+
|
| 152 |
+
multi_run:
|
| 153 |
+
run_dir: data/outputs/${now:%Y.%m.%d}/${now:%H.%M.%S}_${name}_${task_name}
|
| 154 |
+
wandb_name_base: ${name}_${task_name}
|
| 155 |
+
|
| 156 |
+
hydra:
|
| 157 |
+
job:
|
| 158 |
+
override_dirname: ${name}
|
| 159 |
+
run:
|
| 160 |
+
dir: data/outputs/${now:%Y.%m.%d}/${now:%H.%M.%S}_${name}_${task_name}
|
| 161 |
+
sweep:
|
| 162 |
+
dir: data/outputs/${now:%Y.%m.%d}/${now:%H.%M.%S}_${name}_${task_name}
|
| 163 |
+
subdir: ${hydra.job.num}
|
config/pbrl_reflow_transformer/train_pbrl_reflow_policy_transformer_lowdim_can_mh_abs.yaml
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
defaults:
|
| 2 |
+
- _self_
|
| 3 |
+
- task: can_lowdim_abs_pbrl
|
| 4 |
+
|
| 5 |
+
name: train_pbrl_reflow_transformer
|
| 6 |
+
|
| 7 |
+
_target_: diffusion_policy.workspace.train_pbrl_reflow_transformer_lowdim_offline_workspace.PbrlReFlowTransformerLowdimWorkspace
|
| 8 |
+
checkpoint_dir: logs/pretrain/can_lowdim/reflow_transformer_ERate.98/reflow_transformer_ERate.98_42/checkpoints/epoch=0100-test_mean_score=0.320.ckpt
|
| 9 |
+
|
| 10 |
+
obs_dim: ${task.obs_dim}
|
| 11 |
+
action_dim: ${task.action_dim}
|
| 12 |
+
task_name: ${task.name}
|
| 13 |
+
|
| 14 |
+
gamma: 0.999 # discount factor in MDP
|
| 15 |
+
horizon: 10 # this horizon is for dataloader
|
| 16 |
+
n_obs_steps: 2
|
| 17 |
+
n_action_steps: 8
|
| 18 |
+
n_latency_steps: 0
|
| 19 |
+
past_action_visible: False
|
| 20 |
+
|
| 21 |
+
# Used during evaluation (both during training and evaluation)
|
| 22 |
+
test_clip_intermediate_actions: True
|
| 23 |
+
test_denoising_steps: 8
|
| 24 |
+
test_model_type: 'original' # can be "ema", "original"
|
| 25 |
+
|
| 26 |
+
policy:
|
| 27 |
+
_target_: diffusion_policy.policy.cpl_reflow_transformer_lowdim_policy.CplReFlowTransformerLowdimPolicy
|
| 28 |
+
|
| 29 |
+
model:
|
| 30 |
+
_target_: diffusion_policy.model.diffusion.transformer_for_diffusion.TransformerForDiffusion
|
| 31 |
+
input_dim: ${action_dim}
|
| 32 |
+
output_dim: ${policy.model.input_dim}
|
| 33 |
+
horizon: ${eval:'${horizon}-${n_obs_steps}+1'}
|
| 34 |
+
n_obs_steps: ${n_obs_steps}
|
| 35 |
+
cond_dim: ${obs_dim}
|
| 36 |
+
|
| 37 |
+
n_layer: 8
|
| 38 |
+
n_head: 4
|
| 39 |
+
n_emb: 256
|
| 40 |
+
p_drop_emb: 0.0
|
| 41 |
+
p_drop_attn: 0.3
|
| 42 |
+
|
| 43 |
+
causal_attn: True
|
| 44 |
+
time_as_cond: True # if false, use BERT like encoder only arch, time as input
|
| 45 |
+
obs_as_cond: True
|
| 46 |
+
n_cond_layers: 0 # >0: use transformer encoder for cond, otherwise use MLP
|
| 47 |
+
|
| 48 |
+
horizon: ${eval:'${horizon}-${n_obs_steps}+1'}
|
| 49 |
+
obs_dim: ${obs_dim}
|
| 50 |
+
action_dim: ${action_dim}
|
| 51 |
+
n_action_steps: ${eval:'${n_action_steps}+${n_latency_steps}'}
|
| 52 |
+
n_obs_steps: ${n_obs_steps}
|
| 53 |
+
sample_t_type: uniform # beta, logitnormal
|
| 54 |
+
|
| 55 |
+
beta: 0.1
|
| 56 |
+
bias_reg: 0.25
|
| 57 |
+
ignore_equal_pref: False
|
| 58 |
+
clip_margin: null # null = disabled; recommended 10.0 for beta=0.0003125, 0.3 for beta=0.01
|
| 59 |
+
smooth_label: 0.0
|
| 60 |
+
confidence_weight: False
|
| 61 |
+
cw_temperature: 0.03
|
| 62 |
+
unclip_win: False
|
| 63 |
+
|
| 64 |
+
ema:
|
| 65 |
+
decay: 0.995
|
| 66 |
+
|
| 67 |
+
dataloader:
|
| 68 |
+
batch_size: 128
|
| 69 |
+
num_workers: 1
|
| 70 |
+
shuffle: True
|
| 71 |
+
pin_memory: True
|
| 72 |
+
persistent_workers: False
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
optimizer:
|
| 76 |
+
lr: 1e-4 # 1.0e-4
|
| 77 |
+
weight_decay: 1e-6
|
| 78 |
+
|
| 79 |
+
training:
|
| 80 |
+
device: "cuda:0"
|
| 81 |
+
seed: 42
|
| 82 |
+
debug: False
|
| 83 |
+
resume: True
|
| 84 |
+
# optimization
|
| 85 |
+
lr_scheduler: cosine
|
| 86 |
+
# Transformer needs LR warmup
|
| 87 |
+
lr_warmup_steps: 0
|
| 88 |
+
epoch_start_ema: 0
|
| 89 |
+
update_ema_freq: 10
|
| 90 |
+
num_epochs: 1000
|
| 91 |
+
use_ema: True
|
| 92 |
+
# training loop control
|
| 93 |
+
# in epochs
|
| 94 |
+
rollout_every: 25
|
| 95 |
+
checkpoint_every: 25
|
| 96 |
+
val_every: 1
|
| 97 |
+
max_train_steps: null
|
| 98 |
+
max_val_steps: null
|
| 99 |
+
# misc
|
| 100 |
+
tqdm_interval_sec: 1.0
|
| 101 |
+
|
| 102 |
+
cpl_loss_type: 'cplkl' # 'cpl'
|
| 103 |
+
use_expert_data_1: False
|
| 104 |
+
use_expert_data_2: False
|
| 105 |
+
n_epoch_sft: 0
|
| 106 |
+
sft_type: "pos"
|
| 107 |
+
stride_ratio: 0.5
|
| 108 |
+
pseudo_preference: False
|
| 109 |
+
|
| 110 |
+
dataset_1_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5.h5'
|
| 111 |
+
dataset_2_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5_2.h5'
|
| 112 |
+
preference_learning:
|
| 113 |
+
num_rounds: 1
|
| 114 |
+
equal_threshold: 0.05
|
| 115 |
+
|
| 116 |
+
logging:
|
| 117 |
+
project: IL-DP-2026
|
| 118 |
+
resume: False
|
| 119 |
+
mode: online
|
| 120 |
+
name: ${name}_${training.seed}
|
| 121 |
+
tags: ["${task_name}"]
|
| 122 |
+
id: null
|
| 123 |
+
group: ${name}
|
| 124 |
+
|
| 125 |
+
checkpoint:
|
| 126 |
+
topk:
|
| 127 |
+
monitor_key: test_mean_score
|
| 128 |
+
mode: max
|
| 129 |
+
k: 3
|
| 130 |
+
format_str: 'epoch={epoch:04d}-test_mean_score={test_mean_score:.3f}.ckpt'
|
| 131 |
+
save_last_ckpt: True
|
| 132 |
+
save_last_snapshot: False
|
| 133 |
+
|
| 134 |
+
multi_run:
|
| 135 |
+
run_dir: logs/reproduce/${task_name}/${logging.group}/${logging.name}
|
| 136 |
+
wandb_name_base: ${name}_${training.seed}
|
| 137 |
+
|
| 138 |
+
hydra:
|
| 139 |
+
job:
|
| 140 |
+
override_dirname: ${name}
|
| 141 |
+
run:
|
| 142 |
+
dir: logs/reproduce/${task_name}/${logging.group}/${logging.name}
|
| 143 |
+
sweep:
|
| 144 |
+
dir: logs/reproduce/${task_name}/${logging.group}/${logging.name}
|
| 145 |
+
subdir: ${hydra.job.num}
|
config/pbrl_reflow_transformer/train_pbrl_reflow_policy_transformer_lowdim_lift_mh_abs.yaml
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
defaults:
|
| 2 |
+
- _self_
|
| 3 |
+
- task: lift_lowdim_abs_pbrl
|
| 4 |
+
|
| 5 |
+
name: train_pbrl_reflow_transformer
|
| 6 |
+
|
| 7 |
+
_target_: diffusion_policy.workspace.train_pbrl_reflow_transformer_lowdim_offline_workspace.PbrlReFlowTransformerLowdimWorkspace
|
| 8 |
+
checkpoint_dir: logs/pretrain/lift_lowdim/reflow_transformer_ERate.98/reflow_transformer_ERate.98_42/checkpoints/epoch=0005-test_mean_score=0.400.ckpt
|
| 9 |
+
|
| 10 |
+
obs_dim: ${task.obs_dim}
|
| 11 |
+
action_dim: ${task.action_dim}
|
| 12 |
+
task_name: ${task.name}
|
| 13 |
+
|
| 14 |
+
gamma: 0.999 # discount factor in MDP
|
| 15 |
+
horizon: 10 # this horizon is for dataloader
|
| 16 |
+
n_obs_steps: 2
|
| 17 |
+
n_action_steps: 8
|
| 18 |
+
n_latency_steps: 0
|
| 19 |
+
past_action_visible: False
|
| 20 |
+
|
| 21 |
+
# Used during evaluation (both during training and evaluation)
|
| 22 |
+
test_clip_intermediate_actions: True
|
| 23 |
+
test_denoising_steps: 8
|
| 24 |
+
test_model_type: 'original' # can be "ema", "original"
|
| 25 |
+
|
| 26 |
+
policy:
|
| 27 |
+
_target_: diffusion_policy.policy.cpl_reflow_transformer_lowdim_policy.CplReFlowTransformerLowdimPolicy
|
| 28 |
+
|
| 29 |
+
model:
|
| 30 |
+
_target_: diffusion_policy.model.diffusion.transformer_for_diffusion.TransformerForDiffusion
|
| 31 |
+
input_dim: ${action_dim}
|
| 32 |
+
output_dim: ${policy.model.input_dim}
|
| 33 |
+
horizon: ${eval:'${horizon}-${n_obs_steps}+1'}
|
| 34 |
+
n_obs_steps: ${n_obs_steps}
|
| 35 |
+
cond_dim: ${obs_dim}
|
| 36 |
+
|
| 37 |
+
n_layer: 8
|
| 38 |
+
n_head: 4
|
| 39 |
+
n_emb: 256
|
| 40 |
+
p_drop_emb: 0.0
|
| 41 |
+
p_drop_attn: 0.3
|
| 42 |
+
|
| 43 |
+
causal_attn: True
|
| 44 |
+
time_as_cond: True # if false, use BERT like encoder only arch, time as input
|
| 45 |
+
obs_as_cond: True
|
| 46 |
+
n_cond_layers: 0 # >0: use transformer encoder for cond, otherwise use MLP
|
| 47 |
+
|
| 48 |
+
horizon: ${eval:'${horizon}-${n_obs_steps}+1'}
|
| 49 |
+
obs_dim: ${obs_dim}
|
| 50 |
+
action_dim: ${action_dim}
|
| 51 |
+
n_action_steps: ${eval:'${n_action_steps}+${n_latency_steps}'}
|
| 52 |
+
n_obs_steps: ${n_obs_steps}
|
| 53 |
+
sample_t_type: uniform # beta, logitnormal
|
| 54 |
+
|
| 55 |
+
beta: 0.1
|
| 56 |
+
bias_reg: 0.25
|
| 57 |
+
ignore_equal_pref: False
|
| 58 |
+
clip_margin: null # null = disabled; recommended 10.0 for beta=0.0003125, 0.3 for beta=0.01
|
| 59 |
+
smooth_label: 0.0
|
| 60 |
+
confidence_weight: False
|
| 61 |
+
cw_temperature: 0.03
|
| 62 |
+
unclip_win: False
|
| 63 |
+
|
| 64 |
+
ema:
|
| 65 |
+
decay: 0.995
|
| 66 |
+
|
| 67 |
+
dataloader:
|
| 68 |
+
batch_size: 128
|
| 69 |
+
num_workers: 1
|
| 70 |
+
shuffle: True
|
| 71 |
+
pin_memory: True
|
| 72 |
+
persistent_workers: False
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
optimizer:
|
| 76 |
+
lr: 1e-4 # 1.0e-4
|
| 77 |
+
weight_decay: 1e-6
|
| 78 |
+
|
| 79 |
+
training:
|
| 80 |
+
device: "cuda:0"
|
| 81 |
+
seed: 42
|
| 82 |
+
debug: False
|
| 83 |
+
resume: True
|
| 84 |
+
# optimization
|
| 85 |
+
lr_scheduler: cosine
|
| 86 |
+
# Transformer needs LR warmup
|
| 87 |
+
lr_warmup_steps: 0
|
| 88 |
+
epoch_start_ema: 0
|
| 89 |
+
update_ema_freq: 10
|
| 90 |
+
num_epochs: 1000
|
| 91 |
+
use_ema: True
|
| 92 |
+
# training loop control
|
| 93 |
+
# in epochs
|
| 94 |
+
rollout_every: 25
|
| 95 |
+
checkpoint_every: 25
|
| 96 |
+
val_every: 1
|
| 97 |
+
max_train_steps: null
|
| 98 |
+
max_val_steps: null
|
| 99 |
+
# misc
|
| 100 |
+
tqdm_interval_sec: 1.0
|
| 101 |
+
|
| 102 |
+
cpl_loss_type: 'cplkl' # 'cpl'
|
| 103 |
+
use_expert_data_1: False
|
| 104 |
+
use_expert_data_2: False
|
| 105 |
+
n_epoch_sft: 0
|
| 106 |
+
sft_type: "pos"
|
| 107 |
+
stride_ratio: 0.5
|
| 108 |
+
pseudo_preference: False
|
| 109 |
+
|
| 110 |
+
dataset_1_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5.h5'
|
| 111 |
+
dataset_2_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5_2.h5'
|
| 112 |
+
preference_learning:
|
| 113 |
+
num_rounds: 1
|
| 114 |
+
equal_threshold: 0.05
|
| 115 |
+
|
| 116 |
+
logging:
|
| 117 |
+
project: IL-DP-2026
|
| 118 |
+
resume: False
|
| 119 |
+
mode: online
|
| 120 |
+
name: ${name}_${training.seed}
|
| 121 |
+
tags: ["${task_name}"]
|
| 122 |
+
id: null
|
| 123 |
+
group: ${name}
|
| 124 |
+
|
| 125 |
+
checkpoint:
|
| 126 |
+
topk:
|
| 127 |
+
monitor_key: test_mean_score
|
| 128 |
+
mode: max
|
| 129 |
+
k: 3
|
| 130 |
+
format_str: 'epoch={epoch:04d}-test_mean_score={test_mean_score:.3f}.ckpt'
|
| 131 |
+
save_last_ckpt: True
|
| 132 |
+
save_last_snapshot: False
|
| 133 |
+
|
| 134 |
+
multi_run:
|
| 135 |
+
run_dir: logs/reproduce/${task_name}/${logging.group}/${logging.name}
|
| 136 |
+
wandb_name_base: ${name}_${training.seed}
|
| 137 |
+
|
| 138 |
+
hydra:
|
| 139 |
+
job:
|
| 140 |
+
override_dirname: ${name}
|
| 141 |
+
run:
|
| 142 |
+
dir: logs/reproduce/${task_name}/${logging.group}/${logging.name}
|
| 143 |
+
sweep:
|
| 144 |
+
dir: logs/reproduce/${task_name}/${logging.group}/${logging.name}
|
| 145 |
+
subdir: ${hydra.job.num}
|
config/pbrl_reflow_transformer/train_pbrl_reflow_policy_transformer_lowdim_square_mh_abs.yaml
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
defaults:
|
| 2 |
+
- _self_
|
| 3 |
+
- task: square_lowdim_abs_pbrl
|
| 4 |
+
|
| 5 |
+
name: train_pbrl_reflow_transformer
|
| 6 |
+
|
| 7 |
+
_target_: diffusion_policy.workspace.train_pbrl_reflow_transformer_lowdim_offline_workspace.PbrlReFlowTransformerLowdimWorkspace
|
| 8 |
+
checkpoint_dir: logs/pretrain/square_lowdim/reflow_transformer_ERate.98/reflow_transformer_ERate.98_42/checkpoints/epoch=0400-test_mean_score=0.440.ckpt
|
| 9 |
+
|
| 10 |
+
obs_dim: ${task.obs_dim}
|
| 11 |
+
action_dim: ${task.action_dim}
|
| 12 |
+
task_name: ${task.name}
|
| 13 |
+
|
| 14 |
+
gamma: 0.999 # discount factor in MDP
|
| 15 |
+
horizon: 10 # this horizon is for dataloader
|
| 16 |
+
n_obs_steps: 2
|
| 17 |
+
n_action_steps: 8
|
| 18 |
+
n_latency_steps: 0
|
| 19 |
+
past_action_visible: False
|
| 20 |
+
|
| 21 |
+
# Used during evaluation (both during training and evaluation)
|
| 22 |
+
test_clip_intermediate_actions: True
|
| 23 |
+
test_denoising_steps: 8
|
| 24 |
+
test_model_type: 'original' # can be "ema", "original"
|
| 25 |
+
|
| 26 |
+
policy:
|
| 27 |
+
_target_: diffusion_policy.policy.cpl_reflow_transformer_lowdim_policy.CplReFlowTransformerLowdimPolicy
|
| 28 |
+
|
| 29 |
+
model:
|
| 30 |
+
_target_: diffusion_policy.model.diffusion.transformer_for_diffusion.TransformerForDiffusion
|
| 31 |
+
input_dim: ${action_dim}
|
| 32 |
+
output_dim: ${policy.model.input_dim}
|
| 33 |
+
horizon: ${eval:'${horizon}-${n_obs_steps}+1'}
|
| 34 |
+
n_obs_steps: ${n_obs_steps}
|
| 35 |
+
cond_dim: ${obs_dim}
|
| 36 |
+
|
| 37 |
+
n_layer: 8
|
| 38 |
+
n_head: 4
|
| 39 |
+
n_emb: 256
|
| 40 |
+
p_drop_emb: 0.0
|
| 41 |
+
p_drop_attn: 0.3
|
| 42 |
+
|
| 43 |
+
causal_attn: True
|
| 44 |
+
time_as_cond: True # if false, use BERT like encoder only arch, time as input
|
| 45 |
+
obs_as_cond: True
|
| 46 |
+
n_cond_layers: 0 # >0: use transformer encoder for cond, otherwise use MLP
|
| 47 |
+
|
| 48 |
+
horizon: ${eval:'${horizon}-${n_obs_steps}+1'}
|
| 49 |
+
obs_dim: ${obs_dim}
|
| 50 |
+
action_dim: ${action_dim}
|
| 51 |
+
n_action_steps: ${eval:'${n_action_steps}+${n_latency_steps}'}
|
| 52 |
+
n_obs_steps: ${n_obs_steps}
|
| 53 |
+
sample_t_type: uniform # beta, logitnormal
|
| 54 |
+
|
| 55 |
+
beta: 0.1
|
| 56 |
+
bias_reg: 0.25
|
| 57 |
+
ignore_equal_pref: False
|
| 58 |
+
clip_margin: null # null = disabled; recommended 10.0 for beta=0.0003125, 0.3 for beta=0.01
|
| 59 |
+
smooth_label: 0.0
|
| 60 |
+
confidence_weight: False
|
| 61 |
+
cw_temperature: 0.03
|
| 62 |
+
unclip_win: False
|
| 63 |
+
|
| 64 |
+
ema:
|
| 65 |
+
decay: 0.995
|
| 66 |
+
|
| 67 |
+
dataloader:
|
| 68 |
+
batch_size: 128
|
| 69 |
+
num_workers: 1
|
| 70 |
+
shuffle: True
|
| 71 |
+
pin_memory: True
|
| 72 |
+
persistent_workers: False
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
optimizer:
|
| 76 |
+
lr: 1e-4 # 1.0e-4
|
| 77 |
+
weight_decay: 1e-6
|
| 78 |
+
|
| 79 |
+
training:
|
| 80 |
+
device: "cuda:0"
|
| 81 |
+
seed: 42
|
| 82 |
+
debug: False
|
| 83 |
+
resume: True
|
| 84 |
+
# optimization
|
| 85 |
+
lr_scheduler: cosine
|
| 86 |
+
# Transformer needs LR warmup
|
| 87 |
+
lr_warmup_steps: 0
|
| 88 |
+
epoch_start_ema: 0
|
| 89 |
+
update_ema_freq: 10
|
| 90 |
+
num_epochs: 1000
|
| 91 |
+
use_ema: True
|
| 92 |
+
# training loop control
|
| 93 |
+
# in epochs
|
| 94 |
+
rollout_every: 25
|
| 95 |
+
checkpoint_every: 25
|
| 96 |
+
val_every: 1
|
| 97 |
+
max_train_steps: null
|
| 98 |
+
max_val_steps: null
|
| 99 |
+
# misc
|
| 100 |
+
tqdm_interval_sec: 1.0
|
| 101 |
+
|
| 102 |
+
cpl_loss_type: 'cplkl' # 'cpl'
|
| 103 |
+
use_expert_data_1: False
|
| 104 |
+
use_expert_data_2: False
|
| 105 |
+
n_epoch_sft: 0
|
| 106 |
+
sft_type: "pos"
|
| 107 |
+
stride_ratio: 0.5
|
| 108 |
+
pseudo_preference: False
|
| 109 |
+
|
| 110 |
+
dataset_1_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5.h5'
|
| 111 |
+
dataset_2_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5_2.h5'
|
| 112 |
+
preference_learning:
|
| 113 |
+
num_rounds: 1
|
| 114 |
+
equal_threshold: 0.05
|
| 115 |
+
|
| 116 |
+
logging:
|
| 117 |
+
project: IL-DP-2026
|
| 118 |
+
resume: False
|
| 119 |
+
mode: online
|
| 120 |
+
name: ${name}_${training.seed}
|
| 121 |
+
tags: ["${task_name}"]
|
| 122 |
+
id: null
|
| 123 |
+
group: ${name}
|
| 124 |
+
|
| 125 |
+
checkpoint:
|
| 126 |
+
topk:
|
| 127 |
+
monitor_key: test_mean_score
|
| 128 |
+
mode: max
|
| 129 |
+
k: 3
|
| 130 |
+
format_str: 'epoch={epoch:04d}-test_mean_score={test_mean_score:.3f}.ckpt'
|
| 131 |
+
save_last_ckpt: True
|
| 132 |
+
save_last_snapshot: False
|
| 133 |
+
|
| 134 |
+
multi_run:
|
| 135 |
+
run_dir: logs/reproduce/${task_name}/${logging.group}/${logging.name}
|
| 136 |
+
wandb_name_base: ${name}_${training.seed}
|
| 137 |
+
|
| 138 |
+
hydra:
|
| 139 |
+
job:
|
| 140 |
+
override_dirname: ${name}
|
| 141 |
+
run:
|
| 142 |
+
dir: logs/reproduce/${task_name}/${logging.group}/${logging.name}
|
| 143 |
+
sweep:
|
| 144 |
+
dir: logs/reproduce/${task_name}/${logging.group}/${logging.name}
|
| 145 |
+
subdir: ${hydra.job.num}
|
config/pbrl_reflow_transformer/train_pbrl_reflow_policy_transformer_lowdim_tool_hang_ph_abs.yaml
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
defaults:
|
| 2 |
+
- _self_
|
| 3 |
+
- task: tool_hang_lowdim_abs_pbrl
|
| 4 |
+
|
| 5 |
+
name: train_pbrl_reflow_transformer
|
| 6 |
+
|
| 7 |
+
_target_: diffusion_policy.workspace.train_pbrl_reflow_transformer_lowdim_offline_workspace.PbrlReFlowTransformerLowdimWorkspace
|
| 8 |
+
checkpoint_dir: logs/pretrain/tool_hang_lowdim/reflow_transformer_ERate.98/reflow_transformer_ERate.98_42/checkpoints/epoch=0600-test_mean_score=0.360.ckpt
|
| 9 |
+
|
| 10 |
+
obs_dim: ${task.obs_dim}
|
| 11 |
+
action_dim: ${task.action_dim}
|
| 12 |
+
task_name: ${task.name}
|
| 13 |
+
|
| 14 |
+
gamma: 0.999 # discount factor in MDP
|
| 15 |
+
horizon: 16 # this horizon is for dataloader
|
| 16 |
+
n_obs_steps: 2
|
| 17 |
+
n_action_steps: 8
|
| 18 |
+
n_latency_steps: 0
|
| 19 |
+
past_action_visible: False
|
| 20 |
+
|
| 21 |
+
# Used during evaluation (both during training and evaluation)
|
| 22 |
+
test_clip_intermediate_actions: True
|
| 23 |
+
test_denoising_steps: 8
|
| 24 |
+
test_model_type: 'original' # can be "ema", "original"
|
| 25 |
+
|
| 26 |
+
policy:
|
| 27 |
+
_target_: diffusion_policy.policy.cpl_reflow_transformer_lowdim_policy.CplReFlowTransformerLowdimPolicy
|
| 28 |
+
|
| 29 |
+
model:
|
| 30 |
+
_target_: diffusion_policy.model.diffusion.transformer_for_diffusion.TransformerForDiffusion
|
| 31 |
+
input_dim: ${action_dim}
|
| 32 |
+
output_dim: ${policy.model.input_dim}
|
| 33 |
+
horizon: ${eval:'${horizon}-${n_obs_steps}+1'}
|
| 34 |
+
n_obs_steps: ${n_obs_steps}
|
| 35 |
+
cond_dim: ${obs_dim}
|
| 36 |
+
|
| 37 |
+
n_layer: 8
|
| 38 |
+
n_head: 4
|
| 39 |
+
n_emb: 256
|
| 40 |
+
p_drop_emb: 0.0
|
| 41 |
+
p_drop_attn: 0.3
|
| 42 |
+
|
| 43 |
+
causal_attn: True
|
| 44 |
+
time_as_cond: True # if false, use BERT like encoder only arch, time as input
|
| 45 |
+
obs_as_cond: True
|
| 46 |
+
n_cond_layers: 0 # >0: use transformer encoder for cond, otherwise use MLP
|
| 47 |
+
|
| 48 |
+
horizon: ${eval:'${horizon}-${n_obs_steps}+1'}
|
| 49 |
+
obs_dim: ${obs_dim}
|
| 50 |
+
action_dim: ${action_dim}
|
| 51 |
+
n_action_steps: ${eval:'${n_action_steps}+${n_latency_steps}'}
|
| 52 |
+
n_obs_steps: ${n_obs_steps}
|
| 53 |
+
sample_t_type: uniform # beta, logitnormal
|
| 54 |
+
|
| 55 |
+
beta: 0.1
|
| 56 |
+
bias_reg: 0.25
|
| 57 |
+
ignore_equal_pref: False
|
| 58 |
+
clip_margin: null # null = disabled; recommended 10.0 for beta=0.0003125, 0.3 for beta=0.01
|
| 59 |
+
smooth_label: 0.0
|
| 60 |
+
confidence_weight: False
|
| 61 |
+
cw_temperature: 0.03
|
| 62 |
+
unclip_win: False
|
| 63 |
+
|
| 64 |
+
ema:
|
| 65 |
+
decay: 0.995
|
| 66 |
+
|
| 67 |
+
dataloader:
|
| 68 |
+
batch_size: 128
|
| 69 |
+
num_workers: 1
|
| 70 |
+
shuffle: True
|
| 71 |
+
pin_memory: True
|
| 72 |
+
persistent_workers: False
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
optimizer:
|
| 76 |
+
lr: 1e-4 # 1.0e-4
|
| 77 |
+
weight_decay: 1e-6
|
| 78 |
+
|
| 79 |
+
training:
|
| 80 |
+
device: "cuda:0"
|
| 81 |
+
seed: 42
|
| 82 |
+
debug: False
|
| 83 |
+
resume: True
|
| 84 |
+
# optimization
|
| 85 |
+
lr_scheduler: cosine
|
| 86 |
+
# Transformer needs LR warmup
|
| 87 |
+
lr_warmup_steps: 0
|
| 88 |
+
epoch_start_ema: 0
|
| 89 |
+
update_ema_freq: 10
|
| 90 |
+
num_epochs: 1000
|
| 91 |
+
use_ema: True
|
| 92 |
+
# training loop control
|
| 93 |
+
# in epochs
|
| 94 |
+
rollout_every: 25
|
| 95 |
+
checkpoint_every: 25
|
| 96 |
+
val_every: 1
|
| 97 |
+
max_train_steps: null
|
| 98 |
+
max_val_steps: null
|
| 99 |
+
# misc
|
| 100 |
+
tqdm_interval_sec: 1.0
|
| 101 |
+
|
| 102 |
+
cpl_loss_type: 'cplkl' # 'cpl'
|
| 103 |
+
use_expert_data_1: False
|
| 104 |
+
use_expert_data_2: False
|
| 105 |
+
n_epoch_sft: 0
|
| 106 |
+
sft_type: "pos"
|
| 107 |
+
stride_ratio: 0.5
|
| 108 |
+
pseudo_preference: False
|
| 109 |
+
|
| 110 |
+
dataset_1_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5.h5'
|
| 111 |
+
dataset_2_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5_2.h5'
|
| 112 |
+
preference_learning:
|
| 113 |
+
num_rounds: 1
|
| 114 |
+
equal_threshold: 0.05
|
| 115 |
+
|
| 116 |
+
logging:
|
| 117 |
+
project: IL-DP-2026
|
| 118 |
+
resume: False
|
| 119 |
+
mode: online
|
| 120 |
+
name: ${name}_${training.seed}
|
| 121 |
+
tags: ["${task_name}"]
|
| 122 |
+
id: null
|
| 123 |
+
group: ${name}
|
| 124 |
+
|
| 125 |
+
checkpoint:
|
| 126 |
+
topk:
|
| 127 |
+
monitor_key: test_mean_score
|
| 128 |
+
mode: max
|
| 129 |
+
k: 3
|
| 130 |
+
format_str: 'epoch={epoch:04d}-test_mean_score={test_mean_score:.3f}.ckpt'
|
| 131 |
+
save_last_ckpt: True
|
| 132 |
+
save_last_snapshot: False
|
| 133 |
+
|
| 134 |
+
multi_run:
|
| 135 |
+
run_dir: logs/reproduce/${task_name}/${logging.group}/${logging.name}
|
| 136 |
+
wandb_name_base: ${name}_${training.seed}
|
| 137 |
+
|
| 138 |
+
hydra:
|
| 139 |
+
job:
|
| 140 |
+
override_dirname: ${name}
|
| 141 |
+
run:
|
| 142 |
+
dir: logs/reproduce/${task_name}/${logging.group}/${logging.name}
|
| 143 |
+
sweep:
|
| 144 |
+
dir: logs/reproduce/${task_name}/${logging.group}/${logging.name}
|
| 145 |
+
subdir: ${hydra.job.num}
|
config/pbrl_reflow_transformer/train_pbrl_reflow_policy_transformer_lowdim_transport_mh_abs.yaml
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
defaults:
|
| 2 |
+
- _self_
|
| 3 |
+
- task: transport_lowdim_abs_pbrl
|
| 4 |
+
|
| 5 |
+
name: train_pbrl_reflow_transformer
|
| 6 |
+
|
| 7 |
+
_target_: diffusion_policy.workspace.train_pbrl_reflow_transformer_lowdim_offline_workspace.PbrlReFlowTransformerLowdimWorkspace
|
| 8 |
+
checkpoint_dir: logs/pretrain/transport_lowdim/reflow_transformer_ERate.98/reflow_transformer_ERate.98_42/checkpoints/epoch=0300-test_mean_score=0.400.ckpt
|
| 9 |
+
|
| 10 |
+
obs_dim: ${task.obs_dim}
|
| 11 |
+
action_dim: ${task.action_dim}
|
| 12 |
+
task_name: ${task.name}
|
| 13 |
+
|
| 14 |
+
gamma: 0.999 # discount factor in MDP
|
| 15 |
+
horizon: 10 # this horizon is for dataloader
|
| 16 |
+
n_obs_steps: 2
|
| 17 |
+
n_action_steps: 8
|
| 18 |
+
n_latency_steps: 0
|
| 19 |
+
past_action_visible: False
|
| 20 |
+
|
| 21 |
+
# Used during evaluation (both during training and evaluation)
|
| 22 |
+
test_clip_intermediate_actions: True
|
| 23 |
+
test_denoising_steps: 8
|
| 24 |
+
test_model_type: 'original' # can be "ema", "original"
|
| 25 |
+
|
| 26 |
+
policy:
|
| 27 |
+
_target_: diffusion_policy.policy.cpl_reflow_transformer_lowdim_policy.CplReFlowTransformerLowdimPolicy
|
| 28 |
+
|
| 29 |
+
model:
|
| 30 |
+
_target_: diffusion_policy.model.diffusion.transformer_for_diffusion.TransformerForDiffusion
|
| 31 |
+
input_dim: ${action_dim}
|
| 32 |
+
output_dim: ${policy.model.input_dim}
|
| 33 |
+
horizon: ${eval:'${horizon}-${n_obs_steps}+1'}
|
| 34 |
+
n_obs_steps: ${n_obs_steps}
|
| 35 |
+
cond_dim: ${obs_dim}
|
| 36 |
+
|
| 37 |
+
n_layer: 8
|
| 38 |
+
n_head: 4
|
| 39 |
+
n_emb: 256
|
| 40 |
+
p_drop_emb: 0.0
|
| 41 |
+
p_drop_attn: 0.3
|
| 42 |
+
|
| 43 |
+
causal_attn: True
|
| 44 |
+
time_as_cond: True # if false, use BERT like encoder only arch, time as input
|
| 45 |
+
obs_as_cond: True
|
| 46 |
+
n_cond_layers: 0 # >0: use transformer encoder for cond, otherwise use MLP
|
| 47 |
+
|
| 48 |
+
horizon: ${eval:'${horizon}-${n_obs_steps}+1'}
|
| 49 |
+
obs_dim: ${obs_dim}
|
| 50 |
+
action_dim: ${action_dim}
|
| 51 |
+
n_action_steps: ${eval:'${n_action_steps}+${n_latency_steps}'}
|
| 52 |
+
n_obs_steps: ${n_obs_steps}
|
| 53 |
+
sample_t_type: uniform # beta, logitnormal
|
| 54 |
+
|
| 55 |
+
beta: 0.1
|
| 56 |
+
bias_reg: 0.25
|
| 57 |
+
ignore_equal_pref: False
|
| 58 |
+
clip_margin: null # null = disabled; recommended 10.0 for beta=0.0003125, 0.3 for beta=0.01
|
| 59 |
+
smooth_label: 0.0
|
| 60 |
+
confidence_weight: False
|
| 61 |
+
cw_temperature: 0.03
|
| 62 |
+
unclip_win: False
|
| 63 |
+
|
| 64 |
+
ema:
|
| 65 |
+
decay: 0.995
|
| 66 |
+
|
| 67 |
+
dataloader:
|
| 68 |
+
batch_size: 128
|
| 69 |
+
num_workers: 1
|
| 70 |
+
shuffle: True
|
| 71 |
+
pin_memory: True
|
| 72 |
+
persistent_workers: False
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
optimizer:
|
| 76 |
+
lr: 1e-4 # 1.0e-4
|
| 77 |
+
weight_decay: 1e-6
|
| 78 |
+
|
| 79 |
+
training:
|
| 80 |
+
device: "cuda:0"
|
| 81 |
+
seed: 42
|
| 82 |
+
debug: False
|
| 83 |
+
resume: True
|
| 84 |
+
# optimization
|
| 85 |
+
lr_scheduler: cosine
|
| 86 |
+
# Transformer needs LR warmup
|
| 87 |
+
lr_warmup_steps: 0
|
| 88 |
+
epoch_start_ema: 0
|
| 89 |
+
update_ema_freq: 10
|
| 90 |
+
num_epochs: 1000
|
| 91 |
+
use_ema: True
|
| 92 |
+
# training loop control
|
| 93 |
+
# in epochs
|
| 94 |
+
rollout_every: 25
|
| 95 |
+
checkpoint_every: 25
|
| 96 |
+
val_every: 1
|
| 97 |
+
max_train_steps: null
|
| 98 |
+
max_val_steps: null
|
| 99 |
+
# misc
|
| 100 |
+
tqdm_interval_sec: 1.0
|
| 101 |
+
|
| 102 |
+
cpl_loss_type: 'cplkl' # 'cpl'
|
| 103 |
+
use_expert_data_1: False
|
| 104 |
+
use_expert_data_2: False
|
| 105 |
+
n_epoch_sft: 0
|
| 106 |
+
sft_type: "pos"
|
| 107 |
+
stride_ratio: 0.5
|
| 108 |
+
pseudo_preference: False
|
| 109 |
+
|
| 110 |
+
dataset_1_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5.h5'
|
| 111 |
+
dataset_2_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5_2.h5'
|
| 112 |
+
preference_learning:
|
| 113 |
+
num_rounds: 1
|
| 114 |
+
equal_threshold: 0.05
|
| 115 |
+
|
| 116 |
+
logging:
|
| 117 |
+
project: IL-DP-2026
|
| 118 |
+
resume: False
|
| 119 |
+
mode: online
|
| 120 |
+
name: ${name}_${training.seed}
|
| 121 |
+
tags: ["${task_name}"]
|
| 122 |
+
id: null
|
| 123 |
+
group: ${name}
|
| 124 |
+
|
| 125 |
+
checkpoint:
|
| 126 |
+
topk:
|
| 127 |
+
monitor_key: test_mean_score
|
| 128 |
+
mode: max
|
| 129 |
+
k: 3
|
| 130 |
+
format_str: 'epoch={epoch:04d}-test_mean_score={test_mean_score:.3f}.ckpt'
|
| 131 |
+
save_last_ckpt: True
|
| 132 |
+
save_last_snapshot: False
|
| 133 |
+
|
| 134 |
+
multi_run:
|
| 135 |
+
run_dir: logs/reproduce/${task_name}/${logging.group}/${logging.name}
|
| 136 |
+
wandb_name_base: ${name}_${training.seed}
|
| 137 |
+
|
| 138 |
+
hydra:
|
| 139 |
+
job:
|
| 140 |
+
override_dirname: ${name}
|
| 141 |
+
run:
|
| 142 |
+
dir: logs/reproduce/${task_name}/${logging.group}/${logging.name}
|
| 143 |
+
sweep:
|
| 144 |
+
dir: logs/reproduce/${task_name}/${logging.group}/${logging.name}
|
| 145 |
+
subdir: ${hydra.job.num}
|
config/task/aloha.yaml
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: aloha
|
| 2 |
+
|
| 3 |
+
shape_meta: &shape_meta
|
| 4 |
+
# acceptable types: rgb, low_dim
|
| 5 |
+
obs:
|
| 6 |
+
high_images:
|
| 7 |
+
shape: [3, 480, 640]
|
| 8 |
+
type: rgb
|
| 9 |
+
wrist_images:
|
| 10 |
+
shape: [3, 480, 640]
|
| 11 |
+
type: rgb
|
| 12 |
+
states:
|
| 13 |
+
shape: [13]
|
| 14 |
+
action:
|
| 15 |
+
shape: [13]
|
| 16 |
+
|
| 17 |
+
task_name: &task_name aloha
|
| 18 |
+
dataset_type: &dataset_type mh
|
| 19 |
+
dataset_path: &dataset_path data/aloha/placing_drawer_demo/placing_drawer_demo.hdf5
|
| 20 |
+
# abs_action: &abs_action True
|
| 21 |
+
|
| 22 |
+
env_runner:
|
| 23 |
+
_target_: diffusion_policy.env_runner.robomimic_image_runner.RobomimicImageRunner
|
| 24 |
+
dataset_path: *dataset_path
|
| 25 |
+
shape_meta: *shape_meta
|
| 26 |
+
n_train: 6
|
| 27 |
+
n_train_vis: 0
|
| 28 |
+
train_start_idx: 0
|
| 29 |
+
n_test: 50
|
| 30 |
+
n_test_vis: 0
|
| 31 |
+
test_start_seed: 100000
|
| 32 |
+
# use python's eval function as resolver, single-quoted string as argument
|
| 33 |
+
max_steps: ${eval:'500 if "${task.dataset_type}" == "mh" else 400'}
|
| 34 |
+
n_obs_steps: ${n_obs_steps}
|
| 35 |
+
n_action_steps: ${n_action_steps}
|
| 36 |
+
render_obs_key: 'agentview_image'
|
| 37 |
+
fps: 10
|
| 38 |
+
crf: 22
|
| 39 |
+
past_action: ${past_action_visible}
|
| 40 |
+
# abs_action: *abs_action
|
| 41 |
+
tqdm_interval_sec: 1.0
|
| 42 |
+
n_envs: 25
|
| 43 |
+
# evaluation at this config requires a 16 core 64GB instance.
|
| 44 |
+
|
| 45 |
+
dataset:
|
| 46 |
+
_target_: diffusion_policy.dataset.aloha_replay_image_dataset.AlohaReplayImageDataset
|
| 47 |
+
shape_meta: *shape_meta
|
| 48 |
+
dataset_path: *dataset_path
|
| 49 |
+
horizon: ${horizon}
|
| 50 |
+
pad_before: ${eval:'${n_obs_steps}-1+${n_latency_steps}'}
|
| 51 |
+
pad_after: ${eval:'${n_action_steps}-1'}
|
| 52 |
+
n_obs_steps: ${dataset_obs_steps}
|
| 53 |
+
# abs_action: *abs_action
|
| 54 |
+
# rotation_rep: 'rotation_6d'
|
| 55 |
+
# use_legacy_normalizer: False
|
| 56 |
+
use_cache: True
|
| 57 |
+
seed: 42
|
| 58 |
+
val_ratio: 0.02
|
config/task/blockpush_lowdim_seed.yaml
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: blockpush_lowdim_seed
|
| 2 |
+
|
| 3 |
+
obs_dim: 16
|
| 4 |
+
action_dim: 2
|
| 5 |
+
keypoint_dim: 2
|
| 6 |
+
obs_eef_target: True
|
| 7 |
+
|
| 8 |
+
env_runner:
|
| 9 |
+
_target_: diffusion_policy.env_runner.blockpush_lowdim_runner.BlockPushLowdimRunner
|
| 10 |
+
n_train: 6
|
| 11 |
+
n_train_vis: 0
|
| 12 |
+
train_start_seed: 0
|
| 13 |
+
n_test: 50
|
| 14 |
+
n_test_vis: 0
|
| 15 |
+
test_start_seed: 100000
|
| 16 |
+
max_steps: 350
|
| 17 |
+
n_obs_steps: ${n_obs_steps}
|
| 18 |
+
n_action_steps: ${n_action_steps}
|
| 19 |
+
fps: 5
|
| 20 |
+
past_action: ${past_action_visible}
|
| 21 |
+
abs_action: False
|
| 22 |
+
obs_eef_target: ${task.obs_eef_target}
|
| 23 |
+
n_envs: null
|
| 24 |
+
|
| 25 |
+
dataset:
|
| 26 |
+
_target_: diffusion_policy.dataset.blockpush_lowdim_dataset.BlockPushLowdimDataset
|
| 27 |
+
zarr_path: data/block_pushing/multimodal_push_seed.zarr
|
| 28 |
+
horizon: ${horizon}
|
| 29 |
+
pad_before: ${eval:'${n_obs_steps}-1'}
|
| 30 |
+
pad_after: ${eval:'${n_action_steps}-1'}
|
| 31 |
+
obs_eef_target: ${task.obs_eef_target}
|
| 32 |
+
use_manual_normalizer: False
|
| 33 |
+
seed: 42
|
| 34 |
+
val_ratio: 0.02
|
config/task/blockpush_lowdim_seed_abs.yaml
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: blockpush_lowdim_seed_abs
|
| 2 |
+
|
| 3 |
+
obs_dim: 16
|
| 4 |
+
action_dim: 2
|
| 5 |
+
keypoint_dim: 2
|
| 6 |
+
obs_eef_target: True
|
| 7 |
+
|
| 8 |
+
env_runner:
|
| 9 |
+
_target_: diffusion_policy.env_runner.blockpush_lowdim_runner.BlockPushLowdimRunner
|
| 10 |
+
n_train: 6
|
| 11 |
+
n_train_vis: 0
|
| 12 |
+
train_start_seed: 0
|
| 13 |
+
n_test: 50
|
| 14 |
+
n_test_vis: 0
|
| 15 |
+
test_start_seed: 100000
|
| 16 |
+
max_steps: 350
|
| 17 |
+
n_obs_steps: ${n_obs_steps}
|
| 18 |
+
n_action_steps: ${n_action_steps}
|
| 19 |
+
fps: 5
|
| 20 |
+
past_action: ${past_action_visible}
|
| 21 |
+
abs_action: True
|
| 22 |
+
obs_eef_target: ${task.obs_eef_target}
|
| 23 |
+
n_envs: null
|
| 24 |
+
|
| 25 |
+
dataset:
|
| 26 |
+
_target_: diffusion_policy.dataset.blockpush_lowdim_dataset.BlockPushLowdimDataset
|
| 27 |
+
zarr_path: data/block_pushing/multimodal_push_seed_abs.zarr
|
| 28 |
+
horizon: ${horizon}
|
| 29 |
+
pad_before: ${eval:'${n_obs_steps}-1'}
|
| 30 |
+
pad_after: ${eval:'${n_action_steps}-1'}
|
| 31 |
+
obs_eef_target: ${task.obs_eef_target}
|
| 32 |
+
use_manual_normalizer: False
|
| 33 |
+
seed: 42
|
| 34 |
+
val_ratio: 0.02
|
config/task/can_image.yaml
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: can_image
|
| 2 |
+
|
| 3 |
+
shape_meta: &shape_meta
|
| 4 |
+
# acceptable types: rgb, low_dim
|
| 5 |
+
obs:
|
| 6 |
+
agentview_image:
|
| 7 |
+
shape: [3, 84, 84]
|
| 8 |
+
type: rgb
|
| 9 |
+
robot0_eye_in_hand_image:
|
| 10 |
+
shape: [3, 84, 84]
|
| 11 |
+
type: rgb
|
| 12 |
+
robot0_eef_pos:
|
| 13 |
+
shape: [3]
|
| 14 |
+
# type default: low_dim
|
| 15 |
+
robot0_eef_quat:
|
| 16 |
+
shape: [4]
|
| 17 |
+
robot0_gripper_qpos:
|
| 18 |
+
shape: [2]
|
| 19 |
+
action:
|
| 20 |
+
shape: [7]
|
| 21 |
+
|
| 22 |
+
task_name: &task_name can
|
| 23 |
+
dataset_type: &dataset_type mh
|
| 24 |
+
dataset_path: &dataset_path data/robomimic/datasets/${task.task_name}/${task.dataset_type}/image.hdf5
|
| 25 |
+
abs_action: &abs_action False
|
| 26 |
+
|
| 27 |
+
env_runner:
|
| 28 |
+
_target_: diffusion_policy.env_runner.robomimic_image_runner.RobomimicImageRunner
|
| 29 |
+
dataset_path: *dataset_path
|
| 30 |
+
shape_meta: *shape_meta
|
| 31 |
+
# costs 1GB per env
|
| 32 |
+
n_train: 6
|
| 33 |
+
n_train_vis: 0
|
| 34 |
+
train_start_idx: 0
|
| 35 |
+
n_test: 50
|
| 36 |
+
n_test_vis: 0
|
| 37 |
+
test_start_seed: 100000
|
| 38 |
+
# use python's eval function as resolver, single-quoted string as argument
|
| 39 |
+
max_steps: ${eval:'500 if "${task.dataset_type}" == "mh" else 400'}
|
| 40 |
+
n_obs_steps: ${n_obs_steps}
|
| 41 |
+
n_action_steps: ${n_action_steps}
|
| 42 |
+
render_obs_key: 'agentview_image'
|
| 43 |
+
fps: 10
|
| 44 |
+
crf: 22
|
| 45 |
+
past_action: ${past_action_visible}
|
| 46 |
+
abs_action: *abs_action
|
| 47 |
+
tqdm_interval_sec: 1.0
|
| 48 |
+
n_envs: 25
|
| 49 |
+
# evaluation at this config requires a 16 core 64GB instance.
|
| 50 |
+
|
| 51 |
+
dataset:
|
| 52 |
+
_target_: diffusion_policy.dataset.robomimic_replay_image_dataset.RobomimicReplayImageDataset
|
| 53 |
+
shape_meta: *shape_meta
|
| 54 |
+
dataset_path: *dataset_path
|
| 55 |
+
horizon: ${horizon}
|
| 56 |
+
pad_before: ${eval:'${n_obs_steps}-1+${n_latency_steps}'}
|
| 57 |
+
pad_after: ${eval:'${n_action_steps}-1'}
|
| 58 |
+
n_obs_steps: ${dataset_obs_steps}
|
| 59 |
+
abs_action: *abs_action
|
| 60 |
+
rotation_rep: 'rotation_6d'
|
| 61 |
+
use_legacy_normalizer: False
|
| 62 |
+
use_cache: True
|
| 63 |
+
seed: 42
|
| 64 |
+
val_ratio: 0.02
|
config/task/can_image_abs.yaml
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: can_image
|
| 2 |
+
|
| 3 |
+
shape_meta: &shape_meta
|
| 4 |
+
# acceptable types: rgb, low_dim
|
| 5 |
+
obs:
|
| 6 |
+
agentview_image:
|
| 7 |
+
shape: [3, 84, 84]
|
| 8 |
+
type: rgb
|
| 9 |
+
robot0_eye_in_hand_image:
|
| 10 |
+
shape: [3, 84, 84]
|
| 11 |
+
type: rgb
|
| 12 |
+
robot0_eef_pos:
|
| 13 |
+
shape: [3]
|
| 14 |
+
# type default: low_dim
|
| 15 |
+
robot0_eef_quat:
|
| 16 |
+
shape: [4]
|
| 17 |
+
robot0_gripper_qpos:
|
| 18 |
+
shape: [2]
|
| 19 |
+
action:
|
| 20 |
+
shape: [10]
|
| 21 |
+
|
| 22 |
+
task_name: &task_name can
|
| 23 |
+
dataset_type: &dataset_type mh
|
| 24 |
+
dataset_path: &dataset_path data/robomimic/datasets/${task.task_name}/${task.dataset_type}/image_abs.hdf5
|
| 25 |
+
abs_action: &abs_action True
|
| 26 |
+
|
| 27 |
+
env_runner:
|
| 28 |
+
_target_: diffusion_policy.env_runner.robomimic_image_runner.RobomimicImageRunner
|
| 29 |
+
dataset_path: *dataset_path
|
| 30 |
+
shape_meta: *shape_meta
|
| 31 |
+
# costs 1GB per env
|
| 32 |
+
n_train: 6
|
| 33 |
+
n_train_vis: 0
|
| 34 |
+
train_start_idx: 0
|
| 35 |
+
n_test: 50
|
| 36 |
+
n_test_vis: 0
|
| 37 |
+
test_start_seed: 100000
|
| 38 |
+
# use python's eval function as resolver, single-quoted string as argument
|
| 39 |
+
max_steps: ${eval:'500 if "${task.dataset_type}" == "mh" else 400'}
|
| 40 |
+
n_obs_steps: ${n_obs_steps}
|
| 41 |
+
n_action_steps: ${n_action_steps}
|
| 42 |
+
render_obs_key: 'agentview_image'
|
| 43 |
+
fps: 10
|
| 44 |
+
crf: 22
|
| 45 |
+
past_action: ${past_action_visible}
|
| 46 |
+
abs_action: *abs_action
|
| 47 |
+
tqdm_interval_sec: 1.0
|
| 48 |
+
n_envs: 25
|
| 49 |
+
# evaluation at this config requires a 16 core 64GB instance.
|
| 50 |
+
|
| 51 |
+
dataset:
|
| 52 |
+
_target_: diffusion_policy.dataset.robomimic_replay_image_dataset.RobomimicReplayImageDataset
|
| 53 |
+
shape_meta: *shape_meta
|
| 54 |
+
dataset_path: *dataset_path
|
| 55 |
+
horizon: ${horizon}
|
| 56 |
+
pad_before: ${eval:'${n_obs_steps}-1+${n_latency_steps}'}
|
| 57 |
+
pad_after: ${eval:'${n_action_steps}-1'}
|
| 58 |
+
n_obs_steps: ${dataset_obs_steps}
|
| 59 |
+
abs_action: *abs_action
|
| 60 |
+
rotation_rep: 'rotation_6d'
|
| 61 |
+
use_legacy_normalizer: False
|
| 62 |
+
use_cache: True
|
| 63 |
+
seed: 42
|
| 64 |
+
val_ratio: 0.02
|