dwehr's picture
Migrate action viewer to local Cosmos generation
9f818c5
Raw
History Blame Contribute Delete
7.63 kB
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: OpenMDW-1.1
"""Convert DCP checkpoint to Hugging Face model."""
from cosmos_framework.inference.common.init import init_script
init_script(
env={
"COSMOS_DEVICE": "cpu",
"COSMOS_TRAINING": "1",
}
)
import json
from pathlib import Path
from typing import Annotated, Any, Callable
import attrs
import safetensors.torch
import torch.distributed.checkpoint as dcp
import tyro
from torch.distributed.checkpoint.filesystem import FileSystemReader
from torch.distributed.checkpoint.state_dict import StateDictOptions, get_model_state_dict
from cosmos_framework.checkpoint.dcp import CustomLoadPlanner
from cosmos_framework.checkpoint.s3_filesystem import S3StorageReader
from cosmos_framework.configs.base.defaults.model_config import OmniMoTModelConfig
from cosmos_framework.inference.common.args import (
CheckpointOverrides,
ParallelismOverrides,
ResolvedPath,
tyro_cli,
)
from cosmos_framework.inference.common.checkpoints import register_checkpoints
from cosmos_framework.inference.common.config import serialize_config_dict
from cosmos_framework.inference.common.init import is_rank0
from cosmos_framework.inference.common.public_model_config import build_public_model_config
from cosmos_framework.inference.model import Cosmos3OmniConfig, Cosmos3OmniModel
from cosmos_framework.model.vfm.omni_mot_model import OmniMoTModel
from cosmos_framework.utils import log
from cosmos_framework.utils.checkpoint_db import CheckpointConfig, sanitize_uri
from cosmos_framework.utils.lazy_config.registry import convert_target_to_string
_INTERNAL_VISUAL_PREFIX = "model.net.language_model.visual."
_EXPORTED_VISUAL_PREFIX = "model.visual."
def _coerce_to_base_model(model_dict: dict[str, Any]) -> None:
"""For distillation training configs, rewrite the target to the base
OmniMoTModel so the exported checkpoint only contains the student network."""
target = model_dict.get("_target_", "")
if "OmniMoTModel" in target:
return
log.info(f"Overriding model target from {target} to OmniMoTModel for export")
model_dict["_target_"] = convert_target_to_string(OmniMoTModel)
config = model_dict["config"]
base_field_names = {f.name for f in attrs.fields(OmniMoTModelConfig)}
extra_keys = [k for k in config if k not in base_field_names and not k.startswith("_")]
for k in extra_keys:
del config[k]
metadata = config.get("_metadata", {})
metadata["object_type"] = convert_target_to_string(OmniMoTModelConfig)
config["_metadata"] = metadata
class Args(ParallelismOverrides):
checkpoint: CheckpointOverrides = CheckpointOverrides.model_construct()
output_dir: Annotated[ResolvedPath, tyro.conf.arg(aliases=("-o",))]
"""Output model directory."""
config_only: bool = False
"""If True, only export config."""
vit: bool = True
"""If True, export ViT weights."""
def _load_safetensor_weights(model_dir: Path, predicate: Callable[[str], bool]) -> dict:
"""Load weights from a safetensors file."""
index_path = model_dir / "model.safetensors.index.json"
if index_path.exists():
with open(index_path) as f:
weight_map = json.load(f)["weight_map"]
shards = {v for k, v in weight_map.items() if predicate(k)}
vision_weights = {}
for shard in shards:
tensors = safetensors.torch.load_file(model_dir / shard)
vision_weights.update({k: v for k, v in tensors.items() if predicate(k)})
else:
tensors = safetensors.torch.load_file(model_dir / "model.safetensors")
vision_weights = {k: v for k, v in tensors.items() if predicate(k)}
return vision_weights
def _rewrite_visual_fqns_for_vfm(state_dict: dict[str, Any]) -> dict[str, Any]:
"""Map HF visual tower FQNs to OmniMoTModel's internal visual tower FQNs."""
remapped_state_dict = {}
for key, value in state_dict.items():
if key.startswith(_EXPORTED_VISUAL_PREFIX):
key = _INTERNAL_VISUAL_PREFIX + key[len(_EXPORTED_VISUAL_PREFIX) :]
remapped_state_dict[key] = value
return remapped_state_dict
def export_model(args: Args):
register_checkpoints()
checkpoint_args = args.checkpoint.build_checkpoint(checkpoints={})
args.output_dir.mkdir(parents=True, exist_ok=True)
# Load config
log.info("Loading config...")
model_dict = checkpoint_args.load_model_config_dict()
if not model_dict["config"]["ema"]["enabled"]:
checkpoint_args.use_ema_weights = False
model_dict["config"]["ema"]["enabled"] = False
# Download VLM checkpoint
if args.vit:
vlm_checkpoint_path = model_dict["config"]["vlm_config"]["pretrained_weights"]["backbone_path"]
vlm_checkpoint_path = sanitize_uri(vlm_checkpoint_path)
checkpoint: CheckpointConfig | None = CheckpointConfig.maybe_from_uri(vlm_checkpoint_path)
if checkpoint is None:
raise ValueError(f"Invalid checkpoint path: {vlm_checkpoint_path}")
vlm_checkpoint_path = checkpoint.hf.download()
else:
vlm_checkpoint_path = None
# Load model
log.info("Loading model...")
_coerce_to_base_model(model_dict)
hf_config = Cosmos3OmniConfig(model=build_public_model_config(model_dict))
hf_config.save_pretrained(args.output_dir)
hf_model = Cosmos3OmniModel(hf_config)
# Save model
log.info("Saving model...")
if not args.config_only:
# Load checkpoint
if checkpoint_args.checkpoint_path.startswith("s3://"):
storage_reader = S3StorageReader(
credential_path=checkpoint_args.credential_path,
path=checkpoint_args.checkpoint_path,
)
else:
storage_reader = FileSystemReader(checkpoint_args.checkpoint_path)
state_dict = get_model_state_dict(hf_model.model)
dcp.load(
state_dict=state_dict,
storage_reader=storage_reader,
planner=CustomLoadPlanner(
load_ema_to_reg=checkpoint_args.use_ema_weights,
),
)
state_dict = get_model_state_dict(
hf_model,
options=StateDictOptions(
full_state_dict=True,
cpu_offload=True,
),
)
if not is_rank0():
return
# Load ViT from VLM checkpoint
if args.vit:
assert vlm_checkpoint_path is not None
vit_state_dict = _load_safetensor_weights(
Path(vlm_checkpoint_path), lambda x: x.startswith("model.visual.")
)
assert vit_state_dict, "No vision weights found"
state_dict.update(_rewrite_visual_fqns_for_vfm(vit_state_dict))
# Save checkpoint
hf_model.save_pretrained(
args.output_dir,
state_dict=state_dict,
)
# Re-write 'config.json' to apply replacements.
hf_config_file = args.output_dir / "config.json"
hf_config_json = json.loads(hf_config_file.read_text())
hf_config_json["model_type"] = "cosmos3_omni"
serialize_config_dict(hf_config_json, hf_config_file)
# Write 'checkpoint.json' last to indicate that the model is complete.
serialize_config_dict(checkpoint_args.model_dump(mode="json"), args.output_dir / "checkpoint.json")
print(f"Saved model to {args.output_dir}")
def main():
args = tyro_cli(Args, description=__doc__, config=(tyro.conf.OmitArgPrefixes,))
export_model(args)
if __name__ == "__main__":
main()