mirror of
https://github.com/huggingface/lerobot.git
synced 2026-08-03 23:19:52 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6986b46fa8 | |||
| b1d72ac29c | |||
| cffd545527 |
@@ -11,6 +11,7 @@ import os
|
|||||||
import signal
|
import signal
|
||||||
import statistics
|
import statistics
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
from copy import deepcopy
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@@ -19,18 +20,18 @@ import torch
|
|||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
|
|
||||||
from lerobot.configs.types import FeatureType
|
from lerobot.configs.types import FeatureType
|
||||||
from lerobot.policies.factory import get_policy_class
|
from lerobot.policies.factory import get_policy_class, make_pre_post_processors
|
||||||
from lerobot.policies.pretrained import PreTrainedPolicy
|
from lerobot.policies.pretrained import PreTrainedPolicy
|
||||||
|
|
||||||
|
|
||||||
class TimeoutException:
|
class TimeoutExceptionError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def timeout(seconds):
|
def timeout(seconds):
|
||||||
def signal_handler(signum, frame):
|
def signal_handler(signum, frame):
|
||||||
raise TimeoutException(f"Timed out after {seconds} seconds")
|
raise TimeoutExceptionError(f"Timed out after {seconds} seconds")
|
||||||
|
|
||||||
# On Windows, signal is not available, so we can't use this timeout mechanism
|
# On Windows, signal is not available, so we can't use this timeout mechanism
|
||||||
if not hasattr(signal, "SIGALRM"):
|
if not hasattr(signal, "SIGALRM"):
|
||||||
@@ -84,12 +85,12 @@ def generate_dummy_observation(input_features: dict, device: str = "cpu") -> dic
|
|||||||
# Default: random normal for unknown types
|
# Default: random normal for unknown types
|
||||||
dummy_obs[key] = torch.randn(shape, dtype=torch.float32, device=device)
|
dummy_obs[key] = torch.randn(shape, dtype=torch.float32, device=device)
|
||||||
|
|
||||||
# Add batch dimension
|
# # Add batch dimension
|
||||||
for key in dummy_obs:
|
# for key in dummy_obs:
|
||||||
dummy_obs[key] = dummy_obs[key].unsqueeze(0)
|
# dummy_obs[key] = dummy_obs[key].unsqueeze(0)
|
||||||
|
|
||||||
# Add task string for language-conditioned policies
|
# Add task string for language-conditioned policies
|
||||||
dummy_obs["task"] = ""
|
dummy_obs["task"] = " this is a dummy task"
|
||||||
|
|
||||||
return dummy_obs
|
return dummy_obs
|
||||||
|
|
||||||
@@ -151,7 +152,9 @@ def main():
|
|||||||
policy_class = get_policy_class(args.policy_type)
|
policy_class = get_policy_class(args.policy_type)
|
||||||
policy: PreTrainedPolicy = policy_class.from_pretrained(args.policy_id)
|
policy: PreTrainedPolicy = policy_class.from_pretrained(args.policy_id)
|
||||||
policy.eval()
|
policy.eval()
|
||||||
policy.to(device)
|
policy.to(device, torch.float32)
|
||||||
|
policy.config.device = device
|
||||||
|
preprocessor, postprocessor = make_pre_post_processors(policy.config)
|
||||||
|
|
||||||
print(f"Policy loaded on {device}")
|
print(f"Policy loaded on {device}")
|
||||||
print(f"Input features: {list(policy.config.input_features.keys())}")
|
print(f"Input features: {list(policy.config.input_features.keys())}")
|
||||||
@@ -159,7 +162,7 @@ def main():
|
|||||||
|
|
||||||
# Generate dummy observation based on policy input features
|
# Generate dummy observation based on policy input features
|
||||||
dummy_observation = generate_dummy_observation(policy.config.input_features, device)
|
dummy_observation = generate_dummy_observation(policy.config.input_features, device)
|
||||||
dummy_observation["task"] = ""
|
dummy_observation["task"] = "this is a dummy task"
|
||||||
|
|
||||||
# Helper to sync for fair timings
|
# Helper to sync for fair timings
|
||||||
def _sync(dev_=device):
|
def _sync(dev_=device):
|
||||||
@@ -175,8 +178,15 @@ def main():
|
|||||||
print("Warming up...")
|
print("Warming up...")
|
||||||
with torch.no_grad():
|
with torch.no_grad():
|
||||||
policy.reset()
|
policy.reset()
|
||||||
|
preprocessor.reset()
|
||||||
|
postprocessor.reset()
|
||||||
|
orginal_dummy_observation = deepcopy(dummy_observation)
|
||||||
for _ in range(args.warmup):
|
for _ in range(args.warmup):
|
||||||
_ = policy.select_action(dummy_observation)
|
dummy_observation_model = deepcopy(orginal_dummy_observation)
|
||||||
|
dummy_observation_model = preprocessor(dummy_observation_model)
|
||||||
|
action_model = policy.select_action(dummy_observation_model)
|
||||||
|
_ = postprocessor(action_model)
|
||||||
|
policy.reset()
|
||||||
_sync()
|
_sync()
|
||||||
|
|
||||||
# Memory footprint before timing
|
# Memory footprint before timing
|
||||||
@@ -193,20 +203,25 @@ def main():
|
|||||||
start_events = []
|
start_events = []
|
||||||
end_events = []
|
end_events = []
|
||||||
timeout_count = 0
|
timeout_count = 0
|
||||||
|
orginal_dummy_observation = deepcopy(dummy_observation)
|
||||||
|
|
||||||
with torch.no_grad():
|
with torch.no_grad():
|
||||||
for forward in tqdm(range(args.num_samples), desc="Trials"):
|
for forward in tqdm(range(args.num_samples), desc="Trials"):
|
||||||
start_event = torch.cuda.Event(enable_timing=True)
|
start_event = torch.cuda.Event(enable_timing=True)
|
||||||
end_event = torch.cuda.Event(enable_timing=True)
|
end_event = torch.cuda.Event(enable_timing=True)
|
||||||
try:
|
try:
|
||||||
|
dummy_observation_model = deepcopy(orginal_dummy_observation)
|
||||||
|
dummy_observation_model = preprocessor(dummy_observation)
|
||||||
with timeout(args.timeout):
|
with timeout(args.timeout):
|
||||||
start_event.record()
|
start_event.record()
|
||||||
_ = policy.select_action(dummy_observation)
|
action_model = policy.select_action(dummy_observation_model)
|
||||||
end_event.record()
|
end_event.record()
|
||||||
|
_ = postprocessor(action_model)
|
||||||
|
policy.reset()
|
||||||
|
|
||||||
start_events.append(start_event)
|
start_events.append(start_event)
|
||||||
end_events.append(end_event)
|
end_events.append(end_event)
|
||||||
except TimeoutException:
|
except TimeoutExceptionError:
|
||||||
timeout_count += 1
|
timeout_count += 1
|
||||||
# Add placeholder for timeout
|
# Add placeholder for timeout
|
||||||
start_events.append(None)
|
start_events.append(None)
|
||||||
@@ -219,7 +234,8 @@ def main():
|
|||||||
per_forward_ms = []
|
per_forward_ms = []
|
||||||
for start_event, end_event in zip(start_events, end_events, strict=True):
|
for start_event, end_event in zip(start_events, end_events, strict=True):
|
||||||
if start_event is None:
|
if start_event is None:
|
||||||
per_forward_ms.append(args.timeout * 1000)
|
# per_forward_ms.append(args.timeout * 1000)
|
||||||
|
continue
|
||||||
else:
|
else:
|
||||||
per_forward_ms.append(start_event.elapsed_time(end_event))
|
per_forward_ms.append(start_event.elapsed_time(end_event))
|
||||||
|
|
||||||
@@ -236,20 +252,25 @@ def main():
|
|||||||
with torch.no_grad():
|
with torch.no_grad():
|
||||||
for sample in tqdm(range(args.num_samples), desc="Samples"):
|
for sample in tqdm(range(args.num_samples), desc="Samples"):
|
||||||
try:
|
try:
|
||||||
|
dummy_observation_model = deepcopy(orginal_dummy_observation)
|
||||||
|
dummy_observation_model = preprocessor(dummy_observation_model)
|
||||||
with timeout(args.timeout):
|
with timeout(args.timeout):
|
||||||
start_time = time.perf_counter()
|
start_time = time.perf_counter()
|
||||||
_ = policy.select_action(dummy_observation)
|
action_model = policy.select_action(dummy_observation_model)
|
||||||
end_time = time.perf_counter()
|
end_time = time.perf_counter()
|
||||||
|
policy.reset()
|
||||||
|
|
||||||
per_forward_ms.append((end_time - start_time) * 1000) # Convert to ms
|
per_forward_ms.append((end_time - start_time) * 1000) # Convert to ms
|
||||||
except TimeoutException:
|
except TimeoutExceptionError:
|
||||||
timeout_count += 1
|
timeout_count += 1
|
||||||
per_forward_ms.append(args.timeout * 1000)
|
# per_forward_ms.append(args.timeout * 1000)
|
||||||
|
|
||||||
print(f"\n[!] Timeout on sample {sample + 1}")
|
print(f"\n[!] Timeout on sample {sample + 1}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if timeout_count > 0:
|
if timeout_count > 0:
|
||||||
print(f"[!] {timeout_count} inference passes timed out (>{args.timeout * 1000:.1f}ms)")
|
print(f"[!] {timeout_count} inference passes timed out (>{args.timeout * 1000:.1f}ms)")
|
||||||
|
print(f"Timeout percentage: {timeout_count / args.num_samples * 100:.1f}%")
|
||||||
|
|
||||||
# Memory footprint after timing
|
# Memory footprint after timing
|
||||||
rss_after = process.memory_info().rss
|
rss_after = process.memory_info().rss
|
||||||
@@ -355,6 +376,7 @@ Benchmark completed successfully at {datetime.now().strftime("%Y-%m-%d %H:%M:%S"
|
|||||||
print(f"Device: {device}")
|
print(f"Device: {device}")
|
||||||
print(f"Samples: {args.num_samples} | Warmup: {args.warmup}")
|
print(f"Samples: {args.num_samples} | Warmup: {args.warmup}")
|
||||||
print(f"Model params: {num_params:,}")
|
print(f"Model params: {num_params:,}")
|
||||||
|
print(f"Timeout percentage: {timeout_count / args.num_samples * 100:.1f}%")
|
||||||
|
|
||||||
print("\nLatency per forward (ms):")
|
print("\nLatency per forward (ms):")
|
||||||
print(f" mean: {mean_ms:.3f} std: {std_ms:.3f}")
|
print(f" mean: {mean_ms:.3f} std: {std_ms:.3f}")
|
||||||
|
|||||||
@@ -493,7 +493,7 @@ class PI0FlowMatching(nn.Module):
|
|||||||
img_mask,
|
img_mask,
|
||||||
) in zip(images, img_masks, strict=False):
|
) in zip(images, img_masks, strict=False):
|
||||||
img_emb = self.paligemma_with_expert.embed_image(img)
|
img_emb = self.paligemma_with_expert.embed_image(img)
|
||||||
img_emb = img_emb.to(dtype=torch.bfloat16)
|
img_emb = img_emb.to(dtype=torch.float32)
|
||||||
|
|
||||||
# Normalize image embeddings
|
# Normalize image embeddings
|
||||||
img_emb_dim = img_emb.shape[-1]
|
img_emb_dim = img_emb.shape[-1]
|
||||||
@@ -536,7 +536,7 @@ class PI0FlowMatching(nn.Module):
|
|||||||
|
|
||||||
# Embed state
|
# Embed state
|
||||||
state_emb = self.state_proj(state)
|
state_emb = self.state_proj(state)
|
||||||
state_emb = state_emb.to(dtype=torch.bfloat16)
|
state_emb = state_emb.to(dtype=torch.float32)
|
||||||
embs.append(state_emb[:, None, :])
|
embs.append(state_emb[:, None, :])
|
||||||
bsize = state_emb.shape[0]
|
bsize = state_emb.shape[0]
|
||||||
dtype = state_emb.dtype
|
dtype = state_emb.dtype
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ class PaliGemmaWithExpertModel(PreTrainedModel):
|
|||||||
self.paligemma.eval()
|
self.paligemma.eval()
|
||||||
|
|
||||||
def to_bfloat16_like_physical_intelligence(self):
|
def to_bfloat16_like_physical_intelligence(self):
|
||||||
self.paligemma = self.paligemma.to(dtype=torch.bfloat16)
|
self.paligemma = self.paligemma.to(dtype=torch.float32)
|
||||||
|
|
||||||
params_to_change_dtype = [
|
params_to_change_dtype = [
|
||||||
"language_model.model.layers",
|
"language_model.model.layers",
|
||||||
@@ -212,7 +212,7 @@ class PaliGemmaWithExpertModel(PreTrainedModel):
|
|||||||
]
|
]
|
||||||
for name, param in self.named_parameters():
|
for name, param in self.named_parameters():
|
||||||
if any(selector in name for selector in params_to_change_dtype):
|
if any(selector in name for selector in params_to_change_dtype):
|
||||||
param.data = param.data.to(dtype=torch.bfloat16)
|
param.data = param.data.to(dtype=torch.float32)
|
||||||
|
|
||||||
def embed_image(self, image: torch.Tensor):
|
def embed_image(self, image: torch.Tensor):
|
||||||
# Handle different transformers versions
|
# Handle different transformers versions
|
||||||
@@ -262,7 +262,7 @@ class PaliGemmaWithExpertModel(PreTrainedModel):
|
|||||||
input_shape = hidden_states.shape[:-1]
|
input_shape = hidden_states.shape[:-1]
|
||||||
hidden_shape = (*input_shape, -1, layer.self_attn.head_dim)
|
hidden_shape = (*input_shape, -1, layer.self_attn.head_dim)
|
||||||
|
|
||||||
hidden_states = hidden_states.to(dtype=torch.bfloat16)
|
hidden_states = hidden_states.to(dtype=torch.float32)
|
||||||
query_state = layer.self_attn.q_proj(hidden_states).view(hidden_shape)
|
query_state = layer.self_attn.q_proj(hidden_states).view(hidden_shape)
|
||||||
key_state = layer.self_attn.k_proj(hidden_states).view(hidden_shape)
|
key_state = layer.self_attn.k_proj(hidden_states).view(hidden_shape)
|
||||||
value_state = layer.self_attn.v_proj(hidden_states).view(hidden_shape)
|
value_state = layer.self_attn.v_proj(hidden_states).view(hidden_shape)
|
||||||
@@ -303,7 +303,7 @@ class PaliGemmaWithExpertModel(PreTrainedModel):
|
|||||||
att_output = attention_interface(
|
att_output = attention_interface(
|
||||||
attention_mask, batch_size, head_dim, query_states, key_states, value_states
|
attention_mask, batch_size, head_dim, query_states, key_states, value_states
|
||||||
)
|
)
|
||||||
att_output = att_output.to(dtype=torch.bfloat16)
|
att_output = att_output.to(dtype=torch.float32)
|
||||||
|
|
||||||
# first part of att_output is prefix (up to sequence length, [:, 0:prefix_seq_len])
|
# first part of att_output is prefix (up to sequence length, [:, 0:prefix_seq_len])
|
||||||
outputs_embeds = []
|
outputs_embeds = []
|
||||||
|
|||||||
Reference in New Issue
Block a user