RAM / model.py
TimWalter's picture
Update model.py
7838c93 verified
Raw
History Blame Contribute Delete
3.63 kB
import json
import re
from pathlib import Path
import torch
import torch.nn as nn
from torch import Tensor
from jaxtyping import Float
from huggingface_hub import PyTorchModelHubMixin
class Model(nn.Module, PyTorchModelHubMixin, repo_url="https://huggingface.co/TimWalter/RAM/new/main", paper_url="https://huggingface.co/papers/2606.09108",license="mit",):
"""
RAM model that predicts reachability from a modified Denavit-Hartenberg morphology parametrisation and a pose.
"""
@classmethod
def from_id(cls, model_id: int):
"""
Instantiate a model from its wandb ID.
Args:
model_id: Wandb ID of the model.
Returns:
model: Instantiated model.
"""
model_dir = Path(__file__).parent.parent / "trained_models"
pattern = rf"{model_id}-[a-z]+-[a-z]+"
folder = next((f for f in model_dir.iterdir() if re.match(pattern, f.name)), None)
metadata_path = model_dir / folder / 'metadata.json'
metadata = json.load(open(metadata_path, 'r'))
model = cls(**metadata["hyperparameter"])
model_folder = Path(str(model_dir / folder))
prime = model_folder / "model.pth"
if prime.exists():
old_state_dict =torch.load(prime)
else:
old_state_dict =torch.load(model_folder / "checkpoint.pth")
new_state_dict = {}
# 2. Map legacy state dict keys to your new flattened structure
for key, value in old_state_dict.items():
new_key = key
if key.startswith("encoder.lstm."):
new_key = key.replace("encoder.lstm.", "encoder.")
elif key.startswith("decoder.model."):
new_key = key.replace("decoder.model.", "decoder.")
new_state_dict[new_key] = value
# Load the corrected state dict into the model
model.load_state_dict(new_state_dict)
return model
def __init__(self,
dim_encoding: int = 128,
num_encoder_layers: int = 1,
drop_prob: float = 0.0,
dim_decoder: int = 1792,
num_decoder_layer: int = 8):
"""
Initialise the model.
Args:
dim_encoding: The dimension of the latent morphology encoding.
num_encoder_layers: Number of LSTM layers.
drop_prob: Dropout probability of the LSTM.
dim_decoder: Hidden dimension of the MLP.
num_decoder_layer: Number of layers of the MLP.
"""
super().__init__()
self.encoder = nn.LSTM(3, dim_encoding, num_encoder_layers, dropout=drop_prob, batch_first=True, bias=False)
self.decoder = nn.Sequential(
nn.Linear(9 + dim_encoding, dim_decoder),
nn.ReLU(),
*[nn.Sequential(nn.Linear(dim_decoder, dim_decoder), nn.ReLU())
for _ in range(num_decoder_layer)],
nn.Linear(dim_decoder, 1)
)
def forward(self, morph: Float[Tensor, "batch seq 3"], pose: Float[Tensor, "batch 9"]) -> Float[Tensor, "batch"]:
"""
Predict reachability.
Args:
morph: Morphology description.
pose: Pose as vector encoded.
Returns:
Reachability logit.
"""
latent = self.encoder(morph)[1][0][-1]
logit = self.decoder(torch.cat([pose, latent], dim=-1)).squeeze(-1)
return logit
@torch.inference_mode()
def predict(self, *args, **kwargs):
"""
forward with inference mode
"""
return self.forward(*args, **kwargs)