scFM-dataselection / tools /portable_models.py
lcrawford's picture
Copilot
Add study-trained SCimilarity checkpoints
e697bd2 verified
Raw History Blame Contribute Delete
2.99 kB
"""Load model weights and PCA components."""
import json
from pathlib import Path
def shared_resources(directory, resources_directory):
if resources_directory is not None:
return Path(resources_directory)
for parent in directory.parents:
resources = parent / "public-resources"
if resources.is_dir():
return resources
raise FileNotFoundError(
"Shared public-resources directory is missing; supply resources_directory explicitly"
)
def load_model(model_directory, adata=None, resources_directory=None):
directory = Path(model_directory).resolve()
if (directory / "encoder.ckpt").is_file():
from _native_scimilarity import load_scimilarity
return load_scimilarity(directory)
config = json.loads((directory / "config.json").read_text())
if config.get("model_type") == "bert":
from transformers import BertForMaskedLM
model, info = BertForMaskedLM.from_pretrained(
directory, local_files_only=True, output_loading_info=True,
attn_implementation="eager",
)
if any(info[key] for key in (
"missing_keys", "unexpected_keys", "mismatched_keys", "error_msgs",
)):
raise ValueError(f"Geneformer strict loading failed: {info}")
model.eval()
return model
family = config.get("family")
if family == "PCA":
import numpy as np
components = np.load(directory / "components.npy", allow_pickle=False)
if list(components.shape) != config["shape"] or str(components.dtype) != config["dtype"]:
raise ValueError("PCA components disagree with the checkpoint config")
if components.dtype.kind != "f" or not np.isfinite(components).all():
raise ValueError("PCA components are not finite numeric data")
return components
from safetensors.torch import load_file
weights = load_file(str(directory / "model.safetensors"), device="cpu")
if family == "SSL":
from _native_ssl import native_ssl_class
resources = shared_resources(directory, resources_directory)
model = native_ssl_class(resources)(**config["constructor"])
model.load_state_dict(weights, strict=True)
model.eval()
return model
if family == "scVI":
import scvi
if adata is None:
raise ValueError("scVI requires AnnData with raw counts in X and the exact gene order")
if list(adata.var_names) != config["gene_names"]:
raise ValueError("AnnData gene order does not match the checkpoint config")
scvi.model.SCVI.setup_anndata(adata, **config["setup_anndata"])
model = scvi.model.SCVI(adata, **config["constructor"])
model.module.load_state_dict(weights, strict=True)
model.module.eval()
model.is_trained = True
model.to_device("cpu")
return model
raise ValueError(f"Unsupported portable model family: {family}")