Feature Extraction
Transformers
Safetensors
mettle
computational-pathology
histopathology
foundation-model
scanner-robustness
custom_code
Instructions to use slideflow-labs/Mettle with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use slideflow-labs/Mettle with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("feature-extraction", model="slideflow-labs/Mettle", trust_remote_code=True)# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("slideflow-labs/Mettle", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
File size: 4,632 Bytes
0e83a2b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | """Convert a flat Mettle checkpoint into a Hugging Face safetensors artifact."""
import argparse
import hashlib
import json
import os
from collections import OrderedDict
from pathlib import Path
import torch
from safetensors.torch import save_file
EXPECTED_HEAD_KEYS = {
"head.P",
"head.Q",
"head.mix.0.bias",
"head.mix.0.weight",
"head.mix.2.bias",
"head.mix.2.weight",
"head.shift.0.bias",
"head.shift.0.weight",
"head.shift.2.bias",
"head.shift.2.weight",
}
RC2_SOURCE_SHA256 = (
"ad79736b61638a58a72c5dedd7eb21582e94c6397564ae28c1c5da0af4d8a9be"
)
def sha256(path, chunk_size=16 * 1024 * 1024):
digest = hashlib.sha256()
with open(path, "rb") as stream:
while chunk := stream.read(chunk_size):
digest.update(chunk)
return digest.hexdigest()
def convert_state_dict(source):
"""Prefix backbone tensors for :class:`MettleModel`."""
head_keys = {key for key in source if key.startswith("head.")}
if head_keys != EXPECTED_HEAD_KEYS:
missing = sorted(EXPECTED_HEAD_KEYS - head_keys)
unexpected = sorted(head_keys - EXPECTED_HEAD_KEYS)
raise ValueError(
"checkpoint refinement-head keys do not match the release contract; "
f"missing={missing}, unexpected={unexpected}"
)
converted = OrderedDict()
for key in sorted(source):
target = key if key.startswith("head.") else f"backbone.{key}"
converted[target] = source[key].contiguous()
return converted
def validate_shapes(state):
p_shape = tuple(state["head.P"].shape)
q_shape = tuple(state["head.Q"].shape)
if p_shape != (16, 1536, 8) or q_shape != p_shape:
raise ValueError(
"checkpoint refinement-head shape does not match RC2: "
f"P={p_shape}, Q={q_shape}"
)
if tuple(state["head.mix.0.weight"].shape) != (256, 1536):
raise ValueError("checkpoint refinement-head hidden size is not 256")
if tuple(state["head.shift.2.weight"].shape) != (1536, 256):
raise ValueError("checkpoint refinement-head output width is not 1536")
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument("checkpoint", help="flat RC2 .pt checkpoint")
parser.add_argument(
"--output-dir",
default=str(Path(__file__).resolve().parent),
help="directory containing the Hugging Face repository files",
)
parser.add_argument(
"--expected-sha256",
default=RC2_SOURCE_SHA256,
help="refuse a source checkpoint whose SHA-256 differs",
)
parser.add_argument(
"--dry-run",
action="store_true",
help="validate and map the checkpoint without writing weights",
)
return parser.parse_args()
def main():
args = parse_args()
checkpoint = Path(args.checkpoint).resolve()
output_dir = Path(args.output_dir).resolve()
source_sha = sha256(checkpoint)
if args.expected_sha256 and source_sha != args.expected_sha256.lower():
raise SystemExit(
f"source SHA-256 mismatch: expected {args.expected_sha256}, "
f"found {source_sha}"
)
source = torch.load(checkpoint, map_location="cpu", weights_only=True)
validate_shapes(source)
converted = convert_state_dict(source)
print(
f"validated {len(source)} tensors; mapped {len(converted)} tensors; "
f"source sha256={source_sha}"
)
if args.dry_run:
return
output_dir.mkdir(parents=True, exist_ok=True)
output_path = output_dir / "model.safetensors"
manifest_path = output_dir / "weights_manifest.json"
if output_path.exists():
raise SystemExit(f"refusing to overwrite {output_path}")
if manifest_path.exists():
raise SystemExit(f"refusing to overwrite {manifest_path}")
temporary = output_dir / ".model.safetensors.incomplete"
if temporary.exists():
raise SystemExit(f"refusing to overwrite incomplete file {temporary}")
save_file(converted, str(temporary), metadata={"format": "pt"})
os.replace(temporary, output_path)
packaged_sha = sha256(output_path)
manifest = {
"format": "safetensors",
"model_file": output_path.name,
"model_sha256": packaged_sha,
"source_checkpoint_sha256": source_sha,
"tensor_count": len(converted),
}
manifest_path.write_text(
json.dumps(manifest, indent=2, sort_keys=True) + "\n",
encoding="utf-8",
)
print(f"wrote {output_path} sha256={packaged_sha}")
print(f"wrote {manifest_path}")
if __name__ == "__main__":
main()
|