COMODO-AnyMo-Bench / modeling_comodo.py
Breezelled's picture
Add direct variant loading and simplify model cards
3f224a9 verified
Raw History Blame Contribute Delete
4.89 kB
# SPDX-License-Identifier: MIT
# Copyright (c) 2026 Baiyu Chen and the COMODO-AnyMo-Bench contributors
"""IMU-only COMODO inference, preserving the original checkpoint parameter names."""
from dataclasses import dataclass
from typing import Optional
import torch
from mantis.architecture import MantisV2
from torch import nn
from torch.nn import functional as F
from transformers import PreTrainedModel
from transformers.utils import ModelOutput
from .configuration_comodo import ComodoConfig
@dataclass
class ComodoOutput(ModelOutput):
embeddings: Optional[torch.Tensor] = None
projection: Optional[torch.Tensor] = None
class ComodoBundle(PreTrainedModel):
"""Resolve the root bundle before loading a selected, fully trained model."""
config_class = ComodoConfig
main_input_name = "imu_values"
@classmethod
def from_pretrained(cls, pretrained_model_name_or_path, *args, config=None, **kwargs):
hub_keys = ("revision", "token", "cache_dir", "local_files_only", "force_download")
hub_kwargs = {k: kwargs[k] for k in hub_keys if k in kwargs}
config = config or cls.config_class.from_pretrained(pretrained_model_name_or_path, **hub_kwargs)
if config._commit_hash is not None:
hub_kwargs["revision"] = config._commit_hash
load_kwargs = {k: v for k, v in kwargs.items()
if k not in ("variant", "_from_auto", "_from_pipeline", "adapter_kwargs")}
load_kwargs.update(hub_kwargs)
if "torch_dtype" not in load_kwargs and config.torch_dtype is not None:
load_kwargs["torch_dtype"] = config.torch_dtype
if kwargs.get("_from_pipeline", getattr(config, "_release_from_pipeline", None)) == "comodo":
# Transformers passes pipeline-specific arguments only after its
# initial AutoModel lookup. Defer weights until variant is available.
selector = cls(config)
selector.name_or_path = str(pretrained_model_name_or_path)
selector._release_load_kwargs = load_kwargs
return selector
variant = kwargs.pop("variant", None) or config.variant
if variant not in ("core50", "fine150"):
raise ValueError("variant must be 'core50' or 'fine150'")
return ComodoModel.from_pretrained(pretrained_model_name_or_path, *args,
subfolder=variant, **load_kwargs)
def forward(self, *args, **kwargs):
raise RuntimeError("Select a variant through pipeline('comodo', ..., variant='core50')")
class ComodoModel(PreTrainedModel):
config_class = ComodoConfig
main_input_name = "imu_values"
def __init__(self, config):
super().__init__(config)
# Construct the architecture only. The released checkpoint contains every
# backbone parameter, so inference never downloads a second model.
self.imu_student = MantisV2(**config.mantis_config, device="cpu")
self.mlp = nn.Sequential(
nn.Linear(config.feature_dim, config.projection_hidden_dim),
nn.BatchNorm1d(config.projection_hidden_dim),
nn.GELU(),
nn.Linear(config.projection_hidden_dim, config.projection_dim),
)
self.post_init()
@classmethod
def from_pretrained(cls, pretrained_model_name_or_path, *args, **kwargs):
subfolder = kwargs.get("subfolder", "")
hub_kwargs = {k: kwargs[k] for k in
("revision", "token", "cache_dir", "local_files_only") if k in kwargs}
model = super().from_pretrained(pretrained_model_name_or_path, *args, **kwargs)
target = model[0] if isinstance(model, tuple) else model
target._release_subfolder = subfolder
target._release_hub_kwargs = hub_kwargs
return model
def forward(self, imu_values, normalize_embeddings=True, return_projection=False,
return_dict=True):
if imu_values.ndim != 3 or imu_values.shape[1] != self.config.n_channels:
raise ValueError(f"Expected [B, {self.config.n_channels}, T] IMU values")
if imu_values.shape[-1] != self.config.sequence_length:
imu_values = F.interpolate(imu_values, size=self.config.sequence_length,
mode="linear", align_corners=False)
features = torch.cat([
self.imu_student(imu_values[:, ch:ch + 1, :])
for ch in range(self.config.n_channels)
], dim=1)
projection = self.mlp(features) if return_projection else None
embeddings = F.normalize(features, p=2, dim=1) if normalize_embeddings else features
if not return_dict:
return (embeddings, projection) if return_projection else (embeddings,)
return ComodoOutput(embeddings=embeddings, projection=projection)
ComodoModel.register_for_auto_class("AutoModel")