Spaces:
Running on Zero
Running on Zero
| #!/usr/bin/env python3 | |
| """ | |
| HF Space 一次性环境搭建:下载权重 + 生成 BEV 地图缓存 | |
| 运行一次后文件持久保存在挂载存储上,之后不再需要重复运行。 | |
| 用法: | |
| python scripts/setup_space.py # 全部下载+生成 | |
| python scripts/setup_space.py --check # 只检查状态 | |
| """ | |
| import argparse | |
| import os | |
| import subprocess | |
| import sys | |
| import time | |
| from pathlib import Path | |
| ROOT = Path(__file__).resolve().parent.parent | |
| # ---- 下载清单 ---- | |
| DOWNLOAD_TASKS = [ | |
| { | |
| "name": "t5", | |
| "repo": "google/t5-v1_1-xxl", | |
| "local": ROOT / "pretrained" / "t5-v1_1-xxl", | |
| "desc": "T5-XXL 文本编码器 (~40 GB)", | |
| }, | |
| { | |
| "name": "ckpt", | |
| "repo": "flymin/MagicDriveDiT-stage3-40k-ft", | |
| "local": ROOT / "ckpts" / "MagicDriveDiT-stage3-40k-ft", | |
| "desc": "Stage-3 DiT 扩散模型 (~8 GB)", | |
| }, | |
| { | |
| "name": "vae", | |
| "repo": "THUDM/CogVideoX-2b", | |
| "local": ROOT / "pretrained" / "CogVideoX-2b", | |
| "allow_patterns": ["vae/**"], | |
| "desc": "CogVideoX-2b VAE 编码/解码器 (~2 GB)", | |
| }, | |
| { | |
| "name": "data_pkl", | |
| "repo": "flymin/MagicDriveDiT-nuScenes-metadata", | |
| "repo_type": "dataset", | |
| "local": ROOT / "data" / "nuscenes_mmdet3d-12Hz", | |
| "allow_patterns": [ | |
| "nuscenes_mmdet3d-12Hz/nuscenes_interp_12Hz_infos_val_with_bid.pkl", | |
| ], | |
| "desc": "nuScenes 标注 PKL (~430 MB)", | |
| }, | |
| { | |
| "name": "ghost_peek", | |
| "repo": "doradream/magicdrive-ghost-peek", | |
| "repo_type": "dataset", | |
| "local": ROOT / "data" / "nuscenes_mmdet3d-12Hz", | |
| "allow_patterns": [ | |
| "ghost_peek_s8.pkl", | |
| "ghost_peek_s25.pkl", | |
| ], | |
| "desc": "鬼探头场景 PKL (~660 MB, 需先上传到 HF Dataset)", | |
| }, | |
| { | |
| "name": "nuscenes_maps", | |
| "repo": "doradream/magicdrive-data", | |
| "repo_type": "dataset", | |
| "local": ROOT / "data" / "nuscenes" / "maps", | |
| "allow_patterns": ["expansion/*.json"], | |
| "desc": "nuScenes 官方地图文件 (~10 MB)", | |
| }, | |
| { | |
| "name": "map_json", | |
| "repo": "flymin/MagicDriveDiT-nuScenes-metadata", | |
| "repo_type": "dataset", | |
| "local": ROOT / "data" / "nuscenes" / "interp_12Hz_trainval", | |
| "allow_patterns": ["nuscenes/interp_12Hz_trainval/*.json"], | |
| "desc": "nuScenes 地图 JSON (~50 MB)", | |
| }, | |
| ] | |
| # ---- 地图缓存生成配置 ---- | |
| MAP_CACHE_DIR = ROOT / "data" / "nuscenes_mmdet3d-12Hz" / "nuscenes_map_aux_12Hz" | |
| MAP_CACHE_CONFIG = ROOT / "configs" / "cache_gen" / "map_cache_gen_interp.yaml" | |
| def check_path(path: Path) -> tuple[bool, str]: | |
| """检查目录/文件是否存在且非空""" | |
| if not path.exists(): | |
| return False, "不存在" | |
| files = list(path.rglob("*")) if path.is_dir() else [path] | |
| real_files = [f for f in files if f.is_file()] | |
| if not real_files: | |
| return False, "空目录" | |
| size_gb = sum(f.stat().st_size for f in real_files) / 1e9 | |
| return True, f"{len(real_files)} 个文件, {size_gb:.1f} GB" | |
| def download_from_hf(task: dict): | |
| """从 HuggingFace 下载单个任务""" | |
| from huggingface_hub import snapshot_download | |
| kwargs = dict( | |
| repo_id=task["repo"], | |
| local_dir=str(task["local"]), | |
| repo_type=task.get("repo_type", "model"), | |
| resume_download=True, | |
| max_workers=4, | |
| ) | |
| if "allow_patterns" in task: | |
| kwargs["allow_patterns"] = task["allow_patterns"] | |
| print(f" 下载中... ({task['desc']})") | |
| t0 = time.time() | |
| snapshot_download(**kwargs) | |
| elapsed = time.time() - t0 | |
| print(f" 完成 ({elapsed / 60:.1f} min)") | |
| def generate_map_cache(): | |
| """调用 prepare_map_aux.py 生成 BEV 地图 h5 缓存""" | |
| print("\n--- 生成 BEV 地图缓存 ---") | |
| # 安装 hydra(如果还没装) | |
| try: | |
| import hydra # noqa | |
| except ImportError: | |
| subprocess.check_call([sys.executable, "-m", "pip", "install", "hydra-core==1.3.0"]) | |
| # 生成 train 缓存 | |
| for split in ["val"]: # 推理只需要 val | |
| h5_file = ROOT / f"{split}_map_cache.h5" | |
| if h5_file.exists(): | |
| print(f" {h5_file} 已存在,跳过") | |
| continue | |
| print(f" 生成 {split} 地图缓存...") | |
| result = subprocess.run( | |
| [ | |
| sys.executable, "-u", | |
| str(ROOT / "tools" / "prepare_data" / "prepare_map_aux.py"), | |
| f"+process={split}", | |
| f"subfix=map_cache", | |
| ], | |
| cwd=str(ROOT), | |
| capture_output=False, | |
| env={**os.environ, "PYTHONPATH": str(ROOT)}, | |
| ) | |
| if result.returncode != 0: | |
| print(f" [WARN] {split} 缓存生成失败 (exit {result.returncode}),将回退到实时生成") | |
| else: | |
| # 移动到正确位置 | |
| MAP_CACHE_DIR.mkdir(parents=True, exist_ok=True) | |
| h5_file.rename(MAP_CACHE_DIR / h5_file.name) | |
| print(f" [OK] 已保存到 {MAP_CACHE_DIR / h5_file.name}") | |
| def main(): | |
| parser = argparse.ArgumentParser(description="HF Space 一次性环境搭建") | |
| parser.add_argument("--check", action="store_true", help="只检查状态,不执行") | |
| parser.add_argument("--skip-download", action="store_true", help="跳过下载") | |
| parser.add_argument("--skip-cache", action="store_true", help="跳过地图缓存生成") | |
| args = parser.parse_args() | |
| os.chdir(str(ROOT)) | |
| sys.path.insert(0, str(ROOT)) | |
| print("=" * 60) | |
| print("MagicDrive-V2 HF Space 环境检查") | |
| print(f"Root: {ROOT}") | |
| print("=" * 60) | |
| # ---- 检查状态 ---- | |
| all_ok = True | |
| print("\n【权重/数据文件】") | |
| for task in DOWNLOAD_TASKS: | |
| ok, msg = check_path(task["local"]) | |
| status = "[OK]" if ok else "[MISS]" | |
| if not ok: | |
| all_ok = False | |
| print(f" {status} {task['name']:12s} {msg}") | |
| print("\n【BEV 地图缓存】") | |
| cache_ok, cache_msg = check_path(MAP_CACHE_DIR) | |
| if not cache_ok: | |
| all_ok = False | |
| print(f" {'[OK]' if cache_ok else '[MISS]'} map_cache {cache_msg}") | |
| if args.check: | |
| if all_ok: | |
| print("\n所有文件就绪,可以直接推理!") | |
| else: | |
| print("\n部分文件缺失,运行: python scripts/setup_space.py") | |
| return | |
| # ---- 下载 ---- | |
| if not args.skip_download: | |
| print("\n" + "=" * 60) | |
| print("下载权重与数据") | |
| print("=" * 60) | |
| for task in DOWNLOAD_TASKS: | |
| ok, msg = check_path(task["local"]) | |
| if ok: | |
| print(f"\n[{task['name']}] 已存在 -> 跳过") | |
| continue | |
| print(f"\n[{task['name']}] {task['desc']}") | |
| try: | |
| download_from_hf(task) | |
| except Exception as e: | |
| print(f" [ERR] 下载失败: {e}") | |
| print(f" 你可以稍后重试: python scripts/setup_space.py") | |
| # ---- 生成地图缓存 ---- | |
| if not args.skip_cache: | |
| cache_ok, _ = check_path(MAP_CACHE_DIR) | |
| if cache_ok: | |
| print("\n[BEV 地图缓存] 已存在 -> 跳过") | |
| else: | |
| generate_map_cache() | |
| # ---- 最终状态 ---- | |
| print("\n" + "=" * 60) | |
| print("最终状态") | |
| print("=" * 60) | |
| all_ok = True | |
| for task in DOWNLOAD_TASKS: | |
| ok, msg = check_path(task["local"]) | |
| if not ok: | |
| all_ok = False | |
| print(f" {'[OK]' if ok else '[MISS]'} {task['name']}") | |
| cache_ok, _ = check_path(MAP_CACHE_DIR) | |
| if not cache_ok: | |
| all_ok = False | |
| print(f" {'[OK]' if cache_ok else '[MISS]'} map_cache") | |
| if all_ok: | |
| print("\n全部就绪!可以运行推理了。") | |
| else: | |
| print("\n有缺失项,请重新运行或检查网络。") | |
| if __name__ == "__main__": | |
| main() | |