Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- aloha-devel/requirements.txt +23 -0
- aloha-devel/robomimic/__init__.py +159 -0
- aloha-devel/robomimic/__pycache__/__init__.cpython-38.pyc +0 -0
- aloha-devel/robomimic/algo/__pycache__/act.cpython-38.pyc +0 -0
- aloha-devel/robomimic/algo/__pycache__/gl.cpython-38.pyc +0 -0
- aloha-devel/robomimic/algo/__pycache__/iql.cpython-38.pyc +0 -0
- aloha-devel/robomimic/algo/__pycache__/iris.cpython-38.pyc +0 -0
- aloha-devel/robomimic/config/__init__.py +14 -0
- aloha-devel/robomimic/config/__pycache__/act_config.cpython-38.pyc +0 -0
- aloha-devel/robomimic/config/__pycache__/base_config.cpython-38.pyc +0 -0
- aloha-devel/robomimic/config/__pycache__/config.cpython-38.pyc +0 -0
- aloha-devel/robomimic/config/__pycache__/cql_config.cpython-38.pyc +0 -0
- aloha-devel/robomimic/config/__pycache__/td3_bc_config.cpython-38.pyc +0 -0
- aloha-devel/robomimic/config/bcq_config.py +83 -0
- aloha-devel/robomimic/config/config.py +322 -0
- aloha-devel/robomimic/config/hbc_config.py +96 -0
- aloha-devel/robomimic/config/iql_config.py +73 -0
- aloha-devel/robomimic/envs/__init__.py +0 -0
- aloha-devel/robomimic/envs/env_base.py +212 -0
- aloha-devel/robomimic/envs/env_gym.py +247 -0
- aloha-devel/robomimic/envs/env_ig_momart.py +395 -0
- aloha-devel/robomimic/envs/env_robosuite.py +415 -0
- aloha-devel/robomimic/envs/wrappers.py +222 -0
- aloha-devel/robomimic/exps/templates/act.json +160 -0
- aloha-devel/robomimic/exps/templates/bc.json +216 -0
- aloha-devel/robomimic/exps/templates/bc_transformer.json +172 -0
- aloha-devel/robomimic/exps/templates/cql.json +182 -0
- aloha-devel/robomimic/exps/templates/gl.json +182 -0
- aloha-devel/robomimic/exps/templates/hbc.json +293 -0
- aloha-devel/robomimic/exps/templates/iql.json +192 -0
- aloha-devel/robomimic/exps/templates/iris.json +465 -0
- aloha-devel/robomimic/exps/templates/td3_bc.json +167 -0
- aloha-devel/robomimic/macros.py +27 -0
- aloha-devel/robomimic/models/__pycache__/__init__.cpython-38.pyc +0 -0
- aloha-devel/robomimic/models/__pycache__/base_nets.cpython-38.pyc +0 -0
- aloha-devel/robomimic/models/__pycache__/distributions.cpython-38.pyc +0 -0
- aloha-devel/robomimic/models/__pycache__/policy_nets.cpython-38.pyc +0 -0
- aloha-devel/robomimic/models/__pycache__/transformers.cpython-38.pyc +0 -0
- aloha-devel/robomimic/models/__pycache__/value_nets.cpython-38.pyc +0 -0
- aloha-devel/robomimic/models/base_nets.py +1156 -0
- aloha-devel/robomimic/models/obs_nets.py +1121 -0
- aloha-devel/robomimic/models/value_nets.py +318 -0
- aloha-devel/robomimic/utils/__init__.py +0 -0
- aloha-devel/robomimic/utils/__pycache__/__init__.cpython-38.pyc +0 -0
- aloha-devel/robomimic/utils/__pycache__/tensor_utils.cpython-38.pyc +0 -0
- aloha-devel/robomimic/utils/action_utils.py +35 -0
- aloha-devel/robomimic/utils/dataset.py +1158 -0
- aloha-devel/robomimic/utils/hyperparam_utils.py +373 -0
- aloha-devel/robomimic/utils/lang_utils.py +27 -0
- aloha-devel/robomimic/utils/loss_utils.py +208 -0
aloha-devel/requirements.txt
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# python=3.9
|
| 2 |
+
# pip==23.0.1
|
| 3 |
+
# pytorch==2.0.0
|
| 4 |
+
# torchvision==0.15.0
|
| 5 |
+
# pytorch-cuda==11.8
|
| 6 |
+
pyquaternion==0.9.9
|
| 7 |
+
pyyaml==6.0
|
| 8 |
+
pexpect==4.8.0
|
| 9 |
+
mujoco==2.3.7
|
| 10 |
+
dm_control==1.0.14
|
| 11 |
+
matplotlib==3.7.5
|
| 12 |
+
einops==0.7.0
|
| 13 |
+
packaging==23.0
|
| 14 |
+
h5py==3.8.0
|
| 15 |
+
ipython==8.12.3
|
| 16 |
+
opencv-python==4.9.0.80
|
| 17 |
+
rospkg==1.5.0
|
| 18 |
+
empy==3.3.4
|
| 19 |
+
catkin-pkg
|
| 20 |
+
diffusers==0.26.3
|
| 21 |
+
termcolor==2.4.0
|
| 22 |
+
imageio==2.34.0
|
| 23 |
+
# cd act/detr && pip install -v -e .
|
aloha-devel/robomimic/__init__.py
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
__version__ = "0.3.0"
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
# stores released dataset links and rollout horizons in global dictionary.
|
| 5 |
+
# Structure is given below for each type of dataset:
|
| 6 |
+
|
| 7 |
+
# robosuite / real
|
| 8 |
+
# {
|
| 9 |
+
# task:
|
| 10 |
+
# dataset_type:
|
| 11 |
+
# hdf5_type:
|
| 12 |
+
# url: link
|
| 13 |
+
# horizon: value
|
| 14 |
+
# ...
|
| 15 |
+
# ...
|
| 16 |
+
# ...
|
| 17 |
+
# }
|
| 18 |
+
DATASET_REGISTRY = {}
|
| 19 |
+
|
| 20 |
+
# momart
|
| 21 |
+
# {
|
| 22 |
+
# task:
|
| 23 |
+
# dataset_type:
|
| 24 |
+
# url: link
|
| 25 |
+
# size: value
|
| 26 |
+
# ...
|
| 27 |
+
# ...
|
| 28 |
+
# }
|
| 29 |
+
MOMART_DATASET_REGISTRY = {}
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def register_dataset_link(task, dataset_type, hdf5_type, link, horizon):
|
| 33 |
+
"""
|
| 34 |
+
Helper function to register dataset link in global dictionary.
|
| 35 |
+
Also takes a @horizon parameter - this corresponds to the evaluation
|
| 36 |
+
rollout horizon that should be used during training.
|
| 37 |
+
|
| 38 |
+
Args:
|
| 39 |
+
task (str): name of task for this dataset
|
| 40 |
+
dataset_type (str): type of dataset (usually identifies the dataset source)
|
| 41 |
+
hdf5_type (str): type of hdf5 - usually one of "raw", "low_dim", or "image",
|
| 42 |
+
to identify the kind of observations in the dataset
|
| 43 |
+
link (str): download link for the dataset
|
| 44 |
+
horizon (int): evaluation rollout horizon that should be used with this dataset
|
| 45 |
+
"""
|
| 46 |
+
if task not in DATASET_REGISTRY:
|
| 47 |
+
DATASET_REGISTRY[task] = {}
|
| 48 |
+
if dataset_type not in DATASET_REGISTRY[task]:
|
| 49 |
+
DATASET_REGISTRY[task][dataset_type] = {}
|
| 50 |
+
DATASET_REGISTRY[task][dataset_type][hdf5_type] = dict(url=link, horizon=horizon)
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
def register_all_links():
|
| 54 |
+
"""
|
| 55 |
+
Record all dataset links in this function.
|
| 56 |
+
"""
|
| 57 |
+
|
| 58 |
+
# all proficient human datasets
|
| 59 |
+
ph_tasks = ["lift", "can", "square", "transport", "tool_hang", "lift_real", "can_real", "tool_hang_real"]
|
| 60 |
+
ph_horizons = [400, 400, 400, 700, 700, 1000, 1000, 1000]
|
| 61 |
+
for task, horizon in zip(ph_tasks, ph_horizons):
|
| 62 |
+
register_dataset_link(task=task, dataset_type="ph", hdf5_type="raw", horizon=horizon,
|
| 63 |
+
link="http://downloads.cs.stanford.edu/downloads/rt_benchmark/{}/ph/demo{}.hdf5".format(
|
| 64 |
+
task, "" if "real" in task else "_v141"
|
| 65 |
+
)
|
| 66 |
+
)
|
| 67 |
+
# real world datasets only have demo.hdf5 files which already contain all observation modalities
|
| 68 |
+
# while sim datasets store raw low-dim mujoco states in the demo.hdf5
|
| 69 |
+
if "real" not in task:
|
| 70 |
+
register_dataset_link(task=task, dataset_type="ph", hdf5_type="low_dim", horizon=horizon,
|
| 71 |
+
link="http://downloads.cs.stanford.edu/downloads/rt_benchmark/{}/ph/low_dim_v141.hdf5".format(task))
|
| 72 |
+
register_dataset_link(task=task, dataset_type="ph", hdf5_type="image", horizon=horizon,
|
| 73 |
+
link=None)
|
| 74 |
+
|
| 75 |
+
# all multi human datasets
|
| 76 |
+
mh_tasks = ["lift", "can", "square", "transport"]
|
| 77 |
+
mh_horizons = [500, 500, 500, 1100]
|
| 78 |
+
for task, horizon in zip(mh_tasks, mh_horizons):
|
| 79 |
+
register_dataset_link(task=task, dataset_type="mh", hdf5_type="raw", horizon=horizon,
|
| 80 |
+
link="http://downloads.cs.stanford.edu/downloads/rt_benchmark/{}/mh/demo_v141.hdf5".format(task))
|
| 81 |
+
register_dataset_link(task=task, dataset_type="mh", hdf5_type="low_dim", horizon=horizon,
|
| 82 |
+
link="http://downloads.cs.stanford.edu/downloads/rt_benchmark/{}/mh/low_dim_v141.hdf5".format(task))
|
| 83 |
+
register_dataset_link(task=task, dataset_type="mh", hdf5_type="image", horizon=horizon,
|
| 84 |
+
link=None)
|
| 85 |
+
|
| 86 |
+
# all machine generated datasets
|
| 87 |
+
for task, horizon in zip(["lift", "can"], [400, 400]):
|
| 88 |
+
register_dataset_link(task=task, dataset_type="mg", hdf5_type="raw", horizon=horizon,
|
| 89 |
+
link="http://downloads.cs.stanford.edu/downloads/rt_benchmark/{}/mg/demo_v141.hdf5".format(task))
|
| 90 |
+
register_dataset_link(task=task, dataset_type="mg", hdf5_type="low_dim_sparse", horizon=horizon,
|
| 91 |
+
link="http://downloads.cs.stanford.edu/downloads/rt_benchmark/{}/mg/low_dim_sparse_v141.hdf5".format(task))
|
| 92 |
+
register_dataset_link(task=task, dataset_type="mg", hdf5_type="image_sparse", horizon=horizon,
|
| 93 |
+
link="http://downloads.cs.stanford.edu/downloads/rt_benchmark/{}/mg/image_sparse_v141.hdf5".format(task))
|
| 94 |
+
register_dataset_link(task=task, dataset_type="mg", hdf5_type="low_dim_dense", horizon=horizon,
|
| 95 |
+
link="http://downloads.cs.stanford.edu/downloads/rt_benchmark/{}/mg/low_dim_dense_v141.hdf5".format(task))
|
| 96 |
+
register_dataset_link(task=task, dataset_type="mg", hdf5_type="image_dense", horizon=horizon,
|
| 97 |
+
link="http://downloads.cs.stanford.edu/downloads/rt_benchmark/{}/mg/image_dense_v141.hdf5".format(task))
|
| 98 |
+
|
| 99 |
+
# can-paired dataset
|
| 100 |
+
register_dataset_link(task="can", dataset_type="paired", hdf5_type="raw", horizon=400,
|
| 101 |
+
link="http://downloads.cs.stanford.edu/downloads/rt_benchmark/can/paired/demo_v141.hdf5")
|
| 102 |
+
register_dataset_link(task="can", dataset_type="paired", hdf5_type="low_dim", horizon=400,
|
| 103 |
+
link="http://downloads.cs.stanford.edu/downloads/rt_benchmark/can/paired/low_dim_v141.hdf5")
|
| 104 |
+
register_dataset_link(task="can", dataset_type="paired", hdf5_type="image", horizon=400,
|
| 105 |
+
link=None)
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
def register_momart_dataset_link(task, dataset_type, link, dataset_size):
|
| 109 |
+
"""
|
| 110 |
+
Helper function to register dataset link in global dictionary.
|
| 111 |
+
Also takes a @horizon parameter - this corresponds to the evaluation
|
| 112 |
+
rollout horizon that should be used during training.
|
| 113 |
+
|
| 114 |
+
Args:
|
| 115 |
+
task (str): name of task for this dataset
|
| 116 |
+
dataset_type (str): type of dataset (usually identifies the dataset source)
|
| 117 |
+
link (str): download link for the dataset
|
| 118 |
+
dataset_size (float): size of the dataset, in GB
|
| 119 |
+
"""
|
| 120 |
+
if task not in MOMART_DATASET_REGISTRY:
|
| 121 |
+
MOMART_DATASET_REGISTRY[task] = {}
|
| 122 |
+
if dataset_type not in MOMART_DATASET_REGISTRY[task]:
|
| 123 |
+
MOMART_DATASET_REGISTRY[task][dataset_type] = {}
|
| 124 |
+
MOMART_DATASET_REGISTRY[task][dataset_type] = dict(url=link, size=dataset_size)
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
def register_all_momart_links():
|
| 128 |
+
"""
|
| 129 |
+
Record all dataset links in this function.
|
| 130 |
+
"""
|
| 131 |
+
# all tasks, mapped to their [exp, sub, gen, sam] sizes
|
| 132 |
+
momart_tasks = {
|
| 133 |
+
"table_setup_from_dishwasher": [14, 14, 3.3, 0.6],
|
| 134 |
+
"table_setup_from_dresser": [16, 17, 3.1, 0.7],
|
| 135 |
+
"table_cleanup_to_dishwasher": [23, 36, 5.3, 1.1],
|
| 136 |
+
"table_cleanup_to_sink": [17, 28, 2.9, 0.8],
|
| 137 |
+
"unload_dishwasher": [21, 27, 5.4, 1.0],
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
momart_dataset_types = [
|
| 141 |
+
"expert",
|
| 142 |
+
"suboptimal",
|
| 143 |
+
"generalize",
|
| 144 |
+
"sample",
|
| 145 |
+
]
|
| 146 |
+
|
| 147 |
+
# Iterate over all combos and register the link
|
| 148 |
+
for task, dataset_sizes in momart_tasks.items():
|
| 149 |
+
for dataset_type, dataset_size in zip(momart_dataset_types, dataset_sizes):
|
| 150 |
+
register_momart_dataset_link(
|
| 151 |
+
task=task,
|
| 152 |
+
dataset_type=dataset_type,
|
| 153 |
+
link=f"http://downloads.cs.stanford.edu/downloads/rt_mm/{dataset_type}/{task}_{dataset_type}.hdf5",
|
| 154 |
+
dataset_size=dataset_size,
|
| 155 |
+
)
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
register_all_links()
|
| 159 |
+
register_all_momart_links()
|
aloha-devel/robomimic/__pycache__/__init__.cpython-38.pyc
ADDED
|
Binary file (4.53 kB). View file
|
|
|
aloha-devel/robomimic/algo/__pycache__/act.cpython-38.pyc
ADDED
|
Binary file (8.26 kB). View file
|
|
|
aloha-devel/robomimic/algo/__pycache__/gl.cpython-38.pyc
ADDED
|
Binary file (20.4 kB). View file
|
|
|
aloha-devel/robomimic/algo/__pycache__/iql.cpython-38.pyc
ADDED
|
Binary file (12 kB). View file
|
|
|
aloha-devel/robomimic/algo/__pycache__/iris.cpython-38.pyc
ADDED
|
Binary file (5.56 kB). View file
|
|
|
aloha-devel/robomimic/config/__init__.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from robomimic.config.config import Config
|
| 2 |
+
from robomimic.config.base_config import config_factory, get_all_registered_configs
|
| 3 |
+
|
| 4 |
+
# note: these imports are needed to register these classes in the global config registry
|
| 5 |
+
from robomimic.config.bc_config import BCConfig
|
| 6 |
+
from robomimic.config.bcq_config import BCQConfig
|
| 7 |
+
from robomimic.config.cql_config import CQLConfig
|
| 8 |
+
from robomimic.config.iql_config import IQLConfig
|
| 9 |
+
from robomimic.config.gl_config import GLConfig
|
| 10 |
+
from robomimic.config.hbc_config import HBCConfig
|
| 11 |
+
from robomimic.config.iris_config import IRISConfig
|
| 12 |
+
from robomimic.config.td3_bc_config import TD3_BCConfig
|
| 13 |
+
from robomimic.config.diffusion_policy_config import DiffusionPolicyConfig
|
| 14 |
+
from robomimic.config.act_config import ACTConfig
|
aloha-devel/robomimic/config/__pycache__/act_config.cpython-38.pyc
ADDED
|
Binary file (1.86 kB). View file
|
|
|
aloha-devel/robomimic/config/__pycache__/base_config.cpython-38.pyc
ADDED
|
Binary file (8.94 kB). View file
|
|
|
aloha-devel/robomimic/config/__pycache__/config.cpython-38.pyc
ADDED
|
Binary file (11.3 kB). View file
|
|
|
aloha-devel/robomimic/config/__pycache__/cql_config.cpython-38.pyc
ADDED
|
Binary file (2.25 kB). View file
|
|
|
aloha-devel/robomimic/config/__pycache__/td3_bc_config.cpython-38.pyc
ADDED
|
Binary file (2.86 kB). View file
|
|
|
aloha-devel/robomimic/config/bcq_config.py
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Config for BCQ algorithm.
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
from robomimic.config.base_config import BaseConfig
|
| 6 |
+
from robomimic.config.bc_config import BCConfig
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class BCQConfig(BaseConfig):
|
| 10 |
+
ALGO_NAME = "bcq"
|
| 11 |
+
|
| 12 |
+
def algo_config(self):
|
| 13 |
+
"""
|
| 14 |
+
This function populates the `config.algo` attribute of the config, and is given to the
|
| 15 |
+
`Algo` subclass (see `algo/algo.py`) for each algorithm through the `algo_config`
|
| 16 |
+
argument to the constructor. Any parameter that an algorithm needs to determine its
|
| 17 |
+
training and test-time behavior should be populated here.
|
| 18 |
+
"""
|
| 19 |
+
|
| 20 |
+
# optimization parameters
|
| 21 |
+
self.algo.optim_params.critic.learning_rate.initial = 1e-3 # critic learning rate
|
| 22 |
+
self.algo.optim_params.critic.learning_rate.decay_factor = 0.1 # factor to decay LR by (if epoch schedule non-empty)
|
| 23 |
+
self.algo.optim_params.critic.learning_rate.epoch_schedule = [] # epochs where LR decay occurs
|
| 24 |
+
self.algo.optim_params.critic.regularization.L2 = 0.00 # L2 regularization strength
|
| 25 |
+
self.algo.optim_params.critic.start_epoch = -1 # number of epochs before starting critic training (-1 means start right away)
|
| 26 |
+
self.algo.optim_params.critic.end_epoch = -1 # number of epochs before ending critic training (-1 means start right away)
|
| 27 |
+
|
| 28 |
+
self.algo.optim_params.action_sampler.learning_rate.initial = 1e-3 # action sampler learning rate
|
| 29 |
+
self.algo.optim_params.action_sampler.learning_rate.decay_factor = 0.1 # factor to decay LR by (if epoch schedule non-empty)
|
| 30 |
+
self.algo.optim_params.action_sampler.learning_rate.epoch_schedule = [] # epochs where LR decay occurs
|
| 31 |
+
self.algo.optim_params.action_sampler.regularization.L2 = 0.00 # L2 regularization strength
|
| 32 |
+
self.algo.optim_params.action_sampler.start_epoch = -1 # number of epochs before starting action sampler training (-1 means start right away)
|
| 33 |
+
self.algo.optim_params.action_sampler.end_epoch = -1 # number of epochs before ending action sampler training (-1 means start right away)
|
| 34 |
+
|
| 35 |
+
self.algo.optim_params.actor.learning_rate.initial = 1e-3 # actor learning rate
|
| 36 |
+
self.algo.optim_params.actor.learning_rate.decay_factor = 0.1 # factor to decay LR by (if epoch schedule non-empty)
|
| 37 |
+
self.algo.optim_params.actor.learning_rate.epoch_schedule = [] # epochs where LR decay occurs
|
| 38 |
+
self.algo.optim_params.actor.regularization.L2 = 0.00 # L2 regularization strength
|
| 39 |
+
self.algo.optim_params.actor.start_epoch = -1 # number of epochs before starting actor training (-1 means start right away)
|
| 40 |
+
self.algo.optim_params.actor.end_epoch = -1 # number of epochs before ending actor training (-1 means start right away)
|
| 41 |
+
|
| 42 |
+
# target network related parameters
|
| 43 |
+
self.algo.discount = 0.99 # discount factor to use
|
| 44 |
+
self.algo.n_step = 1 # for using n-step returns in TD-updates
|
| 45 |
+
self.algo.target_tau = 0.005 # update rate for target networks
|
| 46 |
+
self.algo.infinite_horizon = False # if True, scale terminal rewards by 1 / (1 - discount) to treat as infinite horizon
|
| 47 |
+
|
| 48 |
+
# ================== Critic Network Config ===================
|
| 49 |
+
self.algo.critic.use_huber = False # Huber Loss instead of L2 for critic
|
| 50 |
+
self.algo.critic.max_gradient_norm = None # L2 gradient clipping for critic (None to use no clipping)
|
| 51 |
+
self.algo.critic.value_bounds = None # optional 2-tuple to ensure lower and upper bound on value estimates
|
| 52 |
+
self.algo.critic.num_action_samples = 10 # number of actions to sample per training batch to get target critic value
|
| 53 |
+
self.algo.critic.num_action_samples_rollout = 100 # number of actions to sample per environment step
|
| 54 |
+
|
| 55 |
+
# critic ensemble parameters (TD3 trick)
|
| 56 |
+
self.algo.critic.ensemble.n = 2 # number of Q networks in the ensemble
|
| 57 |
+
self.algo.critic.ensemble.weight = 0.75 # weighting for mixing min and max for target Q value
|
| 58 |
+
|
| 59 |
+
# distributional critic
|
| 60 |
+
self.algo.critic.distributional.enabled = False # train distributional critic (C51)
|
| 61 |
+
self.algo.critic.distributional.num_atoms = 51 # number of values in categorical distribution
|
| 62 |
+
|
| 63 |
+
self.algo.critic.layer_dims = (300, 400) # size of critic MLP
|
| 64 |
+
|
| 65 |
+
# ================== Action Sampler Config ===================
|
| 66 |
+
self.algo.action_sampler = BCConfig().algo
|
| 67 |
+
# use VAE by default
|
| 68 |
+
self.algo.action_sampler.vae.enabled = True
|
| 69 |
+
# remove unused parts of BCConfig algo config
|
| 70 |
+
del self.algo.action_sampler.optim_params # since action sampler optim params specified at top-level
|
| 71 |
+
del self.algo.action_sampler.loss
|
| 72 |
+
del self.algo.action_sampler.gaussian
|
| 73 |
+
del self.algo.action_sampler.rnn
|
| 74 |
+
del self.algo.action_sampler.transformer
|
| 75 |
+
|
| 76 |
+
# Number of epochs before freezing encoder (-1 for no freezing). Only applies to cVAE-based action samplers.
|
| 77 |
+
with self.algo.action_sampler.unlocked():
|
| 78 |
+
self.algo.action_sampler.freeze_encoder_epoch = -1
|
| 79 |
+
|
| 80 |
+
# ================== Actor Network Config ===================
|
| 81 |
+
self.algo.actor.enabled = False # whether to use the actor perturbation network
|
| 82 |
+
self.algo.actor.perturbation_scale = 0.05 # size of learned action perturbations
|
| 83 |
+
self.algo.actor.layer_dims = (300, 400) # size of actor MLP
|
aloha-devel/robomimic/config/config.py
ADDED
|
@@ -0,0 +1,322 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Basic config class - provides a convenient way to work with nested
|
| 3 |
+
dictionaries (by exposing keys as attributes) and to save / load from jsons.
|
| 4 |
+
|
| 5 |
+
Based on addict: https://github.com/mewwts/addict
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
import json
|
| 9 |
+
import copy
|
| 10 |
+
import contextlib
|
| 11 |
+
from copy import deepcopy
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
class Config(dict):
|
| 15 |
+
|
| 16 |
+
def __init__(__self, *args, **kwargs):
|
| 17 |
+
object.__setattr__(__self, '__key_locked', False) # disallow adding new keys
|
| 18 |
+
object.__setattr__(__self, '__all_locked', False) # disallow both key and value update
|
| 19 |
+
object.__setattr__(__self, '__do_not_lock_keys', False) # cannot be key-locked
|
| 20 |
+
object.__setattr__(__self, '__parent', kwargs.pop('__parent', None))
|
| 21 |
+
object.__setattr__(__self, '__key', kwargs.pop('__key', None))
|
| 22 |
+
for arg in args:
|
| 23 |
+
if not arg:
|
| 24 |
+
continue
|
| 25 |
+
elif isinstance(arg, dict):
|
| 26 |
+
for key, val in arg.items():
|
| 27 |
+
__self[key] = __self._hook(val)
|
| 28 |
+
elif isinstance(arg, tuple) and (not isinstance(arg[0], tuple)):
|
| 29 |
+
__self[arg[0]] = __self._hook(arg[1])
|
| 30 |
+
else:
|
| 31 |
+
for key, val in iter(arg):
|
| 32 |
+
__self[key] = __self._hook(val)
|
| 33 |
+
|
| 34 |
+
for key, val in kwargs.items():
|
| 35 |
+
__self[key] = __self._hook(val)
|
| 36 |
+
|
| 37 |
+
def lock(self):
|
| 38 |
+
"""
|
| 39 |
+
Lock the config. Afterwards, new keys cannot be added to the
|
| 40 |
+
config, and the values of existing keys cannot be modified.
|
| 41 |
+
"""
|
| 42 |
+
object.__setattr__(self, '__all_locked', True)
|
| 43 |
+
if self.key_lockable:
|
| 44 |
+
object.__setattr__(self, '__key_locked', True)
|
| 45 |
+
|
| 46 |
+
for k in self:
|
| 47 |
+
if isinstance(self[k], Config):
|
| 48 |
+
self[k].lock()
|
| 49 |
+
|
| 50 |
+
def unlock(self):
|
| 51 |
+
"""
|
| 52 |
+
Unlock the config. Afterwards, new keys can be added to the
|
| 53 |
+
config, and the values of existing keys can be modified.
|
| 54 |
+
"""
|
| 55 |
+
object.__setattr__(self, '__all_locked', False)
|
| 56 |
+
object.__setattr__(self, '__key_locked', False)
|
| 57 |
+
|
| 58 |
+
for k in self:
|
| 59 |
+
if isinstance(self[k], Config):
|
| 60 |
+
self[k].unlock()
|
| 61 |
+
|
| 62 |
+
def _get_lock_state_recursive(self):
|
| 63 |
+
"""
|
| 64 |
+
Internal helper function to get the lock state of all sub-configs recursively.
|
| 65 |
+
"""
|
| 66 |
+
lock_state = {"__all_locked": self.is_locked, "__key_locked": self.is_key_locked}
|
| 67 |
+
for k in self:
|
| 68 |
+
if isinstance(self[k], Config):
|
| 69 |
+
assert k not in ["__all_locked", "__key_locked"]
|
| 70 |
+
lock_state[k] = self[k]._get_lock_state_recursive()
|
| 71 |
+
return lock_state
|
| 72 |
+
|
| 73 |
+
def _set_lock_state_recursive(self, lock_state):
|
| 74 |
+
"""
|
| 75 |
+
Internal helper function to set the lock state of all sub-configs recursively.
|
| 76 |
+
"""
|
| 77 |
+
lock_state = deepcopy(lock_state)
|
| 78 |
+
object.__setattr__(self, '__all_locked', lock_state.pop("__all_locked"))
|
| 79 |
+
object.__setattr__(self, '__key_locked', lock_state.pop("__key_locked"))
|
| 80 |
+
for k in lock_state:
|
| 81 |
+
if isinstance(self[k], Config):
|
| 82 |
+
self[k]._set_lock_state_recursive(lock_state[k])
|
| 83 |
+
|
| 84 |
+
def _get_lock_state(self):
|
| 85 |
+
"""
|
| 86 |
+
Retrieves the lock state of this config.
|
| 87 |
+
|
| 88 |
+
Returns:
|
| 89 |
+
lock_state (dict): a dictionary with an "all_locked" key that is True
|
| 90 |
+
if both key and value updates are locked and False otherwise, and
|
| 91 |
+
a "key_locked" key that is True if only key updates are locked (value
|
| 92 |
+
updates still allowed) and False otherwise
|
| 93 |
+
"""
|
| 94 |
+
return {
|
| 95 |
+
"all_locked": self.is_locked,
|
| 96 |
+
"key_locked": self.is_key_locked
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
def _set_lock_state(self, lock_state):
|
| 100 |
+
"""
|
| 101 |
+
Sets the lock state for this config.
|
| 102 |
+
|
| 103 |
+
Args:
|
| 104 |
+
lock_state (dict): a dictionary with an "all_locked" key that is True
|
| 105 |
+
if both key and value updates should be locked and False otherwise, and
|
| 106 |
+
a "key_locked" key that is True if only key updates should be locked (value
|
| 107 |
+
updates still allowed) and False otherwise
|
| 108 |
+
"""
|
| 109 |
+
if lock_state["all_locked"]:
|
| 110 |
+
self.lock()
|
| 111 |
+
if lock_state["key_locked"]:
|
| 112 |
+
self.lock_keys()
|
| 113 |
+
|
| 114 |
+
@contextlib.contextmanager
|
| 115 |
+
def unlocked(self):
|
| 116 |
+
"""
|
| 117 |
+
A context scope for modifying a Config object. Within the scope,
|
| 118 |
+
both keys and values can be updated. Upon leaving the scope,
|
| 119 |
+
the initial level of locking is restored.
|
| 120 |
+
"""
|
| 121 |
+
lock_state = self._get_lock_state()
|
| 122 |
+
self.unlock()
|
| 123 |
+
yield
|
| 124 |
+
self._set_lock_state(lock_state)
|
| 125 |
+
|
| 126 |
+
@contextlib.contextmanager
|
| 127 |
+
def values_unlocked(self):
|
| 128 |
+
"""
|
| 129 |
+
A context scope for modifying a Config object. Within the scope,
|
| 130 |
+
only values can be updated (new keys cannot be created). Upon
|
| 131 |
+
leaving the scope, the initial level of locking is restored.
|
| 132 |
+
"""
|
| 133 |
+
lock_state = self._get_lock_state()
|
| 134 |
+
self.unlock()
|
| 135 |
+
self.lock_keys()
|
| 136 |
+
yield
|
| 137 |
+
self._set_lock_state(lock_state)
|
| 138 |
+
|
| 139 |
+
def lock_keys(self):
|
| 140 |
+
"""
|
| 141 |
+
Lock this config so that new keys cannot be added.
|
| 142 |
+
"""
|
| 143 |
+
if not self.key_lockable:
|
| 144 |
+
return
|
| 145 |
+
object.__setattr__(self, '__key_locked', True)
|
| 146 |
+
for k in self:
|
| 147 |
+
if isinstance(self[k], Config):
|
| 148 |
+
self[k].lock_keys()
|
| 149 |
+
|
| 150 |
+
def unlock_keys(self):
|
| 151 |
+
"""
|
| 152 |
+
Unlock this config so that new keys can be added.
|
| 153 |
+
"""
|
| 154 |
+
object.__setattr__(self, '__key_locked', False)
|
| 155 |
+
for k in self:
|
| 156 |
+
if isinstance(self[k], Config):
|
| 157 |
+
self[k].unlock_keys()
|
| 158 |
+
|
| 159 |
+
@property
|
| 160 |
+
def is_locked(self):
|
| 161 |
+
"""
|
| 162 |
+
Returns True if the config is locked (no key or value updates allowed).
|
| 163 |
+
"""
|
| 164 |
+
return object.__getattribute__(self, '__all_locked')
|
| 165 |
+
|
| 166 |
+
@property
|
| 167 |
+
def is_key_locked(self):
|
| 168 |
+
"""
|
| 169 |
+
Returns True if the config is key-locked (no key updates allowed).
|
| 170 |
+
"""
|
| 171 |
+
return object.__getattribute__(self, '__key_locked')
|
| 172 |
+
|
| 173 |
+
def do_not_lock_keys(self):
|
| 174 |
+
"""
|
| 175 |
+
Calling this function on this config indicates that key updates should be
|
| 176 |
+
allowed even when this config is key-locked (but not when it is completely
|
| 177 |
+
locked). This is convenient for attributes that contain kwargs, where there
|
| 178 |
+
might be a variable type and number of arguments contained in the sub-config.
|
| 179 |
+
"""
|
| 180 |
+
object.__setattr__(self, '__do_not_lock_keys', True)
|
| 181 |
+
|
| 182 |
+
@property
|
| 183 |
+
def key_lockable(self):
|
| 184 |
+
"""
|
| 185 |
+
Returns true if this config is key-lockable (new keys cannot be inserted in a
|
| 186 |
+
key-locked lock level).
|
| 187 |
+
"""
|
| 188 |
+
return not object.__getattribute__(self, '__do_not_lock_keys')
|
| 189 |
+
|
| 190 |
+
def __setattr__(self, name, value):
|
| 191 |
+
if self.is_locked:
|
| 192 |
+
raise RuntimeError("This config has been locked - cannot set attribute '{}' to {}".format(name, value))
|
| 193 |
+
|
| 194 |
+
if hasattr(Config, name):
|
| 195 |
+
raise AttributeError("'Dict' object attribute "
|
| 196 |
+
"'{0}' is read-only".format(name))
|
| 197 |
+
elif not hasattr(self, name) and self.is_key_locked:
|
| 198 |
+
raise RuntimeError("This config is key-locked - cannot add key '{}'".format(name))
|
| 199 |
+
else:
|
| 200 |
+
self[name] = value
|
| 201 |
+
|
| 202 |
+
def __setitem__(self, name, value):
|
| 203 |
+
super(Config, self).__setitem__(name, value)
|
| 204 |
+
p = object.__getattribute__(self, '__parent')
|
| 205 |
+
key = object.__getattribute__(self, '__key')
|
| 206 |
+
if p is not None:
|
| 207 |
+
p[key] = self
|
| 208 |
+
|
| 209 |
+
def __add__(self, other):
|
| 210 |
+
if not self.keys():
|
| 211 |
+
return other
|
| 212 |
+
else:
|
| 213 |
+
self_type = type(self).__name__
|
| 214 |
+
other_type = type(other).__name__
|
| 215 |
+
msg = "unsupported operand type(s) for +: '{}' and '{}'"
|
| 216 |
+
raise TypeError(msg.format(self_type, other_type))
|
| 217 |
+
|
| 218 |
+
@classmethod
|
| 219 |
+
def _hook(cls, item):
|
| 220 |
+
if isinstance(item, dict):
|
| 221 |
+
# We return Config instance instead of cls instance to ensure all sub-configs are not a top-level class
|
| 222 |
+
return Config(item)
|
| 223 |
+
elif isinstance(item, (list, tuple)):
|
| 224 |
+
return type(item)(Config._hook(elem) for elem in item)
|
| 225 |
+
return item
|
| 226 |
+
|
| 227 |
+
def __getattr__(self, item):
|
| 228 |
+
return self.__getitem__(item)
|
| 229 |
+
|
| 230 |
+
def __repr__(self):
|
| 231 |
+
json_string = json.dumps(self.to_dict(), indent=4)
|
| 232 |
+
return json_string
|
| 233 |
+
|
| 234 |
+
def __getitem__(self, name):
|
| 235 |
+
if name not in self:
|
| 236 |
+
if object.__getattribute__(self, '__all_locked') or object.__getattribute__(self, '__key_locked'):
|
| 237 |
+
raise RuntimeError("This config has been locked and '{}' is not in this config".format(name))
|
| 238 |
+
return Config(__parent=self, __key=name)
|
| 239 |
+
return super(Config, self).__getitem__(name)
|
| 240 |
+
|
| 241 |
+
def __delattr__(self, name):
|
| 242 |
+
del self[name]
|
| 243 |
+
|
| 244 |
+
def to_dict(self):
|
| 245 |
+
base = {}
|
| 246 |
+
for key, value in self.items():
|
| 247 |
+
if isinstance(value, type(self)):
|
| 248 |
+
base[key] = value.to_dict()
|
| 249 |
+
elif isinstance(value, (list, tuple)):
|
| 250 |
+
base[key] = type(value)(
|
| 251 |
+
item.to_dict() if isinstance(item, type(self)) else
|
| 252 |
+
item for item in value)
|
| 253 |
+
else:
|
| 254 |
+
base[key] = value
|
| 255 |
+
return base
|
| 256 |
+
|
| 257 |
+
def copy(self):
|
| 258 |
+
return copy.copy(self)
|
| 259 |
+
|
| 260 |
+
def deepcopy(self):
|
| 261 |
+
return copy.deepcopy(self)
|
| 262 |
+
|
| 263 |
+
def __deepcopy__(self, memo):
|
| 264 |
+
other = self.__class__()
|
| 265 |
+
memo[id(self)] = other
|
| 266 |
+
for key, value in self.items():
|
| 267 |
+
other[copy.deepcopy(key, memo)] = copy.deepcopy(value, memo)
|
| 268 |
+
return other
|
| 269 |
+
|
| 270 |
+
def update(self, *args, **kwargs):
|
| 271 |
+
"""
|
| 272 |
+
Update this config using another config or nested dictionary.
|
| 273 |
+
"""
|
| 274 |
+
if self.is_locked:
|
| 275 |
+
raise RuntimeError('Cannot update - this config has been locked')
|
| 276 |
+
other = {}
|
| 277 |
+
if args:
|
| 278 |
+
if len(args) > 1:
|
| 279 |
+
raise TypeError()
|
| 280 |
+
other.update(args[0])
|
| 281 |
+
other.update(kwargs)
|
| 282 |
+
for k, v in other.items():
|
| 283 |
+
if self.is_key_locked and k not in self:
|
| 284 |
+
raise RuntimeError("Cannot update - this config has been key-locked and key '{}' does not exist".format(k))
|
| 285 |
+
if (not isinstance(self[k], dict)) or (not isinstance(v, dict)):
|
| 286 |
+
self[k] = v
|
| 287 |
+
else:
|
| 288 |
+
self[k].update(v)
|
| 289 |
+
|
| 290 |
+
def __getnewargs__(self):
|
| 291 |
+
return tuple(self.items())
|
| 292 |
+
|
| 293 |
+
def __getstate__(self):
|
| 294 |
+
return self
|
| 295 |
+
|
| 296 |
+
def __setstate__(self, state):
|
| 297 |
+
self.update(state)
|
| 298 |
+
|
| 299 |
+
def setdefault(self, key, default=None):
|
| 300 |
+
if key in self:
|
| 301 |
+
return self[key]
|
| 302 |
+
else:
|
| 303 |
+
self[key] = default
|
| 304 |
+
return default
|
| 305 |
+
|
| 306 |
+
def dump(self, filename=None):
|
| 307 |
+
"""
|
| 308 |
+
Dumps the config to a json.
|
| 309 |
+
|
| 310 |
+
Args:
|
| 311 |
+
filename (str): if not None, save to json file.
|
| 312 |
+
|
| 313 |
+
Returns:
|
| 314 |
+
json_string (str): json string representation of
|
| 315 |
+
this config
|
| 316 |
+
"""
|
| 317 |
+
json_string = json.dumps(self.to_dict(), indent=4)
|
| 318 |
+
if filename is not None:
|
| 319 |
+
f = open(filename, "w")
|
| 320 |
+
f.write(json_string)
|
| 321 |
+
f.close()
|
| 322 |
+
return json_string
|
aloha-devel/robomimic/config/hbc_config.py
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Config for HBC algorithm.
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
from robomimic.config.base_config import BaseConfig
|
| 6 |
+
from robomimic.config.gl_config import GLConfig
|
| 7 |
+
from robomimic.config.bc_config import BCConfig
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
class HBCConfig(BaseConfig):
|
| 11 |
+
ALGO_NAME = "hbc"
|
| 12 |
+
|
| 13 |
+
def train_config(self):
|
| 14 |
+
"""
|
| 15 |
+
Update from superclass to change default sequence length to load from dataset.
|
| 16 |
+
"""
|
| 17 |
+
super(HBCConfig, self).train_config()
|
| 18 |
+
self.train.seq_length = 10 # length of experience sequence to fetch from the buffer
|
| 19 |
+
|
| 20 |
+
def algo_config(self):
|
| 21 |
+
"""
|
| 22 |
+
This function populates the `config.algo` attribute of the config, and is given to the
|
| 23 |
+
`Algo` subclass (see `algo/algo.py`) for each algorithm through the `algo_config`
|
| 24 |
+
argument to the constructor. Any parameter that an algorithm needs to determine its
|
| 25 |
+
training and test-time behavior should be populated here.
|
| 26 |
+
"""
|
| 27 |
+
|
| 28 |
+
# One of ["separate", "cascade"]. In "separate" mode (default),
|
| 29 |
+
# the planner and actor are trained independently and then the planner subgoal predictions are
|
| 30 |
+
# used to condition the actor at test-time. In "cascade" mode, the actor is trained directly
|
| 31 |
+
# on planner subgoal predictions. In "actor_only" mode, only the actor is trained, and in
|
| 32 |
+
# "planner_only" mode, only the planner is trained.
|
| 33 |
+
self.algo.mode = "separate"
|
| 34 |
+
self.algo.actor_use_random_subgoals = False # whether to sample subgoal index from [1, subgoal_horizon]
|
| 35 |
+
self.algo.subgoal_update_interval = 10 # how frequently the subgoal should be updated at test-time
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
# ================== Latent Subgoal Config ==================
|
| 39 |
+
self.algo.latent_subgoal.enabled = False # if True, use VAE latent space as subgoals for actor, instead of reconstructions
|
| 40 |
+
|
| 41 |
+
# prior correction trick for actor and value training: instead of using encoder for
|
| 42 |
+
# transforming subgoals to latent subgoals, generate prior samples and choose
|
| 43 |
+
# the closest one to the encoder output
|
| 44 |
+
self.algo.latent_subgoal.prior_correction.enabled = False
|
| 45 |
+
self.algo.latent_subgoal.prior_correction.num_samples = 100
|
| 46 |
+
|
| 47 |
+
# ================== Planner Config ==================
|
| 48 |
+
self.algo.planner = GLConfig().algo # config for goal learning
|
| 49 |
+
# set subgoal horizon explicitly
|
| 50 |
+
self.algo.planner.subgoal_horizon = 10
|
| 51 |
+
# ensure VAE is used
|
| 52 |
+
self.algo.planner.vae.enabled = True
|
| 53 |
+
|
| 54 |
+
# ================== Actor Config ===================
|
| 55 |
+
self.algo.actor = BCConfig().algo
|
| 56 |
+
# use RNN
|
| 57 |
+
self.algo.actor.rnn.enabled = True
|
| 58 |
+
self.algo.actor.rnn.horizon = 10
|
| 59 |
+
# remove unused parts of BCConfig algo config
|
| 60 |
+
del self.algo.actor.gaussian
|
| 61 |
+
del self.algo.actor.gmm
|
| 62 |
+
del self.algo.actor.vae
|
| 63 |
+
|
| 64 |
+
def observation_config(self):
|
| 65 |
+
"""
|
| 66 |
+
Update from superclass so that planner and actor each get their own observation config.
|
| 67 |
+
"""
|
| 68 |
+
self.observation.planner = GLConfig().observation
|
| 69 |
+
self.observation.actor = BCConfig().observation
|
| 70 |
+
|
| 71 |
+
@property
|
| 72 |
+
def use_goals(self):
|
| 73 |
+
"""
|
| 74 |
+
Update from superclass - planner goal modalities determine goal-conditioning
|
| 75 |
+
"""
|
| 76 |
+
return len(
|
| 77 |
+
self.observation.planner.modalities.goal.low_dim +
|
| 78 |
+
self.observation.planner.modalities.goal.rgb) > 0
|
| 79 |
+
|
| 80 |
+
@property
|
| 81 |
+
def all_obs_keys(self):
|
| 82 |
+
"""
|
| 83 |
+
Update from superclass to include modalities from planner and actor.
|
| 84 |
+
"""
|
| 85 |
+
# pool all modalities
|
| 86 |
+
return sorted(tuple(set([
|
| 87 |
+
obs_key for group in [
|
| 88 |
+
self.observation.planner.modalities.obs.values(),
|
| 89 |
+
self.observation.planner.modalities.goal.values(),
|
| 90 |
+
self.observation.planner.modalities.subgoal.values(),
|
| 91 |
+
self.observation.actor.modalities.obs.values(),
|
| 92 |
+
self.observation.actor.modalities.goal.values(),
|
| 93 |
+
]
|
| 94 |
+
for modality in group
|
| 95 |
+
for obs_key in modality
|
| 96 |
+
])))
|
aloha-devel/robomimic/config/iql_config.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Config for IQL algorithm.
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
from robomimic.config.base_config import BaseConfig
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class IQLConfig(BaseConfig):
|
| 9 |
+
ALGO_NAME = "iql"
|
| 10 |
+
|
| 11 |
+
def algo_config(self):
|
| 12 |
+
"""
|
| 13 |
+
This function populates the `config.algo` attribute of the config, and is given to the
|
| 14 |
+
`Algo` subclass (see `algo/algo.py`) for each algorithm through the `algo_config`
|
| 15 |
+
argument to the constructor. Any parameter that an algorithm needs to determine its
|
| 16 |
+
training and test-time behavior should be populated here.
|
| 17 |
+
"""
|
| 18 |
+
super(IQLConfig, self).algo_config()
|
| 19 |
+
|
| 20 |
+
# optimization parameters
|
| 21 |
+
self.algo.optim_params.critic.learning_rate.initial = 1e-4 # critic learning rate
|
| 22 |
+
self.algo.optim_params.critic.learning_rate.decay_factor = 0.0 # factor to decay LR by (if epoch schedule non-empty)
|
| 23 |
+
self.algo.optim_params.critic.learning_rate.epoch_schedule = [] # epochs where LR decay occurs
|
| 24 |
+
self.algo.optim_params.critic.regularization.L2 = 0.00 # L2 regularization strength
|
| 25 |
+
|
| 26 |
+
self.algo.optim_params.vf.learning_rate.initial = 1e-4 # vf learning rate
|
| 27 |
+
self.algo.optim_params.vf.learning_rate.decay_factor = 0.0 # factor to decay LR by (if epoch schedule non-empty)
|
| 28 |
+
self.algo.optim_params.vf.learning_rate.epoch_schedule = [] # epochs where LR decay occurs
|
| 29 |
+
self.algo.optim_params.vf.regularization.L2 = 0.00 # L2 regularization strength
|
| 30 |
+
|
| 31 |
+
self.algo.optim_params.actor.learning_rate.initial = 1e-4 # actor learning rate
|
| 32 |
+
self.algo.optim_params.actor.learning_rate.decay_factor = 0.0 # factor to decay LR by (if epoch schedule non-empty)
|
| 33 |
+
self.algo.optim_params.actor.learning_rate.epoch_schedule = [] # epochs where LR decay occurs
|
| 34 |
+
self.algo.optim_params.actor.regularization.L2 = 0.00 # L2 regularization strength
|
| 35 |
+
|
| 36 |
+
# target network related parameters
|
| 37 |
+
self.algo.discount = 0.99 # discount factor to use
|
| 38 |
+
self.algo.target_tau = 0.01 # update rate for target networks
|
| 39 |
+
|
| 40 |
+
# ================== Actor Network Config ===================
|
| 41 |
+
# Actor network settings
|
| 42 |
+
self.algo.actor.net.type = "gaussian" # Options are currently ["gaussian", "gmm"]
|
| 43 |
+
|
| 44 |
+
# Actor network settings - shared
|
| 45 |
+
self.algo.actor.net.common.std_activation = "softplus" # Activation to use for std output from policy net
|
| 46 |
+
self.algo.actor.net.common.low_noise_eval = True # Whether to use deterministic action sampling at eval stage
|
| 47 |
+
self.algo.actor.net.common.use_tanh = False # Whether to use tanh at output of actor network
|
| 48 |
+
|
| 49 |
+
# Actor network settings - gaussian
|
| 50 |
+
self.algo.actor.net.gaussian.init_last_fc_weight = 0.001 # If set, will override the initialization of the final fc layer to be uniformly sampled limited by this value
|
| 51 |
+
self.algo.actor.net.gaussian.init_std = 0.3 # Relative scaling factor for std from policy net
|
| 52 |
+
self.algo.actor.net.gaussian.fixed_std = False # Whether to learn std dev or not
|
| 53 |
+
|
| 54 |
+
self.algo.actor.net.gmm.num_modes = 5 # number of GMM modes
|
| 55 |
+
self.algo.actor.net.gmm.min_std = 0.0001 # minimum std output from network
|
| 56 |
+
|
| 57 |
+
self.algo.actor.layer_dims = (300, 400) # actor MLP layer dimensions
|
| 58 |
+
|
| 59 |
+
self.algo.actor.max_gradient_norm = None # L2 gradient clipping for actor
|
| 60 |
+
|
| 61 |
+
# ================== Critic Network Config ===================
|
| 62 |
+
# critic ensemble parameters
|
| 63 |
+
self.algo.critic.ensemble.n = 2 # number of Q networks in the ensemble
|
| 64 |
+
self.algo.critic.layer_dims = (300, 400) # critic MLP layer dimensions
|
| 65 |
+
self.algo.critic.use_huber = False # Huber Loss instead of L2 for critic
|
| 66 |
+
self.algo.critic.max_gradient_norm = None # L2 gradient clipping for actor
|
| 67 |
+
|
| 68 |
+
# ================== Adv Config ==============================
|
| 69 |
+
self.algo.adv.clip_adv_value = None # whether to clip raw advantage estimates
|
| 70 |
+
self.algo.adv.beta = 1.0 # temperature for operator
|
| 71 |
+
self.algo.adv.use_final_clip = True # whether to clip final weight calculations
|
| 72 |
+
|
| 73 |
+
self.algo.vf_quantile = 0.9 # quantile factor in quantile regression
|
aloha-devel/robomimic/envs/__init__.py
ADDED
|
File without changes
|
aloha-devel/robomimic/envs/env_base.py
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
This file contains the base class for environment wrappers that are used
|
| 3 |
+
to provide a standardized environment API for training policies and interacting
|
| 4 |
+
with metadata present in datasets.
|
| 5 |
+
"""
|
| 6 |
+
import abc
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class EnvType:
|
| 10 |
+
"""
|
| 11 |
+
Holds environment types - one per environment class.
|
| 12 |
+
These act as identifiers for different environments.
|
| 13 |
+
"""
|
| 14 |
+
ROBOSUITE_TYPE = 1
|
| 15 |
+
GYM_TYPE = 2
|
| 16 |
+
IG_MOMART_TYPE = 3
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class EnvBase(abc.ABC):
|
| 20 |
+
"""A base class method for environments used by this repo."""
|
| 21 |
+
@abc.abstractmethod
|
| 22 |
+
def __init__(
|
| 23 |
+
self,
|
| 24 |
+
env_name,
|
| 25 |
+
render=False,
|
| 26 |
+
render_offscreen=False,
|
| 27 |
+
use_image_obs=False,
|
| 28 |
+
postprocess_visual_obs=True,
|
| 29 |
+
**kwargs,
|
| 30 |
+
):
|
| 31 |
+
"""
|
| 32 |
+
Args:
|
| 33 |
+
env_name (str): name of environment. Only needs to be provided if making a different
|
| 34 |
+
environment from the one in @env_meta.
|
| 35 |
+
|
| 36 |
+
render (bool): if True, environment supports on-screen rendering
|
| 37 |
+
|
| 38 |
+
render_offscreen (bool): if True, environment supports off-screen rendering. This
|
| 39 |
+
is forced to be True if @env_meta["use_images"] is True.
|
| 40 |
+
|
| 41 |
+
use_image_obs (bool): if True, environment is expected to render rgb image observations
|
| 42 |
+
on every env.step call. Set this to False for efficiency reasons, if image
|
| 43 |
+
observations are not required.
|
| 44 |
+
|
| 45 |
+
postprocess_visual_obs (bool): if True, postprocess image observations
|
| 46 |
+
to prepare for learning. This should only be False when extracting observations
|
| 47 |
+
for saving to a dataset (to save space on RGB images for example).
|
| 48 |
+
"""
|
| 49 |
+
return
|
| 50 |
+
|
| 51 |
+
@abc.abstractmethod
|
| 52 |
+
def step(self, action):
|
| 53 |
+
"""
|
| 54 |
+
Step in the environment with an action.
|
| 55 |
+
|
| 56 |
+
Args:
|
| 57 |
+
action (np.array): action to take
|
| 58 |
+
|
| 59 |
+
Returns:
|
| 60 |
+
observation (dict): new observation dictionary
|
| 61 |
+
reward (float): reward for this step
|
| 62 |
+
done (bool): whether the task is done
|
| 63 |
+
info (dict): extra information
|
| 64 |
+
"""
|
| 65 |
+
return
|
| 66 |
+
|
| 67 |
+
@abc.abstractmethod
|
| 68 |
+
def reset(self):
|
| 69 |
+
"""
|
| 70 |
+
Reset environment.
|
| 71 |
+
|
| 72 |
+
Returns:
|
| 73 |
+
observation (dict): initial observation dictionary.
|
| 74 |
+
"""
|
| 75 |
+
return
|
| 76 |
+
|
| 77 |
+
@abc.abstractmethod
|
| 78 |
+
def reset_to(self, state):
|
| 79 |
+
"""
|
| 80 |
+
Reset to a specific simulator state.
|
| 81 |
+
|
| 82 |
+
Args:
|
| 83 |
+
state (dict): current simulator state
|
| 84 |
+
|
| 85 |
+
Returns:
|
| 86 |
+
observation (dict): observation dictionary after setting the simulator state
|
| 87 |
+
"""
|
| 88 |
+
return
|
| 89 |
+
|
| 90 |
+
@abc.abstractmethod
|
| 91 |
+
def render(self, mode="human", height=None, width=None, camera_name=None):
|
| 92 |
+
"""Render"""
|
| 93 |
+
return
|
| 94 |
+
|
| 95 |
+
@abc.abstractmethod
|
| 96 |
+
def get_observation(self):
|
| 97 |
+
"""Get environment observation"""
|
| 98 |
+
return
|
| 99 |
+
|
| 100 |
+
@abc.abstractmethod
|
| 101 |
+
def get_state(self):
|
| 102 |
+
"""Get environment simulator state, compatible with @reset_to"""
|
| 103 |
+
return
|
| 104 |
+
|
| 105 |
+
@abc.abstractmethod
|
| 106 |
+
def get_reward(self):
|
| 107 |
+
"""
|
| 108 |
+
Get current reward.
|
| 109 |
+
"""
|
| 110 |
+
return
|
| 111 |
+
|
| 112 |
+
@abc.abstractmethod
|
| 113 |
+
def get_goal(self):
|
| 114 |
+
"""
|
| 115 |
+
Get goal observation. Not all environments support this.
|
| 116 |
+
"""
|
| 117 |
+
return
|
| 118 |
+
|
| 119 |
+
@abc.abstractmethod
|
| 120 |
+
def set_goal(self, **kwargs):
|
| 121 |
+
"""
|
| 122 |
+
Set goal observation with external specification. Not all environments support this.
|
| 123 |
+
"""
|
| 124 |
+
return
|
| 125 |
+
|
| 126 |
+
@abc.abstractmethod
|
| 127 |
+
def is_done(self):
|
| 128 |
+
"""
|
| 129 |
+
Check if the task is done (not necessarily successful).
|
| 130 |
+
"""
|
| 131 |
+
return
|
| 132 |
+
|
| 133 |
+
@abc.abstractmethod
|
| 134 |
+
def is_success(self):
|
| 135 |
+
"""
|
| 136 |
+
Check if the task condition(s) is reached. Should return a dictionary
|
| 137 |
+
{ str: bool } with at least a "task" key for the overall task success,
|
| 138 |
+
and additional optional keys corresponding to other task criteria.
|
| 139 |
+
"""
|
| 140 |
+
return
|
| 141 |
+
|
| 142 |
+
@property
|
| 143 |
+
@abc.abstractmethod
|
| 144 |
+
def action_dimension(self):
|
| 145 |
+
"""
|
| 146 |
+
Returns dimension of actions (int).
|
| 147 |
+
"""
|
| 148 |
+
return
|
| 149 |
+
|
| 150 |
+
@property
|
| 151 |
+
@abc.abstractmethod
|
| 152 |
+
def name(self):
|
| 153 |
+
"""
|
| 154 |
+
Returns name of environment name (str).
|
| 155 |
+
"""
|
| 156 |
+
return
|
| 157 |
+
|
| 158 |
+
@property
|
| 159 |
+
@abc.abstractmethod
|
| 160 |
+
def type(self):
|
| 161 |
+
"""
|
| 162 |
+
Returns environment type (int) for this kind of environment.
|
| 163 |
+
This helps identify this env class.
|
| 164 |
+
"""
|
| 165 |
+
return
|
| 166 |
+
|
| 167 |
+
@property
|
| 168 |
+
def version(self):
|
| 169 |
+
"""
|
| 170 |
+
Returns version of environment (str).
|
| 171 |
+
This is not an abstract method, some subclasses do not implement it
|
| 172 |
+
"""
|
| 173 |
+
return None
|
| 174 |
+
|
| 175 |
+
@abc.abstractmethod
|
| 176 |
+
def serialize(self):
|
| 177 |
+
"""
|
| 178 |
+
Save all information needed to re-instantiate this environment in a dictionary.
|
| 179 |
+
This is the same as @env_meta - environment metadata stored in hdf5 datasets,
|
| 180 |
+
and used in utils/env_utils.py.
|
| 181 |
+
"""
|
| 182 |
+
return
|
| 183 |
+
|
| 184 |
+
@classmethod
|
| 185 |
+
@abc.abstractmethod
|
| 186 |
+
def create_for_data_processing(cls, camera_names, camera_height, camera_width, reward_shaping, **kwargs):
|
| 187 |
+
"""
|
| 188 |
+
Create environment for processing datasets, which includes extracting
|
| 189 |
+
observations, labeling dense / sparse rewards, and annotating dones in
|
| 190 |
+
transitions.
|
| 191 |
+
|
| 192 |
+
Args:
|
| 193 |
+
camera_names ([str]): list of camera names that correspond to image observations
|
| 194 |
+
camera_height (int): camera height for all cameras
|
| 195 |
+
camera_width (int): camera width for all cameras
|
| 196 |
+
reward_shaping (bool): if True, use shaped environment rewards, else use sparse task completion rewards
|
| 197 |
+
|
| 198 |
+
Returns:
|
| 199 |
+
env (EnvBase instance)
|
| 200 |
+
"""
|
| 201 |
+
return
|
| 202 |
+
|
| 203 |
+
@property
|
| 204 |
+
@abc.abstractmethod
|
| 205 |
+
def rollout_exceptions(self):
|
| 206 |
+
"""
|
| 207 |
+
Return tuple of exceptions to except when doing rollouts. This is useful to ensure
|
| 208 |
+
that the entire training run doesn't crash because of a bad policy that causes unstable
|
| 209 |
+
simulation computations.
|
| 210 |
+
"""
|
| 211 |
+
return
|
| 212 |
+
|
aloha-devel/robomimic/envs/env_gym.py
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
This file contains the gym environment wrapper that is used
|
| 3 |
+
to provide a standardized environment API for training policies and interacting
|
| 4 |
+
with metadata present in datasets.
|
| 5 |
+
"""
|
| 6 |
+
import json
|
| 7 |
+
import numpy as np
|
| 8 |
+
from copy import deepcopy
|
| 9 |
+
|
| 10 |
+
import gym
|
| 11 |
+
try:
|
| 12 |
+
import d4rl
|
| 13 |
+
except:
|
| 14 |
+
print("WARNING: could not load d4rl environments!")
|
| 15 |
+
|
| 16 |
+
import robomimic.envs.env_base as EB
|
| 17 |
+
import robomimic.utils.obs_utils as ObsUtils
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
class EnvGym(EB.EnvBase):
|
| 21 |
+
"""Wrapper class for gym"""
|
| 22 |
+
def __init__(
|
| 23 |
+
self,
|
| 24 |
+
env_name,
|
| 25 |
+
render=False,
|
| 26 |
+
render_offscreen=False,
|
| 27 |
+
use_image_obs=False,
|
| 28 |
+
postprocess_visual_obs=True,
|
| 29 |
+
**kwargs,
|
| 30 |
+
):
|
| 31 |
+
"""
|
| 32 |
+
Args:
|
| 33 |
+
env_name (str): name of environment. Only needs to be provided if making a different
|
| 34 |
+
environment from the one in @env_meta.
|
| 35 |
+
|
| 36 |
+
render (bool): ignored - gym envs always support on-screen rendering
|
| 37 |
+
|
| 38 |
+
render_offscreen (bool): ignored - gym envs always support off-screen rendering
|
| 39 |
+
|
| 40 |
+
use_image_obs (bool): ignored - gym envs don't typically use images
|
| 41 |
+
|
| 42 |
+
postprocess_visual_obs (bool): ignored - gym envs don't typically use images
|
| 43 |
+
"""
|
| 44 |
+
self._init_kwargs = deepcopy(kwargs)
|
| 45 |
+
self._env_name = env_name
|
| 46 |
+
self._current_obs = None
|
| 47 |
+
self._current_reward = None
|
| 48 |
+
self._current_done = None
|
| 49 |
+
self._done = None
|
| 50 |
+
self.env = gym.make(env_name, **kwargs)
|
| 51 |
+
|
| 52 |
+
def step(self, action):
|
| 53 |
+
"""
|
| 54 |
+
Step in the environment with an action.
|
| 55 |
+
|
| 56 |
+
Args:
|
| 57 |
+
action (np.array): action to take
|
| 58 |
+
|
| 59 |
+
Returns:
|
| 60 |
+
observation (dict): new observation dictionary
|
| 61 |
+
reward (float): reward for this step
|
| 62 |
+
done (bool): whether the task is done
|
| 63 |
+
info (dict): extra information
|
| 64 |
+
"""
|
| 65 |
+
obs, reward, done, info = self.env.step(action)
|
| 66 |
+
self._current_obs = obs
|
| 67 |
+
self._current_reward = reward
|
| 68 |
+
self._current_done = done
|
| 69 |
+
return self.get_observation(obs), reward, self.is_done(), info
|
| 70 |
+
|
| 71 |
+
def reset(self):
|
| 72 |
+
"""
|
| 73 |
+
Reset environment.
|
| 74 |
+
|
| 75 |
+
Returns:
|
| 76 |
+
observation (dict): initial observation dictionary.
|
| 77 |
+
"""
|
| 78 |
+
self._current_obs = self.env.reset()
|
| 79 |
+
self._current_reward = None
|
| 80 |
+
self._current_done = None
|
| 81 |
+
return self.get_observation(self._current_obs)
|
| 82 |
+
|
| 83 |
+
def reset_to(self, state):
|
| 84 |
+
"""
|
| 85 |
+
Reset to a specific simulator state.
|
| 86 |
+
|
| 87 |
+
Args:
|
| 88 |
+
state (dict): current simulator state that contains:
|
| 89 |
+
- states (np.ndarray): initial state of the mujoco environment
|
| 90 |
+
|
| 91 |
+
Returns:
|
| 92 |
+
observation (dict): observation dictionary after setting the simulator state
|
| 93 |
+
"""
|
| 94 |
+
if hasattr(self.env.unwrapped.sim, "set_state_from_flattened"):
|
| 95 |
+
self.env.unwrapped.sim.set_state_from_flattened(state["states"])
|
| 96 |
+
self.env.unwrapped.sim.forward()
|
| 97 |
+
return { "flat" : self.env.unwrapped._get_obs() }
|
| 98 |
+
else:
|
| 99 |
+
raise NotImplementedError
|
| 100 |
+
|
| 101 |
+
def render(self, mode="human", height=None, width=None, camera_name=None, **kwargs):
|
| 102 |
+
"""
|
| 103 |
+
Render from simulation to either an on-screen window or off-screen to RGB array.
|
| 104 |
+
|
| 105 |
+
Args:
|
| 106 |
+
mode (str): pass "human" for on-screen rendering or "rgb_array" for off-screen rendering
|
| 107 |
+
height (int): height of image to render - only used if mode is "rgb_array"
|
| 108 |
+
width (int): width of image to render - only used if mode is "rgb_array"
|
| 109 |
+
"""
|
| 110 |
+
if mode =="human":
|
| 111 |
+
return self.env.render(mode=mode, **kwargs)
|
| 112 |
+
if mode == "rgb_array":
|
| 113 |
+
return self.env.render(mode="rgb_array", height=height, width=width)
|
| 114 |
+
else:
|
| 115 |
+
raise NotImplementedError("mode={} is not implemented".format(mode))
|
| 116 |
+
|
| 117 |
+
def get_observation(self, obs=None):
|
| 118 |
+
"""
|
| 119 |
+
Get current environment observation dictionary.
|
| 120 |
+
|
| 121 |
+
Args:
|
| 122 |
+
ob (np.array): current flat observation vector to wrap and provide as a dictionary.
|
| 123 |
+
If not provided, uses self._current_obs.
|
| 124 |
+
"""
|
| 125 |
+
if obs is None:
|
| 126 |
+
assert self._current_obs is not None
|
| 127 |
+
obs = self._current_obs
|
| 128 |
+
return { "flat" : np.copy(obs) }
|
| 129 |
+
|
| 130 |
+
def get_state(self):
|
| 131 |
+
"""
|
| 132 |
+
Get current environment simulator state as a dictionary. Should be compatible with @reset_to.
|
| 133 |
+
"""
|
| 134 |
+
# NOTE: assumes MuJoCo gym task!
|
| 135 |
+
xml = self.env.sim.model.get_xml() # model xml file
|
| 136 |
+
state = np.array(self.env.sim.get_state().flatten()) # simulator state
|
| 137 |
+
return dict(model=xml, states=state)
|
| 138 |
+
|
| 139 |
+
def get_reward(self):
|
| 140 |
+
"""
|
| 141 |
+
Get current reward.
|
| 142 |
+
"""
|
| 143 |
+
assert self._current_reward is not None
|
| 144 |
+
return self._current_reward
|
| 145 |
+
|
| 146 |
+
def get_goal(self):
|
| 147 |
+
"""
|
| 148 |
+
Get goal observation. Not all environments support this.
|
| 149 |
+
"""
|
| 150 |
+
raise NotImplementedError
|
| 151 |
+
|
| 152 |
+
def set_goal(self, **kwargs):
|
| 153 |
+
"""
|
| 154 |
+
Set goal observation with external specification. Not all environments support this.
|
| 155 |
+
"""
|
| 156 |
+
raise NotImplementedError
|
| 157 |
+
|
| 158 |
+
def is_done(self):
|
| 159 |
+
"""
|
| 160 |
+
Check if the task is done (not necessarily successful).
|
| 161 |
+
"""
|
| 162 |
+
assert self._current_done is not None
|
| 163 |
+
return self._current_done
|
| 164 |
+
|
| 165 |
+
def is_success(self):
|
| 166 |
+
"""
|
| 167 |
+
Check if the task condition(s) is reached. Should return a dictionary
|
| 168 |
+
{ str: bool } with at least a "task" key for the overall task success,
|
| 169 |
+
and additional optional keys corresponding to other task criteria.
|
| 170 |
+
"""
|
| 171 |
+
if hasattr(self.env.unwrapped, "_check_success"):
|
| 172 |
+
return self.env.unwrapped._check_success()
|
| 173 |
+
|
| 174 |
+
# gym envs generally don't check task success - we only compare returns
|
| 175 |
+
return { "task" : False }
|
| 176 |
+
|
| 177 |
+
@property
|
| 178 |
+
def action_dimension(self):
|
| 179 |
+
"""
|
| 180 |
+
Returns dimension of actions (int).
|
| 181 |
+
"""
|
| 182 |
+
return self.env.action_space.shape[0]
|
| 183 |
+
|
| 184 |
+
@property
|
| 185 |
+
def name(self):
|
| 186 |
+
"""
|
| 187 |
+
Returns name of environment name (str).
|
| 188 |
+
"""
|
| 189 |
+
return self._env_name
|
| 190 |
+
|
| 191 |
+
@property
|
| 192 |
+
def type(self):
|
| 193 |
+
"""
|
| 194 |
+
Returns environment type (int) for this kind of environment.
|
| 195 |
+
This helps identify this env class.
|
| 196 |
+
"""
|
| 197 |
+
return EB.EnvType.GYM_TYPE
|
| 198 |
+
|
| 199 |
+
def serialize(self):
|
| 200 |
+
"""
|
| 201 |
+
Save all information needed to re-instantiate this environment in a dictionary.
|
| 202 |
+
This is the same as @env_meta - environment metadata stored in hdf5 datasets,
|
| 203 |
+
and used in utils/env_utils.py.
|
| 204 |
+
"""
|
| 205 |
+
return dict(env_name=self.name, type=self.type, env_kwargs=deepcopy(self._init_kwargs))
|
| 206 |
+
|
| 207 |
+
@classmethod
|
| 208 |
+
def create_for_data_processing(cls, env_name, camera_names, camera_height, camera_width, reward_shaping, **kwargs):
|
| 209 |
+
"""
|
| 210 |
+
Create environment for processing datasets, which includes extracting
|
| 211 |
+
observations, labeling dense / sparse rewards, and annotating dones in
|
| 212 |
+
transitions. For gym environments, input arguments (other than @env_name)
|
| 213 |
+
are ignored, since environments are mostly pre-configured.
|
| 214 |
+
|
| 215 |
+
Args:
|
| 216 |
+
env_name (str): name of gym environment to create
|
| 217 |
+
|
| 218 |
+
Returns:
|
| 219 |
+
env (EnvGym instance)
|
| 220 |
+
"""
|
| 221 |
+
|
| 222 |
+
# make sure to initialize obs utils so it knows which modalities are image modalities.
|
| 223 |
+
# For currently supported gym tasks, there are no image observations.
|
| 224 |
+
obs_modality_specs = {
|
| 225 |
+
"obs": {
|
| 226 |
+
"low_dim": ["flat"],
|
| 227 |
+
"rgb": [],
|
| 228 |
+
}
|
| 229 |
+
}
|
| 230 |
+
ObsUtils.initialize_obs_utils_with_obs_specs(obs_modality_specs)
|
| 231 |
+
|
| 232 |
+
return cls(env_name=env_name, **kwargs)
|
| 233 |
+
|
| 234 |
+
@property
|
| 235 |
+
def rollout_exceptions(self):
|
| 236 |
+
"""
|
| 237 |
+
Return tuple of exceptions to except when doing rollouts. This is useful to ensure
|
| 238 |
+
that the entire training run doesn't crash because of a bad policy that causes unstable
|
| 239 |
+
simulation computations.
|
| 240 |
+
"""
|
| 241 |
+
return ()
|
| 242 |
+
|
| 243 |
+
def __repr__(self):
|
| 244 |
+
"""
|
| 245 |
+
Pretty-print env description.
|
| 246 |
+
"""
|
| 247 |
+
return self.name + "\n" + json.dumps(self._init_kwargs, sort_keys=True, indent=4)
|
aloha-devel/robomimic/envs/env_ig_momart.py
ADDED
|
@@ -0,0 +1,395 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Wrapper environment class to enable using iGibson-based environments used in the MOMART paper
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
from copy import deepcopy
|
| 6 |
+
import numpy as np
|
| 7 |
+
import json
|
| 8 |
+
|
| 9 |
+
import pybullet as p
|
| 10 |
+
import gibson2
|
| 11 |
+
from gibson2.envs.semantic_organize_and_fetch import SemanticOrganizeAndFetch
|
| 12 |
+
from gibson2.utils.custom_utils import ObjectConfig
|
| 13 |
+
import gibson2.external.pybullet_tools.utils as PBU
|
| 14 |
+
import tempfile
|
| 15 |
+
import os
|
| 16 |
+
import yaml
|
| 17 |
+
import cv2
|
| 18 |
+
|
| 19 |
+
import robomimic.utils.obs_utils as ObsUtils
|
| 20 |
+
import robomimic.envs.env_base as EB
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
# TODO: Once iG 2.0 is more stable, automate available environments, similar to robosuite
|
| 24 |
+
ENV_MAPPING = {
|
| 25 |
+
"SemanticOrganizeAndFetch": SemanticOrganizeAndFetch,
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
class EnvGibsonMOMART(EB.EnvBase):
|
| 30 |
+
"""
|
| 31 |
+
Wrapper class for gibson environments (https://github.com/StanfordVL/iGibson) specifically compatible with
|
| 32 |
+
MoMaRT datasets
|
| 33 |
+
"""
|
| 34 |
+
def __init__(
|
| 35 |
+
self,
|
| 36 |
+
env_name,
|
| 37 |
+
ig_config,
|
| 38 |
+
postprocess_visual_obs=True,
|
| 39 |
+
render=False,
|
| 40 |
+
render_offscreen=False,
|
| 41 |
+
use_image_obs=False,
|
| 42 |
+
image_height=None,
|
| 43 |
+
image_width=None,
|
| 44 |
+
physics_timestep=1./240.,
|
| 45 |
+
action_timestep=1./20.,
|
| 46 |
+
**kwargs,
|
| 47 |
+
):
|
| 48 |
+
"""
|
| 49 |
+
Args:
|
| 50 |
+
ig_config (dict): YAML configuration to use for iGibson, as a dict
|
| 51 |
+
|
| 52 |
+
postprocess_visual_obs (bool): if True, postprocess image observations
|
| 53 |
+
to prepare for learning
|
| 54 |
+
|
| 55 |
+
render (bool): if True, environment supports on-screen rendering
|
| 56 |
+
|
| 57 |
+
render_offscreen (bool): if True, environment supports off-screen rendering. This
|
| 58 |
+
is forced to be True if @use_image_obs is True.
|
| 59 |
+
|
| 60 |
+
use_image_obs (bool): if True, environment is expected to render rgb image observations
|
| 61 |
+
on every env.step call. Set this to False for efficiency reasons, if image
|
| 62 |
+
observations are not required.
|
| 63 |
+
|
| 64 |
+
render_mode (str): How to run simulation rendering. Options are {"pbgui", "iggui", or "headless"}
|
| 65 |
+
|
| 66 |
+
image_height (int): If specified, overrides internal iG image height when rendering
|
| 67 |
+
|
| 68 |
+
image_width (int): If specified, overrides internal iG image width when rendering
|
| 69 |
+
|
| 70 |
+
physics_timestep (float): Pybullet physics timestep to use
|
| 71 |
+
|
| 72 |
+
action_timestep (float): Action timestep to use for robot in simulation
|
| 73 |
+
|
| 74 |
+
kwargs (unrolled dict): Any args to substitute in the ig_configuration
|
| 75 |
+
"""
|
| 76 |
+
self._env_name = env_name
|
| 77 |
+
self.ig_config = deepcopy(ig_config)
|
| 78 |
+
self.postprocess_visual_obs = postprocess_visual_obs
|
| 79 |
+
self._init_kwargs = kwargs
|
| 80 |
+
|
| 81 |
+
# Determine rendering mode
|
| 82 |
+
self.render_mode = "iggui" if render else "headless"
|
| 83 |
+
self.render_onscreen = render
|
| 84 |
+
|
| 85 |
+
# Make sure rgb is part of obs in ig config
|
| 86 |
+
self.ig_config["output"] = list(set(self.ig_config["output"] + ["rgb"]))
|
| 87 |
+
|
| 88 |
+
# Warn user that iG always uses a renderer
|
| 89 |
+
if (not render) and (not render_offscreen):
|
| 90 |
+
print("WARNING: iGibson always uses a renderer -- using headless by default.")
|
| 91 |
+
|
| 92 |
+
# Update ig config
|
| 93 |
+
for k, v in kwargs.items():
|
| 94 |
+
assert k in self.ig_config, f"Got unknown ig configuration key {k}!"
|
| 95 |
+
self.ig_config[k] = v
|
| 96 |
+
|
| 97 |
+
# Set rendering values
|
| 98 |
+
self.obs_img_height = image_height if image_height is not None else self.ig_config.get("obs_image_height", 120)
|
| 99 |
+
self.obs_img_width = image_width if image_width is not None else self.ig_config.get("obs_image_width", 120)
|
| 100 |
+
|
| 101 |
+
# Get class to create
|
| 102 |
+
envClass = ENV_MAPPING.get(self._env_name, None)
|
| 103 |
+
|
| 104 |
+
# Make sure we have a valid environment class
|
| 105 |
+
assert envClass is not None, "No valid environment for the requested task was found!"
|
| 106 |
+
|
| 107 |
+
# Set device idx for rendering
|
| 108 |
+
# ensure that we select the correct GPU device for rendering by testing for EGL rendering
|
| 109 |
+
# NOTE: this package should be installed from this link (https://github.com/StanfordVL/egl_probe)
|
| 110 |
+
import egl_probe
|
| 111 |
+
device_idx = 0
|
| 112 |
+
valid_gpu_devices = egl_probe.get_available_devices()
|
| 113 |
+
if len(valid_gpu_devices) > 0:
|
| 114 |
+
device_idx = valid_gpu_devices[0]
|
| 115 |
+
|
| 116 |
+
# Create environment
|
| 117 |
+
self.env = envClass(
|
| 118 |
+
config_file=deepcopy(self.ig_config),
|
| 119 |
+
mode=self.render_mode,
|
| 120 |
+
physics_timestep=physics_timestep,
|
| 121 |
+
action_timestep=action_timestep,
|
| 122 |
+
device_idx=device_idx,
|
| 123 |
+
)
|
| 124 |
+
|
| 125 |
+
# If we have a viewer, make sure to remove all bodies belonging to the visual markers
|
| 126 |
+
self.exclude_body_ids = [] # Bodies to exclude when saving state
|
| 127 |
+
if self.env.simulator.viewer is not None:
|
| 128 |
+
self.exclude_body_ids.append(self.env.simulator.viewer.constraint_marker.body_id)
|
| 129 |
+
self.exclude_body_ids.append(self.env.simulator.viewer.constraint_marker2.body_id)
|
| 130 |
+
|
| 131 |
+
def step(self, action):
|
| 132 |
+
"""
|
| 133 |
+
Step in the environment with an action
|
| 134 |
+
|
| 135 |
+
Args:
|
| 136 |
+
action: action to take
|
| 137 |
+
|
| 138 |
+
Returns:
|
| 139 |
+
observation: new observation
|
| 140 |
+
reward: step reward
|
| 141 |
+
done: whether the task is done
|
| 142 |
+
info: extra information
|
| 143 |
+
"""
|
| 144 |
+
obs, r, done, info = self.env.step(action)
|
| 145 |
+
obs = self.get_observation(obs)
|
| 146 |
+
return obs, r, self.is_done(), info
|
| 147 |
+
|
| 148 |
+
def reset(self):
|
| 149 |
+
"""Reset environment"""
|
| 150 |
+
di = self.env.reset()
|
| 151 |
+
return self.get_observation(di)
|
| 152 |
+
|
| 153 |
+
def reset_to(self, state):
|
| 154 |
+
"""
|
| 155 |
+
Reset to a specific state
|
| 156 |
+
Args:
|
| 157 |
+
state (dict): contains:
|
| 158 |
+
- states (np.ndarray): initial state of the mujoco environment
|
| 159 |
+
- goal (dict): goal components to reset
|
| 160 |
+
Returns:
|
| 161 |
+
new observation
|
| 162 |
+
"""
|
| 163 |
+
if "states" in state:
|
| 164 |
+
self.env.reset_to(state["states"], exclude=self.exclude_body_ids)
|
| 165 |
+
|
| 166 |
+
if "goal" in state:
|
| 167 |
+
self.set_goal(**state["goal"])
|
| 168 |
+
|
| 169 |
+
# Return obs
|
| 170 |
+
return self.get_observation()
|
| 171 |
+
|
| 172 |
+
def render(self, mode="human", camera_name="rgb", height=None, width=None):
|
| 173 |
+
"""
|
| 174 |
+
Render
|
| 175 |
+
|
| 176 |
+
Args:
|
| 177 |
+
mode (str): Mode(s) to render. Options are either 'human' (rendering onscreen) or 'rgb' (rendering to
|
| 178 |
+
frames offscreen)
|
| 179 |
+
camera_name (str): Name of the camera to use -- valid options are "rgb" or "rgb_wrist"
|
| 180 |
+
height (int): If specified with width, resizes the rendered image to this height
|
| 181 |
+
width (int): If specified with height, resizes the rendered image to this width
|
| 182 |
+
|
| 183 |
+
Returns:
|
| 184 |
+
array or None: If rendering to frame, returns the rendered frame. Otherwise, returns None
|
| 185 |
+
"""
|
| 186 |
+
# Only robotview camera is currently supported
|
| 187 |
+
assert camera_name in {"rgb", "rgb_wrist"}, \
|
| 188 |
+
f"Only rgb, rgb_wrist cameras currently supported, got {camera_name}."
|
| 189 |
+
|
| 190 |
+
if mode == "human":
|
| 191 |
+
assert self.render_onscreen, "Rendering has not been enabled for onscreen!"
|
| 192 |
+
self.env.simulator.sync()
|
| 193 |
+
else:
|
| 194 |
+
assert self.env.simulator.renderer is not None, "No renderer enabled for this env!"
|
| 195 |
+
|
| 196 |
+
frame = self.env.sensors["vision"].get_obs(self.env)[camera_name]
|
| 197 |
+
|
| 198 |
+
# Reshape all frames
|
| 199 |
+
if height is not None and width is not None:
|
| 200 |
+
frame = cv2.resize(frame, dsize=(height, width), interpolation=cv2.INTER_CUBIC)
|
| 201 |
+
return frame
|
| 202 |
+
|
| 203 |
+
def resize_obs_frame(self, frame):
|
| 204 |
+
"""
|
| 205 |
+
Resizes frame to be internal height and width values
|
| 206 |
+
"""
|
| 207 |
+
return cv2.resize(frame, dsize=(self.obs_img_width, self.obs_img_height), interpolation=cv2.INTER_CUBIC)
|
| 208 |
+
|
| 209 |
+
def get_observation(self, di=None):
|
| 210 |
+
"""Get environment observation"""
|
| 211 |
+
if di is None:
|
| 212 |
+
di = self.env.get_state()
|
| 213 |
+
ret = {}
|
| 214 |
+
for k in di:
|
| 215 |
+
# RGB Images
|
| 216 |
+
if "rgb" in k:
|
| 217 |
+
ret[k] = di[k]
|
| 218 |
+
# ret[k] = np.transpose(di[k], (2, 0, 1))
|
| 219 |
+
if self.postprocess_visual_obs:
|
| 220 |
+
ret[k] = ObsUtils.process_obs(obs=self.resize_obs_frame(ret[k]), obs_key=k)
|
| 221 |
+
|
| 222 |
+
# Depth images
|
| 223 |
+
elif "depth" in k:
|
| 224 |
+
# ret[k] = np.transpose(di[k], (2, 0, 1))
|
| 225 |
+
# Values can be corrupted (negative or > 1.0, so we clip values)
|
| 226 |
+
ret[k] = np.clip(di[k], 0.0, 1.0)
|
| 227 |
+
if self.postprocess_visual_obs:
|
| 228 |
+
ret[k] = ObsUtils.process_obs(obs=self.resize_obs_frame(ret[k])[..., None], obs_key=k)
|
| 229 |
+
|
| 230 |
+
# Segmentation Images
|
| 231 |
+
elif "seg" in k:
|
| 232 |
+
ret[k] = di[k][..., None]
|
| 233 |
+
if self.postprocess_visual_obs:
|
| 234 |
+
ret[k] = ObsUtils.process_obs(obs=self.resize_obs_frame(ret[k]), obs_key=k)
|
| 235 |
+
|
| 236 |
+
# Scans
|
| 237 |
+
elif "scan" in k:
|
| 238 |
+
ret[k] = np.transpose(np.array(di[k]), axes=(1, 0))
|
| 239 |
+
|
| 240 |
+
# Compose proprio obs
|
| 241 |
+
proprio_obs = di["proprio"]
|
| 242 |
+
|
| 243 |
+
# Compute intermediate values
|
| 244 |
+
lin_vel = np.linalg.norm(proprio_obs["base_lin_vel"][:2])
|
| 245 |
+
ang_vel = proprio_obs["base_ang_vel"][2]
|
| 246 |
+
|
| 247 |
+
ret["proprio"] = np.concatenate([
|
| 248 |
+
proprio_obs["head_joint_pos"],
|
| 249 |
+
proprio_obs["grasped"],
|
| 250 |
+
proprio_obs["eef_pos"],
|
| 251 |
+
proprio_obs["eef_quat"],
|
| 252 |
+
])
|
| 253 |
+
|
| 254 |
+
# Proprio info that's only relevant for navigation
|
| 255 |
+
ret["proprio_nav"] = np.concatenate([
|
| 256 |
+
[lin_vel],
|
| 257 |
+
[ang_vel],
|
| 258 |
+
])
|
| 259 |
+
|
| 260 |
+
# Compose task obs
|
| 261 |
+
ret["object"] = np.concatenate([
|
| 262 |
+
np.array(di["task_obs"]["object-state"]),
|
| 263 |
+
])
|
| 264 |
+
|
| 265 |
+
# Add ground truth navigational state
|
| 266 |
+
ret["gt_nav"] = np.concatenate([
|
| 267 |
+
proprio_obs["base_pos"][:2],
|
| 268 |
+
[np.sin(proprio_obs["base_rpy"][2])],
|
| 269 |
+
[np.cos(proprio_obs["base_rpy"][2])],
|
| 270 |
+
])
|
| 271 |
+
|
| 272 |
+
return ret
|
| 273 |
+
|
| 274 |
+
def sync_task(self):
|
| 275 |
+
"""
|
| 276 |
+
Method to synchronize iG task, since we're not actually resetting the env but instead setting states directly.
|
| 277 |
+
Should only be called after resetting the initial state of an episode
|
| 278 |
+
"""
|
| 279 |
+
self.env.task.update_target_object_init_pos()
|
| 280 |
+
self.env.task.update_location_info()
|
| 281 |
+
|
| 282 |
+
def set_task_conditions(self, task_conditions):
|
| 283 |
+
"""
|
| 284 |
+
Method to override task conditions (e.g.: target object), useful in cases such as playing back
|
| 285 |
+
from demonstrations
|
| 286 |
+
|
| 287 |
+
Args:
|
| 288 |
+
task_conditions (dict): Keyword-mapped arguments to pass to task instance to set internally
|
| 289 |
+
"""
|
| 290 |
+
self.env.set_task_conditions(task_conditions)
|
| 291 |
+
|
| 292 |
+
def get_state(self):
|
| 293 |
+
"""Get iG flattened state"""
|
| 294 |
+
return {"states": PBU.WorldSaver(exclude_body_ids=self.exclude_body_ids).serialize()}
|
| 295 |
+
|
| 296 |
+
def get_reward(self):
|
| 297 |
+
return self.env.task.get_reward(self.env)[0]
|
| 298 |
+
# return float(self.is_success()["task"])
|
| 299 |
+
|
| 300 |
+
def get_goal(self):
|
| 301 |
+
"""Get goal specification"""
|
| 302 |
+
# No support yet in iG
|
| 303 |
+
raise NotImplementedError
|
| 304 |
+
|
| 305 |
+
def set_goal(self, **kwargs):
|
| 306 |
+
"""Set env target with external specification"""
|
| 307 |
+
# No support yet in iG
|
| 308 |
+
raise NotImplementedError
|
| 309 |
+
|
| 310 |
+
def is_done(self):
|
| 311 |
+
"""Check if the agent is done (not necessarily successful)."""
|
| 312 |
+
return False
|
| 313 |
+
|
| 314 |
+
def is_success(self):
|
| 315 |
+
"""
|
| 316 |
+
Check if the task condition(s) is reached. Should return a dictionary
|
| 317 |
+
{ str: bool } with at least a "task" key for the overall task success,
|
| 318 |
+
and additional optional keys corresponding to other task criteria.
|
| 319 |
+
"""
|
| 320 |
+
succ = self.env.check_success()
|
| 321 |
+
if isinstance(succ, dict):
|
| 322 |
+
assert "task" in succ
|
| 323 |
+
return succ
|
| 324 |
+
return { "task" : succ }
|
| 325 |
+
|
| 326 |
+
@classmethod
|
| 327 |
+
def create_for_data_processing(
|
| 328 |
+
cls,
|
| 329 |
+
env_name,
|
| 330 |
+
camera_names,
|
| 331 |
+
camera_height,
|
| 332 |
+
camera_width,
|
| 333 |
+
reward_shaping,
|
| 334 |
+
**kwargs,
|
| 335 |
+
):
|
| 336 |
+
"""
|
| 337 |
+
Create environment for processing datasets, which includes extracting
|
| 338 |
+
observations, labeling dense / sparse rewards, and annotating dones in
|
| 339 |
+
transitions.
|
| 340 |
+
|
| 341 |
+
Args:
|
| 342 |
+
env_name (str): name of environment
|
| 343 |
+
camera_names (list of str): list of camera names that correspond to image observations
|
| 344 |
+
camera_height (int): camera height for all cameras
|
| 345 |
+
camera_width (int): camera width for all cameras
|
| 346 |
+
reward_shaping (bool): if True, use shaped environment rewards, else use sparse task completion rewards
|
| 347 |
+
"""
|
| 348 |
+
has_camera = (len(camera_names) > 0)
|
| 349 |
+
|
| 350 |
+
# note that @postprocess_visual_obs is False since this env's images will be written to a dataset
|
| 351 |
+
return cls(
|
| 352 |
+
env_name=env_name,
|
| 353 |
+
render=False,
|
| 354 |
+
render_offscreen=has_camera,
|
| 355 |
+
use_image_obs=has_camera,
|
| 356 |
+
postprocess_visual_obs=False,
|
| 357 |
+
image_height=camera_height,
|
| 358 |
+
image_width=camera_width,
|
| 359 |
+
**kwargs,
|
| 360 |
+
)
|
| 361 |
+
|
| 362 |
+
@property
|
| 363 |
+
def action_dimension(self):
|
| 364 |
+
"""Action dimension"""
|
| 365 |
+
return self.env.robots[0].action_dim
|
| 366 |
+
|
| 367 |
+
@property
|
| 368 |
+
def name(self):
|
| 369 |
+
"""Environment name"""
|
| 370 |
+
return self._env_name
|
| 371 |
+
|
| 372 |
+
@property
|
| 373 |
+
def type(self):
|
| 374 |
+
"""Environment type"""
|
| 375 |
+
return EB.EnvType.IG_MOMART_TYPE
|
| 376 |
+
|
| 377 |
+
def serialize(self):
|
| 378 |
+
"""Serialize to dictionary"""
|
| 379 |
+
return dict(env_name=self.name, type=self.type,
|
| 380 |
+
ig_config=self.ig_config,
|
| 381 |
+
env_kwargs=deepcopy(self._init_kwargs))
|
| 382 |
+
|
| 383 |
+
@classmethod
|
| 384 |
+
def deserialize(cls, info, postprocess_visual_obs=True):
|
| 385 |
+
"""Create environment with external info"""
|
| 386 |
+
return cls(env_name=info["env_name"], ig_config=info["ig_config"], postprocess_visual_obs=postprocess_visual_obs, **info["env_kwargs"])
|
| 387 |
+
|
| 388 |
+
@property
|
| 389 |
+
def rollout_exceptions(self):
|
| 390 |
+
"""Return tuple of exceptions to except when doing rollouts"""
|
| 391 |
+
return (RuntimeError)
|
| 392 |
+
|
| 393 |
+
def __repr__(self):
|
| 394 |
+
return self.name + "\n" + json.dumps(self._init_kwargs, sort_keys=True, indent=4) + \
|
| 395 |
+
"\niGibson Config: \n" + json.dumps(self.ig_config, sort_keys=True, indent=4)
|
aloha-devel/robomimic/envs/env_robosuite.py
ADDED
|
@@ -0,0 +1,415 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
This file contains the robosuite environment wrapper that is used
|
| 3 |
+
to provide a standardized environment API for training policies and interacting
|
| 4 |
+
with metadata present in datasets.
|
| 5 |
+
"""
|
| 6 |
+
import json
|
| 7 |
+
import numpy as np
|
| 8 |
+
from copy import deepcopy
|
| 9 |
+
|
| 10 |
+
import robosuite
|
| 11 |
+
|
| 12 |
+
import robomimic.utils.obs_utils as ObsUtils
|
| 13 |
+
import robomimic.utils.lang_utils as LangUtils
|
| 14 |
+
import robomimic.envs.env_base as EB
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
class EnvRobosuite(EB.EnvBase):
|
| 18 |
+
"""Wrapper class for robosuite environments (https://github.com/ARISE-Initiative/robosuite)"""
|
| 19 |
+
def __init__(
|
| 20 |
+
self,
|
| 21 |
+
env_name,
|
| 22 |
+
render=False,
|
| 23 |
+
render_offscreen=False,
|
| 24 |
+
use_image_obs=False,
|
| 25 |
+
postprocess_visual_obs=True,
|
| 26 |
+
lang=None,
|
| 27 |
+
**kwargs,
|
| 28 |
+
):
|
| 29 |
+
"""
|
| 30 |
+
Args:
|
| 31 |
+
env_name (str): name of environment. Only needs to be provided if making a different
|
| 32 |
+
environment from the one in @env_meta.
|
| 33 |
+
|
| 34 |
+
render (bool): if True, environment supports on-screen rendering
|
| 35 |
+
|
| 36 |
+
render_offscreen (bool): if True, environment supports off-screen rendering. This
|
| 37 |
+
is forced to be True if @env_meta["use_images"] is True.
|
| 38 |
+
|
| 39 |
+
use_image_obs (bool): if True, environment is expected to render rgb image observations
|
| 40 |
+
on every env.step call. Set this to False for efficiency reasons, if image
|
| 41 |
+
observations are not required.
|
| 42 |
+
|
| 43 |
+
postprocess_visual_obs (bool): if True, postprocess image observations
|
| 44 |
+
to prepare for learning. This should only be False when extracting observations
|
| 45 |
+
for saving to a dataset (to save space on RGB images for example).
|
| 46 |
+
|
| 47 |
+
lang: TODO add documentation
|
| 48 |
+
"""
|
| 49 |
+
self.postprocess_visual_obs = postprocess_visual_obs
|
| 50 |
+
|
| 51 |
+
# robosuite version check
|
| 52 |
+
self._is_v1 = (robosuite.__version__.split(".")[0] == "1")
|
| 53 |
+
if self._is_v1:
|
| 54 |
+
assert (int(robosuite.__version__.split(".")[1]) >= 2), "only support robosuite v0.3 and v1.2+"
|
| 55 |
+
|
| 56 |
+
kwargs = deepcopy(kwargs)
|
| 57 |
+
|
| 58 |
+
# update kwargs based on passed arguments
|
| 59 |
+
update_kwargs = dict(
|
| 60 |
+
has_renderer=render,
|
| 61 |
+
has_offscreen_renderer=(render_offscreen or use_image_obs),
|
| 62 |
+
ignore_done=True,
|
| 63 |
+
use_object_obs=True,
|
| 64 |
+
use_camera_obs=use_image_obs,
|
| 65 |
+
camera_depths=False,
|
| 66 |
+
)
|
| 67 |
+
kwargs.update(update_kwargs)
|
| 68 |
+
|
| 69 |
+
if self._is_v1:
|
| 70 |
+
if kwargs["has_offscreen_renderer"]:
|
| 71 |
+
# ensure that we select the correct GPU device for rendering by testing for EGL rendering
|
| 72 |
+
# NOTE: this package should be installed from this link (https://github.com/StanfordVL/egl_probe)
|
| 73 |
+
import egl_probe
|
| 74 |
+
valid_gpu_devices = egl_probe.get_available_devices()
|
| 75 |
+
if len(valid_gpu_devices) > 0:
|
| 76 |
+
kwargs["render_gpu_device_id"] = valid_gpu_devices[0]
|
| 77 |
+
else:
|
| 78 |
+
# make sure gripper visualization is turned off (we almost always want this for learning)
|
| 79 |
+
kwargs["gripper_visualization"] = False
|
| 80 |
+
del kwargs["camera_depths"]
|
| 81 |
+
kwargs["camera_depth"] = False # rename kwarg
|
| 82 |
+
|
| 83 |
+
self._env_name = env_name
|
| 84 |
+
self._init_kwargs = deepcopy(kwargs)
|
| 85 |
+
self.env = robosuite.make(self._env_name, **kwargs)
|
| 86 |
+
self.base_env = self.env # for mimicgen
|
| 87 |
+
self.lang = lang
|
| 88 |
+
self._lang_emb = LangUtils.get_lang_emb(self.lang)
|
| 89 |
+
|
| 90 |
+
if self._is_v1:
|
| 91 |
+
# Make sure joint position observations and eef vel observations are active
|
| 92 |
+
for ob_name in self.env.observation_names:
|
| 93 |
+
if ("joint_pos" in ob_name) or ("eef_vel" in ob_name):
|
| 94 |
+
self.env.modify_observable(observable_name=ob_name, attribute="active", modifier=True)
|
| 95 |
+
|
| 96 |
+
def step(self, action):
|
| 97 |
+
"""
|
| 98 |
+
Step in the environment with an action.
|
| 99 |
+
|
| 100 |
+
Args:
|
| 101 |
+
action (np.array): action to take
|
| 102 |
+
|
| 103 |
+
Returns:
|
| 104 |
+
observation (dict): new observation dictionary
|
| 105 |
+
reward (float): reward for this step
|
| 106 |
+
done (bool): whether the task is done
|
| 107 |
+
info (dict): extra information
|
| 108 |
+
"""
|
| 109 |
+
obs, r, done, info = self.env.step(action)
|
| 110 |
+
obs = self.get_observation(obs)
|
| 111 |
+
info["is_success"] = self.is_success()
|
| 112 |
+
return obs, r, self.is_done(), info
|
| 113 |
+
|
| 114 |
+
def reset(self):
|
| 115 |
+
"""
|
| 116 |
+
Reset environment.
|
| 117 |
+
|
| 118 |
+
Returns:
|
| 119 |
+
observation (dict): initial observation dictionary.
|
| 120 |
+
"""
|
| 121 |
+
di = self.env.reset()
|
| 122 |
+
return self.get_observation(di)
|
| 123 |
+
|
| 124 |
+
#notifies the environment whether or not the next environemnt testing object should update its category
|
| 125 |
+
def update_env(self, attr, value):
|
| 126 |
+
self.env.attr = value
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
def reset_to(self, state):
|
| 130 |
+
"""
|
| 131 |
+
Reset to a specific simulator state.
|
| 132 |
+
|
| 133 |
+
Args:
|
| 134 |
+
state (dict): current simulator state that contains one or more of:
|
| 135 |
+
- states (np.ndarray): initial state of the mujoco environment
|
| 136 |
+
- model (str): mujoco scene xml
|
| 137 |
+
|
| 138 |
+
Returns:
|
| 139 |
+
observation (dict): observation dictionary after setting the simulator state (only
|
| 140 |
+
if "states" is in @state)
|
| 141 |
+
"""
|
| 142 |
+
should_ret = False
|
| 143 |
+
if "model" in state:
|
| 144 |
+
if state.get("ep_meta", None) is not None:
|
| 145 |
+
# set relevant episode information
|
| 146 |
+
ep_meta = json.loads(state["ep_meta"])
|
| 147 |
+
self.env.set_attrs_from_ep_meta(ep_meta)
|
| 148 |
+
|
| 149 |
+
# this reset is necessary.
|
| 150 |
+
# while the call to env.reset_from_xml_string does call reset,
|
| 151 |
+
# that is only a "soft" reset that doesn't actually reload the model.
|
| 152 |
+
self.reset()
|
| 153 |
+
robosuite_version_id = int(robosuite.__version__.split(".")[1])
|
| 154 |
+
if robosuite_version_id <= 3:
|
| 155 |
+
from robosuite.utils.mjcf_utils import postprocess_model_xml
|
| 156 |
+
xml = postprocess_model_xml(state["model"])
|
| 157 |
+
else:
|
| 158 |
+
# v1.4 and above use the class-based edit_model_xml function
|
| 159 |
+
xml = self.env.edit_model_xml(state["model"])
|
| 160 |
+
self.env.reset_from_xml_string(xml)
|
| 161 |
+
self.env.sim.reset()
|
| 162 |
+
if not self._is_v1:
|
| 163 |
+
# hide teleop visualization after restoring from model
|
| 164 |
+
self.env.sim.model.site_rgba[self.env.eef_site_id] = np.array([0., 0., 0., 0.])
|
| 165 |
+
self.env.sim.model.site_rgba[self.env.eef_cylinder_id] = np.array([0., 0., 0., 0.])
|
| 166 |
+
if "states" in state:
|
| 167 |
+
self.env.sim.set_state_from_flattened(state["states"])
|
| 168 |
+
self.env.sim.forward()
|
| 169 |
+
should_ret = True
|
| 170 |
+
|
| 171 |
+
if "goal" in state:
|
| 172 |
+
self.set_goal(**state["goal"])
|
| 173 |
+
if should_ret:
|
| 174 |
+
# only return obs if we've done a forward call - otherwise the observations will be garbage
|
| 175 |
+
return self.get_observation()
|
| 176 |
+
return None
|
| 177 |
+
|
| 178 |
+
def render(self, mode="human", height=None, width=None, camera_name=None):
|
| 179 |
+
"""
|
| 180 |
+
Render from simulation to either an on-screen window or off-screen to RGB array.
|
| 181 |
+
|
| 182 |
+
Args:
|
| 183 |
+
mode (str): pass "human" for on-screen rendering or "rgb_array" for off-screen rendering
|
| 184 |
+
height (int): height of image to render - only used if mode is "rgb_array"
|
| 185 |
+
width (int): width of image to render - only used if mode is "rgb_array"
|
| 186 |
+
camera_name (str): camera name to use for rendering
|
| 187 |
+
"""
|
| 188 |
+
# if camera_name is None, infer from initial env kwargs
|
| 189 |
+
if camera_name is None:
|
| 190 |
+
camera_name = self._init_kwargs.get("camera_names", ["agentview"])[0]
|
| 191 |
+
|
| 192 |
+
if mode == "human":
|
| 193 |
+
cam_id = self.env.sim.model.camera_name2id(camera_name)
|
| 194 |
+
self.env.viewer.set_camera(cam_id)
|
| 195 |
+
return self.env.render()
|
| 196 |
+
elif mode == "rgb_array":
|
| 197 |
+
return self.env.sim.render(height=height, width=width, camera_name=camera_name)[::-1]
|
| 198 |
+
else:
|
| 199 |
+
raise NotImplementedError("mode={} is not implemented".format(mode))
|
| 200 |
+
|
| 201 |
+
def get_observation(self, di=None):
|
| 202 |
+
"""
|
| 203 |
+
Get current environment observation dictionary.
|
| 204 |
+
|
| 205 |
+
Args:
|
| 206 |
+
di (dict): current raw observation dictionary from robosuite to wrap and provide
|
| 207 |
+
as a dictionary. If not provided, will be queried from robosuite.
|
| 208 |
+
"""
|
| 209 |
+
if di is None:
|
| 210 |
+
di = self.env._get_observations(force_update=True) if self._is_v1 else self.env._get_observation()
|
| 211 |
+
ret = {}
|
| 212 |
+
for k in di:
|
| 213 |
+
if (k in ObsUtils.OBS_KEYS_TO_MODALITIES) and ObsUtils.key_is_obs_modality(key=k, obs_modality="rgb"):
|
| 214 |
+
ret[k] = di[k][::-1]
|
| 215 |
+
if self.postprocess_visual_obs:
|
| 216 |
+
ret[k] = ObsUtils.process_obs(obs=ret[k], obs_key=k)
|
| 217 |
+
|
| 218 |
+
# "object" key contains object information
|
| 219 |
+
ret["object"] = np.array(di["object-state"])
|
| 220 |
+
|
| 221 |
+
if self._is_v1:
|
| 222 |
+
for robot in self.env.robots:
|
| 223 |
+
# add all robot-arm-specific observations. Note the (k not in ret) check
|
| 224 |
+
# ensures that we don't accidentally add robot wrist images a second time
|
| 225 |
+
pf = robot.robot_model.naming_prefix
|
| 226 |
+
for k in di:
|
| 227 |
+
if k.startswith(pf) and (k not in ret) and \
|
| 228 |
+
(not k.endswith("proprio-state")):
|
| 229 |
+
ret[k] = np.array(di[k])
|
| 230 |
+
else:
|
| 231 |
+
# minimal proprioception for older versions of robosuite
|
| 232 |
+
ret["proprio"] = np.array(di["robot-state"])
|
| 233 |
+
ret["eef_pos"] = np.array(di["eef_pos"])
|
| 234 |
+
ret["eef_quat"] = np.array(di["eef_quat"])
|
| 235 |
+
ret["gripper_qpos"] = np.array(di["gripper_qpos"])
|
| 236 |
+
|
| 237 |
+
if self._lang_emb is not None:
|
| 238 |
+
ret["lang_emb"] = np.array(self._lang_emb)
|
| 239 |
+
return ret
|
| 240 |
+
|
| 241 |
+
def get_state(self):
|
| 242 |
+
"""
|
| 243 |
+
Get current environment simulator state as a dictionary. Should be compatible with @reset_to.
|
| 244 |
+
"""
|
| 245 |
+
xml = self.env.sim.model.get_xml() # model xml file
|
| 246 |
+
state = np.array(self.env.sim.get_state().flatten()) # simulator state
|
| 247 |
+
info = dict(model=xml, states=state)
|
| 248 |
+
if hasattr(self.env, "get_ep_meta"):
|
| 249 |
+
# get ep_meta if applicable
|
| 250 |
+
info["ep_meta"] = json.dumps(self.env.get_ep_meta(), indent=4)
|
| 251 |
+
return info
|
| 252 |
+
|
| 253 |
+
def get_reward(self):
|
| 254 |
+
"""
|
| 255 |
+
Get current reward.
|
| 256 |
+
"""
|
| 257 |
+
return self.env.reward()
|
| 258 |
+
|
| 259 |
+
def get_goal(self):
|
| 260 |
+
"""
|
| 261 |
+
Get goal observation. Not all environments support this.
|
| 262 |
+
"""
|
| 263 |
+
return self.get_observation(self.env._get_goal())
|
| 264 |
+
|
| 265 |
+
def set_goal(self, **kwargs):
|
| 266 |
+
"""
|
| 267 |
+
Set goal observation with external specification. Not all environments support this.
|
| 268 |
+
"""
|
| 269 |
+
return self.env.set_goal(**kwargs)
|
| 270 |
+
|
| 271 |
+
def is_done(self):
|
| 272 |
+
"""
|
| 273 |
+
Check if the task is done (not necessarily successful).
|
| 274 |
+
"""
|
| 275 |
+
|
| 276 |
+
# Robosuite envs always rollout to fixed horizon.
|
| 277 |
+
return False
|
| 278 |
+
|
| 279 |
+
def is_success(self):
|
| 280 |
+
"""
|
| 281 |
+
Check if the task condition(s) is reached. Should return a dictionary
|
| 282 |
+
{ str: bool } with at least a "task" key for the overall task success,
|
| 283 |
+
and additional optional keys corresponding to other task criteria.
|
| 284 |
+
"""
|
| 285 |
+
succ = self.env._check_success()
|
| 286 |
+
if isinstance(succ, dict):
|
| 287 |
+
assert "task" in succ
|
| 288 |
+
return succ
|
| 289 |
+
return { "task" : succ }
|
| 290 |
+
|
| 291 |
+
@property
|
| 292 |
+
def action_dimension(self):
|
| 293 |
+
"""
|
| 294 |
+
Returns dimension of actions (int).
|
| 295 |
+
"""
|
| 296 |
+
return self.env.action_spec[0].shape[0]
|
| 297 |
+
|
| 298 |
+
@property
|
| 299 |
+
def name(self):
|
| 300 |
+
"""
|
| 301 |
+
Returns name of environment name (str).
|
| 302 |
+
"""
|
| 303 |
+
return self._env_name
|
| 304 |
+
|
| 305 |
+
@property
|
| 306 |
+
def type(self):
|
| 307 |
+
"""
|
| 308 |
+
Returns environment type (int) for this kind of environment.
|
| 309 |
+
This helps identify this env class.
|
| 310 |
+
"""
|
| 311 |
+
return EB.EnvType.ROBOSUITE_TYPE
|
| 312 |
+
|
| 313 |
+
@property
|
| 314 |
+
def version(self):
|
| 315 |
+
"""
|
| 316 |
+
Returns version of robosuite used for this environment, eg. 1.2.0
|
| 317 |
+
"""
|
| 318 |
+
return robosuite.__version__
|
| 319 |
+
|
| 320 |
+
def serialize(self):
|
| 321 |
+
"""
|
| 322 |
+
Save all information needed to re-instantiate this environment in a dictionary.
|
| 323 |
+
This is the same as @env_meta - environment metadata stored in hdf5 datasets,
|
| 324 |
+
and used in utils/env_utils.py.
|
| 325 |
+
"""
|
| 326 |
+
return dict(
|
| 327 |
+
env_name=self.name,
|
| 328 |
+
env_version=self.version,
|
| 329 |
+
type=self.type,
|
| 330 |
+
env_kwargs=deepcopy(self._init_kwargs)
|
| 331 |
+
)
|
| 332 |
+
|
| 333 |
+
@classmethod
|
| 334 |
+
def create_for_data_processing(
|
| 335 |
+
cls,
|
| 336 |
+
env_name,
|
| 337 |
+
camera_names,
|
| 338 |
+
camera_height,
|
| 339 |
+
camera_width,
|
| 340 |
+
reward_shaping,
|
| 341 |
+
**kwargs,
|
| 342 |
+
):
|
| 343 |
+
"""
|
| 344 |
+
Create environment for processing datasets, which includes extracting
|
| 345 |
+
observations, labeling dense / sparse rewards, and annotating dones in
|
| 346 |
+
transitions.
|
| 347 |
+
|
| 348 |
+
Args:
|
| 349 |
+
env_name (str): name of environment
|
| 350 |
+
camera_names (list of str): list of camera names that correspond to image observations
|
| 351 |
+
camera_height (int): camera height for all cameras
|
| 352 |
+
camera_width (int): camera width for all cameras
|
| 353 |
+
reward_shaping (bool): if True, use shaped environment rewards, else use sparse task completion rewards
|
| 354 |
+
"""
|
| 355 |
+
is_v1 = (robosuite.__version__.split(".")[0] == "1")
|
| 356 |
+
has_camera = (len(camera_names) > 0)
|
| 357 |
+
|
| 358 |
+
new_kwargs = {
|
| 359 |
+
"reward_shaping": reward_shaping,
|
| 360 |
+
}
|
| 361 |
+
|
| 362 |
+
if has_camera:
|
| 363 |
+
if is_v1:
|
| 364 |
+
new_kwargs["camera_names"] = list(camera_names)
|
| 365 |
+
new_kwargs["camera_heights"] = camera_height
|
| 366 |
+
new_kwargs["camera_widths"] = camera_width
|
| 367 |
+
else:
|
| 368 |
+
assert len(camera_names) == 1
|
| 369 |
+
if has_camera:
|
| 370 |
+
new_kwargs["camera_name"] = camera_names[0]
|
| 371 |
+
new_kwargs["camera_height"] = camera_height
|
| 372 |
+
new_kwargs["camera_width"] = camera_width
|
| 373 |
+
|
| 374 |
+
kwargs.update(new_kwargs)
|
| 375 |
+
|
| 376 |
+
# also initialize obs utils so it knows which modalities are image modalities
|
| 377 |
+
image_modalities = list(camera_names)
|
| 378 |
+
if is_v1:
|
| 379 |
+
image_modalities = ["{}_image".format(cn) for cn in camera_names]
|
| 380 |
+
elif has_camera:
|
| 381 |
+
# v0.3 only had support for one image, and it was named "rgb"
|
| 382 |
+
assert len(image_modalities) == 1
|
| 383 |
+
image_modalities = ["rgb"]
|
| 384 |
+
obs_modality_specs = {
|
| 385 |
+
"obs": {
|
| 386 |
+
"low_dim": [], # technically unused, so we don't have to specify all of them
|
| 387 |
+
"rgb": image_modalities,
|
| 388 |
+
}
|
| 389 |
+
}
|
| 390 |
+
ObsUtils.initialize_obs_utils_with_obs_specs(obs_modality_specs)
|
| 391 |
+
|
| 392 |
+
# note that @postprocess_visual_obs is False since this env's images will be written to a dataset
|
| 393 |
+
return cls(
|
| 394 |
+
env_name=env_name,
|
| 395 |
+
render=False,
|
| 396 |
+
render_offscreen=has_camera,
|
| 397 |
+
use_image_obs=has_camera,
|
| 398 |
+
postprocess_visual_obs=False,
|
| 399 |
+
**kwargs,
|
| 400 |
+
)
|
| 401 |
+
|
| 402 |
+
@property
|
| 403 |
+
def rollout_exceptions(self):
|
| 404 |
+
"""
|
| 405 |
+
Return tuple of exceptions to except when doing rollouts. This is useful to ensure
|
| 406 |
+
that the entire training run doesn't crash because of a bad policy that causes unstable
|
| 407 |
+
simulation computations.
|
| 408 |
+
"""
|
| 409 |
+
return (Exception)
|
| 410 |
+
|
| 411 |
+
def __repr__(self):
|
| 412 |
+
"""
|
| 413 |
+
Pretty-print env description.
|
| 414 |
+
"""
|
| 415 |
+
return self.name + "\n" + json.dumps(self._init_kwargs, sort_keys=True, indent=4)
|
aloha-devel/robomimic/envs/wrappers.py
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
A collection of useful environment wrappers.
|
| 3 |
+
"""
|
| 4 |
+
from copy import deepcopy
|
| 5 |
+
import textwrap
|
| 6 |
+
import numpy as np
|
| 7 |
+
from collections import deque
|
| 8 |
+
|
| 9 |
+
import robomimic.envs.env_base as EB
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class EnvWrapper(object):
|
| 13 |
+
"""
|
| 14 |
+
Base class for all environment wrappers in robomimic.
|
| 15 |
+
"""
|
| 16 |
+
def __init__(self, env):
|
| 17 |
+
"""
|
| 18 |
+
Args:
|
| 19 |
+
env (EnvBase instance): The environment to wrap.
|
| 20 |
+
"""
|
| 21 |
+
assert isinstance(env, EB.EnvBase) or isinstance(env, EnvWrapper)
|
| 22 |
+
self.env = env
|
| 23 |
+
|
| 24 |
+
@classmethod
|
| 25 |
+
def class_name(cls):
|
| 26 |
+
return cls.__name__
|
| 27 |
+
|
| 28 |
+
def _warn_double_wrap(self):
|
| 29 |
+
"""
|
| 30 |
+
Utility function that checks if we're accidentally trying to double wrap an env
|
| 31 |
+
Raises:
|
| 32 |
+
Exception: [Double wrapping env]
|
| 33 |
+
"""
|
| 34 |
+
env = self.env
|
| 35 |
+
while True:
|
| 36 |
+
if isinstance(env, EnvWrapper):
|
| 37 |
+
if env.class_name() == self.class_name():
|
| 38 |
+
raise Exception(
|
| 39 |
+
"Attempted to double wrap with Wrapper: {}".format(
|
| 40 |
+
self.__class__.__name__
|
| 41 |
+
)
|
| 42 |
+
)
|
| 43 |
+
env = env.env
|
| 44 |
+
else:
|
| 45 |
+
break
|
| 46 |
+
|
| 47 |
+
@property
|
| 48 |
+
def unwrapped(self):
|
| 49 |
+
"""
|
| 50 |
+
Grabs unwrapped environment
|
| 51 |
+
|
| 52 |
+
Returns:
|
| 53 |
+
env (EnvBase instance): Unwrapped environment
|
| 54 |
+
"""
|
| 55 |
+
if hasattr(self.env, "unwrapped"):
|
| 56 |
+
return self.env.unwrapped
|
| 57 |
+
else:
|
| 58 |
+
return self.env
|
| 59 |
+
|
| 60 |
+
def _to_string(self):
|
| 61 |
+
"""
|
| 62 |
+
Subclasses should override this method to print out info about the
|
| 63 |
+
wrapper (such as arguments passed to it).
|
| 64 |
+
"""
|
| 65 |
+
return ''
|
| 66 |
+
|
| 67 |
+
def __repr__(self):
|
| 68 |
+
"""Pretty print environment."""
|
| 69 |
+
header = '{}'.format(str(self.__class__.__name__))
|
| 70 |
+
msg = ''
|
| 71 |
+
indent = ' ' * 4
|
| 72 |
+
if self._to_string() != '':
|
| 73 |
+
msg += textwrap.indent("\n" + self._to_string(), indent)
|
| 74 |
+
msg += textwrap.indent("\nenv={}".format(self.env), indent)
|
| 75 |
+
msg = header + '(' + msg + '\n)'
|
| 76 |
+
return msg
|
| 77 |
+
|
| 78 |
+
# this method is a fallback option on any methods the original env might support
|
| 79 |
+
def __getattr__(self, attr):
|
| 80 |
+
# using getattr ensures that both __getattribute__ and __getattr__ (fallback) get called
|
| 81 |
+
# (see https://stackoverflow.com/questions/3278077/difference-between-getattr-vs-getattribute)
|
| 82 |
+
orig_attr = getattr(self.env, attr)
|
| 83 |
+
if callable(orig_attr):
|
| 84 |
+
|
| 85 |
+
def hooked(*args, **kwargs):
|
| 86 |
+
result = orig_attr(*args, **kwargs)
|
| 87 |
+
# prevent wrapped_class from becoming unwrapped
|
| 88 |
+
if id(result) == id(self.env):
|
| 89 |
+
return self
|
| 90 |
+
return result
|
| 91 |
+
|
| 92 |
+
return hooked
|
| 93 |
+
else:
|
| 94 |
+
return orig_attr
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
class FrameStackWrapper(EnvWrapper):
|
| 98 |
+
"""
|
| 99 |
+
Wrapper for frame stacking observations during rollouts. The agent
|
| 100 |
+
receives a sequence of past observations instead of a single observation
|
| 101 |
+
when it calls @env.reset, @env.reset_to, or @env.step in the rollout loop.
|
| 102 |
+
"""
|
| 103 |
+
def __init__(self, env, num_frames):
|
| 104 |
+
"""
|
| 105 |
+
Args:
|
| 106 |
+
env (EnvBase instance): The environment to wrap.
|
| 107 |
+
num_frames (int): number of past observations (including current observation)
|
| 108 |
+
to stack together. Must be greater than 1 (otherwise this wrapper would
|
| 109 |
+
be a no-op).
|
| 110 |
+
"""
|
| 111 |
+
assert num_frames > 1, "error: FrameStackWrapper must have num_frames > 1 but got num_frames of {}".format(num_frames)
|
| 112 |
+
|
| 113 |
+
super(FrameStackWrapper, self).__init__(env=env)
|
| 114 |
+
self.num_frames = num_frames
|
| 115 |
+
|
| 116 |
+
### TODO: add action padding option + adding action to obs to include action history in obs ###
|
| 117 |
+
|
| 118 |
+
# keep track of last @num_frames observations for each obs key
|
| 119 |
+
self.obs_history = None
|
| 120 |
+
|
| 121 |
+
def _get_initial_obs_history(self, init_obs):
|
| 122 |
+
"""
|
| 123 |
+
Helper method to get observation history from the initial observation, by
|
| 124 |
+
repeating it.
|
| 125 |
+
|
| 126 |
+
Returns:
|
| 127 |
+
obs_history (dict): a deque for each observation key, with an extra
|
| 128 |
+
leading dimension of 1 for each key (for easy concatenation later)
|
| 129 |
+
"""
|
| 130 |
+
obs_history = {}
|
| 131 |
+
for k in init_obs:
|
| 132 |
+
obs_history[k] = deque(
|
| 133 |
+
[init_obs[k][None] for _ in range(self.num_frames)],
|
| 134 |
+
maxlen=self.num_frames,
|
| 135 |
+
)
|
| 136 |
+
return obs_history
|
| 137 |
+
|
| 138 |
+
def _get_stacked_obs_from_history(self):
|
| 139 |
+
"""
|
| 140 |
+
Helper method to convert internal variable @self.obs_history to a
|
| 141 |
+
stacked observation where each key is a numpy array with leading dimension
|
| 142 |
+
@self.num_frames.
|
| 143 |
+
"""
|
| 144 |
+
# concatenate all frames per key so we return a numpy array per key
|
| 145 |
+
return { k : np.concatenate(self.obs_history[k], axis=0) for k in self.obs_history }
|
| 146 |
+
|
| 147 |
+
def cache_obs_history(self):
|
| 148 |
+
self.obs_history_cache = deepcopy(self.obs_history)
|
| 149 |
+
|
| 150 |
+
def uncache_obs_history(self):
|
| 151 |
+
self.obs_history = self.obs_history_cache
|
| 152 |
+
self.obs_history_cache = None
|
| 153 |
+
|
| 154 |
+
def reset(self):
|
| 155 |
+
"""
|
| 156 |
+
Modify to return frame stacked observation which is @self.num_frames copies of
|
| 157 |
+
the initial observation.
|
| 158 |
+
|
| 159 |
+
Returns:
|
| 160 |
+
obs_stacked (dict): each observation key in original observation now has
|
| 161 |
+
leading shape @self.num_frames and consists of the previous @self.num_frames
|
| 162 |
+
observations
|
| 163 |
+
"""
|
| 164 |
+
obs = self.env.reset()
|
| 165 |
+
self.timestep = 0 # always zero regardless of timestep type
|
| 166 |
+
self.update_obs(obs, reset=True)
|
| 167 |
+
self.obs_history = self._get_initial_obs_history(init_obs=obs)
|
| 168 |
+
return self._get_stacked_obs_from_history()
|
| 169 |
+
|
| 170 |
+
def reset_to(self, state):
|
| 171 |
+
"""
|
| 172 |
+
Modify to return frame stacked observation which is @self.num_frames copies of
|
| 173 |
+
the initial observation.
|
| 174 |
+
|
| 175 |
+
Returns:
|
| 176 |
+
obs_stacked (dict): each observation key in original observation now has
|
| 177 |
+
leading shape @self.num_frames and consists of the previous @self.num_frames
|
| 178 |
+
observations
|
| 179 |
+
"""
|
| 180 |
+
obs = self.env.reset_to(state)
|
| 181 |
+
self.timestep = 0 # always zero regardless of timestep type
|
| 182 |
+
self.update_obs(obs, reset=True)
|
| 183 |
+
self.obs_history = self._get_initial_obs_history(init_obs=obs)
|
| 184 |
+
return self._get_stacked_obs_from_history()
|
| 185 |
+
|
| 186 |
+
def step(self, action):
|
| 187 |
+
"""
|
| 188 |
+
Modify to update the internal frame history and return frame stacked observation,
|
| 189 |
+
which will have leading dimension @self.num_frames for each key.
|
| 190 |
+
|
| 191 |
+
Args:
|
| 192 |
+
action (np.array): action to take
|
| 193 |
+
|
| 194 |
+
Returns:
|
| 195 |
+
obs_stacked (dict): each observation key in original observation now has
|
| 196 |
+
leading shape @self.num_frames and consists of the previous @self.num_frames
|
| 197 |
+
observations
|
| 198 |
+
reward (float): reward for this step
|
| 199 |
+
done (bool): whether the task is done
|
| 200 |
+
info (dict): extra information
|
| 201 |
+
"""
|
| 202 |
+
obs, r, done, info = self.env.step(action)
|
| 203 |
+
self.update_obs(obs, action=action, reset=False)
|
| 204 |
+
# update frame history
|
| 205 |
+
for k in obs:
|
| 206 |
+
# make sure to have leading dim of 1 for easy concatenation
|
| 207 |
+
self.obs_history[k].append(obs[k][None])
|
| 208 |
+
obs_ret = self._get_stacked_obs_from_history()
|
| 209 |
+
return obs_ret, r, done, info
|
| 210 |
+
|
| 211 |
+
def update_obs(self, obs, action=None, reset=False):
|
| 212 |
+
obs["timesteps"] = np.array([self.timestep])
|
| 213 |
+
|
| 214 |
+
if reset:
|
| 215 |
+
obs["actions"] = np.zeros(self.env.action_dimension)
|
| 216 |
+
else:
|
| 217 |
+
self.timestep += 1
|
| 218 |
+
obs["actions"] = action[: self.env.action_dimension]
|
| 219 |
+
|
| 220 |
+
def _to_string(self):
|
| 221 |
+
"""Info to pretty print."""
|
| 222 |
+
return "num_frames={}".format(self.num_frames)
|
aloha-devel/robomimic/exps/templates/act.json
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"algo_name": "act",
|
| 3 |
+
"experiment": {
|
| 4 |
+
"name": "test",
|
| 5 |
+
"validate": false,
|
| 6 |
+
"logging": {
|
| 7 |
+
"terminal_output_to_txt": true,
|
| 8 |
+
"log_tb": true,
|
| 9 |
+
"log_wandb": false,
|
| 10 |
+
"wandb_proj_name": "debug"
|
| 11 |
+
},
|
| 12 |
+
"mse":{},
|
| 13 |
+
"save": {
|
| 14 |
+
"enabled": true,
|
| 15 |
+
"every_n_seconds": null,
|
| 16 |
+
"every_n_epochs": 40,
|
| 17 |
+
"epochs": [],
|
| 18 |
+
"on_best_validation": false,
|
| 19 |
+
"on_best_rollout_return": false,
|
| 20 |
+
"on_best_rollout_success_rate": true
|
| 21 |
+
},
|
| 22 |
+
"epoch_every_n_steps": 500,
|
| 23 |
+
"validation_epoch_every_n_steps": 10,
|
| 24 |
+
"env": null,
|
| 25 |
+
"additional_envs": null,
|
| 26 |
+
"render": false,
|
| 27 |
+
"render_video": true,
|
| 28 |
+
"keep_all_videos": false,
|
| 29 |
+
"video_skip": 5,
|
| 30 |
+
"rollout": {
|
| 31 |
+
"enabled": true,
|
| 32 |
+
"n": 50,
|
| 33 |
+
"horizon": 400,
|
| 34 |
+
"rate": 40,
|
| 35 |
+
"warmstart": 0,
|
| 36 |
+
"terminate_on_success": true
|
| 37 |
+
}
|
| 38 |
+
},
|
| 39 |
+
"train": {
|
| 40 |
+
"data": null,
|
| 41 |
+
"output_dir":"../act_trained_models",
|
| 42 |
+
"num_data_workers": 4,
|
| 43 |
+
"hdf5_cache_mode": "low_dim",
|
| 44 |
+
"hdf5_use_swmr": true,
|
| 45 |
+
"hdf5_load_next_obs": false,
|
| 46 |
+
"hdf5_normalize_obs": false,
|
| 47 |
+
"hdf5_filter_key": null,
|
| 48 |
+
"seq_length": 10,
|
| 49 |
+
"pad_seq_length": true,
|
| 50 |
+
"frame_stack": 1,
|
| 51 |
+
"pad_frame_stack": true,
|
| 52 |
+
"dataset_keys": [
|
| 53 |
+
"actions"
|
| 54 |
+
],
|
| 55 |
+
"goal_mode": null,
|
| 56 |
+
"cuda": true,
|
| 57 |
+
"batch_size": 128,
|
| 58 |
+
"num_epochs": 10000,
|
| 59 |
+
"seed": 1
|
| 60 |
+
},
|
| 61 |
+
"algo": {
|
| 62 |
+
"optim_params": {
|
| 63 |
+
"policy": {
|
| 64 |
+
"optimizer_type": "adamw",
|
| 65 |
+
"learning_rate": {
|
| 66 |
+
"initial": 0.00005,
|
| 67 |
+
"decay_factor": 1,
|
| 68 |
+
"epoch_schedule": [
|
| 69 |
+
100
|
| 70 |
+
],
|
| 71 |
+
"scheduler_type": "linear"
|
| 72 |
+
},
|
| 73 |
+
"regularization": {
|
| 74 |
+
"L2": 0.0001
|
| 75 |
+
}
|
| 76 |
+
}
|
| 77 |
+
},
|
| 78 |
+
"loss": {
|
| 79 |
+
"l2_weight": 0.0,
|
| 80 |
+
"l1_weight": 1.0,
|
| 81 |
+
"cos_weight": 0.0
|
| 82 |
+
},
|
| 83 |
+
"act": {
|
| 84 |
+
"hidden_dim": 512,
|
| 85 |
+
"dim_feedforward": 3200,
|
| 86 |
+
"backbone": "resnet18",
|
| 87 |
+
"enc_layers": 4,
|
| 88 |
+
"dec_layers": 7,
|
| 89 |
+
"nheads": 8,
|
| 90 |
+
"latent_dim": 32,
|
| 91 |
+
"kl_weight": 20
|
| 92 |
+
}
|
| 93 |
+
},
|
| 94 |
+
"observation": {
|
| 95 |
+
"modalities": {
|
| 96 |
+
"obs": {
|
| 97 |
+
"low_dim": [
|
| 98 |
+
"robot0_eef_pos",
|
| 99 |
+
"robot0_eef_quat",
|
| 100 |
+
"robot0_gripper_qpos",
|
| 101 |
+
"object"
|
| 102 |
+
],
|
| 103 |
+
"rgb": [],
|
| 104 |
+
"depth": [],
|
| 105 |
+
"scan": []
|
| 106 |
+
},
|
| 107 |
+
"goal": {
|
| 108 |
+
"low_dim": [],
|
| 109 |
+
"rgb": [],
|
| 110 |
+
"depth": [],
|
| 111 |
+
"scan": []
|
| 112 |
+
}
|
| 113 |
+
},
|
| 114 |
+
"encoder": {
|
| 115 |
+
"low_dim": {
|
| 116 |
+
"core_class": null,
|
| 117 |
+
"core_kwargs": {},
|
| 118 |
+
"obs_randomizer_class": null,
|
| 119 |
+
"obs_randomizer_kwargs": {}
|
| 120 |
+
},
|
| 121 |
+
"rgb": {
|
| 122 |
+
"core_class": "VisualCore",
|
| 123 |
+
"core_kwargs": {
|
| 124 |
+
"feature_dimension": 64,
|
| 125 |
+
"backbone_class": "ResNet18Conv",
|
| 126 |
+
"backbone_kwargs": {
|
| 127 |
+
"pretrained": false,
|
| 128 |
+
"input_coord_conv": false
|
| 129 |
+
},
|
| 130 |
+
"pool_class": "SpatialSoftmax",
|
| 131 |
+
"pool_kwargs": {
|
| 132 |
+
"num_kp": 32,
|
| 133 |
+
"learnable_temperature": false,
|
| 134 |
+
"temperature": 1.0,
|
| 135 |
+
"noise_std": 0.0
|
| 136 |
+
}
|
| 137 |
+
},
|
| 138 |
+
"obs_randomizer_class": "CropRandomizer",
|
| 139 |
+
"obs_randomizer_kwargs": {
|
| 140 |
+
"crop_height": 76,
|
| 141 |
+
"crop_width": 76,
|
| 142 |
+
"num_crops": 1,
|
| 143 |
+
"pos_enc": false
|
| 144 |
+
}
|
| 145 |
+
},
|
| 146 |
+
"depth": {
|
| 147 |
+
"core_class": "VisualCore",
|
| 148 |
+
"core_kwargs": {},
|
| 149 |
+
"obs_randomizer_class": null,
|
| 150 |
+
"obs_randomizer_kwargs": {}
|
| 151 |
+
},
|
| 152 |
+
"scan": {
|
| 153 |
+
"core_class": "ScanCore",
|
| 154 |
+
"core_kwargs": {},
|
| 155 |
+
"obs_randomizer_class": null,
|
| 156 |
+
"obs_randomizer_kwargs": {}
|
| 157 |
+
}
|
| 158 |
+
}
|
| 159 |
+
}
|
| 160 |
+
}
|
aloha-devel/robomimic/exps/templates/bc.json
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"algo_name": "bc",
|
| 3 |
+
"experiment": {
|
| 4 |
+
"name": "test",
|
| 5 |
+
"validate": false,
|
| 6 |
+
"logging": {
|
| 7 |
+
"terminal_output_to_txt": true,
|
| 8 |
+
"log_tb": true,
|
| 9 |
+
"log_wandb": false,
|
| 10 |
+
"wandb_proj_name": "debug"
|
| 11 |
+
},
|
| 12 |
+
"mse":{},
|
| 13 |
+
"save": {
|
| 14 |
+
"enabled": true,
|
| 15 |
+
"every_n_seconds": null,
|
| 16 |
+
"every_n_epochs": 50,
|
| 17 |
+
"epochs": [],
|
| 18 |
+
"on_best_validation": false,
|
| 19 |
+
"on_best_rollout_return": false,
|
| 20 |
+
"on_best_rollout_success_rate": true
|
| 21 |
+
},
|
| 22 |
+
"epoch_every_n_steps": 100,
|
| 23 |
+
"validation_epoch_every_n_steps": 10,
|
| 24 |
+
"env": null,
|
| 25 |
+
"additional_envs": null,
|
| 26 |
+
"render": false,
|
| 27 |
+
"render_video": true,
|
| 28 |
+
"keep_all_videos": false,
|
| 29 |
+
"video_skip": 5,
|
| 30 |
+
"rollout": {
|
| 31 |
+
"enabled": true,
|
| 32 |
+
"n": 50,
|
| 33 |
+
"horizon": 400,
|
| 34 |
+
"rate": 50,
|
| 35 |
+
"warmstart": 0,
|
| 36 |
+
"terminate_on_success": true
|
| 37 |
+
}
|
| 38 |
+
},
|
| 39 |
+
"train": {
|
| 40 |
+
"data": null,
|
| 41 |
+
"output_dir": "../bc_trained_models",
|
| 42 |
+
"num_data_workers": 0,
|
| 43 |
+
"hdf5_cache_mode": "all",
|
| 44 |
+
"hdf5_use_swmr": true,
|
| 45 |
+
"hdf5_load_next_obs": false,
|
| 46 |
+
"hdf5_normalize_obs": false,
|
| 47 |
+
"hdf5_filter_key": null,
|
| 48 |
+
"hdf5_validation_filter_key": null,
|
| 49 |
+
"seq_length": 1,
|
| 50 |
+
"pad_seq_length": true,
|
| 51 |
+
"frame_stack": 1,
|
| 52 |
+
"pad_frame_stack": true,
|
| 53 |
+
"dataset_keys": [
|
| 54 |
+
"actions",
|
| 55 |
+
"rewards",
|
| 56 |
+
"dones"
|
| 57 |
+
],
|
| 58 |
+
"goal_mode": null,
|
| 59 |
+
"cuda": true,
|
| 60 |
+
"batch_size": 100,
|
| 61 |
+
"num_epochs": 2000,
|
| 62 |
+
"seed": 1
|
| 63 |
+
},
|
| 64 |
+
"algo": {
|
| 65 |
+
"optim_params": {
|
| 66 |
+
"policy": {
|
| 67 |
+
"optimizer_type": "adam",
|
| 68 |
+
"learning_rate": {
|
| 69 |
+
"initial": 0.0001,
|
| 70 |
+
"decay_factor": 0.1,
|
| 71 |
+
"epoch_schedule": [],
|
| 72 |
+
"scheduler_type": "multistep"
|
| 73 |
+
},
|
| 74 |
+
"regularization": {
|
| 75 |
+
"L2": 0.0
|
| 76 |
+
}
|
| 77 |
+
}
|
| 78 |
+
},
|
| 79 |
+
"loss": {
|
| 80 |
+
"l2_weight": 1.0,
|
| 81 |
+
"l1_weight": 0.0,
|
| 82 |
+
"cos_weight": 0.0
|
| 83 |
+
},
|
| 84 |
+
"actor_layer_dims": [
|
| 85 |
+
1024,
|
| 86 |
+
1024
|
| 87 |
+
],
|
| 88 |
+
"gaussian": {
|
| 89 |
+
"enabled": false,
|
| 90 |
+
"fixed_std": false,
|
| 91 |
+
"init_std": 0.1,
|
| 92 |
+
"min_std": 0.01,
|
| 93 |
+
"std_activation": "softplus",
|
| 94 |
+
"low_noise_eval": true
|
| 95 |
+
},
|
| 96 |
+
"gmm": {
|
| 97 |
+
"enabled": false,
|
| 98 |
+
"num_modes": 5,
|
| 99 |
+
"min_std": 0.0001,
|
| 100 |
+
"std_activation": "softplus",
|
| 101 |
+
"low_noise_eval": true
|
| 102 |
+
},
|
| 103 |
+
"vae": {
|
| 104 |
+
"enabled": false,
|
| 105 |
+
"latent_dim": 14,
|
| 106 |
+
"latent_clip": null,
|
| 107 |
+
"kl_weight": 1.0,
|
| 108 |
+
"decoder": {
|
| 109 |
+
"is_conditioned": true,
|
| 110 |
+
"reconstruction_sum_across_elements": false
|
| 111 |
+
},
|
| 112 |
+
"prior": {
|
| 113 |
+
"learn": false,
|
| 114 |
+
"is_conditioned": false,
|
| 115 |
+
"use_gmm": false,
|
| 116 |
+
"gmm_num_modes": 10,
|
| 117 |
+
"gmm_learn_weights": false,
|
| 118 |
+
"use_categorical": false,
|
| 119 |
+
"categorical_dim": 10,
|
| 120 |
+
"categorical_gumbel_softmax_hard": false,
|
| 121 |
+
"categorical_init_temp": 1.0,
|
| 122 |
+
"categorical_temp_anneal_step": 0.001,
|
| 123 |
+
"categorical_min_temp": 0.3
|
| 124 |
+
},
|
| 125 |
+
"encoder_layer_dims": [
|
| 126 |
+
300,
|
| 127 |
+
400
|
| 128 |
+
],
|
| 129 |
+
"decoder_layer_dims": [
|
| 130 |
+
300,
|
| 131 |
+
400
|
| 132 |
+
],
|
| 133 |
+
"prior_layer_dims": [
|
| 134 |
+
300,
|
| 135 |
+
400
|
| 136 |
+
]
|
| 137 |
+
},
|
| 138 |
+
"rnn": {
|
| 139 |
+
"enabled": false,
|
| 140 |
+
"horizon": 10,
|
| 141 |
+
"hidden_dim": 400,
|
| 142 |
+
"rnn_type": "LSTM",
|
| 143 |
+
"num_layers": 2,
|
| 144 |
+
"open_loop": false,
|
| 145 |
+
"kwargs": {
|
| 146 |
+
"bidirectional": false
|
| 147 |
+
}
|
| 148 |
+
},
|
| 149 |
+
"transformer": {
|
| 150 |
+
"enabled": false,
|
| 151 |
+
"context_length": 10,
|
| 152 |
+
"embed_dim": 512,
|
| 153 |
+
"num_layers": 6,
|
| 154 |
+
"num_heads": 8,
|
| 155 |
+
"emb_dropout": 0.1,
|
| 156 |
+
"attn_dropout": 0.1,
|
| 157 |
+
"block_output_dropout": 0.1,
|
| 158 |
+
"sinusoidal_embedding": false,
|
| 159 |
+
"activation": "gelu",
|
| 160 |
+
"supervise_all_steps": false,
|
| 161 |
+
"nn_parameter_for_timesteps": true
|
| 162 |
+
}
|
| 163 |
+
},
|
| 164 |
+
"observation": {
|
| 165 |
+
"modalities": {
|
| 166 |
+
"obs": {
|
| 167 |
+
"low_dim": [
|
| 168 |
+
"robot0_eef_pos",
|
| 169 |
+
"robot0_eef_quat",
|
| 170 |
+
"robot0_gripper_qpos",
|
| 171 |
+
"object"
|
| 172 |
+
],
|
| 173 |
+
"rgb": [],
|
| 174 |
+
"depth": [],
|
| 175 |
+
"scan": []
|
| 176 |
+
},
|
| 177 |
+
"goal": {
|
| 178 |
+
"low_dim": [],
|
| 179 |
+
"rgb": [],
|
| 180 |
+
"depth": [],
|
| 181 |
+
"scan": []
|
| 182 |
+
}
|
| 183 |
+
},
|
| 184 |
+
"encoder": {
|
| 185 |
+
"low_dim": {
|
| 186 |
+
"core_class": null,
|
| 187 |
+
"core_kwargs": {},
|
| 188 |
+
"obs_randomizer_class": null,
|
| 189 |
+
"obs_randomizer_kwargs": {}
|
| 190 |
+
},
|
| 191 |
+
"rgb": {
|
| 192 |
+
"core_class": "VisualCore",
|
| 193 |
+
"core_kwargs": {},
|
| 194 |
+
"obs_randomizer_class": null,
|
| 195 |
+
"obs_randomizer_kwargs": {}
|
| 196 |
+
},
|
| 197 |
+
"depth": {
|
| 198 |
+
"core_class": "VisualCore",
|
| 199 |
+
"core_kwargs": {},
|
| 200 |
+
"obs_randomizer_class": null,
|
| 201 |
+
"obs_randomizer_kwargs": {}
|
| 202 |
+
},
|
| 203 |
+
"scan": {
|
| 204 |
+
"core_class": "ScanCore",
|
| 205 |
+
"core_kwargs": {},
|
| 206 |
+
"obs_randomizer_class": null,
|
| 207 |
+
"obs_randomizer_kwargs": {}
|
| 208 |
+
}
|
| 209 |
+
}
|
| 210 |
+
},
|
| 211 |
+
"meta": {
|
| 212 |
+
"hp_base_config_file": null,
|
| 213 |
+
"hp_keys": [],
|
| 214 |
+
"hp_values": []
|
| 215 |
+
}
|
| 216 |
+
}
|
aloha-devel/robomimic/exps/templates/bc_transformer.json
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"algo_name": "bc",
|
| 3 |
+
"experiment": {
|
| 4 |
+
"name": "test",
|
| 5 |
+
"validate": true,
|
| 6 |
+
"logging": {
|
| 7 |
+
"terminal_output_to_txt": true,
|
| 8 |
+
"log_tb": true,
|
| 9 |
+
"log_wandb": false,
|
| 10 |
+
"wandb_proj_name": "debug"
|
| 11 |
+
},
|
| 12 |
+
"mse":{},
|
| 13 |
+
"save": {
|
| 14 |
+
"enabled": true,
|
| 15 |
+
"every_n_seconds": null,
|
| 16 |
+
"every_n_epochs": 50,
|
| 17 |
+
"epochs": [],
|
| 18 |
+
"on_best_validation": false,
|
| 19 |
+
"on_best_rollout_return": false,
|
| 20 |
+
"on_best_rollout_success_rate": true
|
| 21 |
+
},
|
| 22 |
+
"epoch_every_n_steps": 100,
|
| 23 |
+
"validation_epoch_every_n_steps": 10,
|
| 24 |
+
"env": null,
|
| 25 |
+
"additional_envs": null,
|
| 26 |
+
"render": false,
|
| 27 |
+
"render_video": true,
|
| 28 |
+
"keep_all_videos": false,
|
| 29 |
+
"video_skip": 5,
|
| 30 |
+
"rollout": {
|
| 31 |
+
"enabled": true,
|
| 32 |
+
"n": 50,
|
| 33 |
+
"horizon": 400,
|
| 34 |
+
"rate": 50,
|
| 35 |
+
"warmstart": 0,
|
| 36 |
+
"terminate_on_success": true
|
| 37 |
+
}
|
| 38 |
+
},
|
| 39 |
+
"train": {
|
| 40 |
+
"data": null,
|
| 41 |
+
"output_dir": "../bc_transformer_trained_models",
|
| 42 |
+
"num_data_workers": 0,
|
| 43 |
+
"hdf5_cache_mode": "low_dim",
|
| 44 |
+
"hdf5_use_swmr": true,
|
| 45 |
+
"hdf5_load_next_obs": false,
|
| 46 |
+
"hdf5_normalize_obs": false,
|
| 47 |
+
"hdf5_filter_key": null,
|
| 48 |
+
"seq_length": 1,
|
| 49 |
+
"pad_seq_length": true,
|
| 50 |
+
"frame_stack": 10,
|
| 51 |
+
"pad_frame_stack": true,
|
| 52 |
+
"dataset_keys": [
|
| 53 |
+
"actions"
|
| 54 |
+
],
|
| 55 |
+
"goal_mode": null,
|
| 56 |
+
"cuda": true,
|
| 57 |
+
"batch_size": 100,
|
| 58 |
+
"num_epochs": 2000,
|
| 59 |
+
"seed": 1
|
| 60 |
+
},
|
| 61 |
+
"algo": {
|
| 62 |
+
"optim_params": {
|
| 63 |
+
"policy": {
|
| 64 |
+
"optimizer_type": "adamw",
|
| 65 |
+
"learning_rate": {
|
| 66 |
+
"initial": 0.0001,
|
| 67 |
+
"decay_factor": 0.1,
|
| 68 |
+
"epoch_schedule": [100],
|
| 69 |
+
"scheduler_type": "linear"
|
| 70 |
+
},
|
| 71 |
+
"regularization": {
|
| 72 |
+
"L2": 0.01
|
| 73 |
+
}
|
| 74 |
+
}
|
| 75 |
+
},
|
| 76 |
+
"loss": {
|
| 77 |
+
"l2_weight": 1.0,
|
| 78 |
+
"l1_weight": 0.0,
|
| 79 |
+
"cos_weight": 0.0
|
| 80 |
+
},
|
| 81 |
+
"actor_layer_dims": [],
|
| 82 |
+
"gaussian": {
|
| 83 |
+
"enabled": false
|
| 84 |
+
},
|
| 85 |
+
"gmm": {
|
| 86 |
+
"enabled": true,
|
| 87 |
+
"num_modes": 5,
|
| 88 |
+
"min_std": 0.0001,
|
| 89 |
+
"std_activation": "softplus",
|
| 90 |
+
"low_noise_eval": true
|
| 91 |
+
},
|
| 92 |
+
"vae": {
|
| 93 |
+
"enabled": false
|
| 94 |
+
},
|
| 95 |
+
"rnn": {
|
| 96 |
+
"enabled": false
|
| 97 |
+
},
|
| 98 |
+
"transformer": {
|
| 99 |
+
"enabled": true,
|
| 100 |
+
"supervise_all_steps": false,
|
| 101 |
+
"num_layers": 6,
|
| 102 |
+
"embed_dim": 512,
|
| 103 |
+
"num_heads": 8
|
| 104 |
+
}
|
| 105 |
+
},
|
| 106 |
+
"observation": {
|
| 107 |
+
"modalities": {
|
| 108 |
+
"obs": {
|
| 109 |
+
"low_dim": [
|
| 110 |
+
"robot0_eef_pos",
|
| 111 |
+
"robot0_eef_quat",
|
| 112 |
+
"robot0_gripper_qpos",
|
| 113 |
+
"object"
|
| 114 |
+
],
|
| 115 |
+
"rgb": [],
|
| 116 |
+
"depth": [],
|
| 117 |
+
"scan": []
|
| 118 |
+
},
|
| 119 |
+
"goal": {
|
| 120 |
+
"low_dim": [],
|
| 121 |
+
"rgb": [],
|
| 122 |
+
"depth": [],
|
| 123 |
+
"scan": []
|
| 124 |
+
}
|
| 125 |
+
},
|
| 126 |
+
"encoder": {
|
| 127 |
+
"low_dim": {
|
| 128 |
+
"core_class": null,
|
| 129 |
+
"core_kwargs": {},
|
| 130 |
+
"obs_randomizer_class": null,
|
| 131 |
+
"obs_randomizer_kwargs": {}
|
| 132 |
+
},
|
| 133 |
+
"rgb": {
|
| 134 |
+
"core_class": "VisualCore",
|
| 135 |
+
"core_kwargs": {
|
| 136 |
+
"feature_dimension": 64,
|
| 137 |
+
"backbone_class": "ResNet18Conv",
|
| 138 |
+
"backbone_kwargs": {
|
| 139 |
+
"pretrained": false,
|
| 140 |
+
"input_coord_conv": false
|
| 141 |
+
},
|
| 142 |
+
"pool_class": "SpatialSoftmax",
|
| 143 |
+
"pool_kwargs": {
|
| 144 |
+
"num_kp": 32,
|
| 145 |
+
"learnable_temperature": false,
|
| 146 |
+
"temperature": 1.0,
|
| 147 |
+
"noise_std": 0.0
|
| 148 |
+
}
|
| 149 |
+
},
|
| 150 |
+
"obs_randomizer_class": "CropRandomizer",
|
| 151 |
+
"obs_randomizer_kwargs": {
|
| 152 |
+
"crop_height": 76,
|
| 153 |
+
"crop_width": 76,
|
| 154 |
+
"num_crops": 1,
|
| 155 |
+
"pos_enc": false
|
| 156 |
+
}
|
| 157 |
+
},
|
| 158 |
+
"depth": {
|
| 159 |
+
"core_class": "VisualCore",
|
| 160 |
+
"core_kwargs": {},
|
| 161 |
+
"obs_randomizer_class": null,
|
| 162 |
+
"obs_randomizer_kwargs": {}
|
| 163 |
+
},
|
| 164 |
+
"scan": {
|
| 165 |
+
"core_class": "ScanCore",
|
| 166 |
+
"core_kwargs": {},
|
| 167 |
+
"obs_randomizer_class": null,
|
| 168 |
+
"obs_randomizer_kwargs": {}
|
| 169 |
+
}
|
| 170 |
+
}
|
| 171 |
+
}
|
| 172 |
+
}
|
aloha-devel/robomimic/exps/templates/cql.json
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"algo_name": "cql",
|
| 3 |
+
"experiment": {
|
| 4 |
+
"name": "test",
|
| 5 |
+
"validate": false,
|
| 6 |
+
"logging": {
|
| 7 |
+
"terminal_output_to_txt": true,
|
| 8 |
+
"log_tb": true,
|
| 9 |
+
"log_wandb": false,
|
| 10 |
+
"wandb_proj_name": "debug"
|
| 11 |
+
},
|
| 12 |
+
"save": {
|
| 13 |
+
"enabled": true,
|
| 14 |
+
"every_n_seconds": null,
|
| 15 |
+
"every_n_epochs": 50,
|
| 16 |
+
"epochs": [],
|
| 17 |
+
"on_best_validation": false,
|
| 18 |
+
"on_best_rollout_return": false,
|
| 19 |
+
"on_best_rollout_success_rate": true
|
| 20 |
+
},
|
| 21 |
+
"epoch_every_n_steps": 100,
|
| 22 |
+
"validation_epoch_every_n_steps": 10,
|
| 23 |
+
"env": null,
|
| 24 |
+
"additional_envs": null,
|
| 25 |
+
"render": false,
|
| 26 |
+
"render_video": true,
|
| 27 |
+
"keep_all_videos": false,
|
| 28 |
+
"video_skip": 5,
|
| 29 |
+
"rollout": {
|
| 30 |
+
"enabled": true,
|
| 31 |
+
"n": 50,
|
| 32 |
+
"horizon": 400,
|
| 33 |
+
"rate": 50,
|
| 34 |
+
"warmstart": 0,
|
| 35 |
+
"terminate_on_success": true
|
| 36 |
+
}
|
| 37 |
+
},
|
| 38 |
+
"train": {
|
| 39 |
+
"data": null,
|
| 40 |
+
"output_dir": "../cql_trained_models",
|
| 41 |
+
"num_data_workers": 0,
|
| 42 |
+
"hdf5_cache_mode": "all",
|
| 43 |
+
"hdf5_use_swmr": true,
|
| 44 |
+
"hdf5_load_next_obs": true,
|
| 45 |
+
"hdf5_normalize_obs": false,
|
| 46 |
+
"hdf5_filter_key": null,
|
| 47 |
+
"hdf5_validation_filter_key": null,
|
| 48 |
+
"seq_length": 1,
|
| 49 |
+
"pad_seq_length": true,
|
| 50 |
+
"frame_stack": 1,
|
| 51 |
+
"pad_frame_stack": true,
|
| 52 |
+
"dataset_keys": [
|
| 53 |
+
"actions",
|
| 54 |
+
"rewards",
|
| 55 |
+
"dones"
|
| 56 |
+
],
|
| 57 |
+
"goal_mode": null,
|
| 58 |
+
"cuda": true,
|
| 59 |
+
"batch_size": 1024,
|
| 60 |
+
"num_epochs": 2000,
|
| 61 |
+
"seed": 1
|
| 62 |
+
},
|
| 63 |
+
"algo": {
|
| 64 |
+
"optim_params": {
|
| 65 |
+
"critic": {
|
| 66 |
+
"learning_rate": {
|
| 67 |
+
"initial": 0.001,
|
| 68 |
+
"decay_factor": 0.0,
|
| 69 |
+
"epoch_schedule": []
|
| 70 |
+
},
|
| 71 |
+
"regularization": {
|
| 72 |
+
"L2": 0.0
|
| 73 |
+
}
|
| 74 |
+
},
|
| 75 |
+
"actor": {
|
| 76 |
+
"learning_rate": {
|
| 77 |
+
"initial": 0.0003,
|
| 78 |
+
"decay_factor": 0.0,
|
| 79 |
+
"epoch_schedule": []
|
| 80 |
+
},
|
| 81 |
+
"regularization": {
|
| 82 |
+
"L2": 0.0
|
| 83 |
+
}
|
| 84 |
+
}
|
| 85 |
+
},
|
| 86 |
+
"discount": 0.99,
|
| 87 |
+
"n_step": 1,
|
| 88 |
+
"target_tau": 0.005,
|
| 89 |
+
"actor": {
|
| 90 |
+
"bc_start_steps": 0,
|
| 91 |
+
"target_entropy": "default",
|
| 92 |
+
"max_gradient_norm": null,
|
| 93 |
+
"net": {
|
| 94 |
+
"type": "gaussian",
|
| 95 |
+
"common": {
|
| 96 |
+
"std_activation": "exp",
|
| 97 |
+
"use_tanh": true,
|
| 98 |
+
"low_noise_eval": true
|
| 99 |
+
},
|
| 100 |
+
"gaussian": {
|
| 101 |
+
"init_last_fc_weight": 0.001,
|
| 102 |
+
"init_std": 0.3,
|
| 103 |
+
"fixed_std": false
|
| 104 |
+
}
|
| 105 |
+
},
|
| 106 |
+
"layer_dims": [
|
| 107 |
+
300,
|
| 108 |
+
400
|
| 109 |
+
]
|
| 110 |
+
},
|
| 111 |
+
"critic": {
|
| 112 |
+
"use_huber": false,
|
| 113 |
+
"max_gradient_norm": null,
|
| 114 |
+
"value_bounds": null,
|
| 115 |
+
"num_action_samples": 1,
|
| 116 |
+
"cql_weight": 1.0,
|
| 117 |
+
"deterministic_backup": true,
|
| 118 |
+
"min_q_weight": 1.0,
|
| 119 |
+
"target_q_gap": 5.0,
|
| 120 |
+
"num_random_actions": 10,
|
| 121 |
+
"ensemble": {
|
| 122 |
+
"n": 2
|
| 123 |
+
},
|
| 124 |
+
"layer_dims": [
|
| 125 |
+
300,
|
| 126 |
+
400
|
| 127 |
+
]
|
| 128 |
+
}
|
| 129 |
+
},
|
| 130 |
+
"observation": {
|
| 131 |
+
"modalities": {
|
| 132 |
+
"obs": {
|
| 133 |
+
"low_dim": [
|
| 134 |
+
"robot0_eef_pos",
|
| 135 |
+
"robot0_eef_quat",
|
| 136 |
+
"robot0_gripper_qpos",
|
| 137 |
+
"object"
|
| 138 |
+
],
|
| 139 |
+
"rgb": [],
|
| 140 |
+
"depth": [],
|
| 141 |
+
"scan": []
|
| 142 |
+
},
|
| 143 |
+
"goal": {
|
| 144 |
+
"low_dim": [],
|
| 145 |
+
"rgb": [],
|
| 146 |
+
"depth": [],
|
| 147 |
+
"scan": []
|
| 148 |
+
}
|
| 149 |
+
},
|
| 150 |
+
"encoder": {
|
| 151 |
+
"low_dim": {
|
| 152 |
+
"core_class": null,
|
| 153 |
+
"core_kwargs": {},
|
| 154 |
+
"obs_randomizer_class": null,
|
| 155 |
+
"obs_randomizer_kwargs": {}
|
| 156 |
+
},
|
| 157 |
+
"rgb": {
|
| 158 |
+
"core_class": "VisualCore",
|
| 159 |
+
"core_kwargs": {},
|
| 160 |
+
"obs_randomizer_class": null,
|
| 161 |
+
"obs_randomizer_kwargs": {}
|
| 162 |
+
},
|
| 163 |
+
"depth": {
|
| 164 |
+
"core_class": "VisualCore",
|
| 165 |
+
"core_kwargs": {},
|
| 166 |
+
"obs_randomizer_class": null,
|
| 167 |
+
"obs_randomizer_kwargs": {}
|
| 168 |
+
},
|
| 169 |
+
"scan": {
|
| 170 |
+
"core_class": "ScanCore",
|
| 171 |
+
"core_kwargs": {},
|
| 172 |
+
"obs_randomizer_class": null,
|
| 173 |
+
"obs_randomizer_kwargs": {}
|
| 174 |
+
}
|
| 175 |
+
}
|
| 176 |
+
},
|
| 177 |
+
"meta": {
|
| 178 |
+
"hp_base_config_file": null,
|
| 179 |
+
"hp_keys": [],
|
| 180 |
+
"hp_values": []
|
| 181 |
+
}
|
| 182 |
+
}
|
aloha-devel/robomimic/exps/templates/gl.json
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"algo_name": "gl",
|
| 3 |
+
"experiment": {
|
| 4 |
+
"name": "test",
|
| 5 |
+
"validate": false,
|
| 6 |
+
"logging": {
|
| 7 |
+
"terminal_output_to_txt": true,
|
| 8 |
+
"log_tb": true,
|
| 9 |
+
"log_wandb": false,
|
| 10 |
+
"wandb_proj_name": "debug"
|
| 11 |
+
},
|
| 12 |
+
"save": {
|
| 13 |
+
"enabled": true,
|
| 14 |
+
"every_n_seconds": null,
|
| 15 |
+
"every_n_epochs": 50,
|
| 16 |
+
"epochs": [],
|
| 17 |
+
"on_best_validation": false,
|
| 18 |
+
"on_best_rollout_return": false,
|
| 19 |
+
"on_best_rollout_success_rate": true
|
| 20 |
+
},
|
| 21 |
+
"epoch_every_n_steps": 100,
|
| 22 |
+
"validation_epoch_every_n_steps": 10,
|
| 23 |
+
"env": null,
|
| 24 |
+
"additional_envs": null,
|
| 25 |
+
"render": false,
|
| 26 |
+
"render_video": true,
|
| 27 |
+
"keep_all_videos": false,
|
| 28 |
+
"video_skip": 5,
|
| 29 |
+
"rollout": {
|
| 30 |
+
"enabled": true,
|
| 31 |
+
"n": 50,
|
| 32 |
+
"horizon": 400,
|
| 33 |
+
"rate": 50,
|
| 34 |
+
"warmstart": 0,
|
| 35 |
+
"terminate_on_success": true
|
| 36 |
+
}
|
| 37 |
+
},
|
| 38 |
+
"train": {
|
| 39 |
+
"data": null,
|
| 40 |
+
"output_dir": "../gl_trained_models",
|
| 41 |
+
"num_data_workers": 0,
|
| 42 |
+
"hdf5_cache_mode": "all",
|
| 43 |
+
"hdf5_use_swmr": true,
|
| 44 |
+
"hdf5_load_next_obs": true,
|
| 45 |
+
"hdf5_normalize_obs": false,
|
| 46 |
+
"hdf5_filter_key": null,
|
| 47 |
+
"hdf5_validation_filter_key": null,
|
| 48 |
+
"seq_length": 1,
|
| 49 |
+
"pad_seq_length": true,
|
| 50 |
+
"frame_stack": 1,
|
| 51 |
+
"pad_frame_stack": true,
|
| 52 |
+
"dataset_keys": [
|
| 53 |
+
"actions",
|
| 54 |
+
"rewards",
|
| 55 |
+
"dones"
|
| 56 |
+
],
|
| 57 |
+
"goal_mode": null,
|
| 58 |
+
"cuda": true,
|
| 59 |
+
"batch_size": 100,
|
| 60 |
+
"num_epochs": 2000,
|
| 61 |
+
"seed": 1
|
| 62 |
+
},
|
| 63 |
+
"algo": {
|
| 64 |
+
"optim_params": {
|
| 65 |
+
"goal_network": {
|
| 66 |
+
"learning_rate": {
|
| 67 |
+
"initial": 0.0001,
|
| 68 |
+
"decay_factor": 0.1,
|
| 69 |
+
"epoch_schedule": []
|
| 70 |
+
},
|
| 71 |
+
"regularization": {
|
| 72 |
+
"L2": 0.0
|
| 73 |
+
}
|
| 74 |
+
}
|
| 75 |
+
},
|
| 76 |
+
"subgoal_horizon": 10,
|
| 77 |
+
"ae": {
|
| 78 |
+
"planner_layer_dims": [
|
| 79 |
+
300,
|
| 80 |
+
400
|
| 81 |
+
]
|
| 82 |
+
},
|
| 83 |
+
"vae": {
|
| 84 |
+
"enabled": true,
|
| 85 |
+
"latent_dim": 16,
|
| 86 |
+
"latent_clip": null,
|
| 87 |
+
"kl_weight": 1.0,
|
| 88 |
+
"decoder": {
|
| 89 |
+
"is_conditioned": true,
|
| 90 |
+
"reconstruction_sum_across_elements": false
|
| 91 |
+
},
|
| 92 |
+
"prior": {
|
| 93 |
+
"learn": false,
|
| 94 |
+
"is_conditioned": false,
|
| 95 |
+
"use_gmm": false,
|
| 96 |
+
"gmm_num_modes": 10,
|
| 97 |
+
"gmm_learn_weights": false,
|
| 98 |
+
"use_categorical": false,
|
| 99 |
+
"categorical_dim": 10,
|
| 100 |
+
"categorical_gumbel_softmax_hard": false,
|
| 101 |
+
"categorical_init_temp": 1.0,
|
| 102 |
+
"categorical_temp_anneal_step": 0.001,
|
| 103 |
+
"categorical_min_temp": 0.3
|
| 104 |
+
},
|
| 105 |
+
"encoder_layer_dims": [
|
| 106 |
+
300,
|
| 107 |
+
400
|
| 108 |
+
],
|
| 109 |
+
"decoder_layer_dims": [
|
| 110 |
+
300,
|
| 111 |
+
400
|
| 112 |
+
],
|
| 113 |
+
"prior_layer_dims": [
|
| 114 |
+
300,
|
| 115 |
+
400
|
| 116 |
+
]
|
| 117 |
+
}
|
| 118 |
+
},
|
| 119 |
+
"observation": {
|
| 120 |
+
"modalities": {
|
| 121 |
+
"obs": {
|
| 122 |
+
"low_dim": [
|
| 123 |
+
"robot0_eef_pos",
|
| 124 |
+
"robot0_eef_quat",
|
| 125 |
+
"robot0_gripper_qpos",
|
| 126 |
+
"object"
|
| 127 |
+
],
|
| 128 |
+
"rgb": [],
|
| 129 |
+
"depth": [],
|
| 130 |
+
"scan": []
|
| 131 |
+
},
|
| 132 |
+
"goal": {
|
| 133 |
+
"low_dim": [],
|
| 134 |
+
"rgb": [],
|
| 135 |
+
"depth": [],
|
| 136 |
+
"scan": []
|
| 137 |
+
},
|
| 138 |
+
"subgoal": {
|
| 139 |
+
"low_dim": [
|
| 140 |
+
"robot0_eef_pos",
|
| 141 |
+
"robot0_eef_quat",
|
| 142 |
+
"robot0_gripper_qpos",
|
| 143 |
+
"object"
|
| 144 |
+
],
|
| 145 |
+
"rgb": [],
|
| 146 |
+
"depth": [],
|
| 147 |
+
"scan": []
|
| 148 |
+
}
|
| 149 |
+
},
|
| 150 |
+
"encoder": {
|
| 151 |
+
"low_dim": {
|
| 152 |
+
"core_class": null,
|
| 153 |
+
"core_kwargs": {},
|
| 154 |
+
"obs_randomizer_class": null,
|
| 155 |
+
"obs_randomizer_kwargs": {}
|
| 156 |
+
},
|
| 157 |
+
"rgb": {
|
| 158 |
+
"core_class": "VisualCore",
|
| 159 |
+
"core_kwargs": {},
|
| 160 |
+
"obs_randomizer_class": null,
|
| 161 |
+
"obs_randomizer_kwargs": {}
|
| 162 |
+
},
|
| 163 |
+
"depth": {
|
| 164 |
+
"core_class": "VisualCore",
|
| 165 |
+
"core_kwargs": {},
|
| 166 |
+
"obs_randomizer_class": null,
|
| 167 |
+
"obs_randomizer_kwargs": {}
|
| 168 |
+
},
|
| 169 |
+
"scan": {
|
| 170 |
+
"core_class": "ScanCore",
|
| 171 |
+
"core_kwargs": {},
|
| 172 |
+
"obs_randomizer_class": null,
|
| 173 |
+
"obs_randomizer_kwargs": {}
|
| 174 |
+
}
|
| 175 |
+
}
|
| 176 |
+
},
|
| 177 |
+
"meta": {
|
| 178 |
+
"hp_base_config_file": null,
|
| 179 |
+
"hp_keys": [],
|
| 180 |
+
"hp_values": []
|
| 181 |
+
}
|
| 182 |
+
}
|
aloha-devel/robomimic/exps/templates/hbc.json
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"algo_name": "hbc",
|
| 3 |
+
"experiment": {
|
| 4 |
+
"name": "test",
|
| 5 |
+
"validate": false,
|
| 6 |
+
"logging": {
|
| 7 |
+
"terminal_output_to_txt": true,
|
| 8 |
+
"log_tb": true,
|
| 9 |
+
"log_wandb": false,
|
| 10 |
+
"wandb_proj_name": "debug"
|
| 11 |
+
},
|
| 12 |
+
"save": {
|
| 13 |
+
"enabled": true,
|
| 14 |
+
"every_n_seconds": null,
|
| 15 |
+
"every_n_epochs": 50,
|
| 16 |
+
"epochs": [],
|
| 17 |
+
"on_best_validation": false,
|
| 18 |
+
"on_best_rollout_return": false,
|
| 19 |
+
"on_best_rollout_success_rate": true
|
| 20 |
+
},
|
| 21 |
+
"epoch_every_n_steps": 100,
|
| 22 |
+
"validation_epoch_every_n_steps": 10,
|
| 23 |
+
"env": null,
|
| 24 |
+
"additional_envs": null,
|
| 25 |
+
"render": false,
|
| 26 |
+
"render_video": true,
|
| 27 |
+
"keep_all_videos": false,
|
| 28 |
+
"video_skip": 5,
|
| 29 |
+
"rollout": {
|
| 30 |
+
"enabled": true,
|
| 31 |
+
"n": 50,
|
| 32 |
+
"horizon": 400,
|
| 33 |
+
"rate": 50,
|
| 34 |
+
"warmstart": 0,
|
| 35 |
+
"terminate_on_success": true
|
| 36 |
+
}
|
| 37 |
+
},
|
| 38 |
+
"train": {
|
| 39 |
+
"data": null,
|
| 40 |
+
"output_dir": "../hbc_trained_models",
|
| 41 |
+
"num_data_workers": 0,
|
| 42 |
+
"hdf5_cache_mode": "all",
|
| 43 |
+
"hdf5_use_swmr": true,
|
| 44 |
+
"hdf5_load_next_obs": true,
|
| 45 |
+
"hdf5_normalize_obs": false,
|
| 46 |
+
"hdf5_filter_key": null,
|
| 47 |
+
"hdf5_validation_filter_key": null,
|
| 48 |
+
"seq_length": 10,
|
| 49 |
+
"pad_seq_length": true,
|
| 50 |
+
"frame_stack": 1,
|
| 51 |
+
"pad_frame_stack": true,
|
| 52 |
+
"dataset_keys": [
|
| 53 |
+
"actions",
|
| 54 |
+
"rewards",
|
| 55 |
+
"dones"
|
| 56 |
+
],
|
| 57 |
+
"goal_mode": null,
|
| 58 |
+
"cuda": true,
|
| 59 |
+
"batch_size": 100,
|
| 60 |
+
"num_epochs": 2000,
|
| 61 |
+
"seed": 1
|
| 62 |
+
},
|
| 63 |
+
"algo": {
|
| 64 |
+
"mode": "separate",
|
| 65 |
+
"actor_use_random_subgoals": false,
|
| 66 |
+
"subgoal_update_interval": 10,
|
| 67 |
+
"latent_subgoal": {
|
| 68 |
+
"enabled": false,
|
| 69 |
+
"prior_correction": {
|
| 70 |
+
"enabled": false,
|
| 71 |
+
"num_samples": 100
|
| 72 |
+
}
|
| 73 |
+
},
|
| 74 |
+
"planner": {
|
| 75 |
+
"optim_params": {
|
| 76 |
+
"goal_network": {
|
| 77 |
+
"learning_rate": {
|
| 78 |
+
"initial": 0.0001,
|
| 79 |
+
"decay_factor": 0.1,
|
| 80 |
+
"epoch_schedule": []
|
| 81 |
+
},
|
| 82 |
+
"regularization": {
|
| 83 |
+
"L2": 0.0
|
| 84 |
+
}
|
| 85 |
+
}
|
| 86 |
+
},
|
| 87 |
+
"subgoal_horizon": 10,
|
| 88 |
+
"ae": {
|
| 89 |
+
"planner_layer_dims": [
|
| 90 |
+
300,
|
| 91 |
+
400
|
| 92 |
+
]
|
| 93 |
+
},
|
| 94 |
+
"vae": {
|
| 95 |
+
"enabled": true,
|
| 96 |
+
"latent_dim": 16,
|
| 97 |
+
"latent_clip": null,
|
| 98 |
+
"kl_weight": 1.0,
|
| 99 |
+
"decoder": {
|
| 100 |
+
"is_conditioned": true,
|
| 101 |
+
"reconstruction_sum_across_elements": false
|
| 102 |
+
},
|
| 103 |
+
"prior": {
|
| 104 |
+
"learn": false,
|
| 105 |
+
"is_conditioned": false,
|
| 106 |
+
"use_gmm": false,
|
| 107 |
+
"gmm_num_modes": 10,
|
| 108 |
+
"gmm_learn_weights": false,
|
| 109 |
+
"use_categorical": false,
|
| 110 |
+
"categorical_dim": 10,
|
| 111 |
+
"categorical_gumbel_softmax_hard": false,
|
| 112 |
+
"categorical_init_temp": 1.0,
|
| 113 |
+
"categorical_temp_anneal_step": 0.001,
|
| 114 |
+
"categorical_min_temp": 0.3
|
| 115 |
+
},
|
| 116 |
+
"encoder_layer_dims": [
|
| 117 |
+
300,
|
| 118 |
+
400
|
| 119 |
+
],
|
| 120 |
+
"decoder_layer_dims": [
|
| 121 |
+
300,
|
| 122 |
+
400
|
| 123 |
+
],
|
| 124 |
+
"prior_layer_dims": [
|
| 125 |
+
300,
|
| 126 |
+
400
|
| 127 |
+
]
|
| 128 |
+
}
|
| 129 |
+
},
|
| 130 |
+
"actor": {
|
| 131 |
+
"optim_params": {
|
| 132 |
+
"policy": {
|
| 133 |
+
"optimizer_type": "adam",
|
| 134 |
+
"learning_rate": {
|
| 135 |
+
"initial": 0.0001,
|
| 136 |
+
"decay_factor": 0.1,
|
| 137 |
+
"epoch_schedule": [],
|
| 138 |
+
"scheduler_type": "multistep"
|
| 139 |
+
},
|
| 140 |
+
"regularization": {
|
| 141 |
+
"L2": 0.0
|
| 142 |
+
}
|
| 143 |
+
}
|
| 144 |
+
},
|
| 145 |
+
"loss": {
|
| 146 |
+
"l2_weight": 1.0,
|
| 147 |
+
"l1_weight": 0.0,
|
| 148 |
+
"cos_weight": 0.0
|
| 149 |
+
},
|
| 150 |
+
"actor_layer_dims": [
|
| 151 |
+
1024,
|
| 152 |
+
1024
|
| 153 |
+
],
|
| 154 |
+
"rnn": {
|
| 155 |
+
"enabled": true,
|
| 156 |
+
"horizon": 10,
|
| 157 |
+
"hidden_dim": 400,
|
| 158 |
+
"rnn_type": "LSTM",
|
| 159 |
+
"num_layers": 2,
|
| 160 |
+
"open_loop": false,
|
| 161 |
+
"kwargs": {
|
| 162 |
+
"bidirectional": false
|
| 163 |
+
}
|
| 164 |
+
},
|
| 165 |
+
"transformer": {
|
| 166 |
+
"enabled": false,
|
| 167 |
+
"context_length": 10,
|
| 168 |
+
"embed_dim": 512,
|
| 169 |
+
"num_layers": 6,
|
| 170 |
+
"num_heads": 8,
|
| 171 |
+
"emb_dropout": 0.1,
|
| 172 |
+
"attn_dropout": 0.1,
|
| 173 |
+
"block_output_dropout": 0.1,
|
| 174 |
+
"sinusoidal_embedding": false,
|
| 175 |
+
"activation": "gelu",
|
| 176 |
+
"supervise_all_steps": false,
|
| 177 |
+
"nn_parameter_for_timesteps": true
|
| 178 |
+
}
|
| 179 |
+
}
|
| 180 |
+
},
|
| 181 |
+
"observation": {
|
| 182 |
+
"planner": {
|
| 183 |
+
"modalities": {
|
| 184 |
+
"obs": {
|
| 185 |
+
"low_dim": [
|
| 186 |
+
"robot0_eef_pos",
|
| 187 |
+
"robot0_eef_quat",
|
| 188 |
+
"robot0_gripper_qpos",
|
| 189 |
+
"object"
|
| 190 |
+
],
|
| 191 |
+
"rgb": [],
|
| 192 |
+
"depth": [],
|
| 193 |
+
"scan": []
|
| 194 |
+
},
|
| 195 |
+
"goal": {
|
| 196 |
+
"low_dim": [],
|
| 197 |
+
"rgb": [],
|
| 198 |
+
"depth": [],
|
| 199 |
+
"scan": []
|
| 200 |
+
},
|
| 201 |
+
"subgoal": {
|
| 202 |
+
"low_dim": [
|
| 203 |
+
"robot0_eef_pos",
|
| 204 |
+
"robot0_eef_quat",
|
| 205 |
+
"robot0_gripper_qpos",
|
| 206 |
+
"object"
|
| 207 |
+
],
|
| 208 |
+
"rgb": [],
|
| 209 |
+
"depth": [],
|
| 210 |
+
"scan": []
|
| 211 |
+
}
|
| 212 |
+
},
|
| 213 |
+
"encoder": {
|
| 214 |
+
"low_dim": {
|
| 215 |
+
"core_class": null,
|
| 216 |
+
"core_kwargs": {},
|
| 217 |
+
"obs_randomizer_class": null,
|
| 218 |
+
"obs_randomizer_kwargs": {}
|
| 219 |
+
},
|
| 220 |
+
"rgb": {
|
| 221 |
+
"core_class": "VisualCore",
|
| 222 |
+
"core_kwargs": {},
|
| 223 |
+
"obs_randomizer_class": null,
|
| 224 |
+
"obs_randomizer_kwargs": {}
|
| 225 |
+
},
|
| 226 |
+
"depth": {
|
| 227 |
+
"core_class": "VisualCore",
|
| 228 |
+
"core_kwargs": {},
|
| 229 |
+
"obs_randomizer_class": null,
|
| 230 |
+
"obs_randomizer_kwargs": {}
|
| 231 |
+
},
|
| 232 |
+
"scan": {
|
| 233 |
+
"core_class": "ScanCore",
|
| 234 |
+
"core_kwargs": {},
|
| 235 |
+
"obs_randomizer_class": null,
|
| 236 |
+
"obs_randomizer_kwargs": {}
|
| 237 |
+
}
|
| 238 |
+
}
|
| 239 |
+
},
|
| 240 |
+
"actor": {
|
| 241 |
+
"modalities": {
|
| 242 |
+
"obs": {
|
| 243 |
+
"low_dim": [
|
| 244 |
+
"robot0_eef_pos",
|
| 245 |
+
"robot0_eef_quat",
|
| 246 |
+
"robot0_gripper_qpos",
|
| 247 |
+
"object"
|
| 248 |
+
],
|
| 249 |
+
"rgb": [],
|
| 250 |
+
"depth": [],
|
| 251 |
+
"scan": []
|
| 252 |
+
},
|
| 253 |
+
"goal": {
|
| 254 |
+
"low_dim": [],
|
| 255 |
+
"rgb": [],
|
| 256 |
+
"depth": [],
|
| 257 |
+
"scan": []
|
| 258 |
+
}
|
| 259 |
+
},
|
| 260 |
+
"encoder": {
|
| 261 |
+
"low_dim": {
|
| 262 |
+
"core_class": null,
|
| 263 |
+
"core_kwargs": {},
|
| 264 |
+
"obs_randomizer_class": null,
|
| 265 |
+
"obs_randomizer_kwargs": {}
|
| 266 |
+
},
|
| 267 |
+
"rgb": {
|
| 268 |
+
"core_class": "VisualCore",
|
| 269 |
+
"core_kwargs": {},
|
| 270 |
+
"obs_randomizer_class": null,
|
| 271 |
+
"obs_randomizer_kwargs": {}
|
| 272 |
+
},
|
| 273 |
+
"depth": {
|
| 274 |
+
"core_class": "VisualCore",
|
| 275 |
+
"core_kwargs": {},
|
| 276 |
+
"obs_randomizer_class": null,
|
| 277 |
+
"obs_randomizer_kwargs": {}
|
| 278 |
+
},
|
| 279 |
+
"scan": {
|
| 280 |
+
"core_class": "ScanCore",
|
| 281 |
+
"core_kwargs": {},
|
| 282 |
+
"obs_randomizer_class": null,
|
| 283 |
+
"obs_randomizer_kwargs": {}
|
| 284 |
+
}
|
| 285 |
+
}
|
| 286 |
+
}
|
| 287 |
+
},
|
| 288 |
+
"meta": {
|
| 289 |
+
"hp_base_config_file": null,
|
| 290 |
+
"hp_keys": [],
|
| 291 |
+
"hp_values": []
|
| 292 |
+
}
|
| 293 |
+
}
|
aloha-devel/robomimic/exps/templates/iql.json
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"algo_name": "iql",
|
| 3 |
+
"experiment": {
|
| 4 |
+
"name": "test",
|
| 5 |
+
"validate": false,
|
| 6 |
+
"logging": {
|
| 7 |
+
"terminal_output_to_txt": true,
|
| 8 |
+
"log_tb": true,
|
| 9 |
+
"log_wandb": false,
|
| 10 |
+
"wandb_proj_name": "debug"
|
| 11 |
+
},
|
| 12 |
+
"save": {
|
| 13 |
+
"enabled": true,
|
| 14 |
+
"every_n_seconds": null,
|
| 15 |
+
"every_n_epochs": 50,
|
| 16 |
+
"epochs": [],
|
| 17 |
+
"on_best_validation": false,
|
| 18 |
+
"on_best_rollout_return": false,
|
| 19 |
+
"on_best_rollout_success_rate": true
|
| 20 |
+
},
|
| 21 |
+
"epoch_every_n_steps": 100,
|
| 22 |
+
"validation_epoch_every_n_steps": 10,
|
| 23 |
+
"env": null,
|
| 24 |
+
"additional_envs": null,
|
| 25 |
+
"render": false,
|
| 26 |
+
"render_video": true,
|
| 27 |
+
"keep_all_videos": false,
|
| 28 |
+
"video_skip": 5,
|
| 29 |
+
"rollout": {
|
| 30 |
+
"enabled": true,
|
| 31 |
+
"n": 50,
|
| 32 |
+
"horizon": 400,
|
| 33 |
+
"rate": 50,
|
| 34 |
+
"warmstart": 0,
|
| 35 |
+
"terminate_on_success": true
|
| 36 |
+
}
|
| 37 |
+
},
|
| 38 |
+
"train": {
|
| 39 |
+
"data": null,
|
| 40 |
+
"output_dir": "../iql_trained_models",
|
| 41 |
+
"num_data_workers": 0,
|
| 42 |
+
"hdf5_cache_mode": "all",
|
| 43 |
+
"hdf5_use_swmr": true,
|
| 44 |
+
"hdf5_load_next_obs": true,
|
| 45 |
+
"hdf5_normalize_obs": false,
|
| 46 |
+
"hdf5_filter_key": null,
|
| 47 |
+
"hdf5_validation_filter_key": null,
|
| 48 |
+
"seq_length": 1,
|
| 49 |
+
"pad_seq_length": true,
|
| 50 |
+
"frame_stack": 1,
|
| 51 |
+
"pad_frame_stack": true,
|
| 52 |
+
"dataset_keys": [
|
| 53 |
+
"actions",
|
| 54 |
+
"rewards",
|
| 55 |
+
"dones"
|
| 56 |
+
],
|
| 57 |
+
"goal_mode": null,
|
| 58 |
+
"cuda": true,
|
| 59 |
+
"batch_size": 100,
|
| 60 |
+
"num_epochs": 2000,
|
| 61 |
+
"seed": 1
|
| 62 |
+
},
|
| 63 |
+
"algo": {
|
| 64 |
+
"optim_params": {
|
| 65 |
+
"critic": {
|
| 66 |
+
"learning_rate": {
|
| 67 |
+
"initial": 0.0001,
|
| 68 |
+
"decay_factor": 0.0,
|
| 69 |
+
"epoch_schedule": []
|
| 70 |
+
},
|
| 71 |
+
"regularization": {
|
| 72 |
+
"L2": 0.0
|
| 73 |
+
}
|
| 74 |
+
},
|
| 75 |
+
"vf": {
|
| 76 |
+
"learning_rate": {
|
| 77 |
+
"initial": 0.0001,
|
| 78 |
+
"decay_factor": 0.0,
|
| 79 |
+
"epoch_schedule": []
|
| 80 |
+
},
|
| 81 |
+
"regularization": {
|
| 82 |
+
"L2": 0.0
|
| 83 |
+
}
|
| 84 |
+
},
|
| 85 |
+
"actor": {
|
| 86 |
+
"learning_rate": {
|
| 87 |
+
"initial": 0.0001,
|
| 88 |
+
"decay_factor": 0.0,
|
| 89 |
+
"epoch_schedule": []
|
| 90 |
+
},
|
| 91 |
+
"regularization": {
|
| 92 |
+
"L2": 0.0
|
| 93 |
+
}
|
| 94 |
+
}
|
| 95 |
+
},
|
| 96 |
+
"discount": 0.99,
|
| 97 |
+
"target_tau": 0.01,
|
| 98 |
+
"actor": {
|
| 99 |
+
"net": {
|
| 100 |
+
"type": "gaussian",
|
| 101 |
+
"common": {
|
| 102 |
+
"std_activation": "softplus",
|
| 103 |
+
"low_noise_eval": true,
|
| 104 |
+
"use_tanh": false
|
| 105 |
+
},
|
| 106 |
+
"gaussian": {
|
| 107 |
+
"init_last_fc_weight": 0.001,
|
| 108 |
+
"init_std": 0.3,
|
| 109 |
+
"fixed_std": false
|
| 110 |
+
},
|
| 111 |
+
"gmm": {
|
| 112 |
+
"num_modes": 5,
|
| 113 |
+
"min_std": 0.0001
|
| 114 |
+
}
|
| 115 |
+
},
|
| 116 |
+
"layer_dims": [
|
| 117 |
+
300,
|
| 118 |
+
400
|
| 119 |
+
],
|
| 120 |
+
"max_gradient_norm": null
|
| 121 |
+
},
|
| 122 |
+
"critic": {
|
| 123 |
+
"ensemble": {
|
| 124 |
+
"n": 2
|
| 125 |
+
},
|
| 126 |
+
"layer_dims": [
|
| 127 |
+
300,
|
| 128 |
+
400
|
| 129 |
+
],
|
| 130 |
+
"use_huber": false,
|
| 131 |
+
"max_gradient_norm": null
|
| 132 |
+
},
|
| 133 |
+
"adv": {
|
| 134 |
+
"clip_adv_value": null,
|
| 135 |
+
"beta": 1.0,
|
| 136 |
+
"use_final_clip": true
|
| 137 |
+
},
|
| 138 |
+
"vf_quantile": 0.9
|
| 139 |
+
},
|
| 140 |
+
"observation": {
|
| 141 |
+
"modalities": {
|
| 142 |
+
"obs": {
|
| 143 |
+
"low_dim": [
|
| 144 |
+
"robot0_eef_pos",
|
| 145 |
+
"robot0_eef_quat",
|
| 146 |
+
"robot0_gripper_qpos",
|
| 147 |
+
"object"
|
| 148 |
+
],
|
| 149 |
+
"rgb": [],
|
| 150 |
+
"depth": [],
|
| 151 |
+
"scan": []
|
| 152 |
+
},
|
| 153 |
+
"goal": {
|
| 154 |
+
"low_dim": [],
|
| 155 |
+
"rgb": [],
|
| 156 |
+
"depth": [],
|
| 157 |
+
"scan": []
|
| 158 |
+
}
|
| 159 |
+
},
|
| 160 |
+
"encoder": {
|
| 161 |
+
"low_dim": {
|
| 162 |
+
"core_class": null,
|
| 163 |
+
"core_kwargs": {},
|
| 164 |
+
"obs_randomizer_class": null,
|
| 165 |
+
"obs_randomizer_kwargs": {}
|
| 166 |
+
},
|
| 167 |
+
"rgb": {
|
| 168 |
+
"core_class": "VisualCore",
|
| 169 |
+
"core_kwargs": {},
|
| 170 |
+
"obs_randomizer_class": null,
|
| 171 |
+
"obs_randomizer_kwargs": {}
|
| 172 |
+
},
|
| 173 |
+
"depth": {
|
| 174 |
+
"core_class": "VisualCore",
|
| 175 |
+
"core_kwargs": {},
|
| 176 |
+
"obs_randomizer_class": null,
|
| 177 |
+
"obs_randomizer_kwargs": {}
|
| 178 |
+
},
|
| 179 |
+
"scan": {
|
| 180 |
+
"core_class": "ScanCore",
|
| 181 |
+
"core_kwargs": {},
|
| 182 |
+
"obs_randomizer_class": null,
|
| 183 |
+
"obs_randomizer_kwargs": {}
|
| 184 |
+
}
|
| 185 |
+
}
|
| 186 |
+
},
|
| 187 |
+
"meta": {
|
| 188 |
+
"hp_base_config_file": null,
|
| 189 |
+
"hp_keys": [],
|
| 190 |
+
"hp_values": []
|
| 191 |
+
}
|
| 192 |
+
}
|
aloha-devel/robomimic/exps/templates/iris.json
ADDED
|
@@ -0,0 +1,465 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"algo_name": "iris",
|
| 3 |
+
"experiment": {
|
| 4 |
+
"name": "test",
|
| 5 |
+
"validate": false,
|
| 6 |
+
"logging": {
|
| 7 |
+
"terminal_output_to_txt": true,
|
| 8 |
+
"log_tb": true,
|
| 9 |
+
"log_wandb": false,
|
| 10 |
+
"wandb_proj_name": "debug"
|
| 11 |
+
},
|
| 12 |
+
"save": {
|
| 13 |
+
"enabled": true,
|
| 14 |
+
"every_n_seconds": null,
|
| 15 |
+
"every_n_epochs": 50,
|
| 16 |
+
"epochs": [],
|
| 17 |
+
"on_best_validation": false,
|
| 18 |
+
"on_best_rollout_return": false,
|
| 19 |
+
"on_best_rollout_success_rate": true
|
| 20 |
+
},
|
| 21 |
+
"epoch_every_n_steps": 100,
|
| 22 |
+
"validation_epoch_every_n_steps": 10,
|
| 23 |
+
"env": null,
|
| 24 |
+
"additional_envs": null,
|
| 25 |
+
"render": false,
|
| 26 |
+
"render_video": true,
|
| 27 |
+
"keep_all_videos": false,
|
| 28 |
+
"video_skip": 5,
|
| 29 |
+
"rollout": {
|
| 30 |
+
"enabled": true,
|
| 31 |
+
"n": 50,
|
| 32 |
+
"horizon": 400,
|
| 33 |
+
"rate": 50,
|
| 34 |
+
"warmstart": 0,
|
| 35 |
+
"terminate_on_success": true
|
| 36 |
+
}
|
| 37 |
+
},
|
| 38 |
+
"train": {
|
| 39 |
+
"data": null,
|
| 40 |
+
"output_dir": "../iris_trained_models",
|
| 41 |
+
"num_data_workers": 0,
|
| 42 |
+
"hdf5_cache_mode": "all",
|
| 43 |
+
"hdf5_use_swmr": true,
|
| 44 |
+
"hdf5_load_next_obs": true,
|
| 45 |
+
"hdf5_normalize_obs": false,
|
| 46 |
+
"hdf5_filter_key": null,
|
| 47 |
+
"hdf5_validation_filter_key": null,
|
| 48 |
+
"seq_length": 10,
|
| 49 |
+
"pad_seq_length": true,
|
| 50 |
+
"frame_stack": 1,
|
| 51 |
+
"pad_frame_stack": true,
|
| 52 |
+
"dataset_keys": [
|
| 53 |
+
"actions",
|
| 54 |
+
"rewards",
|
| 55 |
+
"dones"
|
| 56 |
+
],
|
| 57 |
+
"goal_mode": null,
|
| 58 |
+
"cuda": true,
|
| 59 |
+
"batch_size": 100,
|
| 60 |
+
"num_epochs": 2000,
|
| 61 |
+
"seed": 1
|
| 62 |
+
},
|
| 63 |
+
"algo": {
|
| 64 |
+
"mode": "separate",
|
| 65 |
+
"actor_use_random_subgoals": false,
|
| 66 |
+
"subgoal_update_interval": 10,
|
| 67 |
+
"latent_subgoal": {
|
| 68 |
+
"enabled": false,
|
| 69 |
+
"prior_correction": {
|
| 70 |
+
"enabled": false,
|
| 71 |
+
"num_samples": 100
|
| 72 |
+
}
|
| 73 |
+
},
|
| 74 |
+
"value_planner": {
|
| 75 |
+
"planner": {
|
| 76 |
+
"optim_params": {
|
| 77 |
+
"goal_network": {
|
| 78 |
+
"learning_rate": {
|
| 79 |
+
"initial": 0.0001,
|
| 80 |
+
"decay_factor": 0.1,
|
| 81 |
+
"epoch_schedule": []
|
| 82 |
+
},
|
| 83 |
+
"regularization": {
|
| 84 |
+
"L2": 0.0
|
| 85 |
+
}
|
| 86 |
+
}
|
| 87 |
+
},
|
| 88 |
+
"subgoal_horizon": 10,
|
| 89 |
+
"ae": {
|
| 90 |
+
"planner_layer_dims": [
|
| 91 |
+
300,
|
| 92 |
+
400
|
| 93 |
+
]
|
| 94 |
+
},
|
| 95 |
+
"vae": {
|
| 96 |
+
"enabled": true,
|
| 97 |
+
"latent_dim": 16,
|
| 98 |
+
"latent_clip": null,
|
| 99 |
+
"kl_weight": 1.0,
|
| 100 |
+
"decoder": {
|
| 101 |
+
"is_conditioned": true,
|
| 102 |
+
"reconstruction_sum_across_elements": false
|
| 103 |
+
},
|
| 104 |
+
"prior": {
|
| 105 |
+
"learn": false,
|
| 106 |
+
"is_conditioned": false,
|
| 107 |
+
"use_gmm": false,
|
| 108 |
+
"gmm_num_modes": 10,
|
| 109 |
+
"gmm_learn_weights": false,
|
| 110 |
+
"use_categorical": false,
|
| 111 |
+
"categorical_dim": 10,
|
| 112 |
+
"categorical_gumbel_softmax_hard": false,
|
| 113 |
+
"categorical_init_temp": 1.0,
|
| 114 |
+
"categorical_temp_anneal_step": 0.001,
|
| 115 |
+
"categorical_min_temp": 0.3
|
| 116 |
+
},
|
| 117 |
+
"encoder_layer_dims": [
|
| 118 |
+
300,
|
| 119 |
+
400
|
| 120 |
+
],
|
| 121 |
+
"decoder_layer_dims": [
|
| 122 |
+
300,
|
| 123 |
+
400
|
| 124 |
+
],
|
| 125 |
+
"prior_layer_dims": [
|
| 126 |
+
300,
|
| 127 |
+
400
|
| 128 |
+
]
|
| 129 |
+
}
|
| 130 |
+
},
|
| 131 |
+
"value": {
|
| 132 |
+
"optim_params": {
|
| 133 |
+
"critic": {
|
| 134 |
+
"learning_rate": {
|
| 135 |
+
"initial": 0.001,
|
| 136 |
+
"decay_factor": 0.1,
|
| 137 |
+
"epoch_schedule": []
|
| 138 |
+
},
|
| 139 |
+
"regularization": {
|
| 140 |
+
"L2": 0.0
|
| 141 |
+
},
|
| 142 |
+
"start_epoch": -1,
|
| 143 |
+
"end_epoch": -1
|
| 144 |
+
},
|
| 145 |
+
"action_sampler": {
|
| 146 |
+
"learning_rate": {
|
| 147 |
+
"initial": 0.001,
|
| 148 |
+
"decay_factor": 0.1,
|
| 149 |
+
"epoch_schedule": []
|
| 150 |
+
},
|
| 151 |
+
"regularization": {
|
| 152 |
+
"L2": 0.0
|
| 153 |
+
},
|
| 154 |
+
"start_epoch": -1,
|
| 155 |
+
"end_epoch": -1
|
| 156 |
+
},
|
| 157 |
+
"actor": {
|
| 158 |
+
"learning_rate": {
|
| 159 |
+
"initial": 0.001,
|
| 160 |
+
"decay_factor": 0.1,
|
| 161 |
+
"epoch_schedule": []
|
| 162 |
+
},
|
| 163 |
+
"regularization": {
|
| 164 |
+
"L2": 0.0
|
| 165 |
+
},
|
| 166 |
+
"start_epoch": -1,
|
| 167 |
+
"end_epoch": -1
|
| 168 |
+
}
|
| 169 |
+
},
|
| 170 |
+
"discount": 0.99,
|
| 171 |
+
"n_step": 1,
|
| 172 |
+
"target_tau": 0.005,
|
| 173 |
+
"infinite_horizon": false,
|
| 174 |
+
"critic": {
|
| 175 |
+
"use_huber": false,
|
| 176 |
+
"max_gradient_norm": null,
|
| 177 |
+
"value_bounds": null,
|
| 178 |
+
"num_action_samples": 10,
|
| 179 |
+
"num_action_samples_rollout": 100,
|
| 180 |
+
"ensemble": {
|
| 181 |
+
"n": 2,
|
| 182 |
+
"weight": 0.75
|
| 183 |
+
},
|
| 184 |
+
"distributional": {
|
| 185 |
+
"enabled": false,
|
| 186 |
+
"num_atoms": 51
|
| 187 |
+
},
|
| 188 |
+
"layer_dims": [
|
| 189 |
+
300,
|
| 190 |
+
400
|
| 191 |
+
]
|
| 192 |
+
},
|
| 193 |
+
"action_sampler": {
|
| 194 |
+
"actor_layer_dims": [
|
| 195 |
+
1024,
|
| 196 |
+
1024
|
| 197 |
+
],
|
| 198 |
+
"gmm": {
|
| 199 |
+
"enabled": false,
|
| 200 |
+
"num_modes": 5,
|
| 201 |
+
"min_std": 0.0001,
|
| 202 |
+
"std_activation": "softplus",
|
| 203 |
+
"low_noise_eval": true
|
| 204 |
+
},
|
| 205 |
+
"vae": {
|
| 206 |
+
"enabled": true,
|
| 207 |
+
"latent_dim": 14,
|
| 208 |
+
"latent_clip": null,
|
| 209 |
+
"kl_weight": 1.0,
|
| 210 |
+
"decoder": {
|
| 211 |
+
"is_conditioned": true,
|
| 212 |
+
"reconstruction_sum_across_elements": false
|
| 213 |
+
},
|
| 214 |
+
"prior": {
|
| 215 |
+
"learn": false,
|
| 216 |
+
"is_conditioned": false,
|
| 217 |
+
"use_gmm": false,
|
| 218 |
+
"gmm_num_modes": 10,
|
| 219 |
+
"gmm_learn_weights": false,
|
| 220 |
+
"use_categorical": false,
|
| 221 |
+
"categorical_dim": 10,
|
| 222 |
+
"categorical_gumbel_softmax_hard": false,
|
| 223 |
+
"categorical_init_temp": 1.0,
|
| 224 |
+
"categorical_temp_anneal_step": 0.001,
|
| 225 |
+
"categorical_min_temp": 0.3
|
| 226 |
+
},
|
| 227 |
+
"encoder_layer_dims": [
|
| 228 |
+
300,
|
| 229 |
+
400
|
| 230 |
+
],
|
| 231 |
+
"decoder_layer_dims": [
|
| 232 |
+
300,
|
| 233 |
+
400
|
| 234 |
+
],
|
| 235 |
+
"prior_layer_dims": [
|
| 236 |
+
300,
|
| 237 |
+
400
|
| 238 |
+
]
|
| 239 |
+
},
|
| 240 |
+
"freeze_encoder_epoch": -1
|
| 241 |
+
},
|
| 242 |
+
"actor": {
|
| 243 |
+
"enabled": false,
|
| 244 |
+
"perturbation_scale": 0.05,
|
| 245 |
+
"layer_dims": [
|
| 246 |
+
300,
|
| 247 |
+
400
|
| 248 |
+
]
|
| 249 |
+
}
|
| 250 |
+
},
|
| 251 |
+
"num_samples": 100
|
| 252 |
+
},
|
| 253 |
+
"actor": {
|
| 254 |
+
"optim_params": {
|
| 255 |
+
"policy": {
|
| 256 |
+
"optimizer_type": "adam",
|
| 257 |
+
"learning_rate": {
|
| 258 |
+
"initial": 0.0001,
|
| 259 |
+
"decay_factor": 0.1,
|
| 260 |
+
"epoch_schedule": [],
|
| 261 |
+
"scheduler_type": "multistep"
|
| 262 |
+
},
|
| 263 |
+
"regularization": {
|
| 264 |
+
"L2": 0.0
|
| 265 |
+
}
|
| 266 |
+
}
|
| 267 |
+
},
|
| 268 |
+
"loss": {
|
| 269 |
+
"l2_weight": 1.0,
|
| 270 |
+
"l1_weight": 0.0,
|
| 271 |
+
"cos_weight": 0.0
|
| 272 |
+
},
|
| 273 |
+
"actor_layer_dims": [
|
| 274 |
+
1024,
|
| 275 |
+
1024
|
| 276 |
+
],
|
| 277 |
+
"rnn": {
|
| 278 |
+
"enabled": true,
|
| 279 |
+
"horizon": 10,
|
| 280 |
+
"hidden_dim": 400,
|
| 281 |
+
"rnn_type": "LSTM",
|
| 282 |
+
"num_layers": 2,
|
| 283 |
+
"open_loop": false,
|
| 284 |
+
"kwargs": {
|
| 285 |
+
"bidirectional": false
|
| 286 |
+
}
|
| 287 |
+
},
|
| 288 |
+
"transformer": {
|
| 289 |
+
"enabled": false,
|
| 290 |
+
"context_length": 10,
|
| 291 |
+
"embed_dim": 512,
|
| 292 |
+
"num_layers": 6,
|
| 293 |
+
"num_heads": 8,
|
| 294 |
+
"emb_dropout": 0.1,
|
| 295 |
+
"attn_dropout": 0.1,
|
| 296 |
+
"block_output_dropout": 0.1,
|
| 297 |
+
"sinusoidal_embedding": false,
|
| 298 |
+
"activation": "gelu",
|
| 299 |
+
"supervise_all_steps": false,
|
| 300 |
+
"nn_parameter_for_timesteps": true
|
| 301 |
+
}
|
| 302 |
+
}
|
| 303 |
+
},
|
| 304 |
+
"observation": {
|
| 305 |
+
"value_planner": {
|
| 306 |
+
"planner": {
|
| 307 |
+
"modalities": {
|
| 308 |
+
"obs": {
|
| 309 |
+
"low_dim": [
|
| 310 |
+
"robot0_eef_pos",
|
| 311 |
+
"robot0_eef_quat",
|
| 312 |
+
"robot0_gripper_qpos",
|
| 313 |
+
"object"
|
| 314 |
+
],
|
| 315 |
+
"rgb": [],
|
| 316 |
+
"depth": [],
|
| 317 |
+
"scan": []
|
| 318 |
+
},
|
| 319 |
+
"goal": {
|
| 320 |
+
"low_dim": [],
|
| 321 |
+
"rgb": [],
|
| 322 |
+
"depth": [],
|
| 323 |
+
"scan": []
|
| 324 |
+
},
|
| 325 |
+
"subgoal": {
|
| 326 |
+
"low_dim": [
|
| 327 |
+
"robot0_eef_pos",
|
| 328 |
+
"robot0_eef_quat",
|
| 329 |
+
"robot0_gripper_qpos",
|
| 330 |
+
"object"
|
| 331 |
+
],
|
| 332 |
+
"rgb": [],
|
| 333 |
+
"depth": [],
|
| 334 |
+
"scan": []
|
| 335 |
+
}
|
| 336 |
+
},
|
| 337 |
+
"encoder": {
|
| 338 |
+
"low_dim": {
|
| 339 |
+
"core_class": null,
|
| 340 |
+
"core_kwargs": {},
|
| 341 |
+
"obs_randomizer_class": null,
|
| 342 |
+
"obs_randomizer_kwargs": {}
|
| 343 |
+
},
|
| 344 |
+
"rgb": {
|
| 345 |
+
"core_class": "VisualCore",
|
| 346 |
+
"core_kwargs": {},
|
| 347 |
+
"obs_randomizer_class": null,
|
| 348 |
+
"obs_randomizer_kwargs": {}
|
| 349 |
+
},
|
| 350 |
+
"depth": {
|
| 351 |
+
"core_class": "VisualCore",
|
| 352 |
+
"core_kwargs": {},
|
| 353 |
+
"obs_randomizer_class": null,
|
| 354 |
+
"obs_randomizer_kwargs": {}
|
| 355 |
+
},
|
| 356 |
+
"scan": {
|
| 357 |
+
"core_class": "ScanCore",
|
| 358 |
+
"core_kwargs": {},
|
| 359 |
+
"obs_randomizer_class": null,
|
| 360 |
+
"obs_randomizer_kwargs": {}
|
| 361 |
+
}
|
| 362 |
+
}
|
| 363 |
+
},
|
| 364 |
+
"value": {
|
| 365 |
+
"modalities": {
|
| 366 |
+
"obs": {
|
| 367 |
+
"low_dim": [
|
| 368 |
+
"robot0_eef_pos",
|
| 369 |
+
"robot0_eef_quat",
|
| 370 |
+
"robot0_gripper_qpos",
|
| 371 |
+
"object"
|
| 372 |
+
],
|
| 373 |
+
"rgb": [],
|
| 374 |
+
"depth": [],
|
| 375 |
+
"scan": []
|
| 376 |
+
},
|
| 377 |
+
"goal": {
|
| 378 |
+
"low_dim": [],
|
| 379 |
+
"rgb": [],
|
| 380 |
+
"depth": [],
|
| 381 |
+
"scan": []
|
| 382 |
+
}
|
| 383 |
+
},
|
| 384 |
+
"encoder": {
|
| 385 |
+
"low_dim": {
|
| 386 |
+
"core_class": null,
|
| 387 |
+
"core_kwargs": {},
|
| 388 |
+
"obs_randomizer_class": null,
|
| 389 |
+
"obs_randomizer_kwargs": {}
|
| 390 |
+
},
|
| 391 |
+
"rgb": {
|
| 392 |
+
"core_class": "VisualCore",
|
| 393 |
+
"core_kwargs": {},
|
| 394 |
+
"obs_randomizer_class": null,
|
| 395 |
+
"obs_randomizer_kwargs": {}
|
| 396 |
+
},
|
| 397 |
+
"depth": {
|
| 398 |
+
"core_class": "VisualCore",
|
| 399 |
+
"core_kwargs": {},
|
| 400 |
+
"obs_randomizer_class": null,
|
| 401 |
+
"obs_randomizer_kwargs": {}
|
| 402 |
+
},
|
| 403 |
+
"scan": {
|
| 404 |
+
"core_class": "ScanCore",
|
| 405 |
+
"core_kwargs": {},
|
| 406 |
+
"obs_randomizer_class": null,
|
| 407 |
+
"obs_randomizer_kwargs": {}
|
| 408 |
+
}
|
| 409 |
+
}
|
| 410 |
+
}
|
| 411 |
+
},
|
| 412 |
+
"actor": {
|
| 413 |
+
"modalities": {
|
| 414 |
+
"obs": {
|
| 415 |
+
"low_dim": [
|
| 416 |
+
"robot0_eef_pos",
|
| 417 |
+
"robot0_eef_quat",
|
| 418 |
+
"robot0_gripper_qpos",
|
| 419 |
+
"object"
|
| 420 |
+
],
|
| 421 |
+
"rgb": [],
|
| 422 |
+
"depth": [],
|
| 423 |
+
"scan": []
|
| 424 |
+
},
|
| 425 |
+
"goal": {
|
| 426 |
+
"low_dim": [],
|
| 427 |
+
"rgb": [],
|
| 428 |
+
"depth": [],
|
| 429 |
+
"scan": []
|
| 430 |
+
}
|
| 431 |
+
},
|
| 432 |
+
"encoder": {
|
| 433 |
+
"low_dim": {
|
| 434 |
+
"core_class": null,
|
| 435 |
+
"core_kwargs": {},
|
| 436 |
+
"obs_randomizer_class": null,
|
| 437 |
+
"obs_randomizer_kwargs": {}
|
| 438 |
+
},
|
| 439 |
+
"rgb": {
|
| 440 |
+
"core_class": "VisualCore",
|
| 441 |
+
"core_kwargs": {},
|
| 442 |
+
"obs_randomizer_class": null,
|
| 443 |
+
"obs_randomizer_kwargs": {}
|
| 444 |
+
},
|
| 445 |
+
"depth": {
|
| 446 |
+
"core_class": "VisualCore",
|
| 447 |
+
"core_kwargs": {},
|
| 448 |
+
"obs_randomizer_class": null,
|
| 449 |
+
"obs_randomizer_kwargs": {}
|
| 450 |
+
},
|
| 451 |
+
"scan": {
|
| 452 |
+
"core_class": "ScanCore",
|
| 453 |
+
"core_kwargs": {},
|
| 454 |
+
"obs_randomizer_class": null,
|
| 455 |
+
"obs_randomizer_kwargs": {}
|
| 456 |
+
}
|
| 457 |
+
}
|
| 458 |
+
}
|
| 459 |
+
},
|
| 460 |
+
"meta": {
|
| 461 |
+
"hp_base_config_file": null,
|
| 462 |
+
"hp_keys": [],
|
| 463 |
+
"hp_values": []
|
| 464 |
+
}
|
| 465 |
+
}
|
aloha-devel/robomimic/exps/templates/td3_bc.json
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"algo_name": "td3_bc",
|
| 3 |
+
"experiment": {
|
| 4 |
+
"name": "test",
|
| 5 |
+
"validate": false,
|
| 6 |
+
"logging": {
|
| 7 |
+
"terminal_output_to_txt": true,
|
| 8 |
+
"log_tb": true,
|
| 9 |
+
"log_wandb": false,
|
| 10 |
+
"wandb_proj_name": "debug"
|
| 11 |
+
},
|
| 12 |
+
"save": {
|
| 13 |
+
"enabled": true,
|
| 14 |
+
"every_n_seconds": null,
|
| 15 |
+
"every_n_epochs": 20,
|
| 16 |
+
"epochs": [],
|
| 17 |
+
"on_best_validation": false,
|
| 18 |
+
"on_best_rollout_return": true,
|
| 19 |
+
"on_best_rollout_success_rate": false
|
| 20 |
+
},
|
| 21 |
+
"epoch_every_n_steps": 5000,
|
| 22 |
+
"validation_epoch_every_n_steps": 10,
|
| 23 |
+
"env": null,
|
| 24 |
+
"additional_envs": null,
|
| 25 |
+
"render": false,
|
| 26 |
+
"render_video": false,
|
| 27 |
+
"keep_all_videos": false,
|
| 28 |
+
"video_skip": 5,
|
| 29 |
+
"rollout": {
|
| 30 |
+
"enabled": true,
|
| 31 |
+
"n": 50,
|
| 32 |
+
"horizon": 1000,
|
| 33 |
+
"rate": 1,
|
| 34 |
+
"warmstart": 0,
|
| 35 |
+
"terminate_on_success": true
|
| 36 |
+
}
|
| 37 |
+
},
|
| 38 |
+
"train": {
|
| 39 |
+
"data": null,
|
| 40 |
+
"output_dir": "../td3_bc_trained_models",
|
| 41 |
+
"num_data_workers": 0,
|
| 42 |
+
"hdf5_cache_mode": "all",
|
| 43 |
+
"hdf5_use_swmr": true,
|
| 44 |
+
"hdf5_load_next_obs": true,
|
| 45 |
+
"hdf5_normalize_obs": true,
|
| 46 |
+
"hdf5_filter_key": null,
|
| 47 |
+
"hdf5_validation_filter_key": null,
|
| 48 |
+
"seq_length": 1,
|
| 49 |
+
"pad_seq_length": true,
|
| 50 |
+
"frame_stack": 1,
|
| 51 |
+
"pad_frame_stack": true,
|
| 52 |
+
"dataset_keys": [
|
| 53 |
+
"actions",
|
| 54 |
+
"rewards",
|
| 55 |
+
"dones"
|
| 56 |
+
],
|
| 57 |
+
"goal_mode": null,
|
| 58 |
+
"cuda": true,
|
| 59 |
+
"batch_size": 256,
|
| 60 |
+
"num_epochs": 200,
|
| 61 |
+
"seed": 1
|
| 62 |
+
},
|
| 63 |
+
"algo": {
|
| 64 |
+
"optim_params": {
|
| 65 |
+
"critic": {
|
| 66 |
+
"learning_rate": {
|
| 67 |
+
"initial": 0.0003,
|
| 68 |
+
"decay_factor": 0.1,
|
| 69 |
+
"epoch_schedule": []
|
| 70 |
+
},
|
| 71 |
+
"regularization": {
|
| 72 |
+
"L2": 0.0
|
| 73 |
+
},
|
| 74 |
+
"start_epoch": -1,
|
| 75 |
+
"end_epoch": -1
|
| 76 |
+
},
|
| 77 |
+
"actor": {
|
| 78 |
+
"learning_rate": {
|
| 79 |
+
"initial": 0.0003,
|
| 80 |
+
"decay_factor": 0.1,
|
| 81 |
+
"epoch_schedule": []
|
| 82 |
+
},
|
| 83 |
+
"regularization": {
|
| 84 |
+
"L2": 0.0
|
| 85 |
+
},
|
| 86 |
+
"start_epoch": -1,
|
| 87 |
+
"end_epoch": -1
|
| 88 |
+
}
|
| 89 |
+
},
|
| 90 |
+
"alpha": 2.5,
|
| 91 |
+
"discount": 0.99,
|
| 92 |
+
"n_step": 1,
|
| 93 |
+
"target_tau": 0.005,
|
| 94 |
+
"infinite_horizon": false,
|
| 95 |
+
"critic": {
|
| 96 |
+
"use_huber": false,
|
| 97 |
+
"max_gradient_norm": null,
|
| 98 |
+
"value_bounds": null,
|
| 99 |
+
"ensemble": {
|
| 100 |
+
"n": 2,
|
| 101 |
+
"weight": 1.0
|
| 102 |
+
},
|
| 103 |
+
"layer_dims": [
|
| 104 |
+
256,
|
| 105 |
+
256
|
| 106 |
+
]
|
| 107 |
+
},
|
| 108 |
+
"actor": {
|
| 109 |
+
"update_freq": 2,
|
| 110 |
+
"noise_std": 0.2,
|
| 111 |
+
"noise_clip": 0.5,
|
| 112 |
+
"layer_dims": [
|
| 113 |
+
256,
|
| 114 |
+
256
|
| 115 |
+
]
|
| 116 |
+
}
|
| 117 |
+
},
|
| 118 |
+
"observation": {
|
| 119 |
+
"modalities": {
|
| 120 |
+
"obs": {
|
| 121 |
+
"low_dim": [
|
| 122 |
+
"flat"
|
| 123 |
+
],
|
| 124 |
+
"rgb": [],
|
| 125 |
+
"depth": [],
|
| 126 |
+
"scan": []
|
| 127 |
+
},
|
| 128 |
+
"goal": {
|
| 129 |
+
"low_dim": [],
|
| 130 |
+
"rgb": [],
|
| 131 |
+
"depth": [],
|
| 132 |
+
"scan": []
|
| 133 |
+
}
|
| 134 |
+
},
|
| 135 |
+
"encoder": {
|
| 136 |
+
"low_dim": {
|
| 137 |
+
"core_class": null,
|
| 138 |
+
"core_kwargs": {},
|
| 139 |
+
"obs_randomizer_class": null,
|
| 140 |
+
"obs_randomizer_kwargs": {}
|
| 141 |
+
},
|
| 142 |
+
"rgb": {
|
| 143 |
+
"core_class": "VisualCore",
|
| 144 |
+
"core_kwargs": {},
|
| 145 |
+
"obs_randomizer_class": null,
|
| 146 |
+
"obs_randomizer_kwargs": {}
|
| 147 |
+
},
|
| 148 |
+
"depth": {
|
| 149 |
+
"core_class": "VisualCore",
|
| 150 |
+
"core_kwargs": {},
|
| 151 |
+
"obs_randomizer_class": null,
|
| 152 |
+
"obs_randomizer_kwargs": {}
|
| 153 |
+
},
|
| 154 |
+
"scan": {
|
| 155 |
+
"core_class": "ScanCore",
|
| 156 |
+
"core_kwargs": {},
|
| 157 |
+
"obs_randomizer_class": null,
|
| 158 |
+
"obs_randomizer_kwargs": {}
|
| 159 |
+
}
|
| 160 |
+
}
|
| 161 |
+
},
|
| 162 |
+
"meta": {
|
| 163 |
+
"hp_base_config_file": null,
|
| 164 |
+
"hp_keys": [],
|
| 165 |
+
"hp_values": []
|
| 166 |
+
}
|
| 167 |
+
}
|
aloha-devel/robomimic/macros.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Set of global variables shared across robomimic
|
| 3 |
+
"""
|
| 4 |
+
# Sets debugging mode. Should be set at top-level script so that internal
|
| 5 |
+
# debugging functionalities are made active
|
| 6 |
+
DEBUG = False
|
| 7 |
+
|
| 8 |
+
# Whether to visualize the before & after of an observation randomizer
|
| 9 |
+
VISUALIZE_RANDOMIZER = False
|
| 10 |
+
|
| 11 |
+
# wandb entity (eg. username or team name)
|
| 12 |
+
WANDB_ENTITY = None
|
| 13 |
+
|
| 14 |
+
# wandb api key (obtain from https://wandb.ai/authorize)
|
| 15 |
+
# alternatively, set up wandb from terminal with `wandb login`
|
| 16 |
+
WANDB_API_KEY = None
|
| 17 |
+
|
| 18 |
+
try:
|
| 19 |
+
from robomimic.macros_private import *
|
| 20 |
+
except ImportError:
|
| 21 |
+
from robomimic.utils.log_utils import log_warning
|
| 22 |
+
import robomimic
|
| 23 |
+
log_warning(
|
| 24 |
+
"No private macro file found!"\
|
| 25 |
+
"\nIt is recommended to use a private macro file"\
|
| 26 |
+
"\nTo setup, run: python {}/scripts/setup_macros.py".format(robomimic.__path__[0])
|
| 27 |
+
)
|
aloha-devel/robomimic/models/__pycache__/__init__.cpython-38.pyc
ADDED
|
Binary file (223 Bytes). View file
|
|
|
aloha-devel/robomimic/models/__pycache__/base_nets.cpython-38.pyc
ADDED
|
Binary file (33.4 kB). View file
|
|
|
aloha-devel/robomimic/models/__pycache__/distributions.cpython-38.pyc
ADDED
|
Binary file (5.54 kB). View file
|
|
|
aloha-devel/robomimic/models/__pycache__/policy_nets.cpython-38.pyc
ADDED
|
Binary file (47.4 kB). View file
|
|
|
aloha-devel/robomimic/models/__pycache__/transformers.cpython-38.pyc
ADDED
|
Binary file (12.5 kB). View file
|
|
|
aloha-devel/robomimic/models/__pycache__/value_nets.cpython-38.pyc
ADDED
|
Binary file (11.4 kB). View file
|
|
|
aloha-devel/robomimic/models/base_nets.py
ADDED
|
@@ -0,0 +1,1156 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Contains torch Modules that correspond to basic network building blocks, like
|
| 3 |
+
MLP, RNN, and CNN backbones.
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import math
|
| 7 |
+
import abc
|
| 8 |
+
import numpy as np
|
| 9 |
+
import textwrap
|
| 10 |
+
from collections import OrderedDict
|
| 11 |
+
|
| 12 |
+
import torch
|
| 13 |
+
import torch.nn as nn
|
| 14 |
+
import torch.nn.functional as F
|
| 15 |
+
from torchvision import models as vision_models
|
| 16 |
+
from torchvision import transforms
|
| 17 |
+
|
| 18 |
+
import robomimic.utils.tensor_utils as TensorUtils
|
| 19 |
+
|
| 20 |
+
CONV_ACTIVATIONS = {
|
| 21 |
+
"relu": nn.ReLU,
|
| 22 |
+
"None": None,
|
| 23 |
+
None: None,
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def rnn_args_from_config(rnn_config):
|
| 28 |
+
"""
|
| 29 |
+
Takes a Config object corresponding to RNN settings
|
| 30 |
+
(for example `config.algo.rnn` in BCConfig) and extracts
|
| 31 |
+
rnn kwargs for instantiating rnn networks.
|
| 32 |
+
"""
|
| 33 |
+
return dict(
|
| 34 |
+
rnn_hidden_dim=rnn_config.hidden_dim,
|
| 35 |
+
rnn_num_layers=rnn_config.num_layers,
|
| 36 |
+
rnn_type=rnn_config.rnn_type,
|
| 37 |
+
rnn_kwargs=dict(rnn_config.kwargs),
|
| 38 |
+
)
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def transformer_args_from_config(transformer_config):
|
| 42 |
+
"""
|
| 43 |
+
Takes a Config object corresponding to Transformer settings
|
| 44 |
+
(for example `config.algo.transformer` in BCConfig) and extracts
|
| 45 |
+
transformer kwargs for instantiating transformer networks.
|
| 46 |
+
"""
|
| 47 |
+
transformer_args = dict(
|
| 48 |
+
transformer_context_length=transformer_config.context_length,
|
| 49 |
+
transformer_embed_dim=transformer_config.embed_dim,
|
| 50 |
+
transformer_num_heads=transformer_config.num_heads,
|
| 51 |
+
transformer_emb_dropout=transformer_config.emb_dropout,
|
| 52 |
+
transformer_attn_dropout=transformer_config.attn_dropout,
|
| 53 |
+
transformer_block_output_dropout=transformer_config.block_output_dropout,
|
| 54 |
+
transformer_sinusoidal_embedding=transformer_config.sinusoidal_embedding,
|
| 55 |
+
transformer_activation=transformer_config.activation,
|
| 56 |
+
transformer_nn_parameter_for_timesteps=transformer_config.nn_parameter_for_timesteps,
|
| 57 |
+
)
|
| 58 |
+
|
| 59 |
+
if "num_layers" in transformer_config:
|
| 60 |
+
transformer_args["transformer_num_layers"] = transformer_config.num_layers
|
| 61 |
+
|
| 62 |
+
return transformer_args
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
class Module(torch.nn.Module):
|
| 66 |
+
"""
|
| 67 |
+
Base class for networks. The only difference from torch.nn.Module is that it
|
| 68 |
+
requires implementing @output_shape.
|
| 69 |
+
"""
|
| 70 |
+
@abc.abstractmethod
|
| 71 |
+
def output_shape(self, input_shape=None):
|
| 72 |
+
"""
|
| 73 |
+
Function to compute output shape from inputs to this module.
|
| 74 |
+
|
| 75 |
+
Args:
|
| 76 |
+
input_shape (iterable of int): shape of input. Does not include batch dimension.
|
| 77 |
+
Some modules may not need this argument, if their output does not depend
|
| 78 |
+
on the size of the input, or if they assume fixed size input.
|
| 79 |
+
|
| 80 |
+
Returns:
|
| 81 |
+
out_shape ([int]): list of integers corresponding to output shape
|
| 82 |
+
"""
|
| 83 |
+
raise NotImplementedError
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
class Sequential(torch.nn.Sequential, Module):
|
| 87 |
+
"""
|
| 88 |
+
Compose multiple Modules together (defined above).
|
| 89 |
+
"""
|
| 90 |
+
def __init__(self, *args):
|
| 91 |
+
for arg in args:
|
| 92 |
+
assert isinstance(arg, Module)
|
| 93 |
+
torch.nn.Sequential.__init__(self, *args)
|
| 94 |
+
self.fixed = False
|
| 95 |
+
|
| 96 |
+
def output_shape(self, input_shape=None):
|
| 97 |
+
"""
|
| 98 |
+
Function to compute output shape from inputs to this module.
|
| 99 |
+
|
| 100 |
+
Args:
|
| 101 |
+
input_shape (iterable of int): shape of input. Does not include batch dimension.
|
| 102 |
+
Some modules may not need this argument, if their output does not depend
|
| 103 |
+
on the size of the input, or if they assume fixed size input.
|
| 104 |
+
|
| 105 |
+
Returns:
|
| 106 |
+
out_shape ([int]): list of integers corresponding to output shape
|
| 107 |
+
"""
|
| 108 |
+
out_shape = input_shape
|
| 109 |
+
for module in self:
|
| 110 |
+
out_shape = module.output_shape(out_shape)
|
| 111 |
+
return out_shape
|
| 112 |
+
|
| 113 |
+
def freeze(self):
|
| 114 |
+
self.fixed = True
|
| 115 |
+
|
| 116 |
+
def train(self, mode):
|
| 117 |
+
if self.fixed:
|
| 118 |
+
super().train(False)
|
| 119 |
+
else:
|
| 120 |
+
super().train(mode)
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
class Parameter(Module):
|
| 124 |
+
"""
|
| 125 |
+
A class that is a thin wrapper around a torch.nn.Parameter to make for easy saving
|
| 126 |
+
and optimization.
|
| 127 |
+
"""
|
| 128 |
+
def __init__(self, init_tensor):
|
| 129 |
+
"""
|
| 130 |
+
Args:
|
| 131 |
+
init_tensor (torch.Tensor): initial tensor
|
| 132 |
+
"""
|
| 133 |
+
super(Parameter, self).__init__()
|
| 134 |
+
self.param = torch.nn.Parameter(init_tensor)
|
| 135 |
+
|
| 136 |
+
def output_shape(self, input_shape=None):
|
| 137 |
+
"""
|
| 138 |
+
Function to compute output shape from inputs to this module.
|
| 139 |
+
|
| 140 |
+
Args:
|
| 141 |
+
input_shape (iterable of int): shape of input. Does not include batch dimension.
|
| 142 |
+
Some modules may not need this argument, if their output does not depend
|
| 143 |
+
on the size of the input, or if they assume fixed size input.
|
| 144 |
+
|
| 145 |
+
Returns:
|
| 146 |
+
out_shape ([int]): list of integers corresponding to output shape
|
| 147 |
+
"""
|
| 148 |
+
return list(self.param.shape)
|
| 149 |
+
|
| 150 |
+
def forward(self, inputs=None):
|
| 151 |
+
"""
|
| 152 |
+
Forward call just returns the parameter tensor.
|
| 153 |
+
"""
|
| 154 |
+
return self.param
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
class Unsqueeze(Module):
|
| 158 |
+
"""
|
| 159 |
+
Trivial class that unsqueezes the input. Useful for including in a nn.Sequential network
|
| 160 |
+
"""
|
| 161 |
+
def __init__(self, dim):
|
| 162 |
+
super(Unsqueeze, self).__init__()
|
| 163 |
+
self.dim = dim
|
| 164 |
+
|
| 165 |
+
def output_shape(self, input_shape=None):
|
| 166 |
+
assert input_shape is not None
|
| 167 |
+
return input_shape + [1] if self.dim == -1 else input_shape[:self.dim + 1] + [1] + input_shape[self.dim + 1:]
|
| 168 |
+
|
| 169 |
+
def forward(self, x):
|
| 170 |
+
return x.unsqueeze(dim=self.dim)
|
| 171 |
+
|
| 172 |
+
|
| 173 |
+
class Squeeze(Module):
|
| 174 |
+
"""
|
| 175 |
+
Trivial class that squeezes the input. Useful for including in a nn.Sequential network
|
| 176 |
+
"""
|
| 177 |
+
|
| 178 |
+
def __init__(self, dim):
|
| 179 |
+
super(Squeeze, self).__init__()
|
| 180 |
+
self.dim = dim
|
| 181 |
+
|
| 182 |
+
def output_shape(self, input_shape=None):
|
| 183 |
+
assert input_shape is not None
|
| 184 |
+
return input_shape[:self.dim] + input_shape[self.dim+1:] if input_shape[self.dim] == 1 else input_shape
|
| 185 |
+
|
| 186 |
+
def forward(self, x):
|
| 187 |
+
return x.squeeze(dim=self.dim)
|
| 188 |
+
|
| 189 |
+
|
| 190 |
+
class MLP(Module):
|
| 191 |
+
"""
|
| 192 |
+
Base class for simple Multi-Layer Perceptrons.
|
| 193 |
+
"""
|
| 194 |
+
def __init__(
|
| 195 |
+
self,
|
| 196 |
+
input_dim,
|
| 197 |
+
output_dim,
|
| 198 |
+
layer_dims=(),
|
| 199 |
+
layer_func=nn.Linear,
|
| 200 |
+
layer_func_kwargs=None,
|
| 201 |
+
activation=nn.ReLU,
|
| 202 |
+
dropouts=None,
|
| 203 |
+
normalization=False,
|
| 204 |
+
output_activation=None,
|
| 205 |
+
):
|
| 206 |
+
"""
|
| 207 |
+
Args:
|
| 208 |
+
input_dim (int): dimension of inputs
|
| 209 |
+
|
| 210 |
+
output_dim (int): dimension of outputs
|
| 211 |
+
|
| 212 |
+
layer_dims ([int]): sequence of integers for the hidden layers sizes
|
| 213 |
+
|
| 214 |
+
layer_func: mapping per layer - defaults to Linear
|
| 215 |
+
|
| 216 |
+
layer_func_kwargs (dict): kwargs for @layer_func
|
| 217 |
+
|
| 218 |
+
activation: non-linearity per layer - defaults to ReLU
|
| 219 |
+
|
| 220 |
+
dropouts ([float]): if not None, adds dropout layers with the corresponding probabilities
|
| 221 |
+
after every layer. Must be same size as @layer_dims.
|
| 222 |
+
|
| 223 |
+
normalization (bool): if True, apply layer normalization after each layer
|
| 224 |
+
|
| 225 |
+
output_activation: if provided, applies the provided non-linearity to the output layer
|
| 226 |
+
"""
|
| 227 |
+
super(MLP, self).__init__()
|
| 228 |
+
layers = []
|
| 229 |
+
dim = input_dim
|
| 230 |
+
if layer_func_kwargs is None:
|
| 231 |
+
layer_func_kwargs = dict()
|
| 232 |
+
if dropouts is not None:
|
| 233 |
+
assert(len(dropouts) == len(layer_dims))
|
| 234 |
+
for i, l in enumerate(layer_dims):
|
| 235 |
+
layers.append(layer_func(dim, l, **layer_func_kwargs))
|
| 236 |
+
if normalization:
|
| 237 |
+
layers.append(nn.LayerNorm(l))
|
| 238 |
+
layers.append(activation())
|
| 239 |
+
if dropouts is not None and dropouts[i] > 0.:
|
| 240 |
+
layers.append(nn.Dropout(dropouts[i]))
|
| 241 |
+
dim = l
|
| 242 |
+
layers.append(layer_func(dim, output_dim))
|
| 243 |
+
if output_activation is not None:
|
| 244 |
+
layers.append(output_activation())
|
| 245 |
+
self._layer_func = layer_func
|
| 246 |
+
self.nets = layers
|
| 247 |
+
self._model = nn.Sequential(*layers)
|
| 248 |
+
|
| 249 |
+
self._layer_dims = layer_dims
|
| 250 |
+
self._input_dim = input_dim
|
| 251 |
+
self._output_dim = output_dim
|
| 252 |
+
self._dropouts = dropouts
|
| 253 |
+
self._act = activation
|
| 254 |
+
self._output_act = output_activation
|
| 255 |
+
|
| 256 |
+
def output_shape(self, input_shape=None):
|
| 257 |
+
"""
|
| 258 |
+
Function to compute output shape from inputs to this module.
|
| 259 |
+
|
| 260 |
+
Args:
|
| 261 |
+
input_shape (iterable of int): shape of input. Does not include batch dimension.
|
| 262 |
+
Some modules may not need this argument, if their output does not depend
|
| 263 |
+
on the size of the input, or if they assume fixed size input.
|
| 264 |
+
|
| 265 |
+
Returns:
|
| 266 |
+
out_shape ([int]): list of integers corresponding to output shape
|
| 267 |
+
"""
|
| 268 |
+
return [self._output_dim]
|
| 269 |
+
|
| 270 |
+
def forward(self, inputs):
|
| 271 |
+
"""
|
| 272 |
+
Forward pass.
|
| 273 |
+
"""
|
| 274 |
+
return self._model(inputs)
|
| 275 |
+
|
| 276 |
+
def __repr__(self):
|
| 277 |
+
"""Pretty print network."""
|
| 278 |
+
header = str(self.__class__.__name__)
|
| 279 |
+
act = None if self._act is None else self._act.__name__
|
| 280 |
+
output_act = None if self._output_act is None else self._output_act.__name__
|
| 281 |
+
|
| 282 |
+
indent = ' ' * 4
|
| 283 |
+
msg = "input_dim={}\noutput_dim={}\nlayer_dims={}\nlayer_func={}\ndropout={}\nact={}\noutput_act={}".format(
|
| 284 |
+
self._input_dim, self._output_dim, self._layer_dims,
|
| 285 |
+
self._layer_func.__name__, self._dropouts, act, output_act
|
| 286 |
+
)
|
| 287 |
+
msg = textwrap.indent(msg, indent)
|
| 288 |
+
msg = header + '(\n' + msg + '\n)'
|
| 289 |
+
return msg
|
| 290 |
+
|
| 291 |
+
|
| 292 |
+
class RNN_Base(Module):
|
| 293 |
+
"""
|
| 294 |
+
A wrapper class for a multi-step RNN and a per-step network.
|
| 295 |
+
"""
|
| 296 |
+
def __init__(
|
| 297 |
+
self,
|
| 298 |
+
input_dim,
|
| 299 |
+
rnn_hidden_dim,
|
| 300 |
+
rnn_num_layers,
|
| 301 |
+
rnn_type="LSTM", # [LSTM, GRU]
|
| 302 |
+
rnn_kwargs=None,
|
| 303 |
+
per_step_net=None,
|
| 304 |
+
):
|
| 305 |
+
"""
|
| 306 |
+
Args:
|
| 307 |
+
input_dim (int): dimension of inputs
|
| 308 |
+
|
| 309 |
+
rnn_hidden_dim (int): RNN hidden dimension
|
| 310 |
+
|
| 311 |
+
rnn_num_layers (int): number of RNN layers
|
| 312 |
+
|
| 313 |
+
rnn_type (str): [LSTM, GRU]
|
| 314 |
+
|
| 315 |
+
rnn_kwargs (dict): kwargs for the torch.nn.LSTM / GRU
|
| 316 |
+
|
| 317 |
+
per_step_net: a network that runs per time step on top of the RNN output
|
| 318 |
+
"""
|
| 319 |
+
super(RNN_Base, self).__init__()
|
| 320 |
+
self.per_step_net = per_step_net
|
| 321 |
+
if per_step_net is not None:
|
| 322 |
+
assert isinstance(per_step_net, Module), "RNN_Base: per_step_net is not instance of Module"
|
| 323 |
+
|
| 324 |
+
assert rnn_type in ["LSTM", "GRU"]
|
| 325 |
+
rnn_cls = nn.LSTM if rnn_type == "LSTM" else nn.GRU
|
| 326 |
+
rnn_kwargs = rnn_kwargs if rnn_kwargs is not None else {}
|
| 327 |
+
rnn_is_bidirectional = rnn_kwargs.get("bidirectional", False)
|
| 328 |
+
|
| 329 |
+
self.nets = rnn_cls(
|
| 330 |
+
input_size=input_dim,
|
| 331 |
+
hidden_size=rnn_hidden_dim,
|
| 332 |
+
num_layers=rnn_num_layers,
|
| 333 |
+
batch_first=True,
|
| 334 |
+
**rnn_kwargs,
|
| 335 |
+
)
|
| 336 |
+
|
| 337 |
+
self._hidden_dim = rnn_hidden_dim
|
| 338 |
+
self._num_layers = rnn_num_layers
|
| 339 |
+
self._rnn_type = rnn_type
|
| 340 |
+
self._num_directions = int(rnn_is_bidirectional) + 1 # 2 if bidirectional, 1 otherwise
|
| 341 |
+
|
| 342 |
+
@property
|
| 343 |
+
def rnn_type(self):
|
| 344 |
+
return self._rnn_type
|
| 345 |
+
|
| 346 |
+
def get_rnn_init_state(self, batch_size, device):
|
| 347 |
+
"""
|
| 348 |
+
Get a default RNN state (zeros)
|
| 349 |
+
Args:
|
| 350 |
+
batch_size (int): batch size dimension
|
| 351 |
+
|
| 352 |
+
device: device the hidden state should be sent to.
|
| 353 |
+
|
| 354 |
+
Returns:
|
| 355 |
+
hidden_state (torch.Tensor or tuple): returns hidden state tensor or tuple of hidden state tensors
|
| 356 |
+
depending on the RNN type
|
| 357 |
+
"""
|
| 358 |
+
h_0 = torch.zeros(self._num_layers * self._num_directions, batch_size, self._hidden_dim).to(device)
|
| 359 |
+
if self._rnn_type == "LSTM":
|
| 360 |
+
c_0 = torch.zeros(self._num_layers * self._num_directions, batch_size, self._hidden_dim).to(device)
|
| 361 |
+
return h_0, c_0
|
| 362 |
+
else:
|
| 363 |
+
return h_0
|
| 364 |
+
|
| 365 |
+
def output_shape(self, input_shape):
|
| 366 |
+
"""
|
| 367 |
+
Function to compute output shape from inputs to this module.
|
| 368 |
+
|
| 369 |
+
Args:
|
| 370 |
+
input_shape (iterable of int): shape of input. Does not include batch dimension.
|
| 371 |
+
Some modules may not need this argument, if their output does not depend
|
| 372 |
+
on the size of the input, or if they assume fixed size input.
|
| 373 |
+
|
| 374 |
+
Returns:
|
| 375 |
+
out_shape ([int]): list of integers corresponding to output shape
|
| 376 |
+
"""
|
| 377 |
+
|
| 378 |
+
# infer time dimension from input shape and add to per_step_net output shape
|
| 379 |
+
if self.per_step_net is not None:
|
| 380 |
+
out = self.per_step_net.output_shape(input_shape[1:])
|
| 381 |
+
if isinstance(out, dict):
|
| 382 |
+
out = {k: [input_shape[0]] + out[k] for k in out}
|
| 383 |
+
else:
|
| 384 |
+
out = [input_shape[0]] + out
|
| 385 |
+
else:
|
| 386 |
+
out = [input_shape[0], self._num_layers * self._hidden_dim]
|
| 387 |
+
return out
|
| 388 |
+
|
| 389 |
+
def forward(self, inputs, rnn_init_state=None, return_state=False):
|
| 390 |
+
"""
|
| 391 |
+
Forward a sequence of inputs through the RNN and the per-step network.
|
| 392 |
+
|
| 393 |
+
Args:
|
| 394 |
+
inputs (torch.Tensor): tensor input of shape [B, T, D], where D is the RNN input size
|
| 395 |
+
|
| 396 |
+
rnn_init_state: rnn hidden state, initialize to zero state if set to None
|
| 397 |
+
|
| 398 |
+
return_state (bool): whether to return hidden state
|
| 399 |
+
|
| 400 |
+
Returns:
|
| 401 |
+
outputs: outputs of the per_step_net
|
| 402 |
+
|
| 403 |
+
rnn_state: return rnn state at the end if return_state is set to True
|
| 404 |
+
"""
|
| 405 |
+
assert inputs.ndimension() == 3 # [B, T, D]
|
| 406 |
+
batch_size, seq_length, inp_dim = inputs.shape
|
| 407 |
+
if rnn_init_state is None:
|
| 408 |
+
rnn_init_state = self.get_rnn_init_state(batch_size, device=inputs.device)
|
| 409 |
+
|
| 410 |
+
outputs, rnn_state = self.nets(inputs, rnn_init_state)
|
| 411 |
+
if self.per_step_net is not None:
|
| 412 |
+
outputs = TensorUtils.time_distributed(outputs, self.per_step_net)
|
| 413 |
+
|
| 414 |
+
if return_state:
|
| 415 |
+
return outputs, rnn_state
|
| 416 |
+
else:
|
| 417 |
+
return outputs
|
| 418 |
+
|
| 419 |
+
def forward_step(self, inputs, rnn_state):
|
| 420 |
+
"""
|
| 421 |
+
Forward a single step input through the RNN and per-step network, and return the new hidden state.
|
| 422 |
+
Args:
|
| 423 |
+
inputs (torch.Tensor): tensor input of shape [B, D], where D is the RNN input size
|
| 424 |
+
|
| 425 |
+
rnn_state: rnn hidden state, initialize to zero state if set to None
|
| 426 |
+
|
| 427 |
+
Returns:
|
| 428 |
+
outputs: outputs of the per_step_net
|
| 429 |
+
|
| 430 |
+
rnn_state: return the new rnn state
|
| 431 |
+
"""
|
| 432 |
+
assert inputs.ndimension() == 2
|
| 433 |
+
inputs = TensorUtils.to_sequence(inputs)
|
| 434 |
+
outputs, rnn_state = self.forward(
|
| 435 |
+
inputs,
|
| 436 |
+
rnn_init_state=rnn_state,
|
| 437 |
+
return_state=True,
|
| 438 |
+
)
|
| 439 |
+
return outputs[:, 0], rnn_state
|
| 440 |
+
|
| 441 |
+
|
| 442 |
+
"""
|
| 443 |
+
================================================
|
| 444 |
+
Visual Backbone Networks
|
| 445 |
+
================================================
|
| 446 |
+
"""
|
| 447 |
+
class ConvBase(Module):
|
| 448 |
+
"""
|
| 449 |
+
Base class for ConvNets.
|
| 450 |
+
"""
|
| 451 |
+
def __init__(self):
|
| 452 |
+
super(ConvBase, self).__init__()
|
| 453 |
+
|
| 454 |
+
# dirty hack - re-implement to pass the buck onto subclasses from ABC parent
|
| 455 |
+
def output_shape(self, input_shape):
|
| 456 |
+
"""
|
| 457 |
+
Function to compute output shape from inputs to this module.
|
| 458 |
+
|
| 459 |
+
Args:
|
| 460 |
+
input_shape (iterable of int): shape of input. Does not include batch dimension.
|
| 461 |
+
Some modules may not need this argument, if their output does not depend
|
| 462 |
+
on the size of the input, or if they assume fixed size input.
|
| 463 |
+
|
| 464 |
+
Returns:
|
| 465 |
+
out_shape ([int]): list of integers corresponding to output shape
|
| 466 |
+
"""
|
| 467 |
+
raise NotImplementedError
|
| 468 |
+
|
| 469 |
+
def forward(self, inputs):
|
| 470 |
+
x = self.nets(inputs)
|
| 471 |
+
if list(self.output_shape(list(inputs.shape)[1:])) != list(x.shape)[1:]:
|
| 472 |
+
raise ValueError('Size mismatch: expect size %s, but got size %s' % (
|
| 473 |
+
str(self.output_shape(list(inputs.shape)[1:])), str(list(x.shape)[1:]))
|
| 474 |
+
)
|
| 475 |
+
return x
|
| 476 |
+
|
| 477 |
+
|
| 478 |
+
class ResNet18Conv(ConvBase):
|
| 479 |
+
"""
|
| 480 |
+
A ResNet18 block that can be used to process input images.
|
| 481 |
+
"""
|
| 482 |
+
def __init__(
|
| 483 |
+
self,
|
| 484 |
+
input_channel=3,
|
| 485 |
+
pretrained=False,
|
| 486 |
+
input_coord_conv=False,
|
| 487 |
+
):
|
| 488 |
+
"""
|
| 489 |
+
Args:
|
| 490 |
+
input_channel (int): number of input channels for input images to the network.
|
| 491 |
+
If not equal to 3, modifies first conv layer in ResNet to handle the number
|
| 492 |
+
of input channels.
|
| 493 |
+
pretrained (bool): if True, load pretrained weights for all ResNet layers.
|
| 494 |
+
input_coord_conv (bool): if True, use a coordinate convolution for the first layer
|
| 495 |
+
(a convolution where input channels are modified to encode spatial pixel location)
|
| 496 |
+
"""
|
| 497 |
+
super(ResNet18Conv, self).__init__()
|
| 498 |
+
net = vision_models.resnet18(pretrained=pretrained)
|
| 499 |
+
|
| 500 |
+
if input_coord_conv:
|
| 501 |
+
net.conv1 = CoordConv2d(input_channel, 64, kernel_size=7, stride=2, padding=3, bias=False)
|
| 502 |
+
elif input_channel != 3:
|
| 503 |
+
net.conv1 = nn.Conv2d(input_channel, 64, kernel_size=7, stride=2, padding=3, bias=False)
|
| 504 |
+
|
| 505 |
+
# cut the last fc layer
|
| 506 |
+
self._input_coord_conv = input_coord_conv
|
| 507 |
+
self._input_channel = input_channel
|
| 508 |
+
self.nets = torch.nn.Sequential(*(list(net.children())[:-2]))
|
| 509 |
+
|
| 510 |
+
def output_shape(self, input_shape):
|
| 511 |
+
"""
|
| 512 |
+
Function to compute output shape from inputs to this module.
|
| 513 |
+
|
| 514 |
+
Args:
|
| 515 |
+
input_shape (iterable of int): shape of input. Does not include batch dimension.
|
| 516 |
+
Some modules may not need this argument, if their output does not depend
|
| 517 |
+
on the size of the input, or if they assume fixed size input.
|
| 518 |
+
|
| 519 |
+
Returns:
|
| 520 |
+
out_shape ([int]): list of integers corresponding to output shape
|
| 521 |
+
"""
|
| 522 |
+
assert(len(input_shape) == 3)
|
| 523 |
+
out_h = int(math.ceil(input_shape[1] / 32.))
|
| 524 |
+
out_w = int(math.ceil(input_shape[2] / 32.))
|
| 525 |
+
return [512, out_h, out_w]
|
| 526 |
+
|
| 527 |
+
def __repr__(self):
|
| 528 |
+
"""Pretty print network."""
|
| 529 |
+
header = '{}'.format(str(self.__class__.__name__))
|
| 530 |
+
return header + '(input_channel={}, input_coord_conv={})'.format(self._input_channel, self._input_coord_conv)
|
| 531 |
+
|
| 532 |
+
|
| 533 |
+
class ResNet50Conv(ConvBase):
|
| 534 |
+
"""
|
| 535 |
+
A ResNet50 block that can be used to process input images.
|
| 536 |
+
"""
|
| 537 |
+
def __init__(
|
| 538 |
+
self,
|
| 539 |
+
input_channel=3,
|
| 540 |
+
pretrained=False,
|
| 541 |
+
input_coord_conv=False,
|
| 542 |
+
):
|
| 543 |
+
"""
|
| 544 |
+
Args:
|
| 545 |
+
input_channel (int): number of input channels for input images to the network.
|
| 546 |
+
If not equal to 3, modifies first conv layer in ResNet to handle the number
|
| 547 |
+
of input channels.
|
| 548 |
+
pretrained (bool): if True, load pretrained weights for all ResNet layers.
|
| 549 |
+
input_coord_conv (bool): if True, use a coordinate convolution for the first layer
|
| 550 |
+
(a convolution where input channels are modified to encode spatial pixel location)
|
| 551 |
+
"""
|
| 552 |
+
super(ResNet50Conv, self).__init__()
|
| 553 |
+
net = vision_models.resnet50(pretrained=pretrained)
|
| 554 |
+
|
| 555 |
+
if input_coord_conv:
|
| 556 |
+
# copied from ResNet18Conv. TODO: check if sizes need to be changed
|
| 557 |
+
net.conv1 = CoordConv2d(input_channel, 64, kernel_size=7, stride=2, padding=3, bias=False)
|
| 558 |
+
elif input_channel != 3:
|
| 559 |
+
# copied from ResNet18Conv. TODO: check if sizes need to be changed
|
| 560 |
+
net.conv1 = nn.Conv2d(input_channel, 64, kernel_size=7, stride=2, padding=3, bias=False)
|
| 561 |
+
|
| 562 |
+
# cut the last fc layer
|
| 563 |
+
self._input_coord_conv = input_coord_conv
|
| 564 |
+
self._input_channel = input_channel
|
| 565 |
+
self.nets = torch.nn.Sequential(*(list(net.children())[:-2]))
|
| 566 |
+
|
| 567 |
+
def output_shape(self, input_shape):
|
| 568 |
+
"""
|
| 569 |
+
Function to compute output shape from inputs to this module.
|
| 570 |
+
|
| 571 |
+
Args:
|
| 572 |
+
input_shape (iterable of int): shape of input. Does not include batch dimension.
|
| 573 |
+
Some modules may not need this argument, if their output does not depend
|
| 574 |
+
on the size of the input, or if they assume fixed size input.
|
| 575 |
+
|
| 576 |
+
Returns:
|
| 577 |
+
out_shape ([int]): list of integers corresponding to output shape
|
| 578 |
+
"""
|
| 579 |
+
assert(len(input_shape) == 3)
|
| 580 |
+
out_h = int(math.ceil(input_shape[1] / 32.))
|
| 581 |
+
out_w = int(math.ceil(input_shape[2] / 32.))
|
| 582 |
+
return [2048, out_h, out_w]
|
| 583 |
+
|
| 584 |
+
def __repr__(self):
|
| 585 |
+
"""Pretty print network."""
|
| 586 |
+
header = '{}'.format(str(self.__class__.__name__))
|
| 587 |
+
return header + '(input_channel={}, input_coord_conv={})'.format(self._input_channel, self._input_coord_conv)
|
| 588 |
+
|
| 589 |
+
|
| 590 |
+
class R3MConv(ConvBase):
|
| 591 |
+
"""
|
| 592 |
+
Base class for ConvNets pretrained with R3M (https://arxiv.org/abs/2203.12601)
|
| 593 |
+
"""
|
| 594 |
+
def __init__(
|
| 595 |
+
self,
|
| 596 |
+
input_channel=3,
|
| 597 |
+
r3m_model_class='resnet18',
|
| 598 |
+
freeze=True,
|
| 599 |
+
):
|
| 600 |
+
"""
|
| 601 |
+
Using R3M pretrained observation encoder network proposed by https://arxiv.org/abs/2203.12601
|
| 602 |
+
Args:
|
| 603 |
+
input_channel (int): number of input channels for input images to the network.
|
| 604 |
+
If not equal to 3, modifies first conv layer in ResNet to handle the number
|
| 605 |
+
of input channels.
|
| 606 |
+
r3m_model_class (str): select one of the r3m pretrained model "resnet18", "resnet34" or "resnet50"
|
| 607 |
+
freeze (bool): if True, use a frozen R3M pretrained model.
|
| 608 |
+
"""
|
| 609 |
+
super(R3MConv, self).__init__()
|
| 610 |
+
|
| 611 |
+
try:
|
| 612 |
+
from r3m import load_r3m
|
| 613 |
+
except ImportError:
|
| 614 |
+
print("WARNING: could not load r3m library! Please follow https://github.com/facebookresearch/r3m to install R3M")
|
| 615 |
+
|
| 616 |
+
net = load_r3m(r3m_model_class)
|
| 617 |
+
|
| 618 |
+
assert input_channel == 3 # R3M only support input image with channel size 3
|
| 619 |
+
assert r3m_model_class in ["resnet18", "resnet34", "resnet50"] # make sure the selected r3m model do exist
|
| 620 |
+
|
| 621 |
+
# cut the last fc layer
|
| 622 |
+
self._input_channel = input_channel
|
| 623 |
+
self._r3m_model_class = r3m_model_class
|
| 624 |
+
self._freeze = freeze
|
| 625 |
+
self._input_coord_conv = False
|
| 626 |
+
self._pretrained = True
|
| 627 |
+
|
| 628 |
+
preprocess = nn.Sequential(
|
| 629 |
+
transforms.Resize(256),
|
| 630 |
+
transforms.CenterCrop(224),
|
| 631 |
+
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
|
| 632 |
+
)
|
| 633 |
+
self.nets = Sequential(*([preprocess] + list(net.module.convnet.children())))
|
| 634 |
+
if freeze:
|
| 635 |
+
self.nets.freeze()
|
| 636 |
+
|
| 637 |
+
self.weight_sum = np.sum([param.cpu().data.numpy().sum() for param in self.nets.parameters()])
|
| 638 |
+
if freeze:
|
| 639 |
+
for param in self.nets.parameters():
|
| 640 |
+
param.requires_grad = False
|
| 641 |
+
|
| 642 |
+
self.nets.eval()
|
| 643 |
+
|
| 644 |
+
def output_shape(self, input_shape):
|
| 645 |
+
"""
|
| 646 |
+
Function to compute output shape from inputs to this module.
|
| 647 |
+
Args:
|
| 648 |
+
input_shape (iterable of int): shape of input. Does not include batch dimension.
|
| 649 |
+
Some modules may not need this argument, if their output does not depend
|
| 650 |
+
on the size of the input, or if they assume fixed size input.
|
| 651 |
+
Returns:
|
| 652 |
+
out_shape ([int]): list of integers corresponding to output shape
|
| 653 |
+
"""
|
| 654 |
+
assert(len(input_shape) == 3)
|
| 655 |
+
|
| 656 |
+
if self._r3m_model_class == 'resnet50':
|
| 657 |
+
out_dim = 2048
|
| 658 |
+
else:
|
| 659 |
+
out_dim = 512
|
| 660 |
+
|
| 661 |
+
return [out_dim, 1, 1]
|
| 662 |
+
|
| 663 |
+
def __repr__(self):
|
| 664 |
+
"""Pretty print network."""
|
| 665 |
+
header = '{}'.format(str(self.__class__.__name__))
|
| 666 |
+
return header + '(input_channel={}, input_coord_conv={}, pretrained={}, freeze={})'.format(self._input_channel, self._input_coord_conv, self._pretrained, self._freeze)
|
| 667 |
+
|
| 668 |
+
|
| 669 |
+
class MVPConv(ConvBase):
|
| 670 |
+
"""
|
| 671 |
+
Base class for ConvNets pretrained with MVP (https://arxiv.org/abs/2203.06173)
|
| 672 |
+
"""
|
| 673 |
+
def __init__(
|
| 674 |
+
self,
|
| 675 |
+
input_channel=3,
|
| 676 |
+
mvp_model_class='vitb-mae-egosoup',
|
| 677 |
+
freeze=True,
|
| 678 |
+
):
|
| 679 |
+
"""
|
| 680 |
+
Using MVP pretrained observation encoder network proposed by https://arxiv.org/abs/2203.06173
|
| 681 |
+
Args:
|
| 682 |
+
input_channel (int): number of input channels for input images to the network.
|
| 683 |
+
If not equal to 3, modifies first conv layer in ResNet to handle the number
|
| 684 |
+
of input channels.
|
| 685 |
+
mvp_model_class (str): select one of the mvp pretrained model "vits-mae-hoi", "vits-mae-in", "vits-sup-in", "vitb-mae-egosoup" or "vitl-256-mae-egosoup"
|
| 686 |
+
freeze (bool): if True, use a frozen MVP pretrained model.
|
| 687 |
+
"""
|
| 688 |
+
super(MVPConv, self).__init__()
|
| 689 |
+
|
| 690 |
+
try:
|
| 691 |
+
import mvp
|
| 692 |
+
except ImportError:
|
| 693 |
+
print("WARNING: could not load mvp library! Please follow https://github.com/ir413/mvp to install MVP.")
|
| 694 |
+
|
| 695 |
+
self.nets = mvp.load(mvp_model_class)
|
| 696 |
+
if freeze:
|
| 697 |
+
self.nets.freeze()
|
| 698 |
+
|
| 699 |
+
assert input_channel == 3 # MVP only support input image with channel size 3
|
| 700 |
+
assert mvp_model_class in ["vits-mae-hoi", "vits-mae-in", "vits-sup-in", "vitb-mae-egosoup", "vitl-256-mae-egosoup"] # make sure the selected r3m model do exist
|
| 701 |
+
|
| 702 |
+
self._input_channel = input_channel
|
| 703 |
+
self._freeze = freeze
|
| 704 |
+
self._mvp_model_class = mvp_model_class
|
| 705 |
+
self._input_coord_conv = False
|
| 706 |
+
self._pretrained = True
|
| 707 |
+
|
| 708 |
+
if '256' in mvp_model_class:
|
| 709 |
+
input_img_size = 256
|
| 710 |
+
else:
|
| 711 |
+
input_img_size = 224
|
| 712 |
+
self.preprocess = nn.Sequential(
|
| 713 |
+
transforms.Resize(input_img_size)
|
| 714 |
+
)
|
| 715 |
+
|
| 716 |
+
def forward(self, inputs):
|
| 717 |
+
x = self.preprocess(inputs)
|
| 718 |
+
x = self.nets(x)
|
| 719 |
+
if list(self.output_shape(list(inputs.shape)[1:])) != list(x.shape)[1:]:
|
| 720 |
+
raise ValueError('Size mismatch: expect size %s, but got size %s' % (
|
| 721 |
+
str(self.output_shape(list(inputs.shape)[1:])), str(list(x.shape)[1:]))
|
| 722 |
+
)
|
| 723 |
+
return x
|
| 724 |
+
|
| 725 |
+
def output_shape(self, input_shape):
|
| 726 |
+
"""
|
| 727 |
+
Function to compute output shape from inputs to this module.
|
| 728 |
+
Args:
|
| 729 |
+
input_shape (iterable of int): shape of input. Does not include batch dimension.
|
| 730 |
+
Some modules may not need this argument, if their output does not depend
|
| 731 |
+
on the size of the input, or if they assume fixed size input.
|
| 732 |
+
Returns:
|
| 733 |
+
out_shape ([int]): list of integers corresponding to output shape
|
| 734 |
+
"""
|
| 735 |
+
assert(len(input_shape) == 3)
|
| 736 |
+
if 'vitb' in self._mvp_model_class:
|
| 737 |
+
output_shape = [768]
|
| 738 |
+
elif 'vitl' in self._mvp_model_class:
|
| 739 |
+
output_shape = [1024]
|
| 740 |
+
else:
|
| 741 |
+
output_shape = [384]
|
| 742 |
+
return output_shape
|
| 743 |
+
|
| 744 |
+
def __repr__(self):
|
| 745 |
+
"""Pretty print network."""
|
| 746 |
+
header = '{}'.format(str(self.__class__.__name__))
|
| 747 |
+
return header + '(input_channel={}, input_coord_conv={}, pretrained={}, freeze={})'.format(self._input_channel, self._input_coord_conv, self._pretrained, self._freeze)
|
| 748 |
+
|
| 749 |
+
|
| 750 |
+
class CoordConv2d(nn.Conv2d, Module):
|
| 751 |
+
"""
|
| 752 |
+
2D Coordinate Convolution
|
| 753 |
+
|
| 754 |
+
Source: An Intriguing Failing of Convolutional Neural Networks and the CoordConv Solution
|
| 755 |
+
https://arxiv.org/abs/1807.03247
|
| 756 |
+
(e.g. adds 2 channels per input feature map corresponding to (x, y) location on map)
|
| 757 |
+
"""
|
| 758 |
+
def __init__(
|
| 759 |
+
self,
|
| 760 |
+
in_channels,
|
| 761 |
+
out_channels,
|
| 762 |
+
kernel_size,
|
| 763 |
+
stride=1,
|
| 764 |
+
padding=0,
|
| 765 |
+
dilation=1,
|
| 766 |
+
groups=1,
|
| 767 |
+
bias=True,
|
| 768 |
+
padding_mode='zeros',
|
| 769 |
+
coord_encoding='position',
|
| 770 |
+
):
|
| 771 |
+
"""
|
| 772 |
+
Args:
|
| 773 |
+
in_channels: number of channels of the input tensor [C, H, W]
|
| 774 |
+
out_channels: number of output channels of the layer
|
| 775 |
+
kernel_size: convolution kernel size
|
| 776 |
+
stride: conv stride
|
| 777 |
+
padding: conv padding
|
| 778 |
+
dilation: conv dilation
|
| 779 |
+
groups: conv groups
|
| 780 |
+
bias: conv bias
|
| 781 |
+
padding_mode: conv padding mode
|
| 782 |
+
coord_encoding: type of coordinate encoding. currently only 'position' is implemented
|
| 783 |
+
"""
|
| 784 |
+
|
| 785 |
+
assert(coord_encoding in ['position'])
|
| 786 |
+
self.coord_encoding = coord_encoding
|
| 787 |
+
if coord_encoding == 'position':
|
| 788 |
+
in_channels += 2 # two extra channel for positional encoding
|
| 789 |
+
self._position_enc = None # position encoding
|
| 790 |
+
else:
|
| 791 |
+
raise Exception("CoordConv2d: coord encoding {} not implemented".format(self.coord_encoding))
|
| 792 |
+
nn.Conv2d.__init__(
|
| 793 |
+
self,
|
| 794 |
+
in_channels=in_channels,
|
| 795 |
+
out_channels=out_channels,
|
| 796 |
+
kernel_size=kernel_size,
|
| 797 |
+
stride=stride,
|
| 798 |
+
padding=padding,
|
| 799 |
+
dilation=dilation,
|
| 800 |
+
groups=groups,
|
| 801 |
+
bias=bias,
|
| 802 |
+
padding_mode=padding_mode
|
| 803 |
+
)
|
| 804 |
+
|
| 805 |
+
def output_shape(self, input_shape):
|
| 806 |
+
"""
|
| 807 |
+
Function to compute output shape from inputs to this module.
|
| 808 |
+
|
| 809 |
+
Args:
|
| 810 |
+
input_shape (iterable of int): shape of input. Does not include batch dimension.
|
| 811 |
+
Some modules may not need this argument, if their output does not depend
|
| 812 |
+
on the size of the input, or if they assume fixed size input.
|
| 813 |
+
|
| 814 |
+
Returns:
|
| 815 |
+
out_shape ([int]): list of integers corresponding to output shape
|
| 816 |
+
"""
|
| 817 |
+
|
| 818 |
+
# adds 2 to channel dimension
|
| 819 |
+
return [input_shape[0] + 2] + input_shape[1:]
|
| 820 |
+
|
| 821 |
+
def forward(self, input):
|
| 822 |
+
b, c, h, w = input.shape
|
| 823 |
+
if self.coord_encoding == 'position':
|
| 824 |
+
if self._position_enc is None:
|
| 825 |
+
pos_y, pos_x = torch.meshgrid(torch.arange(h), torch.arange(w))
|
| 826 |
+
pos_y = pos_y.float().to(input.device) / float(h)
|
| 827 |
+
pos_x = pos_x.float().to(input.device) / float(w)
|
| 828 |
+
self._position_enc = torch.stack((pos_y, pos_x)).unsqueeze(0)
|
| 829 |
+
pos_enc = self._position_enc.expand(b, -1, -1, -1)
|
| 830 |
+
input = torch.cat((input, pos_enc), dim=1)
|
| 831 |
+
return super(CoordConv2d, self).forward(input)
|
| 832 |
+
|
| 833 |
+
|
| 834 |
+
class ShallowConv(ConvBase):
|
| 835 |
+
"""
|
| 836 |
+
A shallow convolutional encoder from https://rll.berkeley.edu/dsae/dsae.pdf
|
| 837 |
+
"""
|
| 838 |
+
def __init__(self, input_channel=3, output_channel=32):
|
| 839 |
+
super(ShallowConv, self).__init__()
|
| 840 |
+
self._input_channel = input_channel
|
| 841 |
+
self._output_channel = output_channel
|
| 842 |
+
self.nets = nn.Sequential(
|
| 843 |
+
torch.nn.Conv2d(input_channel, 64, kernel_size=7, stride=2, padding=3),
|
| 844 |
+
torch.nn.ReLU(),
|
| 845 |
+
torch.nn.Conv2d(64, 32, kernel_size=1, stride=1, padding=0),
|
| 846 |
+
torch.nn.ReLU(),
|
| 847 |
+
torch.nn.Conv2d(32, 32, kernel_size=3, stride=1, padding=1),
|
| 848 |
+
torch.nn.ReLU(),
|
| 849 |
+
torch.nn.Conv2d(32, 32, kernel_size=3, stride=1, padding=1),
|
| 850 |
+
)
|
| 851 |
+
|
| 852 |
+
def output_shape(self, input_shape):
|
| 853 |
+
"""
|
| 854 |
+
Function to compute output shape from inputs to this module.
|
| 855 |
+
|
| 856 |
+
Args:
|
| 857 |
+
input_shape (iterable of int): shape of input. Does not include batch dimension.
|
| 858 |
+
Some modules may not need this argument, if their output does not depend
|
| 859 |
+
on the size of the input, or if they assume fixed size input.
|
| 860 |
+
|
| 861 |
+
Returns:
|
| 862 |
+
out_shape ([int]): list of integers corresponding to output shape
|
| 863 |
+
"""
|
| 864 |
+
assert(len(input_shape) == 3)
|
| 865 |
+
assert(input_shape[0] == self._input_channel)
|
| 866 |
+
out_h = int(math.floor(input_shape[1] / 2.))
|
| 867 |
+
out_w = int(math.floor(input_shape[2] / 2.))
|
| 868 |
+
return [self._output_channel, out_h, out_w]
|
| 869 |
+
|
| 870 |
+
|
| 871 |
+
class Conv1dBase(Module):
|
| 872 |
+
"""
|
| 873 |
+
Base class for stacked Conv1d layers.
|
| 874 |
+
|
| 875 |
+
Args:
|
| 876 |
+
input_channel (int): Number of channels for inputs to this network
|
| 877 |
+
activation (None or str): Per-layer activation to use. Defaults to "relu". Valid options are
|
| 878 |
+
currently {relu, None} for no activation
|
| 879 |
+
out_channels (list of int): Output channel size for each sequential Conv1d layer
|
| 880 |
+
kernel_size (list of int): Kernel sizes for each sequential Conv1d layer
|
| 881 |
+
stride (list of int): Stride sizes for each sequential Conv1d layer
|
| 882 |
+
conv_kwargs (dict): additional nn.Conv1D args to use, in list form, where the ith element corresponds to the
|
| 883 |
+
argument to be passed to the ith Conv1D layer.
|
| 884 |
+
See https://pytorch.org/docs/stable/generated/torch.nn.Conv1d.html for specific possible arguments.
|
| 885 |
+
"""
|
| 886 |
+
def __init__(
|
| 887 |
+
self,
|
| 888 |
+
input_channel=1,
|
| 889 |
+
activation="relu",
|
| 890 |
+
out_channels=(32, 64, 64),
|
| 891 |
+
kernel_size=(8, 4, 2),
|
| 892 |
+
stride=(4, 2, 1),
|
| 893 |
+
**conv_kwargs,
|
| 894 |
+
):
|
| 895 |
+
super(Conv1dBase, self).__init__()
|
| 896 |
+
|
| 897 |
+
# Get activation requested
|
| 898 |
+
activation = CONV_ACTIVATIONS[activation]
|
| 899 |
+
|
| 900 |
+
# Generate network
|
| 901 |
+
self.n_layers = len(out_channels)
|
| 902 |
+
layers = OrderedDict()
|
| 903 |
+
for i in range(self.n_layers):
|
| 904 |
+
layer_kwargs = {k: v[i] for k, v in conv_kwargs.items()}
|
| 905 |
+
layers[f'conv{i}'] = nn.Conv1d(
|
| 906 |
+
in_channels=input_channel,
|
| 907 |
+
**layer_kwargs,
|
| 908 |
+
)
|
| 909 |
+
if activation is not None:
|
| 910 |
+
layers[f'act{i}'] = activation()
|
| 911 |
+
input_channel = layer_kwargs["out_channels"]
|
| 912 |
+
|
| 913 |
+
# Store network
|
| 914 |
+
self.nets = nn.Sequential(layers)
|
| 915 |
+
|
| 916 |
+
def output_shape(self, input_shape):
|
| 917 |
+
"""
|
| 918 |
+
Function to compute output shape from inputs to this module.
|
| 919 |
+
|
| 920 |
+
Args:
|
| 921 |
+
input_shape (iterable of int): shape of input. Does not include batch dimension.
|
| 922 |
+
Some modules may not need this argument, if their output does not depend
|
| 923 |
+
on the size of the input, or if they assume fixed size input.
|
| 924 |
+
|
| 925 |
+
Returns:
|
| 926 |
+
out_shape ([int]): list of integers corresponding to output shape
|
| 927 |
+
"""
|
| 928 |
+
channels, length = input_shape
|
| 929 |
+
for i in range(self.n_layers):
|
| 930 |
+
net = getattr(self.nets, f"conv{i}")
|
| 931 |
+
channels = net.out_channels
|
| 932 |
+
length = int((length + 2 * net.padding[0] - net.dilation[0] * (net.kernel_size[0] - 1) - 1) / net.stride[0]) + 1
|
| 933 |
+
return [channels, length]
|
| 934 |
+
|
| 935 |
+
def forward(self, inputs):
|
| 936 |
+
x = self.nets(inputs)
|
| 937 |
+
if list(self.output_shape(list(inputs.shape)[1:])) != list(x.shape)[1:]:
|
| 938 |
+
raise ValueError('Size mismatch: expect size %s, but got size %s' % (
|
| 939 |
+
str(self.output_shape(list(inputs.shape)[1:])), str(list(x.shape)[1:]))
|
| 940 |
+
)
|
| 941 |
+
return x
|
| 942 |
+
|
| 943 |
+
|
| 944 |
+
"""
|
| 945 |
+
================================================
|
| 946 |
+
Pooling Networks
|
| 947 |
+
================================================
|
| 948 |
+
"""
|
| 949 |
+
class SpatialSoftmax(ConvBase):
|
| 950 |
+
"""
|
| 951 |
+
Spatial Softmax Layer.
|
| 952 |
+
|
| 953 |
+
Based on Deep Spatial Autoencoders for Visuomotor Learning by Finn et al.
|
| 954 |
+
https://rll.berkeley.edu/dsae/dsae.pdf
|
| 955 |
+
"""
|
| 956 |
+
def __init__(
|
| 957 |
+
self,
|
| 958 |
+
input_shape,
|
| 959 |
+
num_kp=32,
|
| 960 |
+
temperature=1.,
|
| 961 |
+
learnable_temperature=False,
|
| 962 |
+
output_variance=False,
|
| 963 |
+
noise_std=0.0,
|
| 964 |
+
):
|
| 965 |
+
"""
|
| 966 |
+
Args:
|
| 967 |
+
input_shape (list): shape of the input feature (C, H, W)
|
| 968 |
+
num_kp (int): number of keypoints (None for not using spatialsoftmax)
|
| 969 |
+
temperature (float): temperature term for the softmax.
|
| 970 |
+
learnable_temperature (bool): whether to learn the temperature
|
| 971 |
+
output_variance (bool): treat attention as a distribution, and compute second-order statistics to return
|
| 972 |
+
noise_std (float): add random spatial noise to the predicted keypoints
|
| 973 |
+
"""
|
| 974 |
+
super(SpatialSoftmax, self).__init__()
|
| 975 |
+
assert len(input_shape) == 3
|
| 976 |
+
self._in_c, self._in_h, self._in_w = input_shape # (C, H, W)
|
| 977 |
+
|
| 978 |
+
if num_kp is not None:
|
| 979 |
+
self.nets = torch.nn.Conv2d(self._in_c, num_kp, kernel_size=1)
|
| 980 |
+
self._num_kp = num_kp
|
| 981 |
+
else:
|
| 982 |
+
self.nets = None
|
| 983 |
+
self._num_kp = self._in_c
|
| 984 |
+
self.learnable_temperature = learnable_temperature
|
| 985 |
+
self.output_variance = output_variance
|
| 986 |
+
self.noise_std = noise_std
|
| 987 |
+
|
| 988 |
+
if self.learnable_temperature:
|
| 989 |
+
# temperature will be learned
|
| 990 |
+
temperature = torch.nn.Parameter(torch.ones(1) * temperature, requires_grad=True)
|
| 991 |
+
self.register_parameter('temperature', temperature)
|
| 992 |
+
else:
|
| 993 |
+
# temperature held constant after initialization
|
| 994 |
+
temperature = torch.nn.Parameter(torch.ones(1) * temperature, requires_grad=False)
|
| 995 |
+
self.register_buffer('temperature', temperature)
|
| 996 |
+
|
| 997 |
+
pos_x, pos_y = np.meshgrid(
|
| 998 |
+
np.linspace(-1., 1., self._in_w),
|
| 999 |
+
np.linspace(-1., 1., self._in_h)
|
| 1000 |
+
)
|
| 1001 |
+
pos_x = torch.from_numpy(pos_x.reshape(1, self._in_h * self._in_w)).float()
|
| 1002 |
+
pos_y = torch.from_numpy(pos_y.reshape(1, self._in_h * self._in_w)).float()
|
| 1003 |
+
self.register_buffer('pos_x', pos_x)
|
| 1004 |
+
self.register_buffer('pos_y', pos_y)
|
| 1005 |
+
|
| 1006 |
+
self.kps = None
|
| 1007 |
+
|
| 1008 |
+
def __repr__(self):
|
| 1009 |
+
"""Pretty print network."""
|
| 1010 |
+
header = format(str(self.__class__.__name__))
|
| 1011 |
+
return header + '(num_kp={}, temperature={}, noise={})'.format(
|
| 1012 |
+
self._num_kp, self.temperature.item(), self.noise_std)
|
| 1013 |
+
|
| 1014 |
+
def output_shape(self, input_shape):
|
| 1015 |
+
"""
|
| 1016 |
+
Function to compute output shape from inputs to this module.
|
| 1017 |
+
|
| 1018 |
+
Args:
|
| 1019 |
+
input_shape (iterable of int): shape of input. Does not include batch dimension.
|
| 1020 |
+
Some modules may not need this argument, if their output does not depend
|
| 1021 |
+
on the size of the input, or if they assume fixed size input.
|
| 1022 |
+
|
| 1023 |
+
Returns:
|
| 1024 |
+
out_shape ([int]): list of integers corresponding to output shape
|
| 1025 |
+
"""
|
| 1026 |
+
assert(len(input_shape) == 3)
|
| 1027 |
+
assert(input_shape[0] == self._in_c)
|
| 1028 |
+
return [self._num_kp, 2]
|
| 1029 |
+
|
| 1030 |
+
def forward(self, feature):
|
| 1031 |
+
"""
|
| 1032 |
+
Forward pass through spatial softmax layer. For each keypoint, a 2D spatial
|
| 1033 |
+
probability distribution is created using a softmax, where the support is the
|
| 1034 |
+
pixel locations. This distribution is used to compute the expected value of
|
| 1035 |
+
the pixel location, which becomes a keypoint of dimension 2. K such keypoints
|
| 1036 |
+
are created.
|
| 1037 |
+
|
| 1038 |
+
Returns:
|
| 1039 |
+
out (torch.Tensor or tuple): mean keypoints of shape [B, K, 2], and possibly
|
| 1040 |
+
keypoint variance of shape [B, K, 2, 2] corresponding to the covariance
|
| 1041 |
+
under the 2D spatial softmax distribution
|
| 1042 |
+
"""
|
| 1043 |
+
assert(feature.shape[1] == self._in_c)
|
| 1044 |
+
assert(feature.shape[2] == self._in_h)
|
| 1045 |
+
assert(feature.shape[3] == self._in_w)
|
| 1046 |
+
if self.nets is not None:
|
| 1047 |
+
feature = self.nets(feature)
|
| 1048 |
+
|
| 1049 |
+
# [B, K, H, W] -> [B * K, H * W] where K is number of keypoints
|
| 1050 |
+
feature = feature.reshape(-1, self._in_h * self._in_w)
|
| 1051 |
+
# 2d softmax normalization
|
| 1052 |
+
attention = F.softmax(feature / self.temperature, dim=-1)
|
| 1053 |
+
# [1, H * W] x [B * K, H * W] -> [B * K, 1] for spatial coordinate mean in x and y dimensions
|
| 1054 |
+
expected_x = torch.sum(self.pos_x * attention, dim=1, keepdim=True)
|
| 1055 |
+
expected_y = torch.sum(self.pos_y * attention, dim=1, keepdim=True)
|
| 1056 |
+
# stack to [B * K, 2]
|
| 1057 |
+
expected_xy = torch.cat([expected_x, expected_y], 1)
|
| 1058 |
+
# reshape to [B, K, 2]
|
| 1059 |
+
feature_keypoints = expected_xy.view(-1, self._num_kp, 2)
|
| 1060 |
+
|
| 1061 |
+
if self.training:
|
| 1062 |
+
noise = torch.randn_like(feature_keypoints) * self.noise_std
|
| 1063 |
+
feature_keypoints += noise
|
| 1064 |
+
|
| 1065 |
+
if self.output_variance:
|
| 1066 |
+
# treat attention as a distribution, and compute second-order statistics to return
|
| 1067 |
+
expected_xx = torch.sum(self.pos_x * self.pos_x * attention, dim=1, keepdim=True)
|
| 1068 |
+
expected_yy = torch.sum(self.pos_y * self.pos_y * attention, dim=1, keepdim=True)
|
| 1069 |
+
expected_xy = torch.sum(self.pos_x * self.pos_y * attention, dim=1, keepdim=True)
|
| 1070 |
+
var_x = expected_xx - expected_x * expected_x
|
| 1071 |
+
var_y = expected_yy - expected_y * expected_y
|
| 1072 |
+
var_xy = expected_xy - expected_x * expected_y
|
| 1073 |
+
# stack to [B * K, 4] and then reshape to [B, K, 2, 2] where last 2 dims are covariance matrix
|
| 1074 |
+
feature_covar = torch.cat([var_x, var_xy, var_xy, var_y], 1).reshape(-1, self._num_kp, 2, 2)
|
| 1075 |
+
feature_keypoints = (feature_keypoints, feature_covar)
|
| 1076 |
+
|
| 1077 |
+
if isinstance(feature_keypoints, tuple):
|
| 1078 |
+
self.kps = (feature_keypoints[0].detach(), feature_keypoints[1].detach())
|
| 1079 |
+
else:
|
| 1080 |
+
self.kps = feature_keypoints.detach()
|
| 1081 |
+
return feature_keypoints
|
| 1082 |
+
|
| 1083 |
+
|
| 1084 |
+
class SpatialMeanPool(Module):
|
| 1085 |
+
"""
|
| 1086 |
+
Module that averages inputs across all spatial dimensions (dimension 2 and after),
|
| 1087 |
+
leaving only the batch and channel dimensions.
|
| 1088 |
+
"""
|
| 1089 |
+
def __init__(self, input_shape):
|
| 1090 |
+
super(SpatialMeanPool, self).__init__()
|
| 1091 |
+
assert len(input_shape) == 3 # [C, H, W]
|
| 1092 |
+
self.in_shape = input_shape
|
| 1093 |
+
|
| 1094 |
+
def output_shape(self, input_shape=None):
|
| 1095 |
+
"""
|
| 1096 |
+
Function to compute output shape from inputs to this module.
|
| 1097 |
+
|
| 1098 |
+
Args:
|
| 1099 |
+
input_shape (iterable of int): shape of input. Does not include batch dimension.
|
| 1100 |
+
Some modules may not need this argument, if their output does not depend
|
| 1101 |
+
on the size of the input, or if they assume fixed size input.
|
| 1102 |
+
|
| 1103 |
+
Returns:
|
| 1104 |
+
out_shape ([int]): list of integers corresponding to output shape
|
| 1105 |
+
"""
|
| 1106 |
+
return list(self.in_shape[:1]) # [C, H, W] -> [C]
|
| 1107 |
+
|
| 1108 |
+
def forward(self, inputs):
|
| 1109 |
+
"""Forward pass - average across all dimensions except batch and channel."""
|
| 1110 |
+
return TensorUtils.flatten(inputs, begin_axis=2).mean(dim=2)
|
| 1111 |
+
|
| 1112 |
+
|
| 1113 |
+
class FeatureAggregator(Module):
|
| 1114 |
+
"""
|
| 1115 |
+
Helpful class for aggregating features across a dimension. This is useful in
|
| 1116 |
+
practice when training models that break an input image up into several patches
|
| 1117 |
+
since features can be extraced per-patch using the same encoder and then
|
| 1118 |
+
aggregated using this module.
|
| 1119 |
+
"""
|
| 1120 |
+
def __init__(self, dim=1, agg_type="avg"):
|
| 1121 |
+
super(FeatureAggregator, self).__init__()
|
| 1122 |
+
self.dim = dim
|
| 1123 |
+
self.agg_type = agg_type
|
| 1124 |
+
|
| 1125 |
+
def set_weight(self, w):
|
| 1126 |
+
assert self.agg_type == "w_avg"
|
| 1127 |
+
self.agg_weight = w
|
| 1128 |
+
|
| 1129 |
+
def clear_weight(self):
|
| 1130 |
+
assert self.agg_type == "w_avg"
|
| 1131 |
+
self.agg_weight = None
|
| 1132 |
+
|
| 1133 |
+
def output_shape(self, input_shape):
|
| 1134 |
+
"""
|
| 1135 |
+
Function to compute output shape from inputs to this module.
|
| 1136 |
+
|
| 1137 |
+
Args:
|
| 1138 |
+
input_shape (iterable of int): shape of input. Does not include batch dimension.
|
| 1139 |
+
Some modules may not need this argument, if their output does not depend
|
| 1140 |
+
on the size of the input, or if they assume fixed size input.
|
| 1141 |
+
|
| 1142 |
+
Returns:
|
| 1143 |
+
out_shape ([int]): list of integers corresponding to output shape
|
| 1144 |
+
"""
|
| 1145 |
+
# aggregates on @self.dim, so it is removed from the output shape
|
| 1146 |
+
return list(input_shape[:self.dim]) + list(input_shape[self.dim+1:])
|
| 1147 |
+
|
| 1148 |
+
def forward(self, x):
|
| 1149 |
+
"""Forward pooling pass."""
|
| 1150 |
+
if self.agg_type == "avg":
|
| 1151 |
+
# mean-pooling
|
| 1152 |
+
return torch.mean(x, dim=1)
|
| 1153 |
+
if self.agg_type == "w_avg":
|
| 1154 |
+
# weighted mean-pooling
|
| 1155 |
+
return torch.sum(x * self.agg_weight, dim=1)
|
| 1156 |
+
raise Exception("unexpected agg type: {}".forward(self.agg_type))
|
aloha-devel/robomimic/models/obs_nets.py
ADDED
|
@@ -0,0 +1,1121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Contains torch Modules that help deal with inputs consisting of multiple
|
| 3 |
+
modalities. This is extremely common when networks must deal with one or
|
| 4 |
+
more observation dictionaries, where each input dictionary can have
|
| 5 |
+
observation keys of a certain modality and shape.
|
| 6 |
+
|
| 7 |
+
As an example, an observation could consist of a flat "robot0_eef_pos" observation key,
|
| 8 |
+
and a 3-channel RGB "agentview_image" observation key.
|
| 9 |
+
"""
|
| 10 |
+
import sys
|
| 11 |
+
import numpy as np
|
| 12 |
+
import textwrap
|
| 13 |
+
from copy import deepcopy
|
| 14 |
+
from collections import OrderedDict
|
| 15 |
+
|
| 16 |
+
import torch
|
| 17 |
+
import torch.nn as nn
|
| 18 |
+
import torch.nn.functional as F
|
| 19 |
+
import torch.distributions as D
|
| 20 |
+
|
| 21 |
+
from robomimic.utils.python_utils import extract_class_init_kwargs_from_dict
|
| 22 |
+
import robomimic.utils.tensor_utils as TensorUtils
|
| 23 |
+
import robomimic.utils.obs_utils as ObsUtils
|
| 24 |
+
from robomimic.models.base_nets import Module, Sequential, MLP, RNN_Base, ResNet18Conv, SpatialSoftmax, \
|
| 25 |
+
FeatureAggregator
|
| 26 |
+
from robomimic.models.obs_core import VisualCore, Randomizer
|
| 27 |
+
from robomimic.models.transformers import PositionalEncoding, GPT_Backbone
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def obs_encoder_factory(
|
| 31 |
+
obs_shapes,
|
| 32 |
+
feature_activation=nn.ReLU,
|
| 33 |
+
encoder_kwargs=None,
|
| 34 |
+
):
|
| 35 |
+
"""
|
| 36 |
+
Utility function to create an @ObservationEncoder from kwargs specified in config.
|
| 37 |
+
|
| 38 |
+
Args:
|
| 39 |
+
obs_shapes (OrderedDict): a dictionary that maps observation key to
|
| 40 |
+
expected shapes for observations.
|
| 41 |
+
|
| 42 |
+
feature_activation: non-linearity to apply after each obs net - defaults to ReLU. Pass
|
| 43 |
+
None to apply no activation.
|
| 44 |
+
|
| 45 |
+
encoder_kwargs (dict or None): If None, results in default encoder_kwargs being applied. Otherwise, should be
|
| 46 |
+
nested dictionary containing relevant per-modality information for encoder networks.
|
| 47 |
+
Should be of form:
|
| 48 |
+
|
| 49 |
+
obs_modality1: dict
|
| 50 |
+
feature_dimension: int
|
| 51 |
+
core_class: str
|
| 52 |
+
core_kwargs: dict
|
| 53 |
+
...
|
| 54 |
+
...
|
| 55 |
+
obs_randomizer_class: str
|
| 56 |
+
obs_randomizer_kwargs: dict
|
| 57 |
+
...
|
| 58 |
+
...
|
| 59 |
+
obs_modality2: dict
|
| 60 |
+
...
|
| 61 |
+
"""
|
| 62 |
+
enc = ObservationEncoder(feature_activation=feature_activation)
|
| 63 |
+
for k, obs_shape in obs_shapes.items():
|
| 64 |
+
obs_modality = ObsUtils.OBS_KEYS_TO_MODALITIES[k]
|
| 65 |
+
enc_kwargs = deepcopy(ObsUtils.DEFAULT_ENCODER_KWARGS[obs_modality]) if encoder_kwargs is None else \
|
| 66 |
+
deepcopy(encoder_kwargs[obs_modality])
|
| 67 |
+
|
| 68 |
+
# Sanity check for kwargs in case they don't exist / are None
|
| 69 |
+
if enc_kwargs.get("core_kwargs", None) is None:
|
| 70 |
+
enc_kwargs["core_kwargs"] = {}
|
| 71 |
+
# Add in input shape info
|
| 72 |
+
enc_kwargs["core_kwargs"]["input_shape"] = obs_shape
|
| 73 |
+
# If group class is specified, then make sure corresponding kwargs only contain relevant kwargs
|
| 74 |
+
if enc_kwargs["core_class"] is not None:
|
| 75 |
+
enc_kwargs["core_kwargs"] = extract_class_init_kwargs_from_dict(
|
| 76 |
+
cls=ObsUtils.OBS_ENCODER_CORES[enc_kwargs["core_class"]],
|
| 77 |
+
dic=enc_kwargs["core_kwargs"],
|
| 78 |
+
copy=False,
|
| 79 |
+
)
|
| 80 |
+
|
| 81 |
+
# Add in input shape info
|
| 82 |
+
randomizers = []
|
| 83 |
+
obs_randomizer_class_list = enc_kwargs["obs_randomizer_class"]
|
| 84 |
+
obs_randomizer_kwargs_list = enc_kwargs["obs_randomizer_kwargs"]
|
| 85 |
+
|
| 86 |
+
if not isinstance(obs_randomizer_class_list, list):
|
| 87 |
+
obs_randomizer_class_list = [obs_randomizer_class_list]
|
| 88 |
+
|
| 89 |
+
if not isinstance(obs_randomizer_kwargs_list, list):
|
| 90 |
+
obs_randomizer_kwargs_list = [obs_randomizer_kwargs_list]
|
| 91 |
+
|
| 92 |
+
for rand_class, rand_kwargs in zip(obs_randomizer_class_list, obs_randomizer_kwargs_list):
|
| 93 |
+
rand = None
|
| 94 |
+
if rand_class is not None:
|
| 95 |
+
rand_kwargs["input_shape"] = obs_shape
|
| 96 |
+
rand_kwargs = extract_class_init_kwargs_from_dict(
|
| 97 |
+
cls=ObsUtils.OBS_RANDOMIZERS[rand_class],
|
| 98 |
+
dic=rand_kwargs,
|
| 99 |
+
copy=False,
|
| 100 |
+
)
|
| 101 |
+
rand = ObsUtils.OBS_RANDOMIZERS[rand_class](**rand_kwargs)
|
| 102 |
+
randomizers.append(rand)
|
| 103 |
+
|
| 104 |
+
enc.register_obs_key(
|
| 105 |
+
name=k,
|
| 106 |
+
shape=obs_shape,
|
| 107 |
+
net_class=enc_kwargs["core_class"],
|
| 108 |
+
net_kwargs=enc_kwargs["core_kwargs"],
|
| 109 |
+
randomizers=randomizers,
|
| 110 |
+
)
|
| 111 |
+
|
| 112 |
+
enc.make()
|
| 113 |
+
return enc
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
class ObservationEncoder(Module):
|
| 117 |
+
"""
|
| 118 |
+
Module that processes inputs by observation key and then concatenates the processed
|
| 119 |
+
observation keys together. Each key is processed with an encoder head network.
|
| 120 |
+
Call @register_obs_key to register observation keys with the encoder and then
|
| 121 |
+
finally call @make to create the encoder networks.
|
| 122 |
+
"""
|
| 123 |
+
def __init__(self, feature_activation=nn.ReLU):
|
| 124 |
+
"""
|
| 125 |
+
Args:
|
| 126 |
+
feature_activation: non-linearity to apply after each obs net - defaults to ReLU. Pass
|
| 127 |
+
None to apply no activation.
|
| 128 |
+
"""
|
| 129 |
+
super(ObservationEncoder, self).__init__()
|
| 130 |
+
self.obs_shapes = OrderedDict()
|
| 131 |
+
self.obs_nets_classes = OrderedDict()
|
| 132 |
+
self.obs_nets_kwargs = OrderedDict()
|
| 133 |
+
self.obs_share_mods = OrderedDict()
|
| 134 |
+
self.obs_nets = nn.ModuleDict()
|
| 135 |
+
self.obs_randomizers = nn.ModuleDict()
|
| 136 |
+
self.feature_activation = feature_activation
|
| 137 |
+
self._locked = False
|
| 138 |
+
|
| 139 |
+
def register_obs_key(
|
| 140 |
+
self,
|
| 141 |
+
name,
|
| 142 |
+
shape,
|
| 143 |
+
net_class=None,
|
| 144 |
+
net_kwargs=None,
|
| 145 |
+
net=None,
|
| 146 |
+
randomizers=None,
|
| 147 |
+
share_net_from=None,
|
| 148 |
+
):
|
| 149 |
+
"""
|
| 150 |
+
Register an observation key that this encoder should be responsible for.
|
| 151 |
+
|
| 152 |
+
Args:
|
| 153 |
+
name (str): modality name
|
| 154 |
+
shape (int tuple): shape of modality
|
| 155 |
+
net_class (str): name of class in base_nets.py that should be used
|
| 156 |
+
to process this observation key before concatenation. Pass None to flatten
|
| 157 |
+
and concatenate the observation key directly.
|
| 158 |
+
net_kwargs (dict): arguments to pass to @net_class
|
| 159 |
+
net (Module instance): if provided, use this Module to process the observation key
|
| 160 |
+
instead of creating a different net
|
| 161 |
+
randomizer (Randomizer instance): if provided, use this Module to augment observation keys
|
| 162 |
+
coming in to the encoder, and possibly augment the processed output as well
|
| 163 |
+
share_net_from (str): if provided, use the same instance of @net_class
|
| 164 |
+
as another observation key. This observation key must already exist in this encoder.
|
| 165 |
+
Warning: Note that this does not share the observation key randomizer
|
| 166 |
+
"""
|
| 167 |
+
assert not self._locked, "ObservationEncoder: @register_obs_key called after @make"
|
| 168 |
+
assert name not in self.obs_shapes, "ObservationEncoder: modality {} already exists".format(name)
|
| 169 |
+
|
| 170 |
+
if net is not None:
|
| 171 |
+
assert isinstance(net, Module), "ObservationEncoder: @net must be instance of Module class"
|
| 172 |
+
assert (net_class is None) and (net_kwargs is None) and (share_net_from is None), \
|
| 173 |
+
"ObservationEncoder: @net provided - ignore other net creation options"
|
| 174 |
+
|
| 175 |
+
if share_net_from is not None:
|
| 176 |
+
# share processing with another modality
|
| 177 |
+
assert (net_class is None) and (net_kwargs is None)
|
| 178 |
+
assert share_net_from in self.obs_shapes
|
| 179 |
+
|
| 180 |
+
net_kwargs = deepcopy(net_kwargs) if net_kwargs is not None else {}
|
| 181 |
+
for rand in randomizers:
|
| 182 |
+
if rand is not None:
|
| 183 |
+
assert isinstance(rand, Randomizer)
|
| 184 |
+
if net_kwargs is not None:
|
| 185 |
+
# update input shape to visual core
|
| 186 |
+
net_kwargs["input_shape"] = rand.output_shape_in(shape)
|
| 187 |
+
|
| 188 |
+
self.obs_shapes[name] = shape
|
| 189 |
+
self.obs_nets_classes[name] = net_class
|
| 190 |
+
self.obs_nets_kwargs[name] = net_kwargs
|
| 191 |
+
self.obs_nets[name] = net
|
| 192 |
+
self.obs_randomizers[name] = nn.ModuleList(randomizers)
|
| 193 |
+
self.obs_share_mods[name] = share_net_from
|
| 194 |
+
|
| 195 |
+
def make(self):
|
| 196 |
+
"""
|
| 197 |
+
Creates the encoder networks and locks the encoder so that more modalities cannot be added.
|
| 198 |
+
"""
|
| 199 |
+
assert not self._locked, "ObservationEncoder: @make called more than once"
|
| 200 |
+
self._create_layers()
|
| 201 |
+
self._locked = True
|
| 202 |
+
|
| 203 |
+
def _create_layers(self):
|
| 204 |
+
"""
|
| 205 |
+
Creates all networks and layers required by this encoder using the registered modalities.
|
| 206 |
+
"""
|
| 207 |
+
assert not self._locked, "ObservationEncoder: layers have already been created"
|
| 208 |
+
|
| 209 |
+
for k in self.obs_shapes:
|
| 210 |
+
if self.obs_nets_classes[k] is not None:
|
| 211 |
+
# create net to process this modality
|
| 212 |
+
self.obs_nets[k] = ObsUtils.OBS_ENCODER_CORES[self.obs_nets_classes[k]](**self.obs_nets_kwargs[k])
|
| 213 |
+
elif self.obs_share_mods[k] is not None:
|
| 214 |
+
# make sure net is shared with another modality
|
| 215 |
+
self.obs_nets[k] = self.obs_nets[self.obs_share_mods[k]]
|
| 216 |
+
|
| 217 |
+
self.activation = None
|
| 218 |
+
if self.feature_activation is not None:
|
| 219 |
+
self.activation = self.feature_activation()
|
| 220 |
+
|
| 221 |
+
def forward(self, obs_dict):
|
| 222 |
+
"""
|
| 223 |
+
Processes modalities according to the ordering in @self.obs_shapes. For each
|
| 224 |
+
modality, it is processed with a randomizer (if present), an encoder
|
| 225 |
+
network (if present), and again with the randomizer (if present), flattened,
|
| 226 |
+
and then concatenated with the other processed modalities.
|
| 227 |
+
|
| 228 |
+
Args:
|
| 229 |
+
obs_dict (OrderedDict): dictionary that maps modalities to torch.Tensor
|
| 230 |
+
batches that agree with @self.obs_shapes. All modalities in
|
| 231 |
+
@self.obs_shapes must be present, but additional modalities
|
| 232 |
+
can also be present.
|
| 233 |
+
|
| 234 |
+
Returns:
|
| 235 |
+
feats (torch.Tensor): flat features of shape [B, D]
|
| 236 |
+
"""
|
| 237 |
+
assert self._locked, "ObservationEncoder: @make has not been called yet"
|
| 238 |
+
|
| 239 |
+
# ensure all modalities that the encoder handles are present
|
| 240 |
+
assert set(self.obs_shapes.keys()).issubset(obs_dict), "ObservationEncoder: {} does not contain all modalities {}".format(
|
| 241 |
+
list(obs_dict.keys()), list(self.obs_shapes.keys())
|
| 242 |
+
)
|
| 243 |
+
|
| 244 |
+
# process modalities by order given by @self.obs_shapes
|
| 245 |
+
feats = []
|
| 246 |
+
for k in self.obs_shapes:
|
| 247 |
+
x = obs_dict[k]
|
| 248 |
+
# maybe process encoder input with randomizer
|
| 249 |
+
for rand in self.obs_randomizers[k]:
|
| 250 |
+
if rand is not None:
|
| 251 |
+
x = rand.forward_in(x)
|
| 252 |
+
# maybe process with obs net
|
| 253 |
+
if self.obs_nets[k] is not None:
|
| 254 |
+
x = self.obs_nets[k](x)
|
| 255 |
+
if self.activation is not None:
|
| 256 |
+
x = self.activation(x)
|
| 257 |
+
# maybe process encoder output with randomizer
|
| 258 |
+
for rand in self.obs_randomizers[k]:
|
| 259 |
+
if rand is not None:
|
| 260 |
+
x = rand.forward_out(x)
|
| 261 |
+
# flatten to [B, D]
|
| 262 |
+
x = TensorUtils.flatten(x, begin_axis=1)
|
| 263 |
+
feats.append(x)
|
| 264 |
+
|
| 265 |
+
# concatenate all features together
|
| 266 |
+
return torch.cat(feats, dim=-1)
|
| 267 |
+
|
| 268 |
+
def output_shape(self, input_shape=None):
|
| 269 |
+
"""
|
| 270 |
+
Compute the output shape of the encoder.
|
| 271 |
+
"""
|
| 272 |
+
feat_dim = 0
|
| 273 |
+
for k in self.obs_shapes:
|
| 274 |
+
feat_shape = self.obs_shapes[k]
|
| 275 |
+
for rand in self.obs_randomizers[k]:
|
| 276 |
+
if rand is not None:
|
| 277 |
+
feat_shape = rand.output_shape_in(feat_shape)
|
| 278 |
+
if self.obs_nets[k] is not None:
|
| 279 |
+
feat_shape = self.obs_nets[k].output_shape(feat_shape)
|
| 280 |
+
for rand in self.obs_randomizers[k]:
|
| 281 |
+
if rand is not None:
|
| 282 |
+
feat_shape = rand.output_shape_out(feat_shape)
|
| 283 |
+
feat_dim += int(np.prod(feat_shape))
|
| 284 |
+
return [feat_dim]
|
| 285 |
+
|
| 286 |
+
def __repr__(self):
|
| 287 |
+
"""
|
| 288 |
+
Pretty print the encoder.
|
| 289 |
+
"""
|
| 290 |
+
header = '{}'.format(str(self.__class__.__name__))
|
| 291 |
+
msg = ''
|
| 292 |
+
for k in self.obs_shapes:
|
| 293 |
+
msg += textwrap.indent('\nKey(\n', ' ' * 4)
|
| 294 |
+
indent = ' ' * 8
|
| 295 |
+
msg += textwrap.indent("name={}\nshape={}\n".format(k, self.obs_shapes[k]), indent)
|
| 296 |
+
msg += textwrap.indent("modality={}\n".format(ObsUtils.OBS_KEYS_TO_MODALITIES[k]), indent)
|
| 297 |
+
msg += textwrap.indent("randomizer={}\n".format(self.obs_randomizers[k]), indent)
|
| 298 |
+
msg += textwrap.indent("net={}\n".format(self.obs_nets[k]), indent)
|
| 299 |
+
msg += textwrap.indent("sharing_from={}\n".format(self.obs_share_mods[k]), indent)
|
| 300 |
+
msg += textwrap.indent(")", ' ' * 4)
|
| 301 |
+
msg += textwrap.indent("\noutput_shape={}".format(self.output_shape()), ' ' * 4)
|
| 302 |
+
msg = header + '(' + msg + '\n)'
|
| 303 |
+
return msg
|
| 304 |
+
|
| 305 |
+
|
| 306 |
+
class ObservationDecoder(Module):
|
| 307 |
+
"""
|
| 308 |
+
Module that can generate observation outputs by modality. Inputs are assumed
|
| 309 |
+
to be flat (usually outputs from some hidden layer). Each observation output
|
| 310 |
+
is generated with a linear layer from these flat inputs. Subclass this
|
| 311 |
+
module in order to implement more complex schemes for generating each
|
| 312 |
+
modality.
|
| 313 |
+
"""
|
| 314 |
+
def __init__(
|
| 315 |
+
self,
|
| 316 |
+
decode_shapes,
|
| 317 |
+
input_feat_dim,
|
| 318 |
+
):
|
| 319 |
+
"""
|
| 320 |
+
Args:
|
| 321 |
+
decode_shapes (OrderedDict): a dictionary that maps observation key to
|
| 322 |
+
expected shape. This is used to generate output modalities from the
|
| 323 |
+
input features.
|
| 324 |
+
|
| 325 |
+
input_feat_dim (int): flat input dimension size
|
| 326 |
+
"""
|
| 327 |
+
super(ObservationDecoder, self).__init__()
|
| 328 |
+
|
| 329 |
+
# important: sort observation keys to ensure consistent ordering of modalities
|
| 330 |
+
assert isinstance(decode_shapes, OrderedDict)
|
| 331 |
+
self.obs_shapes = OrderedDict()
|
| 332 |
+
for k in decode_shapes:
|
| 333 |
+
self.obs_shapes[k] = decode_shapes[k]
|
| 334 |
+
|
| 335 |
+
self.input_feat_dim = input_feat_dim
|
| 336 |
+
self._create_layers()
|
| 337 |
+
|
| 338 |
+
def _create_layers(self):
|
| 339 |
+
"""
|
| 340 |
+
Create a linear layer to predict each modality.
|
| 341 |
+
"""
|
| 342 |
+
self.nets = nn.ModuleDict()
|
| 343 |
+
for k in self.obs_shapes:
|
| 344 |
+
layer_out_dim = int(np.prod(self.obs_shapes[k]))
|
| 345 |
+
self.nets[k] = nn.Linear(self.input_feat_dim, layer_out_dim)
|
| 346 |
+
|
| 347 |
+
def output_shape(self, input_shape=None):
|
| 348 |
+
"""
|
| 349 |
+
Returns output shape for this module, which is a dictionary instead
|
| 350 |
+
of a list since outputs are dictionaries.
|
| 351 |
+
"""
|
| 352 |
+
return { k : list(self.obs_shapes[k]) for k in self.obs_shapes }
|
| 353 |
+
|
| 354 |
+
def forward(self, feats):
|
| 355 |
+
"""
|
| 356 |
+
Predict each modality from input features, and reshape to each modality's shape.
|
| 357 |
+
"""
|
| 358 |
+
output = {}
|
| 359 |
+
for k in self.obs_shapes:
|
| 360 |
+
out = self.nets[k](feats)
|
| 361 |
+
output[k] = out.reshape(-1, *self.obs_shapes[k])
|
| 362 |
+
return output
|
| 363 |
+
|
| 364 |
+
def __repr__(self):
|
| 365 |
+
"""Pretty print network."""
|
| 366 |
+
header = '{}'.format(str(self.__class__.__name__))
|
| 367 |
+
msg = ''
|
| 368 |
+
for k in self.obs_shapes:
|
| 369 |
+
msg += textwrap.indent('\nKey(\n', ' ' * 4)
|
| 370 |
+
indent = ' ' * 8
|
| 371 |
+
msg += textwrap.indent("name={}\nshape={}\n".format(k, self.obs_shapes[k]), indent)
|
| 372 |
+
msg += textwrap.indent("modality={}\n".format(ObsUtils.OBS_KEYS_TO_MODALITIES[k]), indent)
|
| 373 |
+
msg += textwrap.indent("net=({})\n".format(self.nets[k]), indent)
|
| 374 |
+
msg += textwrap.indent(")", ' ' * 4)
|
| 375 |
+
msg = header + '(' + msg + '\n)'
|
| 376 |
+
return msg
|
| 377 |
+
|
| 378 |
+
|
| 379 |
+
class ObservationGroupEncoder(Module):
|
| 380 |
+
"""
|
| 381 |
+
This class allows networks to encode multiple observation dictionaries into a single
|
| 382 |
+
flat, concatenated vector representation. It does this by assigning each observation
|
| 383 |
+
dictionary (observation group) an @ObservationEncoder object.
|
| 384 |
+
|
| 385 |
+
The class takes a dictionary of dictionaries, @observation_group_shapes.
|
| 386 |
+
Each key corresponds to a observation group (e.g. 'obs', 'subgoal', 'goal')
|
| 387 |
+
and each OrderedDict should be a map between modalities and
|
| 388 |
+
expected input shapes (e.g. { 'image' : (3, 120, 160) }).
|
| 389 |
+
"""
|
| 390 |
+
def __init__(
|
| 391 |
+
self,
|
| 392 |
+
observation_group_shapes,
|
| 393 |
+
feature_activation=nn.ReLU,
|
| 394 |
+
encoder_kwargs=None,
|
| 395 |
+
):
|
| 396 |
+
"""
|
| 397 |
+
Args:
|
| 398 |
+
observation_group_shapes (OrderedDict): a dictionary of dictionaries.
|
| 399 |
+
Each key in this dictionary should specify an observation group, and
|
| 400 |
+
the value should be an OrderedDict that maps modalities to
|
| 401 |
+
expected shapes.
|
| 402 |
+
|
| 403 |
+
feature_activation: non-linearity to apply after each obs net - defaults to ReLU. Pass
|
| 404 |
+
None to apply no activation.
|
| 405 |
+
|
| 406 |
+
encoder_kwargs (dict or None): If None, results in default encoder_kwargs being applied. Otherwise, should
|
| 407 |
+
be nested dictionary containing relevant per-modality information for encoder networks.
|
| 408 |
+
Should be of form:
|
| 409 |
+
|
| 410 |
+
obs_modality1: dict
|
| 411 |
+
feature_dimension: int
|
| 412 |
+
core_class: str
|
| 413 |
+
core_kwargs: dict
|
| 414 |
+
...
|
| 415 |
+
...
|
| 416 |
+
obs_randomizer_class: str
|
| 417 |
+
obs_randomizer_kwargs: dict
|
| 418 |
+
...
|
| 419 |
+
...
|
| 420 |
+
obs_modality2: dict
|
| 421 |
+
...
|
| 422 |
+
"""
|
| 423 |
+
super(ObservationGroupEncoder, self).__init__()
|
| 424 |
+
|
| 425 |
+
# type checking
|
| 426 |
+
assert isinstance(observation_group_shapes, OrderedDict)
|
| 427 |
+
assert np.all([isinstance(observation_group_shapes[k], OrderedDict) for k in observation_group_shapes])
|
| 428 |
+
|
| 429 |
+
self.observation_group_shapes = observation_group_shapes
|
| 430 |
+
|
| 431 |
+
# create an observation encoder per observation group
|
| 432 |
+
self.nets = nn.ModuleDict()
|
| 433 |
+
for obs_group in self.observation_group_shapes:
|
| 434 |
+
self.nets[obs_group] = obs_encoder_factory(
|
| 435 |
+
obs_shapes=self.observation_group_shapes[obs_group],
|
| 436 |
+
feature_activation=feature_activation,
|
| 437 |
+
encoder_kwargs=encoder_kwargs,
|
| 438 |
+
)
|
| 439 |
+
|
| 440 |
+
def forward(self, **inputs):
|
| 441 |
+
"""
|
| 442 |
+
Process each set of inputs in its own observation group.
|
| 443 |
+
|
| 444 |
+
Args:
|
| 445 |
+
inputs (dict): dictionary that maps observation groups to observation
|
| 446 |
+
dictionaries of torch.Tensor batches that agree with
|
| 447 |
+
@self.observation_group_shapes. All observation groups in
|
| 448 |
+
@self.observation_group_shapes must be present, but additional
|
| 449 |
+
observation groups can also be present. Note that these are specified
|
| 450 |
+
as kwargs for ease of use with networks that name each observation
|
| 451 |
+
stream in their forward calls.
|
| 452 |
+
|
| 453 |
+
Returns:
|
| 454 |
+
outputs (torch.Tensor): flat outputs of shape [B, D]
|
| 455 |
+
"""
|
| 456 |
+
|
| 457 |
+
# ensure all observation groups we need are present
|
| 458 |
+
assert set(self.observation_group_shapes.keys()).issubset(inputs), "{} does not contain all observation groups {}".format(
|
| 459 |
+
list(inputs.keys()), list(self.observation_group_shapes.keys())
|
| 460 |
+
)
|
| 461 |
+
|
| 462 |
+
outputs = []
|
| 463 |
+
# Deterministic order since self.observation_group_shapes is OrderedDict
|
| 464 |
+
for obs_group in self.observation_group_shapes:
|
| 465 |
+
# pass through encoder
|
| 466 |
+
outputs.append(
|
| 467 |
+
self.nets[obs_group].forward(inputs[obs_group])
|
| 468 |
+
)
|
| 469 |
+
|
| 470 |
+
return torch.cat(outputs, dim=-1)
|
| 471 |
+
|
| 472 |
+
def output_shape(self):
|
| 473 |
+
"""
|
| 474 |
+
Compute the output shape of this encoder.
|
| 475 |
+
"""
|
| 476 |
+
feat_dim = 0
|
| 477 |
+
for obs_group in self.observation_group_shapes:
|
| 478 |
+
# get feature dimension of these keys
|
| 479 |
+
feat_dim += self.nets[obs_group].output_shape()[0]
|
| 480 |
+
return [feat_dim]
|
| 481 |
+
|
| 482 |
+
def __repr__(self):
|
| 483 |
+
"""Pretty print network."""
|
| 484 |
+
header = '{}'.format(str(self.__class__.__name__))
|
| 485 |
+
msg = ''
|
| 486 |
+
for k in self.observation_group_shapes:
|
| 487 |
+
msg += '\n'
|
| 488 |
+
indent = ' ' * 4
|
| 489 |
+
msg += textwrap.indent("group={}\n{}".format(k, self.nets[k]), indent)
|
| 490 |
+
msg = header + '(' + msg + '\n)'
|
| 491 |
+
return msg
|
| 492 |
+
|
| 493 |
+
|
| 494 |
+
class MIMO_MLP(Module):
|
| 495 |
+
"""
|
| 496 |
+
Extension to MLP to accept multiple observation dictionaries as input and
|
| 497 |
+
to output dictionaries of tensors. Inputs are specified as a dictionary of
|
| 498 |
+
observation dictionaries, with each key corresponding to an observation group.
|
| 499 |
+
|
| 500 |
+
This module utilizes @ObservationGroupEncoder to process the multiple input dictionaries and
|
| 501 |
+
@ObservationDecoder to generate tensor dictionaries. The default behavior
|
| 502 |
+
for encoding the inputs is to process visual inputs with a learned CNN and concatenating
|
| 503 |
+
the flat encodings with the other flat inputs. The default behavior for generating
|
| 504 |
+
outputs is to use a linear layer branch to produce each modality separately
|
| 505 |
+
(including visual outputs).
|
| 506 |
+
"""
|
| 507 |
+
def __init__(
|
| 508 |
+
self,
|
| 509 |
+
input_obs_group_shapes,
|
| 510 |
+
output_shapes,
|
| 511 |
+
layer_dims,
|
| 512 |
+
layer_func=nn.Linear,
|
| 513 |
+
activation=nn.ReLU,
|
| 514 |
+
encoder_kwargs=None,
|
| 515 |
+
):
|
| 516 |
+
"""
|
| 517 |
+
Args:
|
| 518 |
+
input_obs_group_shapes (OrderedDict): a dictionary of dictionaries.
|
| 519 |
+
Each key in this dictionary should specify an observation group, and
|
| 520 |
+
the value should be an OrderedDict that maps modalities to
|
| 521 |
+
expected shapes.
|
| 522 |
+
|
| 523 |
+
output_shapes (OrderedDict): a dictionary that maps modality to
|
| 524 |
+
expected shapes for outputs.
|
| 525 |
+
|
| 526 |
+
layer_dims ([int]): sequence of integers for the MLP hidden layer sizes
|
| 527 |
+
|
| 528 |
+
layer_func: mapping per MLP layer - defaults to Linear
|
| 529 |
+
|
| 530 |
+
activation: non-linearity per MLP layer - defaults to ReLU
|
| 531 |
+
|
| 532 |
+
encoder_kwargs (dict or None): If None, results in default encoder_kwargs being applied. Otherwise, should
|
| 533 |
+
be nested dictionary containing relevant per-modality information for encoder networks.
|
| 534 |
+
Should be of form:
|
| 535 |
+
|
| 536 |
+
obs_modality1: dict
|
| 537 |
+
feature_dimension: int
|
| 538 |
+
core_class: str
|
| 539 |
+
core_kwargs: dict
|
| 540 |
+
...
|
| 541 |
+
...
|
| 542 |
+
obs_randomizer_class: str
|
| 543 |
+
obs_randomizer_kwargs: dict
|
| 544 |
+
...
|
| 545 |
+
...
|
| 546 |
+
obs_modality2: dict
|
| 547 |
+
...
|
| 548 |
+
"""
|
| 549 |
+
super(MIMO_MLP, self).__init__()
|
| 550 |
+
|
| 551 |
+
assert isinstance(input_obs_group_shapes, OrderedDict)
|
| 552 |
+
assert np.all([isinstance(input_obs_group_shapes[k], OrderedDict) for k in input_obs_group_shapes])
|
| 553 |
+
assert isinstance(output_shapes, OrderedDict)
|
| 554 |
+
|
| 555 |
+
self.input_obs_group_shapes = input_obs_group_shapes
|
| 556 |
+
self.output_shapes = output_shapes
|
| 557 |
+
|
| 558 |
+
self.nets = nn.ModuleDict()
|
| 559 |
+
|
| 560 |
+
# Encoder for all observation groups.
|
| 561 |
+
self.nets["encoder"] = ObservationGroupEncoder(
|
| 562 |
+
observation_group_shapes=input_obs_group_shapes,
|
| 563 |
+
encoder_kwargs=encoder_kwargs,
|
| 564 |
+
)
|
| 565 |
+
|
| 566 |
+
# flat encoder output dimension
|
| 567 |
+
mlp_input_dim = self.nets["encoder"].output_shape()[0]
|
| 568 |
+
|
| 569 |
+
# intermediate MLP layers
|
| 570 |
+
self.nets["mlp"] = MLP(
|
| 571 |
+
input_dim=mlp_input_dim,
|
| 572 |
+
output_dim=layer_dims[-1],
|
| 573 |
+
layer_dims=layer_dims[:-1],
|
| 574 |
+
layer_func=layer_func,
|
| 575 |
+
activation=activation,
|
| 576 |
+
output_activation=activation, # make sure non-linearity is applied before decoder
|
| 577 |
+
)
|
| 578 |
+
|
| 579 |
+
# decoder for output modalities
|
| 580 |
+
self.nets["decoder"] = ObservationDecoder(
|
| 581 |
+
decode_shapes=self.output_shapes,
|
| 582 |
+
input_feat_dim=layer_dims[-1],
|
| 583 |
+
)
|
| 584 |
+
|
| 585 |
+
def output_shape(self, input_shape=None):
|
| 586 |
+
"""
|
| 587 |
+
Returns output shape for this module, which is a dictionary instead
|
| 588 |
+
of a list since outputs are dictionaries.
|
| 589 |
+
"""
|
| 590 |
+
return { k : list(self.output_shapes[k]) for k in self.output_shapes }
|
| 591 |
+
|
| 592 |
+
def forward(self, **inputs):
|
| 593 |
+
"""
|
| 594 |
+
Process each set of inputs in its own observation group.
|
| 595 |
+
|
| 596 |
+
Args:
|
| 597 |
+
inputs (dict): a dictionary of dictionaries with one dictionary per
|
| 598 |
+
observation group. Each observation group's dictionary should map
|
| 599 |
+
modality to torch.Tensor batches. Should be consistent with
|
| 600 |
+
@self.input_obs_group_shapes.
|
| 601 |
+
|
| 602 |
+
Returns:
|
| 603 |
+
outputs (dict): dictionary of output torch.Tensors, that corresponds
|
| 604 |
+
to @self.output_shapes
|
| 605 |
+
"""
|
| 606 |
+
enc_outputs = self.nets["encoder"](**inputs)
|
| 607 |
+
mlp_out = self.nets["mlp"](enc_outputs)
|
| 608 |
+
return self.nets["decoder"](mlp_out)
|
| 609 |
+
|
| 610 |
+
def _to_string(self):
|
| 611 |
+
"""
|
| 612 |
+
Subclasses should override this method to print out info about network / policy.
|
| 613 |
+
"""
|
| 614 |
+
return ''
|
| 615 |
+
|
| 616 |
+
def __repr__(self):
|
| 617 |
+
"""Pretty print network."""
|
| 618 |
+
header = '{}'.format(str(self.__class__.__name__))
|
| 619 |
+
msg = ''
|
| 620 |
+
indent = ' ' * 4
|
| 621 |
+
if self._to_string() != '':
|
| 622 |
+
msg += textwrap.indent("\n" + self._to_string() + "\n", indent)
|
| 623 |
+
msg += textwrap.indent("\nencoder={}".format(self.nets["encoder"]), indent)
|
| 624 |
+
msg += textwrap.indent("\n\nmlp={}".format(self.nets["mlp"]), indent)
|
| 625 |
+
msg += textwrap.indent("\n\ndecoder={}".format(self.nets["decoder"]), indent)
|
| 626 |
+
msg = header + '(' + msg + '\n)'
|
| 627 |
+
return msg
|
| 628 |
+
|
| 629 |
+
|
| 630 |
+
class RNN_MIMO_MLP(Module):
|
| 631 |
+
"""
|
| 632 |
+
A wrapper class for a multi-step RNN and a per-step MLP and a decoder.
|
| 633 |
+
|
| 634 |
+
Structure: [encoder -> rnn -> mlp -> decoder]
|
| 635 |
+
|
| 636 |
+
All temporal inputs are processed by a shared @ObservationGroupEncoder,
|
| 637 |
+
followed by an RNN, and then a per-step multi-output MLP.
|
| 638 |
+
"""
|
| 639 |
+
def __init__(
|
| 640 |
+
self,
|
| 641 |
+
input_obs_group_shapes,
|
| 642 |
+
output_shapes,
|
| 643 |
+
mlp_layer_dims,
|
| 644 |
+
rnn_hidden_dim,
|
| 645 |
+
rnn_num_layers,
|
| 646 |
+
rnn_type="LSTM", # [LSTM, GRU]
|
| 647 |
+
rnn_kwargs=None,
|
| 648 |
+
mlp_activation=nn.ReLU,
|
| 649 |
+
mlp_layer_func=nn.Linear,
|
| 650 |
+
per_step=True,
|
| 651 |
+
encoder_kwargs=None,
|
| 652 |
+
):
|
| 653 |
+
"""
|
| 654 |
+
Args:
|
| 655 |
+
input_obs_group_shapes (OrderedDict): a dictionary of dictionaries.
|
| 656 |
+
Each key in this dictionary should specify an observation group, and
|
| 657 |
+
the value should be an OrderedDict that maps modalities to
|
| 658 |
+
expected shapes.
|
| 659 |
+
|
| 660 |
+
output_shapes (OrderedDict): a dictionary that maps modality to
|
| 661 |
+
expected shapes for outputs.
|
| 662 |
+
|
| 663 |
+
rnn_hidden_dim (int): RNN hidden dimension
|
| 664 |
+
|
| 665 |
+
rnn_num_layers (int): number of RNN layers
|
| 666 |
+
|
| 667 |
+
rnn_type (str): [LSTM, GRU]
|
| 668 |
+
|
| 669 |
+
rnn_kwargs (dict): kwargs for the rnn model
|
| 670 |
+
|
| 671 |
+
per_step (bool): if True, apply the MLP and observation decoder into @output_shapes
|
| 672 |
+
at every step of the RNN. Otherwise, apply them to the final hidden state of the
|
| 673 |
+
RNN.
|
| 674 |
+
|
| 675 |
+
encoder_kwargs (dict or None): If None, results in default encoder_kwargs being applied. Otherwise, should
|
| 676 |
+
be nested dictionary containing relevant per-modality information for encoder networks.
|
| 677 |
+
Should be of form:
|
| 678 |
+
|
| 679 |
+
obs_modality1: dict
|
| 680 |
+
feature_dimension: int
|
| 681 |
+
core_class: str
|
| 682 |
+
core_kwargs: dict
|
| 683 |
+
...
|
| 684 |
+
...
|
| 685 |
+
obs_randomizer_class: str
|
| 686 |
+
obs_randomizer_kwargs: dict
|
| 687 |
+
...
|
| 688 |
+
...
|
| 689 |
+
obs_modality2: dict
|
| 690 |
+
...
|
| 691 |
+
"""
|
| 692 |
+
super(RNN_MIMO_MLP, self).__init__()
|
| 693 |
+
assert isinstance(input_obs_group_shapes, OrderedDict)
|
| 694 |
+
assert np.all([isinstance(input_obs_group_shapes[k], OrderedDict) for k in input_obs_group_shapes])
|
| 695 |
+
assert isinstance(output_shapes, OrderedDict)
|
| 696 |
+
self.input_obs_group_shapes = input_obs_group_shapes
|
| 697 |
+
self.output_shapes = output_shapes
|
| 698 |
+
self.per_step = per_step
|
| 699 |
+
|
| 700 |
+
self.nets = nn.ModuleDict()
|
| 701 |
+
|
| 702 |
+
# Encoder for all observation groups.
|
| 703 |
+
self.nets["encoder"] = ObservationGroupEncoder(
|
| 704 |
+
observation_group_shapes=input_obs_group_shapes,
|
| 705 |
+
encoder_kwargs=encoder_kwargs,
|
| 706 |
+
)
|
| 707 |
+
|
| 708 |
+
# flat encoder output dimension
|
| 709 |
+
rnn_input_dim = self.nets["encoder"].output_shape()[0]
|
| 710 |
+
|
| 711 |
+
# bidirectional RNNs mean that the output of RNN will be twice the hidden dimension
|
| 712 |
+
rnn_is_bidirectional = rnn_kwargs.get("bidirectional", False)
|
| 713 |
+
num_directions = int(rnn_is_bidirectional) + 1 # 2 if bidirectional, 1 otherwise
|
| 714 |
+
rnn_output_dim = num_directions * rnn_hidden_dim
|
| 715 |
+
|
| 716 |
+
per_step_net = None
|
| 717 |
+
self._has_mlp = (len(mlp_layer_dims) > 0)
|
| 718 |
+
if self._has_mlp:
|
| 719 |
+
self.nets["mlp"] = MLP(
|
| 720 |
+
input_dim=rnn_output_dim,
|
| 721 |
+
output_dim=mlp_layer_dims[-1],
|
| 722 |
+
layer_dims=mlp_layer_dims[:-1],
|
| 723 |
+
output_activation=mlp_activation,
|
| 724 |
+
layer_func=mlp_layer_func
|
| 725 |
+
)
|
| 726 |
+
self.nets["decoder"] = ObservationDecoder(
|
| 727 |
+
decode_shapes=self.output_shapes,
|
| 728 |
+
input_feat_dim=mlp_layer_dims[-1],
|
| 729 |
+
)
|
| 730 |
+
if self.per_step:
|
| 731 |
+
per_step_net = Sequential(self.nets["mlp"], self.nets["decoder"])
|
| 732 |
+
else:
|
| 733 |
+
self.nets["decoder"] = ObservationDecoder(
|
| 734 |
+
decode_shapes=self.output_shapes,
|
| 735 |
+
input_feat_dim=rnn_output_dim,
|
| 736 |
+
)
|
| 737 |
+
if self.per_step:
|
| 738 |
+
per_step_net = self.nets["decoder"]
|
| 739 |
+
|
| 740 |
+
# core network
|
| 741 |
+
self.nets["rnn"] = RNN_Base(
|
| 742 |
+
input_dim=rnn_input_dim,
|
| 743 |
+
rnn_hidden_dim=rnn_hidden_dim,
|
| 744 |
+
rnn_num_layers=rnn_num_layers,
|
| 745 |
+
rnn_type=rnn_type,
|
| 746 |
+
per_step_net=per_step_net,
|
| 747 |
+
rnn_kwargs=rnn_kwargs
|
| 748 |
+
)
|
| 749 |
+
|
| 750 |
+
def get_rnn_init_state(self, batch_size, device):
|
| 751 |
+
"""
|
| 752 |
+
Get a default RNN state (zeros)
|
| 753 |
+
|
| 754 |
+
Args:
|
| 755 |
+
batch_size (int): batch size dimension
|
| 756 |
+
|
| 757 |
+
device: device the hidden state should be sent to.
|
| 758 |
+
|
| 759 |
+
Returns:
|
| 760 |
+
hidden_state (torch.Tensor or tuple): returns hidden state tensor or tuple of hidden state tensors
|
| 761 |
+
depending on the RNN type
|
| 762 |
+
"""
|
| 763 |
+
return self.nets["rnn"].get_rnn_init_state(batch_size, device=device)
|
| 764 |
+
|
| 765 |
+
def output_shape(self, input_shape):
|
| 766 |
+
"""
|
| 767 |
+
Returns output shape for this module, which is a dictionary instead
|
| 768 |
+
of a list since outputs are dictionaries.
|
| 769 |
+
|
| 770 |
+
Args:
|
| 771 |
+
input_shape (dict): dictionary of dictionaries, where each top-level key
|
| 772 |
+
corresponds to an observation group, and the low-level dictionaries
|
| 773 |
+
specify the shape for each modality in an observation dictionary
|
| 774 |
+
"""
|
| 775 |
+
|
| 776 |
+
# infers temporal dimension from input shape
|
| 777 |
+
obs_group = list(self.input_obs_group_shapes.keys())[0]
|
| 778 |
+
mod = list(self.input_obs_group_shapes[obs_group].keys())[0]
|
| 779 |
+
T = input_shape[obs_group][mod][0]
|
| 780 |
+
TensorUtils.assert_size_at_dim(input_shape, size=T, dim=0,
|
| 781 |
+
msg="RNN_MIMO_MLP: input_shape inconsistent in temporal dimension")
|
| 782 |
+
# returns a dictionary instead of list since outputs are dictionaries
|
| 783 |
+
return { k : [T] + list(self.output_shapes[k]) for k in self.output_shapes }
|
| 784 |
+
|
| 785 |
+
def forward(self, rnn_init_state=None, return_state=False, **inputs):
|
| 786 |
+
"""
|
| 787 |
+
Args:
|
| 788 |
+
inputs (dict): a dictionary of dictionaries with one dictionary per
|
| 789 |
+
observation group. Each observation group's dictionary should map
|
| 790 |
+
modality to torch.Tensor batches. Should be consistent with
|
| 791 |
+
@self.input_obs_group_shapes. First two leading dimensions should
|
| 792 |
+
be batch and time [B, T, ...] for each tensor.
|
| 793 |
+
|
| 794 |
+
rnn_init_state: rnn hidden state, initialize to zero state if set to None
|
| 795 |
+
|
| 796 |
+
return_state (bool): whether to return hidden state
|
| 797 |
+
|
| 798 |
+
Returns:
|
| 799 |
+
outputs (dict): dictionary of output torch.Tensors, that corresponds
|
| 800 |
+
to @self.output_shapes. Leading dimensions will be batch and time [B, T, ...]
|
| 801 |
+
for each tensor.
|
| 802 |
+
|
| 803 |
+
rnn_state (torch.Tensor or tuple): return the new rnn state (if @return_state)
|
| 804 |
+
"""
|
| 805 |
+
for obs_group in self.input_obs_group_shapes:
|
| 806 |
+
for k in self.input_obs_group_shapes[obs_group]:
|
| 807 |
+
# first two dimensions should be [B, T] for inputs
|
| 808 |
+
assert inputs[obs_group][k].ndim - 2 == len(self.input_obs_group_shapes[obs_group][k])
|
| 809 |
+
|
| 810 |
+
# use encoder to extract flat rnn inputs
|
| 811 |
+
rnn_inputs = TensorUtils.time_distributed(inputs, self.nets["encoder"], inputs_as_kwargs=True)
|
| 812 |
+
assert rnn_inputs.ndim == 3 # [B, T, D]
|
| 813 |
+
if self.per_step:
|
| 814 |
+
return self.nets["rnn"].forward(inputs=rnn_inputs, rnn_init_state=rnn_init_state, return_state=return_state)
|
| 815 |
+
|
| 816 |
+
# apply MLP + decoder to last RNN output
|
| 817 |
+
outputs = self.nets["rnn"].forward(inputs=rnn_inputs, rnn_init_state=rnn_init_state, return_state=return_state)
|
| 818 |
+
if return_state:
|
| 819 |
+
outputs, rnn_state = outputs
|
| 820 |
+
|
| 821 |
+
assert outputs.ndim == 3 # [B, T, D]
|
| 822 |
+
if self._has_mlp:
|
| 823 |
+
outputs = self.nets["decoder"](self.nets["mlp"](outputs[:, -1]))
|
| 824 |
+
else:
|
| 825 |
+
outputs = self.nets["decoder"](outputs[:, -1])
|
| 826 |
+
|
| 827 |
+
if return_state:
|
| 828 |
+
return outputs, rnn_state
|
| 829 |
+
return outputs
|
| 830 |
+
|
| 831 |
+
def forward_step(self, rnn_state, **inputs):
|
| 832 |
+
"""
|
| 833 |
+
Unroll network over a single timestep.
|
| 834 |
+
|
| 835 |
+
Args:
|
| 836 |
+
inputs (dict): expects same modalities as @self.input_shapes, with
|
| 837 |
+
additional batch dimension (but NOT time), since this is a
|
| 838 |
+
single time step.
|
| 839 |
+
|
| 840 |
+
rnn_state (torch.Tensor): rnn hidden state
|
| 841 |
+
|
| 842 |
+
Returns:
|
| 843 |
+
outputs (dict): dictionary of output torch.Tensors, that corresponds
|
| 844 |
+
to @self.output_shapes. Does not contain time dimension.
|
| 845 |
+
|
| 846 |
+
rnn_state: return the new rnn state
|
| 847 |
+
"""
|
| 848 |
+
# ensure that the only extra dimension is batch dim, not temporal dim
|
| 849 |
+
assert np.all([inputs[k].ndim - 1 == len(self.input_shapes[k]) for k in self.input_shapes])
|
| 850 |
+
|
| 851 |
+
inputs = TensorUtils.to_sequence(inputs)
|
| 852 |
+
outputs, rnn_state = self.forward(
|
| 853 |
+
inputs,
|
| 854 |
+
rnn_init_state=rnn_state,
|
| 855 |
+
return_state=True,
|
| 856 |
+
)
|
| 857 |
+
if self.per_step:
|
| 858 |
+
# if outputs are not per-step, the time dimension is already reduced
|
| 859 |
+
outputs = outputs[:, 0]
|
| 860 |
+
return outputs, rnn_state
|
| 861 |
+
|
| 862 |
+
def _to_string(self):
|
| 863 |
+
"""
|
| 864 |
+
Subclasses should override this method to print out info about network / policy.
|
| 865 |
+
"""
|
| 866 |
+
return ''
|
| 867 |
+
|
| 868 |
+
def __repr__(self):
|
| 869 |
+
"""Pretty print network."""
|
| 870 |
+
header = '{}'.format(str(self.__class__.__name__))
|
| 871 |
+
msg = ''
|
| 872 |
+
indent = ' ' * 4
|
| 873 |
+
msg += textwrap.indent("\n" + self._to_string(), indent)
|
| 874 |
+
msg += textwrap.indent("\n\nencoder={}".format(self.nets["encoder"]), indent)
|
| 875 |
+
msg += textwrap.indent("\n\nrnn={}".format(self.nets["rnn"]), indent)
|
| 876 |
+
msg = header + '(' + msg + '\n)'
|
| 877 |
+
return msg
|
| 878 |
+
|
| 879 |
+
|
| 880 |
+
class MIMO_Transformer(Module):
|
| 881 |
+
"""
|
| 882 |
+
Extension to Transformer (based on GPT architecture) to accept multiple observation
|
| 883 |
+
dictionaries as input and to output dictionaries of tensors. Inputs are specified as
|
| 884 |
+
a dictionary of observation dictionaries, with each key corresponding to an observation group.
|
| 885 |
+
This module utilizes @ObservationGroupEncoder to process the multiple input dictionaries and
|
| 886 |
+
@ObservationDecoder to generate tensor dictionaries. The default behavior
|
| 887 |
+
for encoding the inputs is to process visual inputs with a learned CNN and concatenating
|
| 888 |
+
the flat encodings with the other flat inputs. The default behavior for generating
|
| 889 |
+
outputs is to use a linear layer branch to produce each modality separately
|
| 890 |
+
(including visual outputs).
|
| 891 |
+
"""
|
| 892 |
+
def __init__(
|
| 893 |
+
self,
|
| 894 |
+
input_obs_group_shapes,
|
| 895 |
+
output_shapes,
|
| 896 |
+
transformer_embed_dim,
|
| 897 |
+
transformer_num_layers,
|
| 898 |
+
transformer_num_heads,
|
| 899 |
+
transformer_context_length,
|
| 900 |
+
transformer_emb_dropout=0.1,
|
| 901 |
+
transformer_attn_dropout=0.1,
|
| 902 |
+
transformer_block_output_dropout=0.1,
|
| 903 |
+
transformer_sinusoidal_embedding=False,
|
| 904 |
+
transformer_activation="gelu",
|
| 905 |
+
transformer_nn_parameter_for_timesteps=False,
|
| 906 |
+
encoder_kwargs=None,
|
| 907 |
+
):
|
| 908 |
+
"""
|
| 909 |
+
Args:
|
| 910 |
+
input_obs_group_shapes (OrderedDict): a dictionary of dictionaries.
|
| 911 |
+
Each key in this dictionary should specify an observation group, and
|
| 912 |
+
the value should be an OrderedDict that maps modalities to
|
| 913 |
+
expected shapes.
|
| 914 |
+
output_shapes (OrderedDict): a dictionary that maps modality to
|
| 915 |
+
expected shapes for outputs.
|
| 916 |
+
transformer_embed_dim (int): dimension for embeddings used by transformer
|
| 917 |
+
transformer_num_layers (int): number of transformer blocks to stack
|
| 918 |
+
transformer_num_heads (int): number of attention heads for each
|
| 919 |
+
transformer block - must divide @transformer_embed_dim evenly. Self-attention is
|
| 920 |
+
computed over this many partitions of the embedding dimension separately.
|
| 921 |
+
transformer_context_length (int): expected length of input sequences
|
| 922 |
+
transformer_activation: non-linearity for input and output layers used in transformer
|
| 923 |
+
transformer_emb_dropout (float): dropout probability for embedding inputs in transformer
|
| 924 |
+
transformer_attn_dropout (float): dropout probability for attention outputs for each transformer block
|
| 925 |
+
transformer_block_output_dropout (float): dropout probability for final outputs for each transformer block
|
| 926 |
+
encoder_kwargs (dict): observation encoder config
|
| 927 |
+
"""
|
| 928 |
+
super(MIMO_Transformer, self).__init__()
|
| 929 |
+
|
| 930 |
+
assert isinstance(input_obs_group_shapes, OrderedDict)
|
| 931 |
+
assert np.all([isinstance(input_obs_group_shapes[k], OrderedDict) for k in input_obs_group_shapes])
|
| 932 |
+
assert isinstance(output_shapes, OrderedDict)
|
| 933 |
+
|
| 934 |
+
self.input_obs_group_shapes = input_obs_group_shapes
|
| 935 |
+
self.output_shapes = output_shapes
|
| 936 |
+
|
| 937 |
+
self.nets = nn.ModuleDict()
|
| 938 |
+
self.params = nn.ParameterDict()
|
| 939 |
+
|
| 940 |
+
# Encoder for all observation groups.
|
| 941 |
+
self.nets["encoder"] = ObservationGroupEncoder(
|
| 942 |
+
observation_group_shapes=input_obs_group_shapes,
|
| 943 |
+
encoder_kwargs=encoder_kwargs,
|
| 944 |
+
feature_activation=None,
|
| 945 |
+
)
|
| 946 |
+
|
| 947 |
+
# flat encoder output dimension
|
| 948 |
+
transformer_input_dim = self.nets["encoder"].output_shape()[0]
|
| 949 |
+
|
| 950 |
+
self.nets["embed_encoder"] = nn.Linear(
|
| 951 |
+
transformer_input_dim, transformer_embed_dim
|
| 952 |
+
)
|
| 953 |
+
|
| 954 |
+
max_timestep = transformer_context_length
|
| 955 |
+
|
| 956 |
+
if transformer_sinusoidal_embedding:
|
| 957 |
+
self.nets["embed_timestep"] = PositionalEncoding(transformer_embed_dim)
|
| 958 |
+
elif transformer_nn_parameter_for_timesteps:
|
| 959 |
+
assert (
|
| 960 |
+
not transformer_sinusoidal_embedding
|
| 961 |
+
), "nn.Parameter only works with learned embeddings"
|
| 962 |
+
self.params["embed_timestep"] = nn.Parameter(
|
| 963 |
+
torch.zeros(1, max_timestep, transformer_embed_dim)
|
| 964 |
+
)
|
| 965 |
+
else:
|
| 966 |
+
self.nets["embed_timestep"] = nn.Embedding(max_timestep, transformer_embed_dim)
|
| 967 |
+
|
| 968 |
+
# layer norm for embeddings
|
| 969 |
+
self.nets["embed_ln"] = nn.LayerNorm(transformer_embed_dim)
|
| 970 |
+
|
| 971 |
+
# dropout for input embeddings
|
| 972 |
+
self.nets["embed_drop"] = nn.Dropout(transformer_emb_dropout)
|
| 973 |
+
|
| 974 |
+
# GPT transformer
|
| 975 |
+
self.nets["transformer"] = GPT_Backbone(
|
| 976 |
+
embed_dim=transformer_embed_dim,
|
| 977 |
+
num_layers=transformer_num_layers,
|
| 978 |
+
num_heads=transformer_num_heads,
|
| 979 |
+
context_length=transformer_context_length,
|
| 980 |
+
attn_dropout=transformer_attn_dropout,
|
| 981 |
+
block_output_dropout=transformer_block_output_dropout,
|
| 982 |
+
activation=transformer_activation,
|
| 983 |
+
)
|
| 984 |
+
|
| 985 |
+
# decoder for output modalities
|
| 986 |
+
self.nets["decoder"] = ObservationDecoder(
|
| 987 |
+
decode_shapes=self.output_shapes,
|
| 988 |
+
input_feat_dim=transformer_embed_dim,
|
| 989 |
+
)
|
| 990 |
+
|
| 991 |
+
self.transformer_context_length = transformer_context_length
|
| 992 |
+
self.transformer_embed_dim = transformer_embed_dim
|
| 993 |
+
self.transformer_sinusoidal_embedding = transformer_sinusoidal_embedding
|
| 994 |
+
self.transformer_nn_parameter_for_timesteps = transformer_nn_parameter_for_timesteps
|
| 995 |
+
|
| 996 |
+
def output_shape(self, input_shape=None):
|
| 997 |
+
"""
|
| 998 |
+
Returns output shape for this module, which is a dictionary instead
|
| 999 |
+
of a list since outputs are dictionaries.
|
| 1000 |
+
"""
|
| 1001 |
+
return { k : list(self.output_shapes[k]) for k in self.output_shapes }
|
| 1002 |
+
|
| 1003 |
+
def embed_timesteps(self, embeddings):
|
| 1004 |
+
"""
|
| 1005 |
+
Computes timestep-based embeddings (aka positional embeddings) to add to embeddings.
|
| 1006 |
+
Args:
|
| 1007 |
+
embeddings (torch.Tensor): embeddings prior to positional embeddings are computed
|
| 1008 |
+
Returns:
|
| 1009 |
+
time_embeddings (torch.Tensor): positional embeddings to add to embeddings
|
| 1010 |
+
"""
|
| 1011 |
+
timesteps = (
|
| 1012 |
+
torch.arange(
|
| 1013 |
+
0,
|
| 1014 |
+
embeddings.shape[1],
|
| 1015 |
+
dtype=embeddings.dtype,
|
| 1016 |
+
device=embeddings.device,
|
| 1017 |
+
)
|
| 1018 |
+
.unsqueeze(0)
|
| 1019 |
+
.repeat(embeddings.shape[0], 1)
|
| 1020 |
+
)
|
| 1021 |
+
assert (timesteps >= 0.0).all(), "timesteps must be positive!"
|
| 1022 |
+
if self.transformer_sinusoidal_embedding:
|
| 1023 |
+
assert torch.is_floating_point(timesteps), timesteps.dtype
|
| 1024 |
+
else:
|
| 1025 |
+
timesteps = timesteps.long()
|
| 1026 |
+
|
| 1027 |
+
if self.transformer_nn_parameter_for_timesteps:
|
| 1028 |
+
time_embeddings = self.params["embed_timestep"]
|
| 1029 |
+
else:
|
| 1030 |
+
time_embeddings = self.nets["embed_timestep"](
|
| 1031 |
+
timesteps
|
| 1032 |
+
) # these are NOT fed into transformer, only added to the inputs.
|
| 1033 |
+
# compute how many modalities were combined into embeddings, replicate time embeddings that many times
|
| 1034 |
+
num_replicates = embeddings.shape[-1] // self.transformer_embed_dim
|
| 1035 |
+
time_embeddings = torch.cat([time_embeddings for _ in range(num_replicates)], -1)
|
| 1036 |
+
assert (
|
| 1037 |
+
embeddings.shape == time_embeddings.shape
|
| 1038 |
+
), f"{embeddings.shape}, {time_embeddings.shape}"
|
| 1039 |
+
return time_embeddings
|
| 1040 |
+
|
| 1041 |
+
def input_embedding(
|
| 1042 |
+
self,
|
| 1043 |
+
inputs,
|
| 1044 |
+
):
|
| 1045 |
+
"""
|
| 1046 |
+
Process encoded observations into embeddings to pass to transformer,
|
| 1047 |
+
Adds timestep-based embeddings (aka positional embeddings) to inputs.
|
| 1048 |
+
Args:
|
| 1049 |
+
inputs (torch.Tensor): outputs from observation encoder
|
| 1050 |
+
Returns:
|
| 1051 |
+
embeddings (torch.Tensor): input embeddings to pass to transformer backbone.
|
| 1052 |
+
"""
|
| 1053 |
+
embeddings = self.nets["embed_encoder"](inputs)
|
| 1054 |
+
time_embeddings = self.embed_timesteps(embeddings)
|
| 1055 |
+
embeddings = embeddings + time_embeddings
|
| 1056 |
+
embeddings = self.nets["embed_ln"](embeddings)
|
| 1057 |
+
embeddings = self.nets["embed_drop"](embeddings)
|
| 1058 |
+
|
| 1059 |
+
return embeddings
|
| 1060 |
+
|
| 1061 |
+
|
| 1062 |
+
def forward(self, **inputs):
|
| 1063 |
+
"""
|
| 1064 |
+
Process each set of inputs in its own observation group.
|
| 1065 |
+
Args:
|
| 1066 |
+
inputs (dict): a dictionary of dictionaries with one dictionary per
|
| 1067 |
+
observation group. Each observation group's dictionary should map
|
| 1068 |
+
modality to torch.Tensor batches. Should be consistent with
|
| 1069 |
+
@self.input_obs_group_shapes. First two leading dimensions should
|
| 1070 |
+
be batch and time [B, T, ...] for each tensor.
|
| 1071 |
+
Returns:
|
| 1072 |
+
outputs (dict): dictionary of output torch.Tensors, that corresponds
|
| 1073 |
+
to @self.output_shapes. Leading dimensions will be batch and time [B, T, ...]
|
| 1074 |
+
for each tensor.
|
| 1075 |
+
"""
|
| 1076 |
+
for obs_group in self.input_obs_group_shapes:
|
| 1077 |
+
for k in self.input_obs_group_shapes[obs_group]:
|
| 1078 |
+
# first two dimensions should be [B, T] for inputs
|
| 1079 |
+
if inputs[obs_group][k] is None:
|
| 1080 |
+
continue
|
| 1081 |
+
assert inputs[obs_group][k].ndim - 2 == len(self.input_obs_group_shapes[obs_group][k])
|
| 1082 |
+
|
| 1083 |
+
inputs = inputs.copy()
|
| 1084 |
+
|
| 1085 |
+
transformer_encoder_outputs = None
|
| 1086 |
+
transformer_inputs = TensorUtils.time_distributed(
|
| 1087 |
+
inputs, self.nets["encoder"], inputs_as_kwargs=True
|
| 1088 |
+
)
|
| 1089 |
+
assert transformer_inputs.ndim == 3 # [B, T, D]
|
| 1090 |
+
|
| 1091 |
+
if transformer_encoder_outputs is None:
|
| 1092 |
+
transformer_embeddings = self.input_embedding(transformer_inputs)
|
| 1093 |
+
# pass encoded sequences through transformer
|
| 1094 |
+
transformer_encoder_outputs = self.nets["transformer"].forward(transformer_embeddings)
|
| 1095 |
+
|
| 1096 |
+
transformer_outputs = transformer_encoder_outputs
|
| 1097 |
+
# apply decoder to each timestep of sequence to get a dictionary of outputs
|
| 1098 |
+
transformer_outputs = TensorUtils.time_distributed(
|
| 1099 |
+
transformer_outputs, self.nets["decoder"]
|
| 1100 |
+
)
|
| 1101 |
+
transformer_outputs["transformer_encoder_outputs"] = transformer_encoder_outputs
|
| 1102 |
+
return transformer_outputs
|
| 1103 |
+
|
| 1104 |
+
def _to_string(self):
|
| 1105 |
+
"""
|
| 1106 |
+
Subclasses should override this method to print out info about network / policy.
|
| 1107 |
+
"""
|
| 1108 |
+
return ''
|
| 1109 |
+
|
| 1110 |
+
def __repr__(self):
|
| 1111 |
+
"""Pretty print network."""
|
| 1112 |
+
header = '{}'.format(str(self.__class__.__name__))
|
| 1113 |
+
msg = ''
|
| 1114 |
+
indent = ' ' * 4
|
| 1115 |
+
if self._to_string() != '':
|
| 1116 |
+
msg += textwrap.indent("\n" + self._to_string() + "\n", indent)
|
| 1117 |
+
msg += textwrap.indent("\nencoder={}".format(self.nets["encoder"]), indent)
|
| 1118 |
+
msg += textwrap.indent("\n\ntransformer={}".format(self.nets["transformer"]), indent)
|
| 1119 |
+
msg += textwrap.indent("\n\ndecoder={}".format(self.nets["decoder"]), indent)
|
| 1120 |
+
msg = header + '(' + msg + '\n)'
|
| 1121 |
+
return msg
|
aloha-devel/robomimic/models/value_nets.py
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Contains torch Modules for value networks. These networks take an
|
| 3 |
+
observation dictionary as input (and possibly additional conditioning,
|
| 4 |
+
such as subgoal or goal dictionaries) and produce value or
|
| 5 |
+
action-value estimates or distributions.
|
| 6 |
+
"""
|
| 7 |
+
import numpy as np
|
| 8 |
+
from collections import OrderedDict
|
| 9 |
+
|
| 10 |
+
import torch
|
| 11 |
+
import torch.nn as nn
|
| 12 |
+
import torch.nn.functional as F
|
| 13 |
+
import torch.distributions as D
|
| 14 |
+
|
| 15 |
+
import robomimic.utils.tensor_utils as TensorUtils
|
| 16 |
+
from robomimic.models.obs_nets import MIMO_MLP
|
| 17 |
+
from robomimic.models.distributions import DiscreteValueDistribution
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
class ValueNetwork(MIMO_MLP):
|
| 21 |
+
"""
|
| 22 |
+
A basic value network that predicts values from observations.
|
| 23 |
+
Can optionally be goal conditioned on future observations.
|
| 24 |
+
"""
|
| 25 |
+
def __init__(
|
| 26 |
+
self,
|
| 27 |
+
obs_shapes,
|
| 28 |
+
mlp_layer_dims,
|
| 29 |
+
value_bounds=None,
|
| 30 |
+
goal_shapes=None,
|
| 31 |
+
encoder_kwargs=None,
|
| 32 |
+
):
|
| 33 |
+
"""
|
| 34 |
+
Args:
|
| 35 |
+
obs_shapes (OrderedDict): a dictionary that maps observation keys to
|
| 36 |
+
expected shapes for observations.
|
| 37 |
+
|
| 38 |
+
mlp_layer_dims ([int]): sequence of integers for the MLP hidden layers sizes.
|
| 39 |
+
|
| 40 |
+
value_bounds (tuple): a 2-tuple corresponding to the lowest and highest possible return
|
| 41 |
+
that the network should be possible of generating. The network will rescale outputs
|
| 42 |
+
using a tanh layer to lie within these bounds. If None, no tanh re-scaling is done.
|
| 43 |
+
|
| 44 |
+
goal_shapes (OrderedDict): a dictionary that maps observation keys to
|
| 45 |
+
expected shapes for goal observations.
|
| 46 |
+
|
| 47 |
+
encoder_kwargs (dict or None): If None, results in default encoder_kwargs being applied. Otherwise, should
|
| 48 |
+
be nested dictionary containing relevant per-observation key information for encoder networks.
|
| 49 |
+
Should be of form:
|
| 50 |
+
|
| 51 |
+
obs_modality1: dict
|
| 52 |
+
feature_dimension: int
|
| 53 |
+
core_class: str
|
| 54 |
+
core_kwargs: dict
|
| 55 |
+
...
|
| 56 |
+
...
|
| 57 |
+
obs_randomizer_class: str
|
| 58 |
+
obs_randomizer_kwargs: dict
|
| 59 |
+
...
|
| 60 |
+
...
|
| 61 |
+
obs_modality2: dict
|
| 62 |
+
...
|
| 63 |
+
"""
|
| 64 |
+
self.value_bounds = value_bounds
|
| 65 |
+
if self.value_bounds is not None:
|
| 66 |
+
# convert [lb, ub] to a scale and offset for the tanh output, which is in [-1, 1]
|
| 67 |
+
self._value_scale = (float(self.value_bounds[1]) - float(self.value_bounds[0])) / 2.
|
| 68 |
+
self._value_offset = (float(self.value_bounds[1]) + float(self.value_bounds[0])) / 2.
|
| 69 |
+
|
| 70 |
+
assert isinstance(obs_shapes, OrderedDict)
|
| 71 |
+
self.obs_shapes = obs_shapes
|
| 72 |
+
|
| 73 |
+
# set up different observation groups for @MIMO_MLP
|
| 74 |
+
observation_group_shapes = OrderedDict()
|
| 75 |
+
observation_group_shapes["obs"] = OrderedDict(self.obs_shapes)
|
| 76 |
+
|
| 77 |
+
self._is_goal_conditioned = False
|
| 78 |
+
if goal_shapes is not None and len(goal_shapes) > 0:
|
| 79 |
+
assert isinstance(goal_shapes, OrderedDict)
|
| 80 |
+
self._is_goal_conditioned = True
|
| 81 |
+
self.goal_shapes = OrderedDict(goal_shapes)
|
| 82 |
+
observation_group_shapes["goal"] = OrderedDict(self.goal_shapes)
|
| 83 |
+
else:
|
| 84 |
+
self.goal_shapes = OrderedDict()
|
| 85 |
+
|
| 86 |
+
output_shapes = self._get_output_shapes()
|
| 87 |
+
super(ValueNetwork, self).__init__(
|
| 88 |
+
input_obs_group_shapes=observation_group_shapes,
|
| 89 |
+
output_shapes=output_shapes,
|
| 90 |
+
layer_dims=mlp_layer_dims,
|
| 91 |
+
encoder_kwargs=encoder_kwargs,
|
| 92 |
+
)
|
| 93 |
+
|
| 94 |
+
def _get_output_shapes(self):
|
| 95 |
+
"""
|
| 96 |
+
Allow subclasses to re-define outputs from @MIMO_MLP, since we won't
|
| 97 |
+
always directly predict values, but may instead predict the parameters
|
| 98 |
+
of a value distribution.
|
| 99 |
+
"""
|
| 100 |
+
return OrderedDict(value=(1,))
|
| 101 |
+
|
| 102 |
+
def output_shape(self, input_shape=None):
|
| 103 |
+
"""
|
| 104 |
+
Function to compute output shape from inputs to this module.
|
| 105 |
+
|
| 106 |
+
Args:
|
| 107 |
+
input_shape (iterable of int): shape of input. Does not include batch dimension.
|
| 108 |
+
Some modules may not need this argument, if their output does not depend
|
| 109 |
+
on the size of the input, or if they assume fixed size input.
|
| 110 |
+
|
| 111 |
+
Returns:
|
| 112 |
+
out_shape ([int]): list of integers corresponding to output shape
|
| 113 |
+
"""
|
| 114 |
+
return [1]
|
| 115 |
+
|
| 116 |
+
def forward(self, obs_dict, goal_dict=None):
|
| 117 |
+
"""
|
| 118 |
+
Forward through value network, and then optionally use tanh scaling.
|
| 119 |
+
"""
|
| 120 |
+
values = super(ValueNetwork, self).forward(obs=obs_dict, goal=goal_dict)["value"]
|
| 121 |
+
if self.value_bounds is not None:
|
| 122 |
+
values = self._value_offset + self._value_scale * torch.tanh(values)
|
| 123 |
+
return values
|
| 124 |
+
|
| 125 |
+
def _to_string(self):
|
| 126 |
+
return "value_bounds={}".format(self.value_bounds)
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
class ActionValueNetwork(ValueNetwork):
|
| 130 |
+
"""
|
| 131 |
+
A basic Q (action-value) network that predicts values from observations
|
| 132 |
+
and actions. Can optionally be goal conditioned on future observations.
|
| 133 |
+
"""
|
| 134 |
+
def __init__(
|
| 135 |
+
self,
|
| 136 |
+
obs_shapes,
|
| 137 |
+
ac_dim,
|
| 138 |
+
mlp_layer_dims,
|
| 139 |
+
value_bounds=None,
|
| 140 |
+
goal_shapes=None,
|
| 141 |
+
encoder_kwargs=None,
|
| 142 |
+
):
|
| 143 |
+
"""
|
| 144 |
+
Args:
|
| 145 |
+
obs_shapes (OrderedDict): a dictionary that maps observation keys to
|
| 146 |
+
expected shapes for observations.
|
| 147 |
+
|
| 148 |
+
ac_dim (int): dimension of action space.
|
| 149 |
+
|
| 150 |
+
mlp_layer_dims ([int]): sequence of integers for the MLP hidden layers sizes.
|
| 151 |
+
|
| 152 |
+
value_bounds (tuple): a 2-tuple corresponding to the lowest and highest possible return
|
| 153 |
+
that the network should be possible of generating. The network will rescale outputs
|
| 154 |
+
using a tanh layer to lie within these bounds. If None, no tanh re-scaling is done.
|
| 155 |
+
|
| 156 |
+
goal_shapes (OrderedDict): a dictionary that maps observation keys to
|
| 157 |
+
expected shapes for goal observations.
|
| 158 |
+
|
| 159 |
+
encoder_kwargs (dict or None): If None, results in default encoder_kwargs being applied. Otherwise, should
|
| 160 |
+
be nested dictionary containing relevant per-observation key information for encoder networks.
|
| 161 |
+
Should be of form:
|
| 162 |
+
|
| 163 |
+
obs_modality1: dict
|
| 164 |
+
feature_dimension: int
|
| 165 |
+
core_class: str
|
| 166 |
+
core_kwargs: dict
|
| 167 |
+
...
|
| 168 |
+
...
|
| 169 |
+
obs_randomizer_class: str
|
| 170 |
+
obs_randomizer_kwargs: dict
|
| 171 |
+
...
|
| 172 |
+
...
|
| 173 |
+
obs_modality2: dict
|
| 174 |
+
...
|
| 175 |
+
"""
|
| 176 |
+
|
| 177 |
+
# add in action as a modality
|
| 178 |
+
new_obs_shapes = OrderedDict(obs_shapes)
|
| 179 |
+
new_obs_shapes["action"] = (ac_dim,)
|
| 180 |
+
self.ac_dim = ac_dim
|
| 181 |
+
|
| 182 |
+
# pass to super class to instantiate network
|
| 183 |
+
super(ActionValueNetwork, self).__init__(
|
| 184 |
+
obs_shapes=new_obs_shapes,
|
| 185 |
+
mlp_layer_dims=mlp_layer_dims,
|
| 186 |
+
value_bounds=value_bounds,
|
| 187 |
+
goal_shapes=goal_shapes,
|
| 188 |
+
encoder_kwargs=encoder_kwargs,
|
| 189 |
+
)
|
| 190 |
+
|
| 191 |
+
def forward(self, obs_dict, acts, goal_dict=None):
|
| 192 |
+
"""
|
| 193 |
+
Modify forward from super class to include actions in inputs.
|
| 194 |
+
"""
|
| 195 |
+
inputs = dict(obs_dict)
|
| 196 |
+
inputs["action"] = acts
|
| 197 |
+
return super(ActionValueNetwork, self).forward(inputs, goal_dict)
|
| 198 |
+
|
| 199 |
+
def _to_string(self):
|
| 200 |
+
return "action_dim={}\nvalue_bounds={}".format(self.ac_dim, self.value_bounds)
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
class DistributionalActionValueNetwork(ActionValueNetwork):
|
| 204 |
+
"""
|
| 205 |
+
Distributional Q (action-value) network that outputs a categorical distribution over
|
| 206 |
+
a discrete grid of value atoms. See https://arxiv.org/pdf/1707.06887.pdf for
|
| 207 |
+
more details.
|
| 208 |
+
"""
|
| 209 |
+
def __init__(
|
| 210 |
+
self,
|
| 211 |
+
obs_shapes,
|
| 212 |
+
ac_dim,
|
| 213 |
+
mlp_layer_dims,
|
| 214 |
+
value_bounds,
|
| 215 |
+
num_atoms,
|
| 216 |
+
goal_shapes=None,
|
| 217 |
+
encoder_kwargs=None,
|
| 218 |
+
):
|
| 219 |
+
"""
|
| 220 |
+
Args:
|
| 221 |
+
obs_shapes (OrderedDict): a dictionary that maps modality to
|
| 222 |
+
expected shapes for observations.
|
| 223 |
+
|
| 224 |
+
ac_dim (int): dimension of action space.
|
| 225 |
+
|
| 226 |
+
mlp_layer_dims ([int]): sequence of integers for the MLP hidden layers sizes.
|
| 227 |
+
|
| 228 |
+
value_bounds (tuple): a 2-tuple corresponding to the lowest and highest possible return
|
| 229 |
+
that the network should be possible of generating. This defines the support
|
| 230 |
+
of the value distribution.
|
| 231 |
+
|
| 232 |
+
num_atoms (int): number of value atoms to use for the categorical distribution - which
|
| 233 |
+
is the representation of the value distribution.
|
| 234 |
+
|
| 235 |
+
goal_shapes (OrderedDict): a dictionary that maps modality to
|
| 236 |
+
expected shapes for goal observations.
|
| 237 |
+
|
| 238 |
+
encoder_kwargs (dict or None): If None, results in default encoder_kwargs being applied. Otherwise, should
|
| 239 |
+
be nested dictionary containing relevant per-modality information for encoder networks.
|
| 240 |
+
Should be of form:
|
| 241 |
+
|
| 242 |
+
obs_modality1: dict
|
| 243 |
+
feature_dimension: int
|
| 244 |
+
core_class: str
|
| 245 |
+
core_kwargs: dict
|
| 246 |
+
...
|
| 247 |
+
...
|
| 248 |
+
obs_randomizer_class: str
|
| 249 |
+
obs_randomizer_kwargs: dict
|
| 250 |
+
...
|
| 251 |
+
...
|
| 252 |
+
obs_modality2: dict
|
| 253 |
+
...
|
| 254 |
+
"""
|
| 255 |
+
|
| 256 |
+
# parameters specific to DistributionalActionValueNetwork
|
| 257 |
+
self.num_atoms = num_atoms
|
| 258 |
+
self._atoms = np.linspace(value_bounds[0], value_bounds[1], num_atoms)
|
| 259 |
+
|
| 260 |
+
# pass to super class to instantiate network
|
| 261 |
+
super(DistributionalActionValueNetwork, self).__init__(
|
| 262 |
+
obs_shapes=obs_shapes,
|
| 263 |
+
ac_dim=ac_dim,
|
| 264 |
+
mlp_layer_dims=mlp_layer_dims,
|
| 265 |
+
value_bounds=value_bounds,
|
| 266 |
+
goal_shapes=goal_shapes,
|
| 267 |
+
encoder_kwargs=encoder_kwargs,
|
| 268 |
+
)
|
| 269 |
+
|
| 270 |
+
def _get_output_shapes(self):
|
| 271 |
+
"""
|
| 272 |
+
Network outputs log probabilities for categorical distribution over discrete value grid.
|
| 273 |
+
"""
|
| 274 |
+
return OrderedDict(log_probs=(self.num_atoms,))
|
| 275 |
+
|
| 276 |
+
def forward_train(self, obs_dict, acts, goal_dict=None):
|
| 277 |
+
"""
|
| 278 |
+
Return full critic categorical distribution.
|
| 279 |
+
|
| 280 |
+
Args:
|
| 281 |
+
obs_dict (dict): batch of observations
|
| 282 |
+
acts (torch.Tensor): batch of actions
|
| 283 |
+
goal_dict (dict): if not None, batch of goal observations
|
| 284 |
+
|
| 285 |
+
Returns:
|
| 286 |
+
value_distribution (DiscreteValueDistribution instance)
|
| 287 |
+
"""
|
| 288 |
+
|
| 289 |
+
# add in actions
|
| 290 |
+
inputs = dict(obs_dict)
|
| 291 |
+
inputs["action"] = acts
|
| 292 |
+
|
| 293 |
+
# network returns unnormalized log probabilities (logits) for each of the value atoms
|
| 294 |
+
logits = MIMO_MLP.forward(self, obs=inputs, goal=goal_dict)["log_probs"]
|
| 295 |
+
|
| 296 |
+
# turn these logits into a categorical distribution over the value atoms.
|
| 297 |
+
# (unsqueeze to make sure atoms are compatible with batch operations)
|
| 298 |
+
value_atoms = torch.Tensor(self._atoms).unsqueeze(0).to(logits.device)
|
| 299 |
+
return DiscreteValueDistribution(values=value_atoms, logits=logits)
|
| 300 |
+
|
| 301 |
+
def forward(self, obs_dict, acts, goal_dict=None):
|
| 302 |
+
"""
|
| 303 |
+
Return mean of critic categorical distribution. Useful for obtaining
|
| 304 |
+
point estimates of critic values.
|
| 305 |
+
|
| 306 |
+
Args:
|
| 307 |
+
obs_dict (dict): batch of observations
|
| 308 |
+
acts (torch.Tensor): batch of actions
|
| 309 |
+
goal_dict (dict): if not None, batch of goal observations
|
| 310 |
+
|
| 311 |
+
Returns:
|
| 312 |
+
mean_value (torch.Tensor): expectation of value distribution
|
| 313 |
+
"""
|
| 314 |
+
vd = self.forward_train(obs_dict=obs_dict, acts=acts, goal_dict=goal_dict)
|
| 315 |
+
return vd.mean()
|
| 316 |
+
|
| 317 |
+
def _to_string(self):
|
| 318 |
+
return "action_dim={}\nvalue_bounds={}\nnum_atoms={}".format(self.ac_dim, self.value_bounds, self.num_atoms)
|
aloha-devel/robomimic/utils/__init__.py
ADDED
|
File without changes
|
aloha-devel/robomimic/utils/__pycache__/__init__.cpython-38.pyc
ADDED
|
Binary file (157 Bytes). View file
|
|
|
aloha-devel/robomimic/utils/__pycache__/tensor_utils.cpython-38.pyc
ADDED
|
Binary file (33.1 kB). View file
|
|
|
aloha-devel/robomimic/utils/action_utils.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
from typing import Union, Sequence, Dict, Optional, Tuple
|
| 3 |
+
|
| 4 |
+
from copy import deepcopy
|
| 5 |
+
from collections import OrderedDict
|
| 6 |
+
import functools
|
| 7 |
+
|
| 8 |
+
import numpy as np
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def action_dict_to_vector(
|
| 12 |
+
action_dict: Dict[str, np.ndarray],
|
| 13 |
+
action_keys: Optional[Sequence[str]]=None) -> np.ndarray:
|
| 14 |
+
if action_keys is None:
|
| 15 |
+
action_keys = list(action_dict.keys())
|
| 16 |
+
actions = [action_dict[k] for k in action_keys]
|
| 17 |
+
|
| 18 |
+
action_vec = np.concatenate(actions, axis=-1)
|
| 19 |
+
return action_vec
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def vector_to_action_dict(
|
| 23 |
+
action: np.ndarray,
|
| 24 |
+
action_shapes: Dict[str, Tuple[int]],
|
| 25 |
+
action_keys: Sequence[str]) -> Dict[str, np.ndarray]:
|
| 26 |
+
action_dict = dict()
|
| 27 |
+
start_idx = 0
|
| 28 |
+
for key in action_keys:
|
| 29 |
+
this_act_shape = action_shapes[key]
|
| 30 |
+
this_act_dim = np.prod(this_act_shape)
|
| 31 |
+
end_idx = start_idx + this_act_dim
|
| 32 |
+
action_dict[key] = action[...,start_idx:end_idx].reshape(
|
| 33 |
+
action.shape[:-1]+this_act_shape)
|
| 34 |
+
start_idx = end_idx
|
| 35 |
+
return action_dict
|
aloha-devel/robomimic/utils/dataset.py
ADDED
|
@@ -0,0 +1,1158 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
This file contains Dataset classes that are used by torch dataloaders
|
| 3 |
+
to fetch batches from hdf5 files.
|
| 4 |
+
"""
|
| 5 |
+
import os
|
| 6 |
+
import h5py
|
| 7 |
+
import numpy as np
|
| 8 |
+
import random
|
| 9 |
+
from copy import deepcopy
|
| 10 |
+
from contextlib import contextmanager
|
| 11 |
+
from collections import OrderedDict
|
| 12 |
+
|
| 13 |
+
import torch.utils.data
|
| 14 |
+
|
| 15 |
+
import robomimic.utils.tensor_utils as TensorUtils
|
| 16 |
+
import robomimic.utils.obs_utils as ObsUtils
|
| 17 |
+
import robomimic.utils.action_utils as AcUtils
|
| 18 |
+
import robomimic.utils.log_utils as LogUtils
|
| 19 |
+
import robomimic.utils.lang_utils as LangUtils
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
class SequenceDataset(torch.utils.data.Dataset):
|
| 23 |
+
def __init__(
|
| 24 |
+
self,
|
| 25 |
+
hdf5_path,
|
| 26 |
+
obs_keys,
|
| 27 |
+
action_keys,
|
| 28 |
+
dataset_keys,
|
| 29 |
+
action_config,
|
| 30 |
+
frame_stack=1,
|
| 31 |
+
seq_length=1,
|
| 32 |
+
pad_frame_stack=True,
|
| 33 |
+
pad_seq_length=True,
|
| 34 |
+
get_pad_mask=False,
|
| 35 |
+
goal_mode=None,
|
| 36 |
+
hdf5_cache_mode=None,
|
| 37 |
+
hdf5_use_swmr=True,
|
| 38 |
+
hdf5_normalize_obs=False,
|
| 39 |
+
filter_by_attribute=None,
|
| 40 |
+
load_next_obs=True,
|
| 41 |
+
shuffled_obs_key_groups=None,
|
| 42 |
+
lang=None,
|
| 43 |
+
):
|
| 44 |
+
"""
|
| 45 |
+
Dataset class for fetching sequences of experience.
|
| 46 |
+
Length of the fetched sequence is equal to (@frame_stack - 1 + @seq_length)
|
| 47 |
+
|
| 48 |
+
Args:
|
| 49 |
+
hdf5_path (str): path to hdf5
|
| 50 |
+
|
| 51 |
+
obs_keys (tuple, list): keys to observation items (image, object, etc) to be fetched from the dataset
|
| 52 |
+
|
| 53 |
+
action_config (dict): TODO
|
| 54 |
+
|
| 55 |
+
dataset_keys (tuple, list): keys to dataset items (actions, rewards, etc) to be fetched from the dataset
|
| 56 |
+
|
| 57 |
+
frame_stack (int): numbers of stacked frames to fetch. Defaults to 1 (single frame).
|
| 58 |
+
|
| 59 |
+
seq_length (int): length of sequences to sample. Defaults to 1 (single frame).
|
| 60 |
+
|
| 61 |
+
pad_frame_stack (int): whether to pad sequence for frame stacking at the beginning of a demo. This
|
| 62 |
+
ensures that partial frame stacks are observed, such as (s_0, s_0, s_0, s_1). Otherwise, the
|
| 63 |
+
first frame stacked observation would be (s_0, s_1, s_2, s_3).
|
| 64 |
+
|
| 65 |
+
pad_seq_length (int): whether to pad sequence for sequence fetching at the end of a demo. This
|
| 66 |
+
ensures that partial sequences at the end of a demonstration are observed, such as
|
| 67 |
+
(s_{T-1}, s_{T}, s_{T}, s_{T}). Otherwise, the last sequence provided would be
|
| 68 |
+
(s_{T-3}, s_{T-2}, s_{T-1}, s_{T}).
|
| 69 |
+
|
| 70 |
+
get_pad_mask (bool): if True, also provide padding masks as part of the batch. This can be
|
| 71 |
+
useful for masking loss functions on padded parts of the data.
|
| 72 |
+
|
| 73 |
+
goal_mode (str): either "last" or None. Defaults to None, which is to not fetch goals
|
| 74 |
+
|
| 75 |
+
hdf5_cache_mode (str): one of ["all", "low_dim", or None]. Set to "all" to cache entire hdf5
|
| 76 |
+
in memory - this is by far the fastest for data loading. Set to "low_dim" to cache all
|
| 77 |
+
non-image data. Set to None to use no caching - in this case, every batch sample is
|
| 78 |
+
retrieved via file i/o. You should almost never set this to None, even for large
|
| 79 |
+
image datasets.
|
| 80 |
+
|
| 81 |
+
hdf5_use_swmr (bool): whether to use swmr feature when opening the hdf5 file. This ensures
|
| 82 |
+
that multiple Dataset instances can all access the same hdf5 file without problems.
|
| 83 |
+
|
| 84 |
+
hdf5_normalize_obs (bool): if True, normalize observations by computing the mean observation
|
| 85 |
+
and std of each observation (in each dimension and modality), and normalizing to unit
|
| 86 |
+
mean and variance in each dimension.
|
| 87 |
+
|
| 88 |
+
filter_by_attribute (str): if provided, use the provided filter key to look up a subset of
|
| 89 |
+
demonstrations to load
|
| 90 |
+
|
| 91 |
+
load_next_obs (bool): whether to load next_obs from the dataset
|
| 92 |
+
|
| 93 |
+
shuffled_obs_key_groups (list): TODO
|
| 94 |
+
|
| 95 |
+
lang: TODO documentation
|
| 96 |
+
"""
|
| 97 |
+
super(SequenceDataset, self).__init__()
|
| 98 |
+
|
| 99 |
+
self.hdf5_path = os.path.expanduser(hdf5_path)
|
| 100 |
+
self.hdf5_use_swmr = hdf5_use_swmr
|
| 101 |
+
self.hdf5_normalize_obs = hdf5_normalize_obs
|
| 102 |
+
self._hdf5_file = None
|
| 103 |
+
|
| 104 |
+
assert hdf5_cache_mode in ["all", "low_dim", None]
|
| 105 |
+
self.hdf5_cache_mode = hdf5_cache_mode
|
| 106 |
+
|
| 107 |
+
self.load_next_obs = load_next_obs
|
| 108 |
+
self.filter_by_attribute = filter_by_attribute
|
| 109 |
+
|
| 110 |
+
# get all keys that needs to be fetched
|
| 111 |
+
self.obs_keys = tuple(obs_keys)
|
| 112 |
+
self.action_keys = tuple(action_keys)
|
| 113 |
+
self.dataset_keys = tuple(dataset_keys)
|
| 114 |
+
# add action keys to dataset keys
|
| 115 |
+
if self.action_keys is not None:
|
| 116 |
+
self.dataset_keys = tuple(set(self.dataset_keys).union(set(self.action_keys)))
|
| 117 |
+
|
| 118 |
+
self.action_config = action_config
|
| 119 |
+
|
| 120 |
+
# set up lang and language embedding
|
| 121 |
+
self.lang = lang
|
| 122 |
+
self._lang_emb = LangUtils.get_lang_emb(self.lang)
|
| 123 |
+
|
| 124 |
+
self.n_frame_stack = frame_stack
|
| 125 |
+
assert self.n_frame_stack >= 1
|
| 126 |
+
|
| 127 |
+
self.seq_length = seq_length
|
| 128 |
+
assert self.seq_length >= 1
|
| 129 |
+
|
| 130 |
+
self.goal_mode = goal_mode
|
| 131 |
+
if self.goal_mode is not None:
|
| 132 |
+
assert self.goal_mode in ["last"]
|
| 133 |
+
if not self.load_next_obs:
|
| 134 |
+
assert self.goal_mode != "last" # we use last next_obs as goal
|
| 135 |
+
|
| 136 |
+
self.pad_seq_length = pad_seq_length
|
| 137 |
+
self.pad_frame_stack = pad_frame_stack
|
| 138 |
+
self.get_pad_mask = get_pad_mask
|
| 139 |
+
|
| 140 |
+
self.load_demo_info(filter_by_attribute=self.filter_by_attribute)
|
| 141 |
+
|
| 142 |
+
# maybe prepare for observation normalization
|
| 143 |
+
self.obs_normalization_stats = None
|
| 144 |
+
if self.hdf5_normalize_obs:
|
| 145 |
+
self.obs_normalization_stats = self.normalize_obs()
|
| 146 |
+
|
| 147 |
+
# prepare for action normalization
|
| 148 |
+
self.action_normalization_stats = None
|
| 149 |
+
|
| 150 |
+
# maybe store dataset in memory for fast access
|
| 151 |
+
if self.hdf5_cache_mode in ["all", "low_dim"]:
|
| 152 |
+
obs_keys_in_memory = self.obs_keys
|
| 153 |
+
if self.hdf5_cache_mode == "low_dim":
|
| 154 |
+
# only store low-dim observations
|
| 155 |
+
obs_keys_in_memory = []
|
| 156 |
+
for k in self.obs_keys:
|
| 157 |
+
if ObsUtils.key_is_obs_modality(k, "low_dim"):
|
| 158 |
+
obs_keys_in_memory.append(k)
|
| 159 |
+
self.obs_keys_in_memory = obs_keys_in_memory
|
| 160 |
+
|
| 161 |
+
self.hdf5_cache = self.load_dataset_in_memory(
|
| 162 |
+
demo_list=self.demos,
|
| 163 |
+
hdf5_file=self.hdf5_file,
|
| 164 |
+
obs_keys=self.obs_keys_in_memory,
|
| 165 |
+
dataset_keys=self.dataset_keys,
|
| 166 |
+
load_next_obs=self.load_next_obs
|
| 167 |
+
)
|
| 168 |
+
|
| 169 |
+
if self.hdf5_cache_mode == "all":
|
| 170 |
+
# cache getitem calls for even more speedup. We don't do this for
|
| 171 |
+
# "low-dim" since image observations require calls to getitem anyways.
|
| 172 |
+
print("SequenceDataset: caching get_item calls...")
|
| 173 |
+
self.getitem_cache = [self.get_item(i) for i in LogUtils.custom_tqdm(range(len(self)))]
|
| 174 |
+
|
| 175 |
+
# don't need the previous cache anymore
|
| 176 |
+
del self.hdf5_cache
|
| 177 |
+
self.hdf5_cache = None
|
| 178 |
+
else:
|
| 179 |
+
self.hdf5_cache = None
|
| 180 |
+
|
| 181 |
+
if shuffled_obs_key_groups is None:
|
| 182 |
+
self.shuffled_obs_key_groups = list()
|
| 183 |
+
else:
|
| 184 |
+
self.shuffled_obs_key_groups = shuffled_obs_key_groups
|
| 185 |
+
|
| 186 |
+
self.close_and_delete_hdf5_handle()
|
| 187 |
+
|
| 188 |
+
def load_demo_info(self, filter_by_attribute=None, demos=None):
|
| 189 |
+
"""
|
| 190 |
+
Args:
|
| 191 |
+
filter_by_attribute (str): if provided, use the provided filter key
|
| 192 |
+
to select a subset of demonstration trajectories to load
|
| 193 |
+
|
| 194 |
+
demos (list): list of demonstration keys to load from the hdf5 file. If
|
| 195 |
+
omitted, all demos in the file (or under the @filter_by_attribute
|
| 196 |
+
filter key) are used.
|
| 197 |
+
"""
|
| 198 |
+
# filter demo trajectory by mask
|
| 199 |
+
if demos is not None:
|
| 200 |
+
self.demos = demos
|
| 201 |
+
elif filter_by_attribute is not None:
|
| 202 |
+
self.demos = [elem.decode("utf-8") for elem in np.array(self.hdf5_file["mask/{}".format(filter_by_attribute)][:])]
|
| 203 |
+
else:
|
| 204 |
+
self.demos = list(self.hdf5_file["data"].keys())
|
| 205 |
+
|
| 206 |
+
# sort demo keys
|
| 207 |
+
inds = np.argsort([int(elem[5:]) for elem in self.demos])
|
| 208 |
+
self.demos = [self.demos[i] for i in inds]
|
| 209 |
+
|
| 210 |
+
self.n_demos = len(self.demos)
|
| 211 |
+
|
| 212 |
+
# keep internal index maps to know which transitions belong to which demos
|
| 213 |
+
self._index_to_demo_id = dict() # maps every index to a demo id
|
| 214 |
+
self._demo_id_to_start_indices = dict() # gives start index per demo id
|
| 215 |
+
self._demo_id_to_demo_length = dict()
|
| 216 |
+
|
| 217 |
+
# determine index mapping
|
| 218 |
+
self.total_num_sequences = 0
|
| 219 |
+
for ep in self.demos:
|
| 220 |
+
demo_length = self.hdf5_file["data/{}".format(ep)].attrs["num_samples"]
|
| 221 |
+
self._demo_id_to_start_indices[ep] = self.total_num_sequences
|
| 222 |
+
self._demo_id_to_demo_length[ep] = demo_length
|
| 223 |
+
|
| 224 |
+
num_sequences = demo_length
|
| 225 |
+
# determine actual number of sequences taking into account whether to pad for frame_stack and seq_length
|
| 226 |
+
if not self.pad_frame_stack:
|
| 227 |
+
num_sequences -= (self.n_frame_stack - 1)
|
| 228 |
+
if not self.pad_seq_length:
|
| 229 |
+
num_sequences -= (self.seq_length - 1)
|
| 230 |
+
|
| 231 |
+
if self.pad_seq_length:
|
| 232 |
+
assert demo_length >= 1 # sequence needs to have at least one sample
|
| 233 |
+
num_sequences = max(num_sequences, 1)
|
| 234 |
+
else:
|
| 235 |
+
assert num_sequences >= 1 # assume demo_length >= (self.n_frame_stack - 1 + self.seq_length)
|
| 236 |
+
|
| 237 |
+
for _ in range(num_sequences):
|
| 238 |
+
self._index_to_demo_id[self.total_num_sequences] = ep
|
| 239 |
+
self.total_num_sequences += 1
|
| 240 |
+
|
| 241 |
+
@property
|
| 242 |
+
def hdf5_file(self):
|
| 243 |
+
"""
|
| 244 |
+
This property allows for a lazy hdf5 file open.
|
| 245 |
+
"""
|
| 246 |
+
if self._hdf5_file is None:
|
| 247 |
+
self._hdf5_file = h5py.File(self.hdf5_path, 'r', swmr=self.hdf5_use_swmr, libver='latest')
|
| 248 |
+
return self._hdf5_file
|
| 249 |
+
|
| 250 |
+
def close_and_delete_hdf5_handle(self):
|
| 251 |
+
"""
|
| 252 |
+
Maybe close the file handle.
|
| 253 |
+
"""
|
| 254 |
+
if self._hdf5_file is not None:
|
| 255 |
+
self._hdf5_file.close()
|
| 256 |
+
self._hdf5_file = None
|
| 257 |
+
|
| 258 |
+
@contextmanager
|
| 259 |
+
def hdf5_file_opened(self):
|
| 260 |
+
"""
|
| 261 |
+
Convenient context manager to open the file on entering the scope
|
| 262 |
+
and then close it on leaving.
|
| 263 |
+
"""
|
| 264 |
+
should_close = self._hdf5_file is None
|
| 265 |
+
yield self.hdf5_file
|
| 266 |
+
if should_close:
|
| 267 |
+
self.close_and_delete_hdf5_handle()
|
| 268 |
+
|
| 269 |
+
def __del__(self):
|
| 270 |
+
self.close_and_delete_hdf5_handle()
|
| 271 |
+
|
| 272 |
+
def __repr__(self):
|
| 273 |
+
"""
|
| 274 |
+
Pretty print the class and important attributes on a call to `print`.
|
| 275 |
+
"""
|
| 276 |
+
msg = str(self.__class__.__name__)
|
| 277 |
+
msg += " (\n\tpath={}\n\tobs_keys={}\n\tseq_length={}\n\tfilter_key={}\n\tframe_stack={}\n"
|
| 278 |
+
msg += "\tpad_seq_length={}\n\tpad_frame_stack={}\n\tgoal_mode={}\n"
|
| 279 |
+
msg += "\tcache_mode={}\n"
|
| 280 |
+
msg += "\tnum_demos={}\n\tnum_sequences={}\n)"
|
| 281 |
+
filter_key_str = self.filter_by_attribute if self.filter_by_attribute is not None else "none"
|
| 282 |
+
goal_mode_str = self.goal_mode if self.goal_mode is not None else "none"
|
| 283 |
+
cache_mode_str = self.hdf5_cache_mode if self.hdf5_cache_mode is not None else "none"
|
| 284 |
+
msg = msg.format(self.hdf5_path, self.obs_keys, self.seq_length, filter_key_str, self.n_frame_stack,
|
| 285 |
+
self.pad_seq_length, self.pad_frame_stack, goal_mode_str, cache_mode_str,
|
| 286 |
+
self.n_demos, self.total_num_sequences)
|
| 287 |
+
return msg
|
| 288 |
+
|
| 289 |
+
def __len__(self):
|
| 290 |
+
"""
|
| 291 |
+
Ensure that the torch dataloader will do a complete pass through all sequences in
|
| 292 |
+
the dataset before starting a new iteration.
|
| 293 |
+
"""
|
| 294 |
+
return self.total_num_sequences
|
| 295 |
+
|
| 296 |
+
def load_dataset_in_memory(self, demo_list, hdf5_file, obs_keys, dataset_keys, load_next_obs):
|
| 297 |
+
"""
|
| 298 |
+
Loads the hdf5 dataset into memory, preserving the structure of the file. Note that this
|
| 299 |
+
differs from `self.getitem_cache`, which, if active, actually caches the outputs of the
|
| 300 |
+
`getitem` operation.
|
| 301 |
+
|
| 302 |
+
Args:
|
| 303 |
+
demo_list (list): list of demo keys, e.g., 'demo_0'
|
| 304 |
+
hdf5_file (h5py.File): file handle to the hdf5 dataset.
|
| 305 |
+
obs_keys (list, tuple): observation keys to fetch, e.g., 'images'
|
| 306 |
+
dataset_keys (list, tuple): dataset keys to fetch, e.g., 'actions'
|
| 307 |
+
load_next_obs (bool): whether to load next_obs from the dataset
|
| 308 |
+
|
| 309 |
+
Returns:
|
| 310 |
+
all_data (dict): dictionary of loaded data.
|
| 311 |
+
"""
|
| 312 |
+
all_data = dict()
|
| 313 |
+
print("SequenceDataset: loading dataset into memory...")
|
| 314 |
+
for ep in LogUtils.custom_tqdm(demo_list):
|
| 315 |
+
all_data[ep] = {}
|
| 316 |
+
all_data[ep]["attrs"] = {}
|
| 317 |
+
all_data[ep]["attrs"]["num_samples"] = hdf5_file["data/{}".format(ep)].attrs["num_samples"]
|
| 318 |
+
# get obs
|
| 319 |
+
all_data[ep]["obs"] = {k: hdf5_file["data/{}/obs/{}".format(ep, k)][()] for k in obs_keys}
|
| 320 |
+
if load_next_obs:
|
| 321 |
+
all_data[ep]["next_obs"] = {k: hdf5_file["data/{}/next_obs/{}".format(ep, k)][()] for k in obs_keys}
|
| 322 |
+
# get other dataset keys
|
| 323 |
+
for k in dataset_keys:
|
| 324 |
+
if k in hdf5_file["data/{}".format(ep)]:
|
| 325 |
+
all_data[ep][k] = hdf5_file["data/{}/{}".format(ep, k)][()].astype('float32')
|
| 326 |
+
else:
|
| 327 |
+
all_data[ep][k] = np.zeros((all_data[ep]["attrs"]["num_samples"], 1), dtype=np.float32)
|
| 328 |
+
|
| 329 |
+
if "model_file" in hdf5_file["data/{}".format(ep)].attrs:
|
| 330 |
+
all_data[ep]["attrs"]["model_file"] = hdf5_file["data/{}".format(ep)].attrs["model_file"]
|
| 331 |
+
|
| 332 |
+
return all_data
|
| 333 |
+
|
| 334 |
+
def normalize_obs(self):
|
| 335 |
+
"""
|
| 336 |
+
Computes a dataset-wide mean and standard deviation for the observations
|
| 337 |
+
(per dimension and per obs key) and returns it.
|
| 338 |
+
"""
|
| 339 |
+
|
| 340 |
+
# Run through all trajectories. For each one, compute minimal observation statistics, and then aggregate
|
| 341 |
+
# with the previous statistics.
|
| 342 |
+
ep = self.demos[0]
|
| 343 |
+
obs_traj = {k: self.hdf5_file["data/{}/obs/{}".format(ep, k)][()].astype('float32') for k in self.obs_keys}
|
| 344 |
+
obs_traj = ObsUtils.process_obs_dict(obs_traj)
|
| 345 |
+
merged_stats = _compute_traj_stats(obs_traj)
|
| 346 |
+
print("SequenceDataset: normalizing observations...")
|
| 347 |
+
for ep in LogUtils.custom_tqdm(self.demos[1:]):
|
| 348 |
+
obs_traj = {k: self.hdf5_file["data/{}/obs/{}".format(ep, k)][()].astype('float32') for k in self.obs_keys}
|
| 349 |
+
obs_traj = ObsUtils.process_obs_dict(obs_traj)
|
| 350 |
+
traj_stats = _compute_traj_stats(obs_traj)
|
| 351 |
+
merged_stats = _aggregate_traj_stats(merged_stats, traj_stats)
|
| 352 |
+
|
| 353 |
+
obs_normalization_stats = { k : {} for k in merged_stats }
|
| 354 |
+
for k in merged_stats:
|
| 355 |
+
# note we add a small tolerance of 1e-3 for std
|
| 356 |
+
obs_normalization_stats[k]["mean"] = merged_stats[k]["mean"]
|
| 357 |
+
obs_normalization_stats[k]["std"] = np.sqrt(merged_stats[k]["sqdiff"] / merged_stats[k]["n"]) + 1e-3
|
| 358 |
+
return obs_normalization_stats
|
| 359 |
+
|
| 360 |
+
def get_obs_normalization_stats(self):
|
| 361 |
+
"""
|
| 362 |
+
Returns dictionary of mean and std for each observation key if using
|
| 363 |
+
observation normalization, otherwise None.
|
| 364 |
+
|
| 365 |
+
Returns:
|
| 366 |
+
obs_normalization_stats (dict): a dictionary for observation
|
| 367 |
+
normalization. This maps observation keys to dicts
|
| 368 |
+
with a "mean" and "std" of shape (1, ...) where ... is the default
|
| 369 |
+
shape for the observation.
|
| 370 |
+
"""
|
| 371 |
+
assert self.hdf5_normalize_obs, "not using observation normalization!"
|
| 372 |
+
return deepcopy(self.obs_normalization_stats)
|
| 373 |
+
|
| 374 |
+
def get_action_traj(self, ep):
|
| 375 |
+
action_traj = dict()
|
| 376 |
+
for key in self.action_keys:
|
| 377 |
+
action_traj[key] = self.hdf5_file["data/{}/{}".format(ep, key)][()].astype('float32')
|
| 378 |
+
return action_traj
|
| 379 |
+
|
| 380 |
+
def get_action_stats(self):
|
| 381 |
+
ep = self.demos[0]
|
| 382 |
+
action_traj = self.get_action_traj(ep)
|
| 383 |
+
action_stats = _compute_traj_stats(action_traj)
|
| 384 |
+
print("SequenceDataset: normalizing actions...")
|
| 385 |
+
for ep in LogUtils.custom_tqdm(self.demos[1:]):
|
| 386 |
+
action_traj = self.get_action_traj(ep)
|
| 387 |
+
traj_stats = _compute_traj_stats(action_traj)
|
| 388 |
+
action_stats = _aggregate_traj_stats(action_stats, traj_stats)
|
| 389 |
+
return action_stats
|
| 390 |
+
|
| 391 |
+
def set_action_normalization_stats(self, action_normalization_stats):
|
| 392 |
+
self.action_normalization_stats = action_normalization_stats
|
| 393 |
+
|
| 394 |
+
def get_action_normalization_stats(self):
|
| 395 |
+
"""
|
| 396 |
+
Computes a dataset-wide min, max, mean and standard deviation for the actions
|
| 397 |
+
(per dimension) and returns it.
|
| 398 |
+
"""
|
| 399 |
+
|
| 400 |
+
# Run through all trajectories. For each one, compute minimal observation statistics, and then aggregate
|
| 401 |
+
# with the previous statistics.
|
| 402 |
+
if self.action_normalization_stats is None:
|
| 403 |
+
action_stats = self.get_action_stats()
|
| 404 |
+
self.action_normalization_stats = action_stats_to_normalization_stats(
|
| 405 |
+
action_stats, self.action_config)
|
| 406 |
+
return self.action_normalization_stats
|
| 407 |
+
|
| 408 |
+
def get_dataset_for_ep(self, ep, key):
|
| 409 |
+
"""
|
| 410 |
+
Helper utility to get a dataset for a specific demonstration.
|
| 411 |
+
Takes into account whether the dataset has been loaded into memory.
|
| 412 |
+
"""
|
| 413 |
+
|
| 414 |
+
# check if this key should be in memory
|
| 415 |
+
key_should_be_in_memory = (self.hdf5_cache_mode in ["all", "low_dim"])
|
| 416 |
+
if key_should_be_in_memory:
|
| 417 |
+
# if key is an observation, it may not be in memory
|
| 418 |
+
if '/' in key:
|
| 419 |
+
key1, key2 = key.split('/')
|
| 420 |
+
assert(key1 in ['obs', 'next_obs', 'action_dict'])
|
| 421 |
+
if key2 not in self.obs_keys_in_memory:
|
| 422 |
+
key_should_be_in_memory = False
|
| 423 |
+
|
| 424 |
+
if key_should_be_in_memory:
|
| 425 |
+
# read cache
|
| 426 |
+
if '/' in key:
|
| 427 |
+
key1, key2 = key.split('/')
|
| 428 |
+
assert(key1 in ['obs', 'next_obs', 'action_dict'])
|
| 429 |
+
ret = self.hdf5_cache[ep][key1][key2]
|
| 430 |
+
else:
|
| 431 |
+
ret = self.hdf5_cache[ep][key]
|
| 432 |
+
else:
|
| 433 |
+
# read from file
|
| 434 |
+
hd5key = "data/{}/{}".format(ep, key)
|
| 435 |
+
ret = self.hdf5_file[hd5key]
|
| 436 |
+
return ret
|
| 437 |
+
|
| 438 |
+
def __getitem__(self, index):
|
| 439 |
+
"""
|
| 440 |
+
Fetch dataset sequence @index (inferred through internal index map), using the getitem_cache if available.
|
| 441 |
+
"""
|
| 442 |
+
if self.hdf5_cache_mode == "all":
|
| 443 |
+
output = self.getitem_cache[index]
|
| 444 |
+
else:
|
| 445 |
+
output = self.get_item(index)
|
| 446 |
+
|
| 447 |
+
for (g1, g2) in self.shuffled_obs_key_groups:
|
| 448 |
+
assert len(g1) == len(g2)
|
| 449 |
+
if random.random() > 0.5:
|
| 450 |
+
# shuffle the keys accordingly
|
| 451 |
+
for (o1, o2) in zip(g1, g2):
|
| 452 |
+
for otype in ["obs", "next_obs", "goal_obs"]:
|
| 453 |
+
if output.get(otype, None) is None:
|
| 454 |
+
continue
|
| 455 |
+
if o1 not in output[otype] or o2 not in output[otype]:
|
| 456 |
+
continue
|
| 457 |
+
# swap values
|
| 458 |
+
output[otype][o1], output[otype][o2] = output[otype][o2], output[otype][o1]
|
| 459 |
+
|
| 460 |
+
return output
|
| 461 |
+
|
| 462 |
+
def get_item(self, index):
|
| 463 |
+
"""
|
| 464 |
+
Main implementation of getitem when not using cache.
|
| 465 |
+
"""
|
| 466 |
+
|
| 467 |
+
demo_id = self._index_to_demo_id[index]
|
| 468 |
+
demo_start_index = self._demo_id_to_start_indices[demo_id]
|
| 469 |
+
demo_length = self._demo_id_to_demo_length[demo_id]
|
| 470 |
+
|
| 471 |
+
# start at offset index if not padding for frame stacking
|
| 472 |
+
demo_index_offset = 0 if self.pad_frame_stack else (self.n_frame_stack - 1)
|
| 473 |
+
index_in_demo = index - demo_start_index + demo_index_offset
|
| 474 |
+
|
| 475 |
+
# end at offset index if not padding for seq length
|
| 476 |
+
demo_length_offset = 0 if self.pad_seq_length else (self.seq_length - 1)
|
| 477 |
+
end_index_in_demo = demo_length - demo_length_offset
|
| 478 |
+
|
| 479 |
+
meta = self.get_dataset_sequence_from_demo(
|
| 480 |
+
demo_id,
|
| 481 |
+
index_in_demo=index_in_demo,
|
| 482 |
+
keys=self.dataset_keys,
|
| 483 |
+
num_frames_to_stack=self.n_frame_stack - 1, # note: need to decrement self.n_frame_stack by one
|
| 484 |
+
seq_length=self.seq_length
|
| 485 |
+
)
|
| 486 |
+
|
| 487 |
+
# determine goal index
|
| 488 |
+
goal_index = None
|
| 489 |
+
if self.goal_mode == "last":
|
| 490 |
+
goal_index = end_index_in_demo - 1
|
| 491 |
+
|
| 492 |
+
meta["obs"] = self.get_obs_sequence_from_demo(
|
| 493 |
+
demo_id,
|
| 494 |
+
index_in_demo=index_in_demo,
|
| 495 |
+
keys=self.obs_keys,
|
| 496 |
+
num_frames_to_stack=self.n_frame_stack - 1,
|
| 497 |
+
seq_length=self.seq_length,
|
| 498 |
+
prefix="obs"
|
| 499 |
+
)
|
| 500 |
+
|
| 501 |
+
if self.load_next_obs:
|
| 502 |
+
meta["next_obs"] = self.get_obs_sequence_from_demo(
|
| 503 |
+
demo_id,
|
| 504 |
+
index_in_demo=index_in_demo,
|
| 505 |
+
keys=self.obs_keys,
|
| 506 |
+
num_frames_to_stack=self.n_frame_stack - 1,
|
| 507 |
+
seq_length=self.seq_length,
|
| 508 |
+
prefix="next_obs"
|
| 509 |
+
)
|
| 510 |
+
|
| 511 |
+
if goal_index is not None:
|
| 512 |
+
goal = self.get_obs_sequence_from_demo(
|
| 513 |
+
demo_id,
|
| 514 |
+
index_in_demo=goal_index,
|
| 515 |
+
keys=self.obs_keys,
|
| 516 |
+
num_frames_to_stack=0,
|
| 517 |
+
seq_length=1,
|
| 518 |
+
prefix="next_obs",
|
| 519 |
+
)
|
| 520 |
+
meta["goal_obs"] = {k: goal[k][0] for k in goal} # remove sequence dimension for goal
|
| 521 |
+
|
| 522 |
+
# get action components
|
| 523 |
+
ac_dict = OrderedDict()
|
| 524 |
+
for k in self.action_keys:
|
| 525 |
+
ac = meta[k]
|
| 526 |
+
# expand action shape if needed
|
| 527 |
+
if len(ac.shape) == 1:
|
| 528 |
+
ac = ac.reshape(-1, 1)
|
| 529 |
+
ac_dict[k] = ac
|
| 530 |
+
|
| 531 |
+
# normalize actions
|
| 532 |
+
action_normalization_stats = self.get_action_normalization_stats()
|
| 533 |
+
ac_dict = ObsUtils.normalize_dict(ac_dict, normalization_stats=action_normalization_stats)
|
| 534 |
+
|
| 535 |
+
# concatenate all action components
|
| 536 |
+
meta["actions"] = AcUtils.action_dict_to_vector(ac_dict)
|
| 537 |
+
|
| 538 |
+
# also return the sampled index
|
| 539 |
+
meta["index"] = index
|
| 540 |
+
|
| 541 |
+
# language embedding
|
| 542 |
+
T = meta["actions"].shape[0]
|
| 543 |
+
meta["obs"]["lang_emb"] = np.tile(self._lang_emb, (T, 1))
|
| 544 |
+
|
| 545 |
+
return meta
|
| 546 |
+
|
| 547 |
+
def get_sequence_from_demo(self, demo_id, index_in_demo, keys, num_frames_to_stack=0, seq_length=1):
|
| 548 |
+
"""
|
| 549 |
+
Extract a (sub)sequence of data items from a demo given the @keys of the items.
|
| 550 |
+
|
| 551 |
+
Args:
|
| 552 |
+
demo_id (str): id of the demo, e.g., demo_0
|
| 553 |
+
index_in_demo (int): beginning index of the sequence wrt the demo
|
| 554 |
+
keys (tuple): list of keys to extract
|
| 555 |
+
num_frames_to_stack (int): numbers of frame to stack. Seq gets prepended with repeated items if out of range
|
| 556 |
+
seq_length (int): sequence length to extract. Seq gets post-pended with repeated items if out of range
|
| 557 |
+
|
| 558 |
+
Returns:
|
| 559 |
+
a dictionary of extracted items.
|
| 560 |
+
"""
|
| 561 |
+
assert num_frames_to_stack >= 0
|
| 562 |
+
assert seq_length >= 1
|
| 563 |
+
|
| 564 |
+
demo_length = self._demo_id_to_demo_length[demo_id]
|
| 565 |
+
assert index_in_demo < demo_length
|
| 566 |
+
|
| 567 |
+
# determine begin and end of sequence
|
| 568 |
+
seq_begin_index = max(0, index_in_demo - num_frames_to_stack)
|
| 569 |
+
seq_end_index = min(demo_length, index_in_demo + seq_length)
|
| 570 |
+
|
| 571 |
+
# determine sequence padding
|
| 572 |
+
seq_begin_pad = max(0, num_frames_to_stack - index_in_demo) # pad for frame stacking
|
| 573 |
+
seq_end_pad = max(0, index_in_demo + seq_length - demo_length) # pad for sequence length
|
| 574 |
+
|
| 575 |
+
# make sure we are not padding if specified.
|
| 576 |
+
if not self.pad_frame_stack:
|
| 577 |
+
assert seq_begin_pad == 0
|
| 578 |
+
if not self.pad_seq_length:
|
| 579 |
+
assert seq_end_pad == 0
|
| 580 |
+
|
| 581 |
+
# fetch observation from the dataset file
|
| 582 |
+
seq = dict()
|
| 583 |
+
for k in keys:
|
| 584 |
+
data = self.get_dataset_for_ep(demo_id, k)
|
| 585 |
+
seq[k] = data[seq_begin_index: seq_end_index]
|
| 586 |
+
|
| 587 |
+
seq = TensorUtils.pad_sequence(seq, padding=(seq_begin_pad, seq_end_pad), pad_same=True)
|
| 588 |
+
pad_mask = np.array([0] * seq_begin_pad + [1] * (seq_end_index - seq_begin_index) + [0] * seq_end_pad)
|
| 589 |
+
pad_mask = pad_mask[:, None].astype(bool)
|
| 590 |
+
|
| 591 |
+
return seq, pad_mask
|
| 592 |
+
|
| 593 |
+
def get_obs_sequence_from_demo(self, demo_id, index_in_demo, keys, num_frames_to_stack=0, seq_length=1, prefix="obs"):
|
| 594 |
+
"""
|
| 595 |
+
Extract a (sub)sequence of observation items from a demo given the @keys of the items.
|
| 596 |
+
|
| 597 |
+
Args:
|
| 598 |
+
demo_id (str): id of the demo, e.g., demo_0
|
| 599 |
+
index_in_demo (int): beginning index of the sequence wrt the demo
|
| 600 |
+
keys (tuple): list of keys to extract
|
| 601 |
+
num_frames_to_stack (int): numbers of frame to stack. Seq gets prepended with repeated items if out of range
|
| 602 |
+
seq_length (int): sequence length to extract. Seq gets post-pended with repeated items if out of range
|
| 603 |
+
prefix (str): one of "obs", "next_obs"
|
| 604 |
+
|
| 605 |
+
Returns:
|
| 606 |
+
a dictionary of extracted items.
|
| 607 |
+
"""
|
| 608 |
+
obs, pad_mask = self.get_sequence_from_demo(
|
| 609 |
+
demo_id,
|
| 610 |
+
index_in_demo=index_in_demo,
|
| 611 |
+
keys=tuple('{}/{}'.format(prefix, k) for k in keys),
|
| 612 |
+
num_frames_to_stack=num_frames_to_stack,
|
| 613 |
+
seq_length=seq_length,
|
| 614 |
+
)
|
| 615 |
+
obs = {'/'.join(k.split('/')[1:]): obs[k] for k in obs} # strip the prefix
|
| 616 |
+
if self.get_pad_mask:
|
| 617 |
+
obs["pad_mask"] = pad_mask
|
| 618 |
+
|
| 619 |
+
return obs
|
| 620 |
+
|
| 621 |
+
def get_dataset_sequence_from_demo(self, demo_id, index_in_demo, keys, num_frames_to_stack=0, seq_length=1):
|
| 622 |
+
"""
|
| 623 |
+
Extract a (sub)sequence of dataset items from a demo given the @keys of the items (e.g., states, actions).
|
| 624 |
+
|
| 625 |
+
Args:
|
| 626 |
+
demo_id (str): id of the demo, e.g., demo_0
|
| 627 |
+
index_in_demo (int): beginning index of the sequence wrt the demo
|
| 628 |
+
keys (tuple): list of keys to extract
|
| 629 |
+
num_frames_to_stack (int): numbers of frame to stack. Seq gets prepended with repeated items if out of range
|
| 630 |
+
seq_length (int): sequence length to extract. Seq gets post-pended with repeated items if out of range
|
| 631 |
+
|
| 632 |
+
Returns:
|
| 633 |
+
a dictionary of extracted items.
|
| 634 |
+
"""
|
| 635 |
+
data, pad_mask = self.get_sequence_from_demo(
|
| 636 |
+
demo_id,
|
| 637 |
+
index_in_demo=index_in_demo,
|
| 638 |
+
keys=keys,
|
| 639 |
+
num_frames_to_stack=num_frames_to_stack,
|
| 640 |
+
seq_length=seq_length,
|
| 641 |
+
)
|
| 642 |
+
if self.get_pad_mask:
|
| 643 |
+
data["pad_mask"] = pad_mask
|
| 644 |
+
return data
|
| 645 |
+
|
| 646 |
+
def get_trajectory_at_index(self, index):
|
| 647 |
+
"""
|
| 648 |
+
Method provided as a utility to get an entire trajectory, given
|
| 649 |
+
the corresponding @index.
|
| 650 |
+
"""
|
| 651 |
+
demo_id = self.demos[index]
|
| 652 |
+
demo_length = self._demo_id_to_demo_length[demo_id]
|
| 653 |
+
|
| 654 |
+
meta = self.get_dataset_sequence_from_demo(
|
| 655 |
+
demo_id,
|
| 656 |
+
index_in_demo=0,
|
| 657 |
+
keys=self.dataset_keys,
|
| 658 |
+
num_frames_to_stack=self.n_frame_stack - 1, # note: need to decrement self.n_frame_stack by one
|
| 659 |
+
seq_length=demo_length
|
| 660 |
+
)
|
| 661 |
+
meta["obs"] = self.get_obs_sequence_from_demo(
|
| 662 |
+
demo_id,
|
| 663 |
+
index_in_demo=0,
|
| 664 |
+
keys=self.obs_keys,
|
| 665 |
+
seq_length=demo_length
|
| 666 |
+
)
|
| 667 |
+
if self.load_next_obs:
|
| 668 |
+
meta["next_obs"] = self.get_obs_sequence_from_demo(
|
| 669 |
+
demo_id,
|
| 670 |
+
index_in_demo=0,
|
| 671 |
+
keys=self.obs_keys,
|
| 672 |
+
seq_length=demo_length,
|
| 673 |
+
prefix="next_obs"
|
| 674 |
+
)
|
| 675 |
+
|
| 676 |
+
meta["ep"] = demo_id
|
| 677 |
+
return meta
|
| 678 |
+
|
| 679 |
+
def get_dataset_sampler(self):
|
| 680 |
+
"""
|
| 681 |
+
Return instance of torch.utils.data.Sampler or None. Allows
|
| 682 |
+
for dataset to define custom sampling logic, such as
|
| 683 |
+
re-weighting the probability of samples being drawn.
|
| 684 |
+
See the `train` function in scripts/train.py, and torch
|
| 685 |
+
`DataLoader` documentation, for more info.
|
| 686 |
+
"""
|
| 687 |
+
return None
|
| 688 |
+
|
| 689 |
+
|
| 690 |
+
class R2D2Dataset(SequenceDataset):
|
| 691 |
+
def get_action_traj(self, ep):
|
| 692 |
+
action_traj = dict()
|
| 693 |
+
for key in self.action_keys:
|
| 694 |
+
action_traj[key] = self.hdf5_file[key][()].astype('float32')
|
| 695 |
+
if len(action_traj[key].shape) == 1:
|
| 696 |
+
action_traj[key] = np.reshape(action_traj[key], (-1, 1))
|
| 697 |
+
|
| 698 |
+
return action_traj
|
| 699 |
+
|
| 700 |
+
def load_demo_info(self, filter_by_attribute=None, demos=None, n_demos=None):
|
| 701 |
+
"""
|
| 702 |
+
Args:
|
| 703 |
+
filter_by_attribute (str): if provided, use the provided filter key
|
| 704 |
+
to select a subset of demonstration trajectories to load
|
| 705 |
+
|
| 706 |
+
demos (list): list of demonstration keys to load from the hdf5 file. If
|
| 707 |
+
omitted, all demos in the file (or under the @filter_by_attribute
|
| 708 |
+
filter key) are used.
|
| 709 |
+
"""
|
| 710 |
+
|
| 711 |
+
self.demos = ["demo"]
|
| 712 |
+
|
| 713 |
+
self.n_demos = len(self.demos)
|
| 714 |
+
|
| 715 |
+
# keep internal index maps to know which transitions belong to which demos
|
| 716 |
+
self._index_to_demo_id = dict() # maps every index to a demo id
|
| 717 |
+
self._demo_id_to_start_indices = dict() # gives start index per demo id
|
| 718 |
+
self._demo_id_to_demo_length = dict()
|
| 719 |
+
|
| 720 |
+
# segment time stamps
|
| 721 |
+
self._demo_id_to_segments = dict()
|
| 722 |
+
|
| 723 |
+
ep = self.demos[0]
|
| 724 |
+
|
| 725 |
+
# determine index mapping
|
| 726 |
+
self.total_num_sequences = 0
|
| 727 |
+
demo_length = self.hdf5_file["action/cartesian_velocity"].shape[0]
|
| 728 |
+
self._demo_id_to_start_indices[ep] = self.total_num_sequences
|
| 729 |
+
self._demo_id_to_demo_length[ep] = demo_length
|
| 730 |
+
|
| 731 |
+
# seperate demo into segments for better alignment
|
| 732 |
+
gripper_actions = list(self.hdf5_file["action/gripper_position"])
|
| 733 |
+
gripper_closed = [1 if x > 0 else 0 for x in gripper_actions]
|
| 734 |
+
|
| 735 |
+
try:
|
| 736 |
+
# find when the gripper fist opens/closes
|
| 737 |
+
gripper_close = gripper_closed.index(1)
|
| 738 |
+
gripper_open = gripper_close + gripper_closed[gripper_close:].index(0)
|
| 739 |
+
except ValueError:
|
| 740 |
+
# special case for (invalid) trajectories
|
| 741 |
+
gripper_close, gripper_open = int(demo_length / 3), int(demo_length / 3 * 2)
|
| 742 |
+
print("No gripper action:", gripper_actions)
|
| 743 |
+
self._demo_id_to_segments[ep] = [0, gripper_close, gripper_open, demo_length - 1]
|
| 744 |
+
|
| 745 |
+
num_sequences = demo_length
|
| 746 |
+
# determine actual number of sequences taking into account whether to pad for frame_stack and seq_length
|
| 747 |
+
if not self.pad_frame_stack:
|
| 748 |
+
num_sequences -= (self.n_frame_stack - 1)
|
| 749 |
+
if not self.pad_seq_length:
|
| 750 |
+
num_sequences -= (self.seq_length - 1)
|
| 751 |
+
|
| 752 |
+
if self.pad_seq_length:
|
| 753 |
+
assert demo_length >= 1 # sequence needs to have at least one sample
|
| 754 |
+
num_sequences = max(num_sequences, 1)
|
| 755 |
+
else:
|
| 756 |
+
assert num_sequences >= 1 # assume demo_length >= (self.n_frame_stack - 1 + self.seq_length)
|
| 757 |
+
|
| 758 |
+
for _ in range(num_sequences):
|
| 759 |
+
self._index_to_demo_id[self.total_num_sequences] = ep
|
| 760 |
+
self.total_num_sequences += 1
|
| 761 |
+
|
| 762 |
+
def load_dataset_in_memory(self, demo_list, hdf5_file, obs_keys, dataset_keys, load_next_obs):
|
| 763 |
+
"""
|
| 764 |
+
Loads the hdf5 dataset into memory, preserving the structure of the file. Note that this
|
| 765 |
+
differs from `self.getitem_cache`, which, if active, actually caches the outputs of the
|
| 766 |
+
`getitem` operation.
|
| 767 |
+
|
| 768 |
+
Args:
|
| 769 |
+
demo_list (list): list of demo keys, e.g., 'demo_0'
|
| 770 |
+
hdf5_file (h5py.File): file handle to the hdf5 dataset.
|
| 771 |
+
obs_keys (list, tuple): observation keys to fetch, e.g., 'images'
|
| 772 |
+
dataset_keys (list, tuple): dataset keys to fetch, e.g., 'actions'
|
| 773 |
+
load_next_obs (bool): whether to load next_obs from the dataset
|
| 774 |
+
|
| 775 |
+
Returns:
|
| 776 |
+
all_data (dict): dictionary of loaded data.
|
| 777 |
+
"""
|
| 778 |
+
all_data = dict()
|
| 779 |
+
print("SequenceDataset: loading dataset into memory...")
|
| 780 |
+
|
| 781 |
+
for ep in LogUtils.custom_tqdm(demo_list):
|
| 782 |
+
all_data[ep] = {}
|
| 783 |
+
all_data[ep]["attrs"] = {}
|
| 784 |
+
all_data[ep]["attrs"]["num_samples"] = hdf5_file["action/cartesian_velocity"].shape[0] # hack to get traj len
|
| 785 |
+
# get obs
|
| 786 |
+
all_data[ep]["obs"] = {k: hdf5_file["observation/{}".format(k)][()].astype('float32') for k in obs_keys}
|
| 787 |
+
if load_next_obs:
|
| 788 |
+
raise NotImplementedError
|
| 789 |
+
# get other dataset keys
|
| 790 |
+
for k in dataset_keys:
|
| 791 |
+
if k in hdf5_file.keys():
|
| 792 |
+
all_data[ep][k] = hdf5_file["{}".format(k)][()].astype('float32')
|
| 793 |
+
else:
|
| 794 |
+
raise NotImplementedError
|
| 795 |
+
|
| 796 |
+
return all_data
|
| 797 |
+
|
| 798 |
+
def get_dataset_for_ep(self, ep, key, try_to_use_cache=True):
|
| 799 |
+
"""
|
| 800 |
+
Helper utility to get a dataset for a specific demonstration.
|
| 801 |
+
Takes into account whether the dataset has been loaded into memory.
|
| 802 |
+
"""
|
| 803 |
+
|
| 804 |
+
# check if this key should be in memory
|
| 805 |
+
key_should_be_in_memory = try_to_use_cache and (self.hdf5_cache_mode in ["all", "low_dim"])
|
| 806 |
+
if key_should_be_in_memory:
|
| 807 |
+
# if key is an observation, it may not be in memory
|
| 808 |
+
if '/' in key:
|
| 809 |
+
key_splits = key.split('/')
|
| 810 |
+
key1 = key_splits[0]
|
| 811 |
+
key2 = "/".join(key_splits[1:])
|
| 812 |
+
if key1 == "observation" and key2 not in self.obs_keys_in_memory:
|
| 813 |
+
key_should_be_in_memory = False
|
| 814 |
+
|
| 815 |
+
if key_should_be_in_memory:
|
| 816 |
+
# read cache
|
| 817 |
+
if '/' in key:
|
| 818 |
+
key_splits = key.split('/')
|
| 819 |
+
key1 = key_splits[0]
|
| 820 |
+
key2 = "/".join(key_splits[1:])
|
| 821 |
+
if key1 == "observation":
|
| 822 |
+
ret = self.hdf5_cache[ep]["obs"][key2]
|
| 823 |
+
else:
|
| 824 |
+
ret = self.hdf5_cache[ep][key]
|
| 825 |
+
else:
|
| 826 |
+
ret = self.hdf5_cache[ep][key]
|
| 827 |
+
else:
|
| 828 |
+
# read from file
|
| 829 |
+
hd5key = "{}".format(key) #"data/{}/{}".format(ep, key)
|
| 830 |
+
ret = self.hdf5_file[hd5key]
|
| 831 |
+
return ret
|
| 832 |
+
|
| 833 |
+
|
| 834 |
+
def get_sequence_from_demo(self, demo_id, index_in_demo, keys, num_frames_to_stack=0, seq_length=1):
|
| 835 |
+
"""
|
| 836 |
+
Extract a (sub)sequence of data items from a demo given the @keys of the items.
|
| 837 |
+
|
| 838 |
+
Args:
|
| 839 |
+
demo_id (str): id of the demo, e.g., demo_0
|
| 840 |
+
index_in_demo (int): beginning index of the sequence wrt the demo
|
| 841 |
+
keys (tuple): list of keys to extract
|
| 842 |
+
num_frames_to_stack (int): numbers of frame to stack. Seq gets prepended with repeated items if out of range
|
| 843 |
+
seq_length (int): sequence length to extract. Seq gets post-pended with repeated items if out of range
|
| 844 |
+
|
| 845 |
+
Returns:
|
| 846 |
+
a dictionary of extracted items.
|
| 847 |
+
"""
|
| 848 |
+
assert num_frames_to_stack >= 0
|
| 849 |
+
assert seq_length >= 1
|
| 850 |
+
|
| 851 |
+
demo_length = self._demo_id_to_demo_length[demo_id]
|
| 852 |
+
assert index_in_demo < demo_length
|
| 853 |
+
|
| 854 |
+
# determine begin and end of sequence
|
| 855 |
+
seq_begin_index = max(0, index_in_demo - num_frames_to_stack)
|
| 856 |
+
seq_end_index = min(demo_length, index_in_demo + seq_length)
|
| 857 |
+
|
| 858 |
+
# determine sequence padding
|
| 859 |
+
seq_begin_pad = max(0, num_frames_to_stack - index_in_demo) # pad for frame stacking
|
| 860 |
+
seq_end_pad = max(0, index_in_demo + seq_length - demo_length) # pad for sequence length
|
| 861 |
+
|
| 862 |
+
# make sure we are not padding if specified.
|
| 863 |
+
if not self.pad_frame_stack:
|
| 864 |
+
assert seq_begin_pad == 0
|
| 865 |
+
if not self.pad_seq_length:
|
| 866 |
+
assert seq_end_pad == 0
|
| 867 |
+
|
| 868 |
+
# fetch observation from the dataset file
|
| 869 |
+
seq = dict()
|
| 870 |
+
for k in keys:
|
| 871 |
+
data = self.get_dataset_for_ep(demo_id, k)
|
| 872 |
+
seq[k] = data[seq_begin_index: seq_end_index].astype("float32")
|
| 873 |
+
|
| 874 |
+
seq = TensorUtils.pad_sequence(seq, padding=(seq_begin_pad, seq_end_pad), pad_same=True)
|
| 875 |
+
pad_mask = np.array([0] * seq_begin_pad + [1] * (seq_end_index - seq_begin_index) + [0] * seq_end_pad)
|
| 876 |
+
pad_mask = pad_mask[:, None].astype(np.bool_)
|
| 877 |
+
|
| 878 |
+
return seq, pad_mask
|
| 879 |
+
|
| 880 |
+
|
| 881 |
+
def get_item(self, index):
|
| 882 |
+
"""
|
| 883 |
+
Main implementation of getitem when not using cache.
|
| 884 |
+
"""
|
| 885 |
+
|
| 886 |
+
demo_id = self._index_to_demo_id[index]
|
| 887 |
+
demo_start_index = self._demo_id_to_start_indices[demo_id]
|
| 888 |
+
demo_length = self._demo_id_to_demo_length[demo_id]
|
| 889 |
+
|
| 890 |
+
# start at offset index if not padding for frame stacking
|
| 891 |
+
demo_index_offset = 0 if self.pad_frame_stack else (self.n_frame_stack - 1)
|
| 892 |
+
index_in_demo = index - demo_start_index + demo_index_offset
|
| 893 |
+
|
| 894 |
+
# end at offset index if not padding for seq length
|
| 895 |
+
demo_length_offset = 0 if self.pad_seq_length else (self.seq_length - 1)
|
| 896 |
+
end_index_in_demo = demo_length - demo_length_offset
|
| 897 |
+
|
| 898 |
+
meta = self.get_dataset_sequence_from_demo(
|
| 899 |
+
demo_id,
|
| 900 |
+
index_in_demo=index_in_demo,
|
| 901 |
+
keys=self.dataset_keys,
|
| 902 |
+
num_frames_to_stack=self.n_frame_stack - 1,
|
| 903 |
+
seq_length=self.seq_length,
|
| 904 |
+
)
|
| 905 |
+
|
| 906 |
+
# determine goal index
|
| 907 |
+
goal_index = None
|
| 908 |
+
if self.goal_mode == "last":
|
| 909 |
+
goal_index = end_index_in_demo - 1
|
| 910 |
+
|
| 911 |
+
meta["obs"] = self.get_obs_sequence_from_demo(
|
| 912 |
+
demo_id,
|
| 913 |
+
index_in_demo=index_in_demo,
|
| 914 |
+
keys=self.obs_keys,
|
| 915 |
+
num_frames_to_stack=self.n_frame_stack - 1,
|
| 916 |
+
seq_length=self.seq_length,
|
| 917 |
+
prefix="observation"
|
| 918 |
+
)
|
| 919 |
+
|
| 920 |
+
if self.load_next_obs:
|
| 921 |
+
meta["next_obs"] = self.get_obs_sequence_from_demo(
|
| 922 |
+
demo_id,
|
| 923 |
+
index_in_demo=index_in_demo,
|
| 924 |
+
keys=self.obs_keys,
|
| 925 |
+
num_frames_to_stack=self.n_frame_stack - 1,
|
| 926 |
+
seq_length=self.seq_length,
|
| 927 |
+
prefix="next_obs"
|
| 928 |
+
)
|
| 929 |
+
|
| 930 |
+
if goal_index is not None:
|
| 931 |
+
goal = self.get_obs_sequence_from_demo(
|
| 932 |
+
demo_id,
|
| 933 |
+
index_in_demo=goal_index,
|
| 934 |
+
keys=self.obs_keys,
|
| 935 |
+
num_frames_to_stack=0,
|
| 936 |
+
seq_length=1,
|
| 937 |
+
prefix="next_obs",
|
| 938 |
+
)
|
| 939 |
+
meta["goal_obs"] = {k: goal[k][0] for k in goal} # remove sequence dimension for goal
|
| 940 |
+
|
| 941 |
+
# get action components
|
| 942 |
+
ac_dict = OrderedDict()
|
| 943 |
+
for k in self.action_keys:
|
| 944 |
+
ac = meta[k]
|
| 945 |
+
# expand action shape if needed
|
| 946 |
+
if len(ac.shape) == 1:
|
| 947 |
+
ac = ac.reshape(-1, 1)
|
| 948 |
+
ac_dict[k] = ac
|
| 949 |
+
|
| 950 |
+
# normalize actions
|
| 951 |
+
action_normalization_stats = self.get_action_normalization_stats()
|
| 952 |
+
ac_dict = ObsUtils.normalize_dict(ac_dict, normalization_stats=action_normalization_stats)
|
| 953 |
+
|
| 954 |
+
# concatenate all action components
|
| 955 |
+
meta["actions"] = AcUtils.action_dict_to_vector(ac_dict)
|
| 956 |
+
|
| 957 |
+
# keys to reshape
|
| 958 |
+
for k in meta["obs"]:
|
| 959 |
+
if len(meta["obs"][k].shape) == 1:
|
| 960 |
+
meta["obs"][k] = np.expand_dims(meta["obs"][k], axis=1)
|
| 961 |
+
|
| 962 |
+
# also return the sampled index
|
| 963 |
+
meta["index"] = index
|
| 964 |
+
|
| 965 |
+
# language embedding
|
| 966 |
+
T = meta["actions"].shape[0]
|
| 967 |
+
meta["obs"]["lang_emb"] = np.tile(self._lang_emb, (T, 1))
|
| 968 |
+
|
| 969 |
+
return meta
|
| 970 |
+
|
| 971 |
+
|
| 972 |
+
class MetaDataset(torch.utils.data.Dataset):
|
| 973 |
+
def __init__(
|
| 974 |
+
self,
|
| 975 |
+
datasets,
|
| 976 |
+
ds_weights,
|
| 977 |
+
normalize_weights_by_ds_size=False,
|
| 978 |
+
):
|
| 979 |
+
super(MetaDataset, self).__init__()
|
| 980 |
+
self.datasets = datasets
|
| 981 |
+
ds_lens = np.array([len(ds) for ds in self.datasets])
|
| 982 |
+
if normalize_weights_by_ds_size:
|
| 983 |
+
self.ds_weights = np.array(ds_weights) / ds_lens
|
| 984 |
+
else:
|
| 985 |
+
self.ds_weights = ds_weights
|
| 986 |
+
self._ds_ind_bins = np.cumsum([0] + list(ds_lens))
|
| 987 |
+
|
| 988 |
+
# cache mode "all" not supported! The action normalization stats of each
|
| 989 |
+
# dataset will change after the datasets are already initialized
|
| 990 |
+
for ds in self.datasets:
|
| 991 |
+
assert ds.hdf5_cache_mode != "all"
|
| 992 |
+
|
| 993 |
+
# TODO: comment
|
| 994 |
+
action_stats = self.get_action_stats()
|
| 995 |
+
self.action_normalization_stats = action_stats_to_normalization_stats(
|
| 996 |
+
action_stats, self.datasets[0].action_config)
|
| 997 |
+
self.set_action_normalization_stats(self.action_normalization_stats)
|
| 998 |
+
|
| 999 |
+
def __len__(self):
|
| 1000 |
+
return np.sum([len(ds) for ds in self.datasets])
|
| 1001 |
+
|
| 1002 |
+
def __getitem__(self, idx):
|
| 1003 |
+
ds_ind = np.digitize(idx, self._ds_ind_bins) - 1
|
| 1004 |
+
ind_in_ds = idx - self._ds_ind_bins[ds_ind]
|
| 1005 |
+
meta = self.datasets[ds_ind].__getitem__(ind_in_ds)
|
| 1006 |
+
meta["index"] = idx
|
| 1007 |
+
return meta
|
| 1008 |
+
|
| 1009 |
+
def get_ds_label(self, idx):
|
| 1010 |
+
ds_ind = np.digitize(idx, self._ds_ind_bins) - 1
|
| 1011 |
+
ds_label = self.ds_labels[ds_ind]
|
| 1012 |
+
return ds_label
|
| 1013 |
+
|
| 1014 |
+
def get_ds_id(self, idx):
|
| 1015 |
+
ds_ind = np.digitize(idx, self._ds_ind_bins) - 1
|
| 1016 |
+
ds_label = self.ds_labels[ds_ind]
|
| 1017 |
+
return self.ds_labels_to_ids[ds_label]
|
| 1018 |
+
|
| 1019 |
+
def __repr__(self):
|
| 1020 |
+
str_output = '\n'.join([ds.__repr__() for ds in self.datasets])
|
| 1021 |
+
return str_output
|
| 1022 |
+
|
| 1023 |
+
def get_dataset_sampler(self):
|
| 1024 |
+
weights = np.ones(len(self))
|
| 1025 |
+
for i, (start, end) in enumerate(zip(self._ds_ind_bins[:-1], self._ds_ind_bins[1:])):
|
| 1026 |
+
weights[start:end] = self.ds_weights[i]
|
| 1027 |
+
|
| 1028 |
+
sampler = torch.utils.data.WeightedRandomSampler(
|
| 1029 |
+
weights=weights,
|
| 1030 |
+
num_samples=len(self),
|
| 1031 |
+
replacement=True,
|
| 1032 |
+
)
|
| 1033 |
+
return sampler
|
| 1034 |
+
|
| 1035 |
+
def get_action_stats(self):
|
| 1036 |
+
meta_action_stats = self.datasets[0].get_action_stats()
|
| 1037 |
+
for dataset in self.datasets[1:]:
|
| 1038 |
+
ds_action_stats = dataset.get_action_stats()
|
| 1039 |
+
meta_action_stats = _aggregate_traj_stats(meta_action_stats, ds_action_stats)
|
| 1040 |
+
|
| 1041 |
+
return meta_action_stats
|
| 1042 |
+
|
| 1043 |
+
def set_action_normalization_stats(self, action_normalization_stats):
|
| 1044 |
+
self.action_normalization_stats = action_normalization_stats
|
| 1045 |
+
for ds in self.datasets:
|
| 1046 |
+
ds.set_action_normalization_stats(self.action_normalization_stats)
|
| 1047 |
+
|
| 1048 |
+
def get_action_normalization_stats(self):
|
| 1049 |
+
"""
|
| 1050 |
+
Computes a dataset-wide min, max, mean and standard deviation for the actions
|
| 1051 |
+
(per dimension) and returns it.
|
| 1052 |
+
"""
|
| 1053 |
+
|
| 1054 |
+
# Run through all trajectories. For each one, compute minimal observation statistics, and then aggregate
|
| 1055 |
+
# with the previous statistics.
|
| 1056 |
+
if self.action_normalization_stats is None:
|
| 1057 |
+
action_stats = self.get_action_stats()
|
| 1058 |
+
self.action_normalization_stats = action_stats_to_normalization_stats(
|
| 1059 |
+
action_stats, self.datasets[0].action_config)
|
| 1060 |
+
return self.action_normalization_stats
|
| 1061 |
+
|
| 1062 |
+
def _compute_traj_stats(traj_obs_dict):
|
| 1063 |
+
"""
|
| 1064 |
+
Helper function to compute statistics over a single trajectory of observations.
|
| 1065 |
+
"""
|
| 1066 |
+
traj_stats = { k : {} for k in traj_obs_dict }
|
| 1067 |
+
for k in traj_obs_dict:
|
| 1068 |
+
traj_stats[k]["n"] = traj_obs_dict[k].shape[0]
|
| 1069 |
+
traj_stats[k]["mean"] = traj_obs_dict[k].mean(axis=0, keepdims=True) # [1, ...]
|
| 1070 |
+
traj_stats[k]["sqdiff"] = ((traj_obs_dict[k] - traj_stats[k]["mean"]) ** 2).sum(axis=0, keepdims=True) # [1, ...]
|
| 1071 |
+
traj_stats[k]["min"] = traj_obs_dict[k].min(axis=0, keepdims=True)
|
| 1072 |
+
traj_stats[k]["max"] = traj_obs_dict[k].max(axis=0, keepdims=True)
|
| 1073 |
+
return traj_stats
|
| 1074 |
+
|
| 1075 |
+
def _aggregate_traj_stats(traj_stats_a, traj_stats_b):
|
| 1076 |
+
"""
|
| 1077 |
+
Helper function to aggregate trajectory statistics.
|
| 1078 |
+
See https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Parallel_algorithm
|
| 1079 |
+
for more information.
|
| 1080 |
+
"""
|
| 1081 |
+
merged_stats = {}
|
| 1082 |
+
for k in traj_stats_a:
|
| 1083 |
+
n_a, avg_a, M2_a, min_a, max_a = traj_stats_a[k]["n"], traj_stats_a[k]["mean"], traj_stats_a[k]["sqdiff"], traj_stats_a[k]["min"], traj_stats_a[k]["max"]
|
| 1084 |
+
n_b, avg_b, M2_b, min_b, max_b = traj_stats_b[k]["n"], traj_stats_b[k]["mean"], traj_stats_b[k]["sqdiff"], traj_stats_b[k]["min"], traj_stats_b[k]["max"]
|
| 1085 |
+
n = n_a + n_b
|
| 1086 |
+
mean = (n_a * avg_a + n_b * avg_b) / n
|
| 1087 |
+
delta = (avg_b - avg_a)
|
| 1088 |
+
M2 = M2_a + M2_b + (delta ** 2) * (n_a * n_b) / n
|
| 1089 |
+
min_ = np.minimum(min_a, min_b)
|
| 1090 |
+
max_ = np.maximum(max_a, max_b)
|
| 1091 |
+
merged_stats[k] = dict(n=n, mean=mean, sqdiff=M2, min=min_, max=max_)
|
| 1092 |
+
return merged_stats
|
| 1093 |
+
|
| 1094 |
+
def action_stats_to_normalization_stats(action_stats, action_config):
|
| 1095 |
+
action_normalization_stats = OrderedDict()
|
| 1096 |
+
for action_key in action_stats.keys():
|
| 1097 |
+
# get how this action should be normalized from config, default to None
|
| 1098 |
+
norm_method = action_config[action_key].get("normalization", None)
|
| 1099 |
+
if norm_method is None:
|
| 1100 |
+
# no normalization, unit scale, zero offset
|
| 1101 |
+
action_normalization_stats[action_key] = {
|
| 1102 |
+
"scale": np.ones_like(action_stats[action_key]["mean"], dtype=np.float32),
|
| 1103 |
+
"offset": np.zeros_like(action_stats[action_key]["mean"], dtype=np.float32)
|
| 1104 |
+
}
|
| 1105 |
+
elif norm_method == "min_max":
|
| 1106 |
+
# normalize min to -1 and max to 1
|
| 1107 |
+
range_eps = 1e-4
|
| 1108 |
+
input_min = action_stats[action_key]["min"].astype(np.float32)
|
| 1109 |
+
input_max = action_stats[action_key]["max"].astype(np.float32)
|
| 1110 |
+
# instead of -1 and 1 use numbers just below threshold to prevent numerical instability issues
|
| 1111 |
+
output_min = -0.999999
|
| 1112 |
+
output_max = 0.999999
|
| 1113 |
+
|
| 1114 |
+
# ignore input dimentions that is too small to prevent division by zero
|
| 1115 |
+
input_range = input_max - input_min
|
| 1116 |
+
ignore_dim = input_range < range_eps
|
| 1117 |
+
input_range[ignore_dim] = output_max - output_min
|
| 1118 |
+
|
| 1119 |
+
# expected usage of scale and offset
|
| 1120 |
+
# normalized_action = (raw_action - offset) / scale
|
| 1121 |
+
# raw_action = scale * normalized_action + offset
|
| 1122 |
+
|
| 1123 |
+
# eq1: input_max = scale * output_max + offset
|
| 1124 |
+
# eq2: input_min = scale * output_min + offset
|
| 1125 |
+
|
| 1126 |
+
# solution for scale and offset
|
| 1127 |
+
# eq1 - eq2:
|
| 1128 |
+
# input_max - input_min = scale * (output_max - output_min)
|
| 1129 |
+
# (input_max - input_min) / (output_max - output_min) = scale <- eq3
|
| 1130 |
+
# offset = input_min - scale * output_min <- eq4
|
| 1131 |
+
scale = input_range / (output_max - output_min)
|
| 1132 |
+
offset = input_min - scale * output_min
|
| 1133 |
+
|
| 1134 |
+
offset[ignore_dim] = input_min[ignore_dim] - (output_max + output_min) / 2
|
| 1135 |
+
|
| 1136 |
+
action_normalization_stats[action_key] = {
|
| 1137 |
+
"scale": scale,
|
| 1138 |
+
"offset": offset
|
| 1139 |
+
}
|
| 1140 |
+
elif norm_method == "gaussian":
|
| 1141 |
+
# normalize to zero mean unit variance
|
| 1142 |
+
input_mean = action_stats[action_key]["mean"].astype(np.float32)
|
| 1143 |
+
input_std = np.sqrt(action_stats[action_key]["sqdiff"] / action_stats[action_key]["n"]).astype(np.float32)
|
| 1144 |
+
|
| 1145 |
+
# ignore input dimentions that is too small to prevent division by zero
|
| 1146 |
+
std_eps = 1e-6
|
| 1147 |
+
ignore_dim = input_std < std_eps
|
| 1148 |
+
input_std[ignore_dim] = 1.0
|
| 1149 |
+
|
| 1150 |
+
action_normalization_stats[action_key] = {
|
| 1151 |
+
"scale": input_mean,
|
| 1152 |
+
"offset": input_std
|
| 1153 |
+
}
|
| 1154 |
+
else:
|
| 1155 |
+
raise NotImplementedError(
|
| 1156 |
+
'action_config.actions.normalization: "{}" is not supported'.format(norm_method))
|
| 1157 |
+
|
| 1158 |
+
return action_normalization_stats
|
aloha-devel/robomimic/utils/hyperparam_utils.py
ADDED
|
@@ -0,0 +1,373 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
A collection of utility functions and classes for generating config jsons for hyperparameter sweeps.
|
| 3 |
+
"""
|
| 4 |
+
import argparse
|
| 5 |
+
import os
|
| 6 |
+
import json
|
| 7 |
+
import re
|
| 8 |
+
import itertools
|
| 9 |
+
|
| 10 |
+
from collections import OrderedDict
|
| 11 |
+
from copy import deepcopy
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
class ConfigGenerator(object):
|
| 15 |
+
"""
|
| 16 |
+
Useful class to keep track of hyperparameters to sweep, and to generate
|
| 17 |
+
the json configs for each experiment run.
|
| 18 |
+
"""
|
| 19 |
+
def __init__(self, base_config_file, wandb_proj_name="debug", script_file=None, generated_config_dir=None):
|
| 20 |
+
"""
|
| 21 |
+
Args:
|
| 22 |
+
base_config_file (str): path to a base json config to use as a starting point
|
| 23 |
+
for the parameter sweep.
|
| 24 |
+
|
| 25 |
+
script_file (str): script filename to write as output
|
| 26 |
+
"""
|
| 27 |
+
assert isinstance(base_config_file, str)
|
| 28 |
+
self.base_config_file = base_config_file
|
| 29 |
+
assert generated_config_dir is None or isinstance(generated_config_dir, str)
|
| 30 |
+
if generated_config_dir is not None:
|
| 31 |
+
generated_config_dir = os.path.expanduser(generated_config_dir)
|
| 32 |
+
self.generated_config_dir = generated_config_dir
|
| 33 |
+
assert script_file is None or isinstance(script_file, str)
|
| 34 |
+
if script_file is None:
|
| 35 |
+
self.script_file = os.path.join('~', 'tmp/tmpp.sh')
|
| 36 |
+
else:
|
| 37 |
+
self.script_file = script_file
|
| 38 |
+
self.script_file = os.path.expanduser(self.script_file)
|
| 39 |
+
self.parameters = OrderedDict()
|
| 40 |
+
|
| 41 |
+
assert isinstance(wandb_proj_name, str)
|
| 42 |
+
self.wandb_proj_name = wandb_proj_name
|
| 43 |
+
|
| 44 |
+
def add_param(self, key, name, group, values, value_names=None, hidename=False, prepend=False):
|
| 45 |
+
"""
|
| 46 |
+
Add parameter to the hyperparameter sweep.
|
| 47 |
+
|
| 48 |
+
Args:
|
| 49 |
+
key (str): location of parameter in the config, using hierarchical key format
|
| 50 |
+
(ex. train/data = config.train.data)
|
| 51 |
+
|
| 52 |
+
name (str): name, as it will appear in the experiment name
|
| 53 |
+
|
| 54 |
+
group (int): group id - parameters with the same ID have their values swept
|
| 55 |
+
together
|
| 56 |
+
|
| 57 |
+
values (list): list of values to sweep over for this parameter
|
| 58 |
+
|
| 59 |
+
value_names ([str]): if provided, strings to use in experiment name for
|
| 60 |
+
each value, instead of the parameter value. This is helpful for parameters
|
| 61 |
+
that may have long or large values (for example, dataset path).
|
| 62 |
+
"""
|
| 63 |
+
if value_names is not None:
|
| 64 |
+
assert len(values) == len(value_names)
|
| 65 |
+
self.parameters[key] = argparse.Namespace(
|
| 66 |
+
key=key,
|
| 67 |
+
name=name,
|
| 68 |
+
group=group,
|
| 69 |
+
values=values,
|
| 70 |
+
value_names=value_names,
|
| 71 |
+
hidename=hidename,
|
| 72 |
+
)
|
| 73 |
+
if prepend:
|
| 74 |
+
self.parameters.move_to_end(key, last=False)
|
| 75 |
+
|
| 76 |
+
def generate(self, override_base_name=False):
|
| 77 |
+
"""
|
| 78 |
+
Generates json configs for the hyperparameter sweep using attributes
|
| 79 |
+
@self.parameters, @self.base_config_file, and @self.script_file,
|
| 80 |
+
all of which should have first been set externally by calling
|
| 81 |
+
@add_param, @set_base_config_file, and @set_script_file.
|
| 82 |
+
"""
|
| 83 |
+
assert len(self.parameters) > 0, "must add parameters using add_param first!"
|
| 84 |
+
generated_json_paths = self._generate_jsons(override_base_name=override_base_name)
|
| 85 |
+
self._script_from_jsons(generated_json_paths)
|
| 86 |
+
|
| 87 |
+
def _name_for_experiment(self, base_name, parameter_values, parameter_value_names):
|
| 88 |
+
"""
|
| 89 |
+
This function generates the name for an experiment, given one specific
|
| 90 |
+
parameter setting.
|
| 91 |
+
|
| 92 |
+
Args:
|
| 93 |
+
base_name (str): base experiment name
|
| 94 |
+
parameter_values (OrderedDict): dictionary that maps parameter name to
|
| 95 |
+
the parameter value for this experiment run
|
| 96 |
+
parameter_value_names (dict): dictionary that maps parameter name to
|
| 97 |
+
the name to use for its value in the experiment name
|
| 98 |
+
|
| 99 |
+
Returns:
|
| 100 |
+
name (str): generated experiment name
|
| 101 |
+
"""
|
| 102 |
+
name = base_name
|
| 103 |
+
for k in parameter_values:
|
| 104 |
+
# append parameter name and value to end of base name
|
| 105 |
+
if len(self.parameters[k].name) == 0 or self.parameters[k].hidename:
|
| 106 |
+
# empty string indicates that naming should be skipped
|
| 107 |
+
continue
|
| 108 |
+
if parameter_value_names[k] is not None:
|
| 109 |
+
# take name from passed dictionary
|
| 110 |
+
val_str = parameter_value_names[k]
|
| 111 |
+
else:
|
| 112 |
+
val_str = parameter_values[k]
|
| 113 |
+
if isinstance(parameter_values[k], list) or isinstance(parameter_values[k], tuple):
|
| 114 |
+
# convert list to string to avoid weird spaces and naming problems
|
| 115 |
+
val_str = "_".join([str(x) for x in parameter_values[k]])
|
| 116 |
+
val_str = str(val_str)
|
| 117 |
+
if len(name) > 0:
|
| 118 |
+
name += "_"
|
| 119 |
+
name += '{}'.format(self.parameters[k].name)
|
| 120 |
+
if len(val_str) > 0:
|
| 121 |
+
name += '_{}'.format(val_str)
|
| 122 |
+
return name
|
| 123 |
+
|
| 124 |
+
def _get_parameter_ranges(self):
|
| 125 |
+
"""
|
| 126 |
+
Extract parameter ranges from base json file. Also takes all possible
|
| 127 |
+
combinations of the parameter ranges to generate an expanded set of values.
|
| 128 |
+
|
| 129 |
+
Returns:
|
| 130 |
+
parameter_ranges (dict): dictionary that maps the parameter to a list
|
| 131 |
+
of all values it should take for each generated config. The length
|
| 132 |
+
of the list will be the total number of configs that will be
|
| 133 |
+
generated from this scan.
|
| 134 |
+
|
| 135 |
+
parameter_names (dict): dictionary that maps the parameter to a list
|
| 136 |
+
of all name strings that should contribute to each invididual
|
| 137 |
+
experiment's name. The length of the list will be the total
|
| 138 |
+
number of configs that will be generated from this scan.
|
| 139 |
+
"""
|
| 140 |
+
|
| 141 |
+
# mapping from group id to list of indices to grab from each parameter's list
|
| 142 |
+
# of values in the parameter group
|
| 143 |
+
parameter_group_indices = OrderedDict()
|
| 144 |
+
for k in self.parameters:
|
| 145 |
+
group_id = self.parameters[k].group
|
| 146 |
+
assert isinstance(self.parameters[k].values, list)
|
| 147 |
+
num_param_values = len(self.parameters[k].values)
|
| 148 |
+
if group_id not in parameter_group_indices:
|
| 149 |
+
parameter_group_indices[group_id] = list(range(num_param_values))
|
| 150 |
+
else:
|
| 151 |
+
assert len(parameter_group_indices[group_id]) == num_param_values, \
|
| 152 |
+
"error: inconsistent number of parameter values in group with id {}".format(group_id)
|
| 153 |
+
|
| 154 |
+
keys = list(parameter_group_indices.keys())
|
| 155 |
+
inds = list(parameter_group_indices.values())
|
| 156 |
+
new_parameter_group_indices = OrderedDict(
|
| 157 |
+
{ k : [] for k in keys }
|
| 158 |
+
)
|
| 159 |
+
# get all combinations of the different parameter group indices
|
| 160 |
+
# and then use these indices to determine the new parameter ranges
|
| 161 |
+
# per member of each parameter group.
|
| 162 |
+
#
|
| 163 |
+
# e.g. with two parameter groups, one with two values, and another with three values
|
| 164 |
+
# we have [0, 1] x [0, 1, 2] = [0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2]
|
| 165 |
+
# so the corresponding parameter group indices are [0, 0, 0, 1, 1, 1] and
|
| 166 |
+
# [0, 1, 2, 0, 1, 2], and all parameters in each parameter group are indexed
|
| 167 |
+
# together using these indices, to get each parameter range.
|
| 168 |
+
for comb in itertools.product(*inds):
|
| 169 |
+
for i in range(len(comb)):
|
| 170 |
+
new_parameter_group_indices[keys[i]].append(comb[i])
|
| 171 |
+
parameter_group_indices = new_parameter_group_indices
|
| 172 |
+
|
| 173 |
+
# use the indices to gather the parameter values to sweep per parameter
|
| 174 |
+
parameter_ranges = OrderedDict()
|
| 175 |
+
parameter_names = OrderedDict()
|
| 176 |
+
for k in self.parameters:
|
| 177 |
+
parameter_values = self.parameters[k].values
|
| 178 |
+
group_id = self.parameters[k].group
|
| 179 |
+
inds = parameter_group_indices[group_id]
|
| 180 |
+
parameter_ranges[k] = [parameter_values[ind] for ind in inds]
|
| 181 |
+
|
| 182 |
+
# add in parameter names if supplied
|
| 183 |
+
parameter_names[k] = None
|
| 184 |
+
if self.parameters[k].value_names is not None:
|
| 185 |
+
par_names = self.parameters[k].value_names
|
| 186 |
+
assert isinstance(par_names, list)
|
| 187 |
+
assert len(par_names) == len(parameter_values)
|
| 188 |
+
parameter_names[k] = [par_names[ind] for ind in inds]
|
| 189 |
+
|
| 190 |
+
# ensure that the number of parameter settings is the same per parameter
|
| 191 |
+
first_key = list(parameter_ranges.keys())[0]
|
| 192 |
+
num_settings = len(parameter_ranges[first_key])
|
| 193 |
+
for k in parameter_ranges:
|
| 194 |
+
assert len(parameter_ranges[k]) == num_settings, "inconsistent number of values"
|
| 195 |
+
|
| 196 |
+
return parameter_ranges, parameter_names
|
| 197 |
+
|
| 198 |
+
def _generate_jsons(self, override_base_name=False):
|
| 199 |
+
"""
|
| 200 |
+
Generates json configs for the hyperparameter sweep, using @self.parameters and
|
| 201 |
+
@self.base_config_file.
|
| 202 |
+
|
| 203 |
+
Returns:
|
| 204 |
+
json_paths (list): list of paths to created json files, one per experiment
|
| 205 |
+
"""
|
| 206 |
+
|
| 207 |
+
# base directory for saving jsons
|
| 208 |
+
if self.generated_config_dir:
|
| 209 |
+
base_dir = self.generated_config_dir
|
| 210 |
+
if not os.path.exists(base_dir):
|
| 211 |
+
os.makedirs(base_dir)
|
| 212 |
+
else:
|
| 213 |
+
base_dir = os.path.abspath(os.path.dirname(self.base_config_file))
|
| 214 |
+
|
| 215 |
+
# read base json
|
| 216 |
+
base_config = load_json(self.base_config_file, verbose=False)
|
| 217 |
+
|
| 218 |
+
# base exp name from this base config
|
| 219 |
+
if override_base_name:
|
| 220 |
+
base_exp_name = ""
|
| 221 |
+
else:
|
| 222 |
+
base_exp_name = base_config['experiment']['name']
|
| 223 |
+
|
| 224 |
+
# use base json to determine the parameter ranges
|
| 225 |
+
parameter_ranges, parameter_names = self._get_parameter_ranges()
|
| 226 |
+
|
| 227 |
+
# iterate through each parameter setting to create each json
|
| 228 |
+
first_key = list(parameter_ranges.keys())[0]
|
| 229 |
+
num_settings = len(parameter_ranges[first_key])
|
| 230 |
+
|
| 231 |
+
# keep track of path to generated jsons
|
| 232 |
+
json_paths = []
|
| 233 |
+
|
| 234 |
+
for i in range(num_settings):
|
| 235 |
+
# the specific parameter setting for this experiment
|
| 236 |
+
setting = { k : parameter_ranges[k][i] for k in parameter_ranges }
|
| 237 |
+
maybe_parameter_names = OrderedDict()
|
| 238 |
+
for k in parameter_names:
|
| 239 |
+
maybe_parameter_names[k] = None
|
| 240 |
+
if parameter_names[k] is not None:
|
| 241 |
+
maybe_parameter_names[k] = parameter_names[k][i]
|
| 242 |
+
|
| 243 |
+
# experiment name from setting
|
| 244 |
+
exp_name = self._name_for_experiment(
|
| 245 |
+
base_name=base_exp_name,
|
| 246 |
+
parameter_values=setting,
|
| 247 |
+
parameter_value_names=maybe_parameter_names,
|
| 248 |
+
)
|
| 249 |
+
|
| 250 |
+
# copy old json, but override name, and parameter values
|
| 251 |
+
json_dict = deepcopy(base_config)
|
| 252 |
+
json_dict['experiment']['name'] = exp_name
|
| 253 |
+
for k in parameter_ranges:
|
| 254 |
+
set_value_for_key(json_dict, k, v=parameter_ranges[k][i])
|
| 255 |
+
|
| 256 |
+
# populate list of identifying meta for logger;
|
| 257 |
+
# see meta_config method in base_config.py for more info
|
| 258 |
+
json_dict["experiment"]["logging"]["wandb_proj_name"] = self.wandb_proj_name
|
| 259 |
+
if "meta" not in json_dict:
|
| 260 |
+
json_dict["meta"] = dict()
|
| 261 |
+
json_dict["meta"].update(
|
| 262 |
+
hp_base_config_file=self.base_config_file,
|
| 263 |
+
hp_keys=list(),
|
| 264 |
+
hp_values=list(),
|
| 265 |
+
)
|
| 266 |
+
# logging: keep track of hyp param names and values as meta info
|
| 267 |
+
for k in parameter_ranges.keys():
|
| 268 |
+
key_name = self.parameters[k].name
|
| 269 |
+
if key_name is not None and len(key_name) > 0:
|
| 270 |
+
if maybe_parameter_names[k] is not None:
|
| 271 |
+
value_name = maybe_parameter_names[k]
|
| 272 |
+
else:
|
| 273 |
+
value_name = setting[k]
|
| 274 |
+
|
| 275 |
+
json_dict["meta"]["hp_keys"].append(key_name)
|
| 276 |
+
json_dict["meta"]["hp_values"].append(value_name)
|
| 277 |
+
|
| 278 |
+
# save file in same directory as old json
|
| 279 |
+
json_path = os.path.join(base_dir, "{}.json".format(exp_name))
|
| 280 |
+
save_json(json_dict, json_path)
|
| 281 |
+
json_paths.append(json_path)
|
| 282 |
+
|
| 283 |
+
print("Num exps:", len(json_paths))
|
| 284 |
+
|
| 285 |
+
return json_paths
|
| 286 |
+
|
| 287 |
+
def _script_from_jsons(self, json_paths):
|
| 288 |
+
"""
|
| 289 |
+
Generates a bash script to run the experiments that correspond to
|
| 290 |
+
the input jsons.
|
| 291 |
+
"""
|
| 292 |
+
with open(self.script_file, 'w') as f:
|
| 293 |
+
f.write("#!/bin/bash\n\n")
|
| 294 |
+
for path in json_paths:
|
| 295 |
+
# write python command to file
|
| 296 |
+
import robomimic
|
| 297 |
+
cmd = "python {}/scripts/train.py --config {}\n".format(robomimic.__path__[0], path)
|
| 298 |
+
|
| 299 |
+
print()
|
| 300 |
+
print(cmd)
|
| 301 |
+
f.write(cmd)
|
| 302 |
+
|
| 303 |
+
|
| 304 |
+
def load_json(json_file, verbose=True):
|
| 305 |
+
"""
|
| 306 |
+
Simple utility function to load a json file as a dict.
|
| 307 |
+
|
| 308 |
+
Args:
|
| 309 |
+
json_file (str): path to json file to load
|
| 310 |
+
verbose (bool): if True, pretty print the loaded json dictionary
|
| 311 |
+
|
| 312 |
+
Returns:
|
| 313 |
+
config (dict): json dictionary
|
| 314 |
+
"""
|
| 315 |
+
with open(json_file, 'r') as f:
|
| 316 |
+
config = json.load(f)
|
| 317 |
+
if verbose:
|
| 318 |
+
print('loading external config: =================')
|
| 319 |
+
print(json.dumps(config, indent=4))
|
| 320 |
+
print('==========================================')
|
| 321 |
+
return config
|
| 322 |
+
|
| 323 |
+
|
| 324 |
+
def save_json(config, json_file):
|
| 325 |
+
"""
|
| 326 |
+
Simple utility function to save a dictionary to a json file on disk.
|
| 327 |
+
|
| 328 |
+
Args:
|
| 329 |
+
config (dict): dictionary to save
|
| 330 |
+
json_file (str): path to json file to write
|
| 331 |
+
"""
|
| 332 |
+
with open(json_file, 'w') as f:
|
| 333 |
+
# preserve original key ordering
|
| 334 |
+
json.dump(config, f, sort_keys=False, indent=4)
|
| 335 |
+
|
| 336 |
+
|
| 337 |
+
def get_value_for_key(dic, k):
|
| 338 |
+
"""
|
| 339 |
+
Get value for nested dictionary with levels denoted by "/" or ".".
|
| 340 |
+
For example, if @k is "a/b", then this function returns
|
| 341 |
+
@dic["a"]["b"].
|
| 342 |
+
|
| 343 |
+
Args:
|
| 344 |
+
dic (dict): a nested dictionary
|
| 345 |
+
k (str): a single string meant to index several levels down into
|
| 346 |
+
the nested dictionary, where levels can be denoted by "/" or
|
| 347 |
+
by ".".
|
| 348 |
+
Returns:
|
| 349 |
+
val: the nested dictionary value for the provided key
|
| 350 |
+
"""
|
| 351 |
+
val = dic
|
| 352 |
+
subkeys = re.split('/|\.', k)
|
| 353 |
+
for s in subkeys[:-1]:
|
| 354 |
+
val = val[s]
|
| 355 |
+
return val[subkeys[-1]]
|
| 356 |
+
|
| 357 |
+
|
| 358 |
+
def set_value_for_key(dic, k, v):
|
| 359 |
+
"""
|
| 360 |
+
Set value for hierarchical dictionary with levels denoted by "/" or ".".
|
| 361 |
+
|
| 362 |
+
Args:
|
| 363 |
+
dic (dict): a nested dictionary
|
| 364 |
+
k (str): a single string meant to index several levels down into
|
| 365 |
+
the nested dictionary, where levels can be denoted by "/" or
|
| 366 |
+
by ".".
|
| 367 |
+
v: the value to set at the provided key
|
| 368 |
+
"""
|
| 369 |
+
val = dic
|
| 370 |
+
subkeys = re.split('/|\.', k) #k.split('/')
|
| 371 |
+
for s in subkeys[:-1]:
|
| 372 |
+
val = val[s]
|
| 373 |
+
val[subkeys[-1]] = v
|
aloha-devel/robomimic/utils/lang_utils.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from transformers import AutoModel, pipeline, AutoTokenizer, CLIPTextModelWithProjection
|
| 3 |
+
|
| 4 |
+
os.environ["TOKENIZERS_PARALLELISM"] = "true" # needed to suppress warning about potential deadlock
|
| 5 |
+
tokenizer = "openai/clip-vit-large-patch14" #"openai/clip-vit-base-patch32"
|
| 6 |
+
lang_emb_model = CLIPTextModelWithProjection.from_pretrained(
|
| 7 |
+
tokenizer,
|
| 8 |
+
cache_dir=os.path.expanduser("~/tmp/clip")
|
| 9 |
+
).eval()
|
| 10 |
+
tz = AutoTokenizer.from_pretrained(tokenizer, TOKENIZERS_PARALLELISM=True)
|
| 11 |
+
|
| 12 |
+
def get_lang_emb(lang):
|
| 13 |
+
if lang is None:
|
| 14 |
+
return None
|
| 15 |
+
|
| 16 |
+
tokens = tz(
|
| 17 |
+
text=lang, # the sentence to be encoded
|
| 18 |
+
add_special_tokens=True, # Add [CLS] and [SEP]
|
| 19 |
+
max_length=25, # maximum length of a sentence
|
| 20 |
+
padding="max_length",
|
| 21 |
+
return_attention_mask=True, # Generate the attention mask
|
| 22 |
+
return_tensors="pt", # ask the function to return PyTorch tensors
|
| 23 |
+
)
|
| 24 |
+
lang_emb = lang_emb_model(**tokens)['text_embeds'].detach()[0]
|
| 25 |
+
|
| 26 |
+
return lang_emb
|
| 27 |
+
|
aloha-devel/robomimic/utils/loss_utils.py
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
This file contains a collection of useful loss functions for use with torch tensors.
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
import math
|
| 6 |
+
import numpy as np
|
| 7 |
+
import torch
|
| 8 |
+
import torch.nn.functional as F
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def cosine_loss(preds, labels):
|
| 12 |
+
"""
|
| 13 |
+
Cosine loss between two tensors.
|
| 14 |
+
|
| 15 |
+
Args:
|
| 16 |
+
preds (torch.Tensor): torch tensor
|
| 17 |
+
labels (torch.Tensor): torch tensor
|
| 18 |
+
|
| 19 |
+
Returns:
|
| 20 |
+
loss (torch.Tensor): cosine loss
|
| 21 |
+
"""
|
| 22 |
+
sim = torch.nn.CosineSimilarity(dim=len(preds.shape) - 1)(preds, labels)
|
| 23 |
+
return -torch.mean(sim - 1.0)
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def KLD_0_1_loss(mu, logvar):
|
| 27 |
+
"""
|
| 28 |
+
KL divergence loss. Computes D_KL( N(mu, sigma) || N(0, 1) ). Note that
|
| 29 |
+
this function averages across the batch dimension, but sums across dimension.
|
| 30 |
+
|
| 31 |
+
Args:
|
| 32 |
+
mu (torch.Tensor): mean tensor of shape (B, D)
|
| 33 |
+
logvar (torch.Tensor): logvar tensor of shape (B, D)
|
| 34 |
+
|
| 35 |
+
Returns:
|
| 36 |
+
loss (torch.Tensor): KL divergence loss between the input gaussian distribution
|
| 37 |
+
and N(0, 1)
|
| 38 |
+
"""
|
| 39 |
+
return -0.5 * (1. + logvar - mu.pow(2) - logvar.exp()).sum(dim=1).mean()
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def KLD_gaussian_loss(mu_1, logvar_1, mu_2, logvar_2):
|
| 43 |
+
"""
|
| 44 |
+
KL divergence loss between two Gaussian distributions. This function
|
| 45 |
+
computes the average loss across the batch.
|
| 46 |
+
|
| 47 |
+
Args:
|
| 48 |
+
mu_1 (torch.Tensor): first means tensor of shape (B, D)
|
| 49 |
+
logvar_1 (torch.Tensor): first logvars tensor of shape (B, D)
|
| 50 |
+
mu_2 (torch.Tensor): second means tensor of shape (B, D)
|
| 51 |
+
logvar_2 (torch.Tensor): second logvars tensor of shape (B, D)
|
| 52 |
+
|
| 53 |
+
Returns:
|
| 54 |
+
loss (torch.Tensor): KL divergence loss between the two gaussian distributions
|
| 55 |
+
"""
|
| 56 |
+
return -0.5 * (1. + \
|
| 57 |
+
logvar_1 - logvar_2 \
|
| 58 |
+
- ((mu_2 - mu_1).pow(2) / logvar_2.exp()) \
|
| 59 |
+
- (logvar_1.exp() / logvar_2.exp()) \
|
| 60 |
+
).sum(dim=1).mean()
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def log_normal(x, m, v):
|
| 64 |
+
"""
|
| 65 |
+
Log probability of tensor x under diagonal multivariate normal with
|
| 66 |
+
mean m and variance v. The last dimension of the tensors is treated
|
| 67 |
+
as the dimension of the Gaussian distribution - all other dimensions
|
| 68 |
+
are treated as independent Gaussians. Adapted from CS 236 at Stanford.
|
| 69 |
+
|
| 70 |
+
Args:
|
| 71 |
+
x (torch.Tensor): tensor with shape (B, ..., D)
|
| 72 |
+
m (torch.Tensor): means tensor with shape (B, ..., D) or (1, ..., D)
|
| 73 |
+
v (torch.Tensor): variances tensor with shape (B, ..., D) or (1, ..., D)
|
| 74 |
+
|
| 75 |
+
Returns:
|
| 76 |
+
log_prob (torch.Tensor): log probabilities of shape (B, ...)
|
| 77 |
+
"""
|
| 78 |
+
element_wise = -0.5 * (torch.log(v) + (x - m).pow(2) / v + np.log(2 * np.pi))
|
| 79 |
+
log_prob = element_wise.sum(-1)
|
| 80 |
+
return log_prob
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
def log_normal_mixture(x, m, v, w=None, log_w=None):
|
| 84 |
+
"""
|
| 85 |
+
Log probability of tensor x under a uniform mixture of Gaussians.
|
| 86 |
+
Adapted from CS 236 at Stanford.
|
| 87 |
+
|
| 88 |
+
Args:
|
| 89 |
+
x (torch.Tensor): tensor with shape (B, D)
|
| 90 |
+
m (torch.Tensor): means tensor with shape (B, M, D) or (1, M, D), where
|
| 91 |
+
M is number of mixture components
|
| 92 |
+
v (torch.Tensor): variances tensor with shape (B, M, D) or (1, M, D) where
|
| 93 |
+
M is number of mixture components
|
| 94 |
+
w (torch.Tensor): weights tensor - if provided, should be
|
| 95 |
+
shape (B, M) or (1, M)
|
| 96 |
+
log_w (torch.Tensor): log-weights tensor - if provided, should be
|
| 97 |
+
shape (B, M) or (1, M)
|
| 98 |
+
|
| 99 |
+
Returns:
|
| 100 |
+
log_prob (torch.Tensor): log probabilities of shape (B,)
|
| 101 |
+
"""
|
| 102 |
+
|
| 103 |
+
# (B , D) -> (B , 1, D)
|
| 104 |
+
x = x.unsqueeze(1)
|
| 105 |
+
# (B, 1, D) -> (B, M, D) -> (B, M)
|
| 106 |
+
log_prob = log_normal(x, m, v)
|
| 107 |
+
if w is not None or log_w is not None:
|
| 108 |
+
# this weights the log probabilities by the mixture weights so we have log(w_i * N(x | m_i, v_i))
|
| 109 |
+
if w is not None:
|
| 110 |
+
assert log_w is None
|
| 111 |
+
log_w = torch.log(w)
|
| 112 |
+
log_prob += log_w
|
| 113 |
+
# then compute log sum_i exp [log(w_i * N(x | m_i, v_i))]
|
| 114 |
+
# (B, M) -> (B,)
|
| 115 |
+
log_prob = log_sum_exp(log_prob , dim=1)
|
| 116 |
+
else:
|
| 117 |
+
# (B, M) -> (B,)
|
| 118 |
+
log_prob = log_mean_exp(log_prob , dim=1) # mean accounts for uniform weights
|
| 119 |
+
return log_prob
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
def log_mean_exp(x, dim):
|
| 123 |
+
"""
|
| 124 |
+
Compute the log(mean(exp(x), dim)) in a numerically stable manner.
|
| 125 |
+
Adapted from CS 236 at Stanford.
|
| 126 |
+
|
| 127 |
+
Args:
|
| 128 |
+
x (torch.Tensor): a tensor
|
| 129 |
+
dim (int): dimension along which mean is computed
|
| 130 |
+
|
| 131 |
+
Returns:
|
| 132 |
+
y (torch.Tensor): log(mean(exp(x), dim))
|
| 133 |
+
"""
|
| 134 |
+
return log_sum_exp(x, dim) - np.log(x.size(dim))
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
def log_sum_exp(x, dim=0):
|
| 138 |
+
"""
|
| 139 |
+
Compute the log(sum(exp(x), dim)) in a numerically stable manner.
|
| 140 |
+
Adapted from CS 236 at Stanford.
|
| 141 |
+
|
| 142 |
+
Args:
|
| 143 |
+
x (torch.Tensor): a tensor
|
| 144 |
+
dim (int): dimension along which sum is computed
|
| 145 |
+
|
| 146 |
+
Returns:
|
| 147 |
+
y (torch.Tensor): log(sum(exp(x), dim))
|
| 148 |
+
"""
|
| 149 |
+
max_x = torch.max(x, dim)[0]
|
| 150 |
+
new_x = x - max_x.unsqueeze(dim).expand_as(x)
|
| 151 |
+
return max_x + (new_x.exp().sum(dim)).log()
|
| 152 |
+
|
| 153 |
+
|
| 154 |
+
def project_values_onto_atoms(values, probabilities, atoms):
|
| 155 |
+
"""
|
| 156 |
+
Project the categorical distribution given by @probabilities on the
|
| 157 |
+
grid of values given by @values onto a grid of values given by @atoms.
|
| 158 |
+
This is useful when computing a bellman backup where the backed up
|
| 159 |
+
values from the original grid will not be in the original support,
|
| 160 |
+
requiring L2 projection.
|
| 161 |
+
|
| 162 |
+
Each value in @values has a corresponding probability in @probabilities -
|
| 163 |
+
this probability mass is shifted to the closest neighboring grid points in
|
| 164 |
+
@atoms in proportion. For example, if the value in question is 0.2, and the
|
| 165 |
+
neighboring atoms are 0 and 1, then 0.8 of the probability weight goes to
|
| 166 |
+
atom 0 and 0.2 of the probability weight will go to 1.
|
| 167 |
+
|
| 168 |
+
Adapted from https://github.com/deepmind/acme/blob/master/acme/tf/losses/distributional.py#L42
|
| 169 |
+
|
| 170 |
+
Args:
|
| 171 |
+
values: value grid to project, of shape (batch_size, n_atoms)
|
| 172 |
+
probabilities: probabilities for categorical distribution on @values, shape (batch_size, n_atoms)
|
| 173 |
+
atoms: value grid to project onto, of shape (n_atoms,) or (1, n_atoms)
|
| 174 |
+
|
| 175 |
+
Returns:
|
| 176 |
+
new probability vectors that correspond to the L2 projection of the categorical distribution
|
| 177 |
+
onto @atoms
|
| 178 |
+
"""
|
| 179 |
+
|
| 180 |
+
# make sure @atoms is shape (n_atoms,)
|
| 181 |
+
if len(atoms.shape) > 1:
|
| 182 |
+
atoms = atoms.squeeze(0)
|
| 183 |
+
|
| 184 |
+
# helper tensors from @atoms
|
| 185 |
+
vmin, vmax = atoms[0], atoms[1]
|
| 186 |
+
d_pos = torch.cat([atoms, vmin[None]], dim=0)[1:]
|
| 187 |
+
d_neg = torch.cat([vmax[None], atoms], dim=0)[:-1]
|
| 188 |
+
|
| 189 |
+
# ensure that @values grid is within the support of @atoms
|
| 190 |
+
clipped_values = values.clamp(min=vmin, max=vmax)[:, None, :] # (batch_size, 1, n_atoms)
|
| 191 |
+
clipped_atoms = atoms[None, :, None] # (1, n_atoms, 1)
|
| 192 |
+
|
| 193 |
+
# distance between atom values in support
|
| 194 |
+
d_pos = (d_pos - atoms)[None, :, None] # atoms[i + 1] - atoms[i], shape (1, n_atoms, 1)
|
| 195 |
+
d_neg = (atoms - d_neg)[None, :, None] # atoms[i] - atoms[i - 1], shape (1, n_atoms, 1)
|
| 196 |
+
|
| 197 |
+
# distances between all pairs of grid values
|
| 198 |
+
deltas = clipped_values - clipped_atoms # (batch_size, n_atoms, n_atoms)
|
| 199 |
+
|
| 200 |
+
# computes eqn (7) in distributional RL paper by doing the following - for each
|
| 201 |
+
# output atom in @atoms, consider values that are close enough, and weight their
|
| 202 |
+
# probability mass contribution by the normalized distance in [0, 1] given
|
| 203 |
+
# by (1. - (z_j - z_i) / (delta_z)).
|
| 204 |
+
d_sign = (deltas >= 0.).float()
|
| 205 |
+
delta_hat = (d_sign * deltas / d_pos) - ((1. - d_sign) * deltas / d_neg)
|
| 206 |
+
delta_hat = (1. - delta_hat).clamp(min=0., max=1.)
|
| 207 |
+
probabilities = probabilities[:, None, :]
|
| 208 |
+
return (delta_hat * probabilities).sum(dim=2)
|