PWM / PWM_code /reward_model /robometer /scripts /robometer_wrapper.py
hchen657Naza's picture
Add files using upload-large-folder tool
dd253d4 verified
Raw History Blame Contribute Delete
5.9 kB
#!/usr/bin/env python3
"""Minimal Robometer wrapper that resizes frames to fit in memory."""
from __future__ import annotations
import argparse
import os
from pathlib import Path
from typing import Optional, Tuple
import numpy as np
import torch
import cv2
# Must import unsloth first
import unsloth # noqa
from robometer.data.dataset_types import ProgressSample, Trajectory
from robometer.evals.eval_server import compute_batch_outputs
from robometer.evals.eval_viz_utils import extract_frames
from robometer.utils.save import load_model_from_hf
from robometer.utils.setup_utils import setup_batch_collator
def resize_frames(frames: np.ndarray, max_side: int = 256) -> np.ndarray:
"""Resize frames so max side = max_side."""
T, H, W, C = frames.shape
scale = max_side / max(H, W)
if scale < 1:
new_h = (int(H * scale) // 28) * 28 # must be multiple of 28 for qwen-vl
new_w = (int(W * scale) // 28) * 28
out = np.zeros((T, new_h, new_w, C), dtype=np.uint8)
for i in range(T):
out[i] = cv2.resize(frames[i], (new_w, new_h), interpolation=cv2.INTER_AREA)
return out
return frames
_CACHED_MODEL = None
def load_robometer_once(model_path: str = "robometer/Robometer-4B", device: str = "cuda"):
"""Load model once globally."""
global _CACHED_MODEL
if _CACHED_MODEL is None:
device = torch.device(device)
exp_config, tokenizer, processor, reward_model = load_model_from_hf(
model_path=model_path, device=device,
)
reward_model.eval()
batch_collator = setup_batch_collator(processor, tokenizer, exp_config, is_eval=True)
_CACHED_MODEL = dict(
model=reward_model, tokenizer=tokenizer, processor=processor,
batch_collator=batch_collator, exp_config=exp_config, device=device,
)
return _CACHED_MODEL
def compute_rewards(
video_frames: np.ndarray,
task: str,
max_side: int = 256,
state: Optional[dict] = None,
) -> Tuple[np.ndarray, np.ndarray]:
"""
Compute per-frame progress and success probability.
Args:
video_frames: (T, H, W, C) uint8
task: task instruction string
max_side: resize frames so max dim = max_side
Returns:
(progress, success): both arrays of length T
"""
if state is None:
state = load_robometer_once()
frames = resize_frames(video_frames, max_side=max_side)
T = int(frames.shape[0])
traj = Trajectory(
frames=frames,
frames_shape=tuple(frames.shape),
task=task,
id="0",
metadata={"subsequence_length": T},
video_embeddings=None,
)
progress_sample = ProgressSample(trajectory=traj, sample_type="progress")
batch = state["batch_collator"]([progress_sample])
progress_inputs = batch["progress_inputs"]
for key, value in progress_inputs.items():
if hasattr(value, "to"):
progress_inputs[key] = value.to(state["device"])
loss_config = getattr(state["exp_config"], "loss", None)
is_discrete = (
getattr(loss_config, "progress_loss_type", "l2").lower() == "discrete"
if loss_config else False
)
num_bins = (
getattr(loss_config, "progress_discrete_bins", None)
or getattr(state["exp_config"].model, "progress_discrete_bins", 10)
)
with torch.inference_mode():
results = compute_batch_outputs(
state["model"], state["tokenizer"], progress_inputs,
sample_type="progress", is_discrete_mode=is_discrete, num_bins=num_bins,
)
progress_pred = results.get("progress_pred", [])
progress_array = (
np.array(progress_pred[0], dtype=np.float32)
if progress_pred and len(progress_pred) > 0
else np.array([], dtype=np.float32)
)
outputs_success = results.get("outputs_success", {})
success_probs = outputs_success.get("success_probs", []) if outputs_success else []
success_array = (
np.array(success_probs[0], dtype=np.float32)
if success_probs and len(success_probs) > 0
else np.array([], dtype=np.float32)
)
return progress_array, success_array
def score_video_file(
video_path: str,
task: str,
fps: float = 3.0,
max_frames: int = 8,
max_side: int = 256,
state: Optional[dict] = None,
) -> Tuple[np.ndarray, np.ndarray]:
frames = extract_frames(video_path, fps=fps, max_frames=max_frames)
if frames is None or frames.size == 0:
raise RuntimeError(f"Could not extract frames from {video_path}")
return compute_rewards(frames, task, max_side=max_side, state=state)
if __name__ == "__main__":
p = argparse.ArgumentParser()
p.add_argument("--video", required=True)
p.add_argument("--task", required=True)
p.add_argument("--model-path", default="robometer/Robometer-4B")
p.add_argument("--fps", type=float, default=3.0)
p.add_argument("--max-frames", type=int, default=8)
p.add_argument("--max-side", type=int, default=256)
p.add_argument("--out", default="/tmp/robometer_out")
args = p.parse_args()
print(f"Loading model from {args.model_path}...")
state = load_robometer_once(args.model_path)
print(f"Scoring video: {args.video}")
progress, success = score_video_file(
args.video, args.task,
fps=args.fps, max_frames=args.max_frames, max_side=args.max_side,
state=state,
)
Path(args.out).mkdir(parents=True, exist_ok=True)
np.save(os.path.join(args.out, "progress.npy"), progress)
np.save(os.path.join(args.out, "success.npy"), success)
print(f"Progress: shape={progress.shape}, values={progress.tolist()}")
print(f"Success: shape={success.shape}, values={success.tolist()}")
print(f"Mean progress: {float(np.mean(progress)):.3f}")
print(f"Mean success: {float(np.mean(success)):.3f}")