poet70's picture
Upload folder using huggingface_hub
97dbcca verified
Raw
History Blame Contribute Delete
18.7 kB
import torch, numpy as np, os, pickle
from einops import rearrange
import matplotlib.pyplot as plt
import time
from torchvision import transforms
from module.policy import ACTPolicy, CNNMLPPolicy, DiffusionPolicy
from module.policy_jepa import ACTJEPAPolicy
from module.policy_jepa_adapter import ACTJEPAAdapterPolicy
from module.policy_jepa_adapter_with_hsa import ACTJEPAHsa
from detr.models.latent_model import Latent_Model_Transformer
from ModelTrain.model_train import arg_config
def set_config():
args = arg_config()
ckpt_dir = args["ckpt_dir"]
policy_class = args.get("policy_class", "ACT") # Get from args, default to ACT
task_name = args["task_name"]
batch_size_train = args["batch_size"]
batch_size_val = args["batch_size"]
num_steps = args["num_steps"]
eval_every = args["eval_every"]
validate_every = args["validate_every"]
save_every = args["save_every"]
resume_ckpt_path = args["resume_ckpt_path"]
is_sim = task_name[:4] == "sim_" if len(task_name) >= 4 else False
if is_sim or task_name == "all":
from constants import SIM_TASK_CONFIGS
task_config = SIM_TASK_CONFIGS[task_name]
else:
from constants import TASK_CONFIGS
task_config = TASK_CONFIGS[task_name]
# Extract task config parameters (works for both sim and real tasks)
dataset_dir = task_config["dataset_dir"]
episode_len = task_config["episode_len"]
camera_names = task_config["camera_names"]
tactile_camera_names = task_config.get("tactile_camera_names", [])
stats_dir = task_config.get("stats_dir", None)
sample_weights = task_config.get("sample_weights", None)
train_ratio = task_config.get("train_ratio", 0.99)
name_filter = task_config.get("name_filter", lambda n: True)
state_dim = task_config.get('state_dim', 14)
action_dim = task_config.get('action_dim', 16)
lr_backbone = 1e-05
backbone = "resnet18"
if policy_class == "ACT":
enc_layers = 4
dec_layers = 7
nheads = 8
policy_config = {'lr':args["lr"], 'num_queries':args["chunk_size"],
'kl_weight':args["kl_weight"],
'hidden_dim':args["hidden_dim"],
'dim_feedforward':args["dim_feedforward"],
'lr_backbone':lr_backbone,
'backbone':backbone,
'enc_layers':enc_layers,
'dec_layers':dec_layers,
'nheads':nheads,
'camera_names':camera_names,
'tactile_camera_names':tactile_camera_names,
'vq':False,
'vq_class':None,
'vq_dim':None,
'action_dim':action_dim,
'no_encoder':args["no_encoder"],
'use_vitg':args.get("use_vitg", False),
'vitg_ckpt_path':args.get("vitg_ckpt_path", None) or args.get("vit_ckpt_path", None),
'clip_model':args.get("clip_model", None),
'clip_pretrained':args.get("clip_pretrained", "openai"),
'freeze_clip':args.get("freeze_clip", False)}
elif policy_class == "ACTJEPA":
enc_layers = 4
dec_layers = 7
nheads = 8
vit_ckpt = args.get("vit_ckpt_path") or args.get("vitg_ckpt_path")
policy_config = {'lr':args["lr"], 'num_queries':args["chunk_size"],
'kl_weight':args["kl_weight"],
'hidden_dim':args["hidden_dim"],
'dim_feedforward':args["dim_feedforward"],
'lr_backbone':lr_backbone,
'backbone':backbone,
'enc_layers':enc_layers,
'dec_layers':dec_layers,
'nheads':nheads,
'camera_names':camera_names,
'tactile_camera_names':tactile_camera_names,
'vq':False,
'vq_class':None,
'vq_dim':None,
'action_dim':action_dim,
'no_encoder':args["no_encoder"],
'use_vitg':True,
'vitg_ckpt_path':vit_ckpt,
'vit_model':args.get("vit_model", "vitg"),
'clip_model':args.get("clip_model", None),
'clip_pretrained':args.get("clip_pretrained", "openai"),
'freeze_clip':args.get("freeze_clip", False)}
elif policy_class == "ACTJEPAAdapter":
enc_layers = 4
dec_layers = 7
nheads = 8
vit_ckpt = args.get("vit_ckpt_path") or args.get("vitg_ckpt_path")
policy_config = {'lr':args["lr"], 'num_queries':args["chunk_size"],
'kl_weight':args["kl_weight"],
'hidden_dim':args["hidden_dim"],
'dim_feedforward':args["dim_feedforward"],
'lr_backbone':lr_backbone,
'backbone':backbone,
'enc_layers':enc_layers,
'dec_layers':dec_layers,
'nheads':nheads,
'camera_names':camera_names,
'tactile_camera_names':tactile_camera_names,
'vq':False,
'vq_class':None,
'vq_dim':None,
'action_dim':action_dim,
'no_encoder':args["no_encoder"],
'use_vitg':True,
'vitg_ckpt_path':vit_ckpt,
'vit_model':args.get("vit_model", "vitg"),
'adapter_hidden_dim':args.get("adapter_hidden_dim", 512),
'adapter_depth':args.get("adapter_depth", 3),
'adapter_dropout':args.get("adapter_dropout", 0.1),
'adapter_scale_init':args.get("adapter_scale_init", 0.1),
'adapter_pooling':args.get("adapter_pooling", "attention")}
else:
if policy_class == "Diffusion":
policy_config = {'lr':args["lr"], 'camera_names':camera_names,
'action_dim':action_dim,
'observation_horizon':1,
'action_horizon':8,
'prediction_horizon':args["chunk_size"],
'num_queries':args["chunk_size"],
'num_inference_timesteps':10,
'ema_power':0.75,
'vq':False}
else:
if policy_class == "CNNMLP":
policy_config = {'lr':args["lr"],
'lr_backbone':lr_backbone, 'backbone':backbone, 'num_queries':1, 'camera_names':camera_names}
else:
raise NotImplementedError
# Use all cameras (RGB + tactile) for inference
all_camera_names = camera_names + tactile_camera_names
config = {'num_steps':num_steps, 'eval_every':eval_every,
'validate_every':validate_every,
'save_every':save_every,
'ckpt_dir':ckpt_dir,
'resume_ckpt_path':resume_ckpt_path,
'episode_len':episode_len,
'state_dim':state_dim,
'lr':args["lr"],
'policy_class':policy_class,
'policy_config':policy_config,
'task_name':task_name,
'seed':args["seed"],
'temporal_agg':args["temporal_agg"],
'camera_names':all_camera_names, # Use all cameras for inference
'real_robot':not is_sim,
'load_pretrain':args["load_pretrain"]}
return config
class Imitate_Model:
def __init__(self, ckpt_dir=None, ckpt_name='policy_last.ckpt'):
config = set_config()
self.ckpt_name = ckpt_name
if ckpt_dir == None:
self.ckpt_dir = config["ckpt_dir"]
print(self.ckpt_dir)
else:
self.ckpt_dir = ckpt_dir
self.state_dim = config["state_dim"]
self.policy_class = config["policy_class"]
self.policy_config = config["policy_config"]
self.camera_names = config["camera_names"]
self.max_timesteps = config["episode_len"]
self.temporal_agg = config["temporal_agg"]
self.vq = config["policy_config"]["vq"]
self.t = 0
# Store tactile camera names for proper image processing
self.tactile_camera_names = config["policy_config"].get("tactile_camera_names", [])
def __make_policy(self):
if self.policy_class == "ACT":
policy = ACTPolicy(self.policy_config)
elif self.policy_class == "ACTJEPA":
policy = ACTJEPAPolicy(self.policy_config)
elif self.policy_class == "ACTJEPAAdapter":
policy = ACTJEPAAdapterPolicy(self.policy_config)
elif self.policy_class == "ACTJEPAHsa":
# For inference, create ACTJEPAHsa with HSA disabled (no loss computation needed)
hsa_config = {'enable_hsa': False}
policy = ACTJEPAHsa(self.policy_config, hsa_config)
elif self.policy_class == "CNNMLP":
policy = CNNMLPPolicy(self.policy_config)
elif self.policy_class == "Diffusion":
policy = DiffusionPolicy(self.policy_config)
else:
raise NotImplementedError
return policy
def __image_process(self, observation, camera_names, rand_crop_resize=False):
# For JEPA policies: separate RGB and tactile (different resolutions)
# Match training logic from train_module.py forward_pass()
if self.policy_class in ["ACTJEPA", "ACTJEPAAdapter"] and self.tactile_camera_names:
# Separate RGB cameras from tactile sensors
rgb_cameras = [cam for cam in camera_names if cam not in self.tactile_camera_names]
# Process RGB camera images
rgb_images = []
for cam_name in rgb_cameras:
if "images" in observation and cam_name in observation["images"]:
curr_image = rearrange(observation["images"][cam_name], "h w c -> c h w")
else:
raise KeyError(f"Cannot find RGB camera {cam_name} in observation['images']")
rgb_images.append(curr_image)
# Stack RGB images
rgb_stacked = np.stack(rgb_images, axis=0) # (num_rgb, C, H, W)
rgb_tensor = torch.from_numpy(rgb_stacked / 255.0).float().cuda().unsqueeze(0) # (1, num_rgb, C, H, W)
# Process tactile images
tactile_images = []
for cam_name in self.tactile_camera_names:
if cam_name in observation:
curr_image = rearrange(observation[cam_name], "h w c -> c h w")
else:
raise KeyError(f"Cannot find tactile sensor {cam_name} in observation")
tactile_images.append(curr_image)
# Stack tactile images
tactile_stacked = np.stack(tactile_images, axis=0) # (num_tactile, C, H, W)
tactile_tensor = torch.from_numpy(tactile_stacked / 255.0).float().cuda().unsqueeze(0) # (1, num_tactile, C, H, W)
# Return as list (can't concatenate due to different spatial sizes)
return [rgb_tensor, tactile_tensor]
else:
# Original logic for non-JEPA policies (all cameras same resolution)
curr_images = []
for cam_name in camera_names:
# Try observation["images"][cam_name] first (RGB cameras)
# Then try observation[cam_name] directly (tactile sensors)
if "images" in observation and cam_name in observation["images"]:
curr_image = rearrange(observation["images"][cam_name], "h w c -> c h w")
elif cam_name in observation:
curr_image = rearrange(observation[cam_name], "h w c -> c h w")
else:
raise KeyError(f"Cannot find {cam_name} in observation['images'] or observation")
curr_images.append(curr_image)
curr_image = np.stack(curr_images, axis=0)
curr_image = torch.from_numpy(curr_image / 255.0).float().cuda().unsqueeze(0)
if rand_crop_resize:
print("rand crop resize is used!")
original_size = curr_image.shape[-2:]
ratio = 0.95
curr_image = curr_image[...,
int(original_size[0] * (1 - ratio) / 2):int(original_size[0] * (1 + ratio) / 2),
int(original_size[1] * (1 - ratio) / 2):int(original_size[1] * (1 + ratio) / 2)]
curr_image = curr_image.squeeze(0)
resize_transform = transforms.Resize(original_size, antialias=True)
curr_image = resize_transform(curr_image)
curr_image = curr_image.unsqueeze(0)
return curr_image
def __get_auto_index(self, dataset_dir):
max_idx = 1000
for i in range(max_idx + 1):
if not os.path.isfile(os.path.join(dataset_dir, f"qpos_{i}.npy")):
return i
else:
raise Exception(f"Error getting auto index, or more than {max_idx} episodes")
def loadModel(self):
cur_path = os.path.dirname(os.path.abspath(__file__))
dir_path = os.path.dirname(os.path.dirname(cur_path)) # Go up two levels to project root
ckpt_path = os.path.join(self.ckpt_dir, self.ckpt_name)
ckpt_path = dir_path + ckpt_path[1:]
self.policy = self._Imitate_Model__make_policy()
loading_status = self.policy.deserialize(torch.load(ckpt_path))
print(loading_status)
self.policy.cuda()
self.policy.eval()
if self.vq:
vq_dim = self.config["policy_config"]["vq_dim"]
vq_class = self.config["policy_config"]["vq_class"]
latent_model = Latent_Model_Transformer(vq_dim, vq_dim, vq_class)
latent_model_ckpt_path = os.path.join(self.ckpt_dir, "latent_model_last.ckpt")
latent_model.deserialize(torch.load(latent_model_ckpt_path))
latent_model.eval()
latent_model.cuda()
print(f"Loaded policy from: {ckpt_path}, latent model from: {latent_model_ckpt_path}")
else:
print(f"Loaded: {ckpt_path}")
stats_path = os.path.join(dir_path + self.ckpt_dir[1:], "dataset_stats.pkl")
with open(stats_path, "rb") as f:
stats = pickle.load(f)
self.pre_process = lambda s_qpos: (s_qpos - stats["qpos_mean"]) / stats["qpos_std"]
if self.policy_class == "Diffusion":
self.post_process = lambda a: (a + 1) / 2 * (stats["action_max"] - stats["action_min"]) + stats["action_min"]
else:
self.post_process = lambda a: a * stats["action_std"] + stats["action_mean"]
self.query_frequency = self.policy_config["num_queries"]
if self.temporal_agg:
self.query_frequency = 1
self.num_queries = self.policy_config["num_queries"]
self.max_timesteps = int(self.max_timesteps * 1)
self.episode_returns = []
self.highest_rewards = []
if self.temporal_agg:
self.all_time_actions = torch.zeros([self.max_timesteps, self.max_timesteps + self.num_queries, 16]).cuda()
self.qpos_history_raw = np.zeros((self.max_timesteps, self.state_dim))
self.image_list = []
self.qpos_list = []
self.target_qpos_list = []
self.rewards = []
self.all_actions = []
def predict(self, observation, t, save_qpos_history=False):
with torch.inference_mode():
raw_action = None
qpos_numpy = np.array(observation["qpos"])
self.qpos_history_raw[t] = qpos_numpy
qpos = self.pre_process(qpos_numpy)
qpos = torch.from_numpy(qpos).float().cuda().unsqueeze(0)
# Get current image if at query frequency boundary
if t % self.query_frequency == 0:
curr_image = self._Imitate_Model__image_process(observation, (self.camera_names), rand_crop_resize=(self.policy_class == "Diffusion"))
# Warmup at t=0
if t == 0:
for _ in range(10):
self.policy(qpos, curr_image)
print("network warm up done")
time1 = time.time()
# Prediction logic based on policy class
if self.policy_class in ["ACT", "ACTJEPA", "ACTJEPAAdapter"]:
# Query the policy at the specified frequency
if t % self.query_frequency == 0:
if self.vq:
self.vq_sample = self.latent_model.generate(1, temperature=1, x=None)
self.all_actions = self.policy(qpos, curr_image, vq_sample=(self.vq_sample))
else:
self.all_actions = self.policy(qpos, curr_image)
# Extract action for current timestep
if self.temporal_agg:
self.all_time_actions[[t], t:t + self.num_queries] = self.all_actions
actions_for_curr_step = self.all_time_actions[:, t]
actions_populated = torch.all((actions_for_curr_step != 0), axis=1)
actions_for_curr_step = actions_for_curr_step[actions_populated]
k = 0.01
exp_weights = np.exp(-k * np.arange(len(actions_for_curr_step)))
exp_weights = exp_weights / exp_weights.sum()
exp_weights = torch.from_numpy(exp_weights).cuda().unsqueeze(dim=1)
raw_action = (actions_for_curr_step * exp_weights).sum(dim=0, keepdim=True)
else:
raw_action = self.all_actions[:, t % self.query_frequency]
else:
if self.config["policy_class"] == "Diffusion":
if t % self.query_frequency == 0:
self.all_actions = self.policy(qpos, curr_image)
raw_action = self.all_actions[:, t % self.query_frequency]
else:
if self.config["policy_class"] == "CNNMLP":
raw_action = self.policy(qpos, curr_image)
self.all_actions = raw_action.unsqueeze(0)
else:
raise NotImplementedError
raw_action = raw_action.squeeze(0).cpu().numpy()
action = self.post_process(raw_action)
target_qpos = action[:-2]
base_action = action[-2:]
self.qpos_list.append(qpos_numpy)
self.target_qpos_list.append(target_qpos)
if save_qpos_history:
log_id = self._Imitate_Model__get_auto_index(self.ckpt_dir)
np.save(os.path.join(self.ckpt_dir, f"qpos_{log_id}.npy"), self.qpos_history_raw)
plt.figure(figsize=(10, 20))
for i in range(self.state_dim):
plt.subplot(self.state_dim, 1, i + 1)
plt.plot(self.qpos_history_raw[:, i])
if i != self.state_dim - 1:
plt.xticks([])
plt.tight_layout()
plt.savefig(os.path.join(self.ckpt_dir, f"qpos_{log_id}.png"))
plt.close()
return target_qpos
# okay decompiling model_module.pyc