#!/usr/bin/env python3 """GO-1 e2e inference smoke test on Thor. Validates: * model load on Thor sm_110 GPU * one forward pass via model(**inputs) with the full dataset-style input dict, mirroring evaluate/deploy.py:multi_image_get_item + predict_action Run: export TMPDIR=/data/agi/tmp /data/agi/venvs/go1_torch/bin/python /data/agi/check_model_load.py """ import os import sys import time import traceback import go1_env # 路径来自 go1_env(GO1_* 环境变量, 仓库相对默认) # Stub `decord` — no aarch64 wheel and only used for video reading in training, # but dataset.py imports it at module level. Inference doesn't need it. import types as _types if "decord" not in sys.modules: _fake = _types.ModuleType("decord") _fake.VideoReader = type("VideoReader", (), {}) _fake.cpu = lambda *a, **k: None _fake.bridge = _types.ModuleType("decord.bridge") _fake.bridge.set_bridge = lambda *a, **k: None sys.modules["decord"] = _fake sys.modules["decord.bridge"] = _fake.bridge import torch import numpy as np from PIL import Image from go1_env import CKPT # ── Header ──────────────────────────────────────────────────────────────── print("=" * 60) print(f"torch : {torch.__version__}") print(f"cuda available: {torch.cuda.is_available()}") if not torch.cuda.is_available(): print("✗ CUDA unavailable") raise SystemExit(1) print(f"device : {torch.cuda.get_device_name(0)} (cap {torch.cuda.get_device_capability(0)})") free_before, total = torch.cuda.mem_get_info() print(f"gpu mem free : {free_before / 1e9:.2f} / {total / 1e9:.2f} GB") print("=" * 60) # ── Stage 1: import + load model ────────────────────────────────────────── print("\n[1/3] Load model") t0 = time.perf_counter() from go1.internvl.model.go1 import GO1Model, GO1ModelConfig from go1.internvl.train.constants import IMG_END_TOKEN from go1.internvl.train.dataset import build_transform, dynamic_preprocess, preprocess_internvl2_5 from transformers import AutoTokenizer print(f" imports: {time.perf_counter()-t0:.2f}s") t0 = time.perf_counter() config = GO1ModelConfig.from_pretrained(CKPT, torch_dtype=torch.bfloat16, low_cpu_mem_usage=True) print(f" config: {time.perf_counter()-t0:.2f}s (action_chunk={config.action_chunk_size}, " f"img_size={config.force_image_size}, dynamic={config.dynamic_image_size}, " f"max_patch={config.max_dynamic_patch}, use_thumbnail={config.use_thumbnail})") t0 = time.perf_counter() model = GO1Model.from_pretrained(CKPT, config=config).to(torch.bfloat16).to("cuda").eval() torch.cuda.synchronize() print(f" weights+cuda: {time.perf_counter()-t0:.2f}s") n_params = sum(p.numel() for p in model.parameters()) gpu_used = (free_before - torch.cuda.mem_get_info()[0]) / 1e9 print(f" ✓ {n_params/1e9:.2f}B params, GPU mem occupied: {gpu_used:.2f} GB") tokenizer = AutoTokenizer.from_pretrained(CKPT, trust_remote_code=True, use_fast=False, add_eos_token=False) img_transform = build_transform(is_train=False, input_size=config.force_image_size, pad2square=config.pad2square) num_image_token = int((config.force_image_size // config.vision_config.patch_size) ** 2 * (config.downsample_ratio ** 2)) print(f" tokenizer + transforms ready (num_image_token={num_image_token})") # ── Stage 2: build dataset-style sample (3 random images + prompt + state) ─ print("\n[2/3] Build input sample") def make_sample(prompt, images_pil, state, ctrl_freq): """Replicates evaluate/deploy.py:multi_image_get_item, no server deps.""" images, num_tiles = [], [] num_image = 0 for img in images_pil: num_image += 1 if config.dynamic_image_size: tiles = dynamic_preprocess(img, min_num=config.min_dynamic_patch, max_num=config.max_dynamic_patch, image_size=config.force_image_size, use_thumbnail=config.use_thumbnail) else: tiles = [img] images += tiles num_tiles.append(len(tiles)) pixel_values = torch.stack([img_transform(im) for im in images]) num_patches = pixel_values.size(0) num_image_tokens = [num_image_token * n for n in num_tiles] conversation = [ {"from": "human", "value": f"{''*num_image}{prompt}"}, {"from": "gpt", "value": ""}, ] ret = preprocess_internvl2_5( "internvl2_5", [conversation], tokenizer, num_image_tokens, num_image=num_image, group_by_length=True, ) position_ids = ret["attention_mask"].long().cumsum(-1) - 1 position_ids.masked_fill_(ret["attention_mask"] == 0, 1) image_end_token_id = tokenizer.convert_tokens_to_ids(IMG_END_TOKEN) assert (ret["input_ids"][0] == image_end_token_id).sum() == num_image, "image tokens truncated" return dict( input_ids=ret["input_ids"][0], attention_mask=ret["attention_mask"][0], position_ids=position_ids[0], pixel_values=pixel_values, image_flags=torch.tensor([1] * num_patches, dtype=torch.long), # state needs an extra dim — model's state_adaptor wants (B, 1, state_dim) # and predict_action unsqueezes once, so sample state should be (1, state_dim). state=torch.from_numpy(state.astype(np.float32)).unsqueeze(0), ctrl_freqs=torch.from_numpy(np.array([ctrl_freq], dtype=np.float32)), ) # 3 random color images (cam_head + cam_hand_left + cam_hand_right) images = [Image.fromarray((np.random.rand(480, 640, 3) * 255).astype(np.uint8)) for _ in range(3)] state = np.zeros(22, dtype=np.float32) prompt = "What action should the robot take to pick up the black block and place into bin?" sample = make_sample(prompt, images, state, ctrl_freq=30.0) print(f" pixel_values : {tuple(sample['pixel_values'].shape)} {sample['pixel_values'].dtype}") print(f" input_ids : {tuple(sample['input_ids'].shape)}") print(f" image_flags : {tuple(sample['image_flags'].shape)} sum={int(sample['image_flags'].sum())}") print(f" state : {tuple(sample['state'].shape)}") print(f" ctrl_freqs : {tuple(sample['ctrl_freqs'].shape)}") # ── Stage 3: forward pass on GPU ────────────────────────────────────────── print("\n[3/3] Forward pass (model.forward) — measuring inference latency") device = "cuda" def to_dev(s): """Mirror predict_action's tensor placement + unsqueeze.""" return dict( pixel_values=s["pixel_values"].to(torch.bfloat16).to(device), input_ids=s["input_ids"].to(device).unsqueeze(0), attention_mask=s["attention_mask"].to(device).unsqueeze(0), position_ids=s["position_ids"].to(device).unsqueeze(0), image_flags=s["image_flags"].to(device), state=s["state"].to(torch.bfloat16).to(device).unsqueeze(0), ctrl_freqs=s["ctrl_freqs"].to(torch.bfloat16).to(device).unsqueeze(0), ) inputs = to_dev(sample) # warm-up (first call always slow — cuBLAS/cuDNN heuristics + JIT compile) print(" warmup (first call always slow due to autotune)...") t0 = time.perf_counter() try: with torch.no_grad(): out = model(**inputs) torch.cuda.synchronize() print(f" warmup done in {time.perf_counter()-t0:.1f}s") except Exception as e: print(f" ✗ FAILED: {type(e).__name__}: {e}") traceback.print_exc() raise SystemExit(1) # 3 timed runs timings = [] for i in range(3): t0 = time.perf_counter() with torch.no_grad(): out = model(**inputs) torch.cuda.synchronize() timings.append(time.perf_counter() - t0) print(f" steady-state inference: median={np.median(timings):.3f}s " f"min={min(timings):.3f}s max={max(timings):.3f}s") # Output shape + sanity if isinstance(out, torch.Tensor): print(f" output: tensor shape={tuple(out.shape)}") elif isinstance(out, (tuple, list)): print(f" output: {type(out).__name__} of len {len(out)}") for i, x in enumerate(out): if isinstance(x, torch.Tensor): print(f" [{i}] tensor shape={tuple(x.shape)} dtype={x.dtype}") elif isinstance(x, (tuple, list)): print(f" [{i}] {type(x).__name__} of len {len(x)}") for j, y in enumerate(x): if isinstance(y, torch.Tensor): print(f" [{j}] tensor shape={tuple(y.shape)} " f"dtype={y.dtype} min={y.float().min().item():.3f} max={y.float().max().item():.3f}") elif isinstance(out, dict): for k, v in out.items(): if isinstance(v, torch.Tensor): print(f" {k}: shape={tuple(v.shape)} dtype={v.dtype}") # Final GPU memory state free_final = torch.cuda.mem_get_info()[0] gpu_used = (free_before - free_final) / 1e9 print(f"\n final GPU mem occupied: {gpu_used:.2f} GB") print() print("=" * 60) print("✓ GO-1 INFERENCE WORKS ON THOR") print(f" median latency: {np.median(timings)*1000:.0f} ms") print(f" → action chunk = {config.action_chunk_size} → can re-plan at " f"{1.0/np.median(timings):.1f} Hz (chunk lasts {config.action_chunk_size/30.0:.1f}s @ 30Hz)") print("=" * 60)